Skip to content

Commit

Permalink
camera controllers improvements; bugfix; (#19)
Browse files Browse the repository at this point in the history
* added shooter example

* upgraded three.js

* fixed putting random color on generated primitive with provided texture

* example code adjustments

* Revert "upgraded three.js"

This reverts commit 451dedd.

* input controllers adjustments

* added collision margin to engine and ammo factory

* minor improvements

* minor improvements

* elastic free camera movement

* elastic free camera movement

* finalized elastic free camera movement

* added elasticity to orbit camera controller and free camera rotation

* code alignment

* minor adjustments
  • Loading branch information
AndyGura authored Nov 14, 2024
1 parent 1eb391f commit ff03e10
Show file tree
Hide file tree
Showing 26 changed files with 5,222 additions and 101 deletions.
Binary file added examples/assets/shooter/brick.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/assets/shooter/cement.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/build_examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ examples=(
"collision-groups-three-rapier3d"
"collision-groups-pool-three-ammo"
"collision-groups-pool-three-rapier3d"
"shooter-three-ammo"
)
build_example() {
pushd ./$1
Expand Down
2 changes: 2 additions & 0 deletions examples/collision-groups-pool-three-ammo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const world = new Gg3dWorld<
new ThreeSceneComponent(),
new AmmoWorldComponent(),
);
world.physicsWorld.maxSubSteps = 25;

world.init().then(async () => {
GgStatic.instance.showStats = true;
// GgStatic.instance.devConsoleEnabled = true;
Expand Down
1 change: 1 addition & 0 deletions examples/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ examples=(
"collision-groups-three-rapier3d"
"collision-groups-pool-three-ammo"
"collision-groups-pool-three-rapier3d"
"shooter-three-ammo"
)
for ix in ${!examples[*]}
do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ export class GameCameraController {
renderer,
{
keymap: 'wasd+arrows',
movementOptions: {
speed: 1,
},
cameraLinearSpeed: 40,
cameraMovementElasticity: 100,
cameraRotationElasticity: 50,
ignoreKeyboardUnlessPointerLocked: true,
ignoreMouseUnlessPointerLocked: true,
mouseOptions: {
Expand Down
5 changes: 5 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@
visualModule: 'three',
physicsModule: 'ammo',
},
{
label: 'Shooter',
visualModule: 'three',
physicsModule: 'ammo',
},
{
label: 'Fly city',
visualModule: 'three',
Expand Down
88 changes: 88 additions & 0 deletions examples/shooter-three-ammo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<title>GG example</title>
<meta name='viewport' content='width=device-width,initial-scale=1'>
</head>
<body>
<style>
body {
color: #000;
font-family: Monospace;
text-align: center;
background-color: #bfd1e5;
margin: 0px;
overflow: hidden;
font-size: 150%
}

#message {
position: absolute;
width: 100%;
top: 0;
padding-top: 1em;
color: #777;
font-size: 80%
}

#blocker {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.65);
padding-top: 10em;
color: #FFF;
text-shadow: 1px 1px 20px #000;
pointer-events: none;
}

[hidden] {
display: none
}

div.info {
color: #BBB;
font-size: 80%
}

a {
color: #b7bd6f;
cursor: pointer;
pointer-events: all;
}
#container {
position: absolute;
}
</style>
<div id='container'>
<canvas id='gg'></canvas>
</div>
<div id='message' hidden>
Mouse and W, A, S, D = move<br />
CLICK = shoot balls<br />
Press ESC to pause
</div>
<div id='blocker'>
<span style='font-size:250%'>Click to play</span>
<br /><br />
Mouse and W, A, S, D = move<br />
CLICK = shoot balls<br />
Press ESC to pause
<br /><br /><br /><br />
<div class='info'>
<a href='http://static.componenthouse.com/webgl-shooter/main.html' target='_new'>Original demo</a>
<br /><br />
Author of the original demo: <a href='https://www.linkedin.com/in/hugovteixeira' target='_new'>Hugo Teixeira</a>
| <a href='https://componenthouse.com/2016/11/08/having-fun-with-typescript-threejs-and-ammo-js/' target='_new'>Read
Full Article</a>
| <a href='https://github.com/hvidal/WebGL-Shooter' target='_new'>Source Code</a>
<br /><br />
Ported to <a href='https://github.com/AndyGura/gg-web-engine' target='_new'>GG web engine</a>
by <a href='https://github.com/AndyGura' target='_new'>Andy Gura</a>
| <a href='https://github.com/AndyGura/gg-web-engine/tree/main/examples/shooter-three-ammo' target='_new'>Source
Code</a>
</div>
</div>
</body>
</html>
164 changes: 164 additions & 0 deletions examples/shooter-three-ammo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { FreeCameraController, Gg3dWorld, GgStatic, Pnt3, Qtrn } from '@gg-web-engine/core';
import { ThreeSceneComponent, ThreeVisualTypeDocRepo } from '@gg-web-engine/three';
import { AmbientLight, DirectionalLight, Mesh, MeshPhongMaterial, RepeatWrapping, TextureLoader } from 'three';
import { AmmoPhysicsTypeDocRepo, AmmoWorldComponent } from '@gg-web-engine/ammo';

const world = new Gg3dWorld<
ThreeVisualTypeDocRepo,
AmmoPhysicsTypeDocRepo,
ThreeSceneComponent,
AmmoWorldComponent
>(
new ThreeSceneComponent(),
new AmmoWorldComponent(),
);
world.physicsWorld.maxSubSteps = 10;
world.physicsWorld.fixedTimeStep = null;

GgStatic.instance.showStats = true;
// GgStatic.instance.devConsoleEnabled = true;

world.init().then(async () => {
// init graphics
const canvas = document.getElementById('gg')! as HTMLCanvasElement;
const renderer = world.addRenderer(
world.visualScene.factory.createPerspectiveCamera({ fov: 45, frustrum: { near: 0.2, far: 1000 } }),
canvas,
{ background: 0xBFD1E5 },
);
renderer.camera.position = { x: 40, y: 40, z: 25 };
renderer.camera.rotation = Qtrn.lookAt(renderer.camera.position, { x: 0, y: 0, z: 10 }, Pnt3.Z);

const dirLight = new DirectionalLight(0xffffff, 2.5);
dirLight.castShadow = true;
dirLight.position.set(50, 50, 100);
const d = 100;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
dirLight.shadow.camera.near = 2;
dirLight.shadow.camera.far = 500;
dirLight.shadow.mapSize.x = 4096;
dirLight.shadow.mapSize.y = 4096;
world.visualScene.nativeScene.add(dirLight);

const ambientLight = new AmbientLight(0x606060);
world.visualScene.nativeScene.add(ambientLight);

const textureLoader = new TextureLoader();
// create objects
const [groundTexture, brickTexture] = await Promise.all([
textureLoader.loadAsync('https://gg-web-demos.guraklgames.com/assets/shooter/cement.jpg'),
textureLoader.loadAsync('https://gg-web-demos.guraklgames.com/assets/shooter/brick.jpg'),
]);
groundTexture.wrapS = brickTexture.wrapS = RepeatWrapping;
groundTexture.wrapT = brickTexture.wrapT = RepeatWrapping;
groundTexture.repeat.set(5, 5);

let material = [
new MeshPhongMaterial({ color: 0xB7B7B7, map: brickTexture }),
new MeshPhongMaterial({ color: 0xAAAAAA, map: brickTexture }),
new MeshPhongMaterial({ color: 0xA4A4A4, map: brickTexture }),
new MeshPhongMaterial({ color: 0x979797, map: brickTexture }),
new MeshPhongMaterial({ color: 0x949494, map: brickTexture }),
new MeshPhongMaterial({ color: 0x909090, map: brickTexture }),
];
const brickMass = 20;

const createWall_X_axis = (startX: number, endX: number, y: number, zCount: number, shift: boolean) => {
for (let z = 0; z <= zCount; z += 1.5) {
let offsetX = shift ? 1.5 : 0;
shift = !shift;
for (let x = startX; x <= endX; x += 3) {
const item = world.addPrimitiveRigidBody({
shape: { shape: 'BOX', dimensions: { x: 3, y: 1.5, z: 1.5 }, collisionMargin: 0.05 },
body: { dynamic: true, mass: brickMass },
}, { x: x + offsetX, y, z: z + 0.75 }, Qtrn.O, { castShadow: true, receiveShadow: true });
const materialIndex = Math.floor(Math.random() * material.length);
(item.object3D.nativeMesh as Mesh).material = material[materialIndex];
}
}
};

const createWall_Y_axis = (startY: number, endY: number, x: number, zCount: number, shift: boolean) => {
const quat = Qtrn.fromAngle(Pnt3.Z, Math.PI / 2);
for (let z = 0; z <= zCount; z += 1.5) {
let offsetY = shift ? 1.5 : 0;
shift = !shift;
for (let y = startY; y <= endY; y += 3) {
const item = world.addPrimitiveRigidBody({
shape: { shape: 'BOX', dimensions: { x: 3, y: 1.5, z: 1.5 }, collisionMargin: 0.05 },
body: { dynamic: true, mass: brickMass },
}, { x, y: y + offsetY, z: z + 0.75 }, Qtrn.O, { castShadow: true, receiveShadow: true });
item.rotation = quat;
const materialIndex = Math.floor(Math.random() * material.length);
(item.object3D.nativeMesh as Mesh).material = material[materialIndex];
}
}
};
createWall_X_axis(-8.25, 8.25, -9.75, 15, true);
createWall_X_axis(-8.25, 8.25, 11.25, 15, false);
createWall_Y_axis(-9, 9, -9, 15, false);
createWall_Y_axis(-9, 9, 9, 15, true);

world.addPrimitiveRigidBody(
{
shape: { shape: 'BOX', dimensions: { x: 100, y: 100, z: 1 }, collisionMargin: 0.05 },
body: { dynamic: false, mass: 0 },
},
{ x: 0, y: 0, z: -0.5 },
Qtrn.O,
{ shading: 'phong', castShadow: true, receiveShadow: true, diffuse: groundTexture },
);

const cameraController = new FreeCameraController(
world.keyboardInput,
renderer,
{
keymap: 'wasd',
mouseOptions: { canvas, pointerLock: true },
cameraLinearSpeed: 50,
cameraMovementElasticity: 100,
cameraRotationSensitivity: 0.8,
ignoreMouseUnlessPointerLocked: true,
ignoreKeyboardUnlessPointerLocked: true,
});
world.addEntity(cameraController);


let ballMaterial = new MeshPhongMaterial({ color: 0x202020 });

window.addEventListener('mousedown', (event) => {
let element = <Element> event.target;
if (element.nodeName == 'A' || world.isPaused)
return;
else {
let ball = world.addPrimitiveRigidBody(
{
body: { mass: 10 }, shape: { shape: 'SPHERE', radius: 1.2, collisionMargin: 0.05 },
},
renderer.position,
Qtrn.O,
{ castShadow: true, receiveShadow: true },
);
(ball.object3D.nativeMesh as Mesh).material = ballMaterial;

ball.objectBody.linearVelocity = Pnt3.rot(Pnt3.scalarMult(Pnt3.nZ, 80), renderer.rotation);
}
}, false);

world.start();

cameraController.mouseInput.isPointerLocked$.subscribe((l) => {
if (l) {
world.resumeWorld();
document.getElementById('blocker').style.display = 'none';
document.getElementById('message').style.display = 'block';
} else {
document.getElementById('blocker').style.display = 'block';
document.getElementById('message').style.display = 'none';
world.pauseWorld();
}
});
});
Loading

0 comments on commit ff03e10

Please sign in to comment.