Skip to content

Commit

Permalink
New component-based architecture (#5)
Browse files Browse the repository at this point in the history
* new architecture

* example fix

* 3D renderer adjustments: removed possibility to accidentally use different cameras in one entity

* bugfix

* updated documentation

* added forgotten files; gitignore

* migrated mixin; typing improvements

* renamed wrongly named component; updated readme
  • Loading branch information
AndyGura authored Oct 27, 2023
1 parent d460675 commit c064241
Show file tree
Hide file tree
Showing 474 changed files with 40,533 additions and 16,978 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ cmake-build-debug/
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml

.venv

## File-based project format:
*.iws

Expand Down
91 changes: 65 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,22 @@ Note: at this early step, the project does not give much flexibility in that reg
1) write bootstrap script, example:
```typescript
import { interval } from 'rxjs';
import { Gg3dEntity, Gg3dWorld, Qtrn, Pnt3 } from '@gg-web-engine/core';
import { Gg3dVisualScene, GgRenderer } from '@gg-web-engine/three';
import { Gg3dPhysicsWorld } from '@gg-web-engine/ammo';
import { Gg3dWorld, Pnt3, Qtrn } from '@gg-web-engine/core';
import { ThreeCameraComponent, ThreeSceneComponent } from '@gg-web-engine/three';
import { AmmoWorldComponent } from '@gg-web-engine/ammo';
import { PerspectiveCamera } from 'three';

// create world
const world: Gg3dWorld = new Gg3dWorld(new Gg3dVisualScene(), new Gg3dPhysicsWorld());
const world = new Gg3dWorld(new ThreeSceneComponent(), new AmmoWorldComponent());
await world.init();

// create viewport and renderer
const renderer: GgRenderer = new GgRenderer(document.getElementById('gg')! as HTMLCanvasElement);
renderer.camera.position = { x: 15, y: 15, z: 9 };
renderer.camera.rotation = Qtrn.lookAt(renderer.camera.position, Pnt3.O);
world.addEntity(renderer);
const renderer = world.addRenderer(
new ThreeCameraComponent(new PerspectiveCamera()),
document.getElementById('gg')! as HTMLCanvasElement,
);
renderer.position = { x: 12, y: 12, z: 12 };
renderer.rotation = Qtrn.lookAt(renderer.camera.position, Pnt3.O);

// create floor (static rigid body)
world.addPrimitiveRigidBody({
Expand All @@ -107,7 +110,7 @@ world.addPrimitiveRigidBody({
// spawn cubes with mass 1kg twice a second
interval(500).subscribe(() => {
// generate cube
let item: Gg3dEntity = world.addPrimitiveRigidBody({
let item = world.addPrimitiveRigidBody({
shape: { shape: 'BOX', dimensions: { x: 1, y: 1, z: 1 } },
body: { mass: 1 },
});
Expand Down Expand Up @@ -138,7 +141,17 @@ Try to add to your html <head>:
- #### Fly city. [Live demo](https://gg-web-demos.guraklgames.com/index.html), [Source code](https://github.com/AndyGura/gg-web-engine/blob/main/examples/fly-city/src/app/app.component.ts)

## Architecture
Technical documentation available at [GitHub Pages](https://andygura.github.io/gg-web-engine/)
The most important thing in the engine is [GgWorld](https://andygura.github.io/gg-web-engine/modules/core/base/gg-world.ts/).
It encapsulates everything you need in your game runtime: ticker clock, visual scene, physics simulation world etc.
One browser tab can run one or many GG worlds. In order to add something to the world, caller code needs to add
[Entities](https://andygura.github.io/gg-web-engine/modules/core/base/entities/i-entity.ts/). Entity is anything which
works in the world: tick-based controller, rigid body, renderer etc. World and entity are self-sufficient in gg core
package, so they do not depend on selected integration library. Entity can use from 0 to many
[World Components](https://andygura.github.io/gg-web-engine/modules/core/base/components/i-world-component.ts/#i-world-component-overview) -
those are fully dependent on integration library, so libraries in general case only implement components.

Full technical documentation available at [GitHub Pages](https://andygura.github.io/gg-web-engine/)

### Clock
Clock is an entity, responsible for tracking time and firing ticks. It measures time and on each tick emits two numbers:
`elapsedTime` and `delta`, where `delta` always equals to difference between current elapsed time, and elapsed time,
Expand Down Expand Up @@ -166,33 +179,59 @@ flowchart LR
World is a container of all entities of your game, manages the entire flow. Though it is possible to have multiple
worlds in one page, in most cases you only need one. World consists of:
- clock
- visual scene, e.g. "sub-world", containing everything related to rendering. This is an interface, which has to be
implemented by integration library
- physics world, e.g. "sub-world", containing everything related to physics simulation. This is an interface, which has to be
implemented by integration library
- visual scene, e.g. "sub-world", containing everything related to rendering. This is a [component](https://andygura.github.io/gg-web-engine/modules/core/base/components/rendering/i-visual-scene.component.ts/),
which has to be implemented by integration library
- physics world, e.g. "sub-world", containing everything related to physics simulation. This is a [component](https://andygura.github.io/gg-web-engine/modules/core/base/components/physics/i-physics-world.component.ts/#iphysicsworldcomponent-interface),
which has to be implemented by integration library
- list of all spawned world entities, sorted by tick order, and API for spawning/removing them
- logic to propagate clock ticks to every spawned active entity
- keyboard input

There are two built-in variants of world implementation: **[Gg2dWorld](https://andygura.github.io/gg-web-engine/modules/core/2d/gg-2d-world.ts/)** and **[Gg3dWorld](https://andygura.github.io/gg-web-engine/modules/core/3d/gg-3d-world.ts/)**

### [Entity](https://andygura.github.io/gg-web-engine/modules/core/base/entities/gg-entity.ts/)
Example of hierarchy of entities of simple scene, which uses three.js + ammo.js:
```mermaid
flowchart TB
w{"[CORE]\n3D World"} --> cn0["[CORE]\nsome controller"]
w --> rb0["[CORE]\nrigid body entity"]
w --> rb1["[CORE]\n3d model"]
w --> rb2["[CORE]\ntrigger entity"]
rb0 --> c0("[THREE]\nmesh component")
rb0 --> c1("[AMMO]\nphysics body component")
rb1 --> c2("[THREE]\nmesh component")
rb2 --> c3("[AMMO]\nphysics body component")
```

### [Component](https://andygura.github.io/gg-web-engine/modules/core/base/components/i-component.ts/#icomponent-interface)
Anything, which has to be implemented in integration library:
- **[IVisualScene2dComponent](https://andygura.github.io/gg-web-engine/modules/core/2d/components/rendering/i-visual-scene-2d.component.ts/#ivisualscene2dcomponent-interface) / [IVisualScene3dComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/rendering/i-visual-scene-3d.component.ts/#ivisualscene3dcomponent-interface)** a wrapper around visual scene or display object container
- **[IDisplayObject2dComponent](https://andygura.github.io/gg-web-engine/modules/core/2d/components/rendering/i-display-object-2d.component.ts/#idisplayobject2dcomponent-interface) / [IDisplayObject3dComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/rendering/i-display-object-3d.component.ts/#idisplayobject3dcomponent-interface)** a wrapper around mesh or sprite
- **[IRenderer2dComponent](https://andygura.github.io/gg-web-engine/modules/core/2d/components/rendering/i-renderer-2d.component.ts/#irenderer2dcomponent-class) / [IRenderer3dComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/rendering/i-renderer-3d.component.ts/#irenderer3dcomponent-class)** a wrapper around renderer
- **[IPhysicsWorld2dComponent](https://andygura.github.io/gg-web-engine/modules/core/2d/components/physics/i-physics-world-2d.component.ts/#iphysicsworld2dcomponent-interface) / [IPhysicsWorld3dComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/physics/i-physics-world-3d.component.ts/#iphysicsworld3dcomponent-interface)** a wrapper around physics world
- **[IRigidBody2dComponent](https://andygura.github.io/gg-web-engine/modules/core/2d/components/physics/i-rigid-body-2d.component.ts/#irigidbody2dcomponent-interface) / [IRigidBody3dComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/physics/i-rigid-body-3d.component.ts/#irigidbody3dcomponent-interface)** a wrapper around rigid body
- **[ITrigger2dComponent](https://andygura.github.io/gg-web-engine/modules/core/2d/components/physics/i-trigger-2d.component.ts/#itrigger2dcomponent-interface) / [ITrigger3dComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/physics/i-trigger-3d.component.ts/#itrigger3dcomponent-interface)** a wrapper around physics object, which only detects intersections with other physics objects
- **[ICameraComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/rendering/i-camera.component.ts/#icameracomponent-interface)** a wrapper around camera (3D world)
- **[IRaycastVehicleComponent](https://andygura.github.io/gg-web-engine/modules/core/3d/components/physics/i-raycast-vehicle.component.ts/#iraycastvehiclecomponent-interface)** raycast vehicle (3D world)

For instance, `@gg-web-engine/three` implements 4 components: `IVisualScene3d`, `IDisplayObject3d`, `IRenderer3d`, `ICamera`.

### [Entity](https://andygura.github.io/gg-web-engine/modules/core/base/entities/i-entity.ts/)
Basically, everything that listens ticks and can be added/removed from world. Built-in entities:
- **[Gg2dEntity](https://andygura.github.io/gg-web-engine/modules/core/2d/entities/gg-2d-entity.ts/)**/**[Gg3dEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/gg-3d-entity.ts/)** encapsulates display object (sprite or mesh respectively) and physics body.
- **[Entity2d](https://andygura.github.io/gg-web-engine/modules/core/2d/entities/entity-2d.ts/#entity2d-class)** / **[Entity3d](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/entity-3d.ts/#entity3d-class)** encapsulates display object (sprite or mesh respectively) and rigid body.
Synchronizes position/rotation each tick
- **[Gg2dTriggerEntity](https://andygura.github.io/gg-web-engine/modules/core/2d/entities/gg-2d-trigger.entity.ts/)**/**[Gg3dTriggerEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/gg-3d-trigger.entity.ts/)** has only physics body, but instead of participating in collisions, emits
- **[Trigger2dEntity](https://andygura.github.io/gg-web-engine/modules/core/2d/entities/trigger-2d.entity.ts/#trigger2dentity-class)** / **[Trigger3dEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/trigger-3d.entity.ts/#trigger3dentity-class)** has only physics body, but instead of participating in collisions, emits
events when some another positionable entity entered/left its area
- **[InlineTickController](https://andygura.github.io/gg-web-engine/modules/core/base/entities/inline-controller.ts/)** simple controller, which can be created and added to world using one line of code
- **[Renderer](https://andygura.github.io/gg-web-engine/modules/core/base/entities/base-gg-renderer.ts/)** controller, which renders the scene and controls canvas size (if canvas provided). Makes canvas appearing fullscreen by default
- **[AnimationMixer](https://andygura.github.io/gg-web-engine/modules/core/base/entities/controllers/animation-mixer.ts/)** controller, which mixes animations: use-case is if you have some animation function, and you need a
- **[InlineTickController](https://andygura.github.io/gg-web-engine/modules/core/base/entities/controllers/inline-controller.ts/#createinlinetickcontroller)** simple controller, which can be created and added to world using one line of code
- **[Renderer](https://andygura.github.io/gg-web-engine/modules/core/base/entities/i-renderer.entity.ts/#irendererentity-class)** controller, which renders the scene and controls canvas size (if canvas provided). Makes canvas appearing fullscreen by default
- **[AnimationMixer](https://andygura.github.io/gg-web-engine/modules/core/base/entities/controllers/animation-mixer.ts/#animationmixer-class)** controller, which mixes animations: use-case is if you have some animation function, and you need a
smooth transition to another animation function
- **[Entity2dPositioningAnimator](https://andygura.github.io/gg-web-engine/modules/core/2d/entities/controllers/entity-2d-positioning.animator.ts/)**/**[Entity3dPositioningAnimator](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/animators/entity-3d-positioning.animator.ts/)** controllers extending **AnimationMixer**, which apply
- **[Entity2dPositioningAnimator](https://andygura.github.io/gg-web-engine/modules/core/2d/entities/controllers/entity-2d-positioning.animator.ts/#entity2dpositioninganimator-class)** / **[Entity3dPositioningAnimator](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/animators/entity-3d-positioning.animator.ts/#entity3dpositioninganimator-class)** controllers extending **AnimationMixer**, which apply
position/rotation to positionable entity
- **[Camera3dAnimator](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/animators/camera-3d.animator.ts/)** dedicated **AnimationMixer** for perspective camera: translates camera, target, up, fov etc.
- **[FreeCameraController](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/input/free-camera.controller.ts/)** a controller, allows to control camera with WASD + mouse
- **[CarKeyboardHandlingController](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/input/car-keyboard-handling.controller.ts/)** a controller allowing to control car with keyboard
- **[Gg3dMapGraphEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/gg-3d-map-graph.entity.ts/)** an entity, which loads parts of big map and disposes loaded map chunks, which are far away
- **[Gg3dRaycastVehicleEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/gg-3d-raycast-vehicle.entity.ts/)** a car
- **[Camera3dAnimator](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/animators/camera-3d.animator.ts/#camera3danimator-class)** dedicated **AnimationMixer** for perspective camera: translates camera, target, up, fov etc.
- **[FreeCameraController](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/input/free-camera.controller.ts/#freecameracontroller-class)** a controller, allows to control camera with WASD + mouse
- **[CarKeyboardHandlingController](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/controllers/input/car-keyboard-handling.controller.ts/#carkeyboardhandlingcontroller-class)** a controller allowing to control car with keyboard
- **[MapGraph3dEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/map-graph-3d.entity.ts/#mapgraph3dentity-class)** an entity, which loads parts of big map and disposes loaded map chunks, which are far away
- **[RaycastVehicle3dEntity](https://andygura.github.io/gg-web-engine/modules/core/3d/entities/raycast-vehicle-3d.entity.ts/#raycastvehicle3dentity-class)** a car

### [Input](https://andygura.github.io/gg-web-engine/modules/core/base/inputs/input.ts/)
Input is a class, responsible for handling external actions, such as mouse move, key presses, gamepad interactions etc.
Expand Down
5 changes: 4 additions & 1 deletion documentation/docs/modules/ammo/ammo-debugger.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ parent: Modules

```ts
export declare class AmmoDebugger {
constructor(protected readonly world: Gg3dPhysicsWorld, private readonly drawer: GgDebugPhysicsDrawer<Point3, Point4>)
constructor(
protected readonly world: AmmoWorldComponent,
private readonly drawer: IDebugPhysicsDrawer<Point3, Point4>
)
}
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
---
title: ammo/impl/gg-3d-body-factory.ts
nav_order: 6
title: ammo/ammo-factory.ts
nav_order: 2
parent: Modules
---

## gg-3d-body-factory overview
## ammo-factory overview

---

<h2 class="text-delta">Table of contents</h2>

- [utils](#utils)
- [Gg3dBodyFactory (class)](#gg3dbodyfactory-class)
- [AmmoFactory (class)](#ammofactory-class)
- [createRigidBody (method)](#createrigidbody-method)
- [createTrigger (method)](#createtrigger-method)
- [createShape (method)](#createshape-method)
Expand All @@ -22,13 +22,13 @@ parent: Modules

# utils

## Gg3dBodyFactory (class)
## AmmoFactory (class)

**Signature**

```ts
export declare class Gg3dBodyFactory {
constructor(protected readonly world: Gg3dPhysicsWorld)
export declare class AmmoFactory {
constructor(protected readonly world: AmmoWorldComponent)
}
```

Expand All @@ -43,7 +43,7 @@ createRigidBody(
position?: Point3;
rotation?: Point4;
},
): Gg3dBody
): AmmoRigidBodyComponent
```

### createTrigger (method)
Expand All @@ -57,7 +57,7 @@ createTrigger(
position?: Point3;
rotation?: Point4;
},
): Gg3dTrigger
): AmmoTriggerComponent
```

### createShape (method)
Expand All @@ -77,7 +77,7 @@ public createRigidBodyFromShape(
shape: Ammo.btCollisionShape,
options: Partial<Body3DOptions>,
transform?: { position?: Point3; rotation?: Point4 },
): Gg3dBody
): AmmoRigidBodyComponent
```

### createTriggerFromShape (method)
Expand All @@ -88,5 +88,5 @@ public createRigidBodyFromShape(
public createTriggerFromShape(
shape: Ammo.btCollisionShape,
transform?: { position?: Point3; rotation?: Point4 },
): Gg3dTrigger
): AmmoTriggerComponent
```
28 changes: 28 additions & 0 deletions documentation/docs/modules/ammo/ammo-loader.ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: ammo/ammo-loader.ts
nav_order: 3
parent: Modules
---

## ammo-loader overview

---

<h2 class="text-delta">Table of contents</h2>

- [utils](#utils)
- [AmmoLoader (class)](#ammoloader-class)

---

# utils

## AmmoLoader (class)

**Signature**

```ts
export declare class AmmoLoader {
constructor(protected readonly world: AmmoWorldComponent)
}
```
2 changes: 1 addition & 1 deletion documentation/docs/modules/ammo/ammo-utils.ts.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: ammo/ammo-utils.ts
nav_order: 2
nav_order: 4
parent: Modules
---

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
---
title: ammo/impl/bodies/base-ammo-gg-body.ts
nav_order: 3
title: ammo/components/ammo-body.component.ts
nav_order: 5
parent: Modules
---

## base-ammo-gg-body overview
## ammo-body.component overview

---

<h2 class="text-delta">Table of contents</h2>

- [utils](#utils)
- [BaseAmmoGGBody (class)](#baseammoggbody-class)
- [AmmoBodyComponent (class)](#ammobodycomponent-class)
- [clone (method)](#clone-method)
- [addToWorld (method)](#addtoworld-method)
- [removeFromWorld (method)](#removefromworld-method)
Expand All @@ -24,13 +24,13 @@ parent: Modules

# utils

## BaseAmmoGGBody (class)
## AmmoBodyComponent (class)

**Signature**

```ts
export declare class BaseAmmoGGBody<T> {
protected constructor(protected readonly world: Gg3dPhysicsWorld, protected _nativeBody: T)
export declare class AmmoBodyComponent<T> {
protected constructor(protected readonly world: AmmoWorldComponent, protected _nativeBody: T)
}
```

Expand All @@ -39,23 +39,23 @@ export declare class BaseAmmoGGBody<T> {
**Signature**

```ts
abstract clone(): BaseAmmoGGBody<T>;
abstract clone(): AmmoBodyComponent<T>;
```

### addToWorld (method)

**Signature**

```ts
abstract addToWorld(world: Gg3dPhysicsWorld): void;
abstract addToWorld(world: Gg3dWorld<IVisualScene3dComponent, AmmoWorldComponent>): void;
```

### removeFromWorld (method)

**Signature**

```ts
abstract removeFromWorld(world: Gg3dPhysicsWorld): void;
abstract removeFromWorld(world: Gg3dWorld<IVisualScene3dComponent, AmmoWorldComponent>): void;
```

### dispose (method)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
---
title: ammo/impl/gg-3d-raycast-vehicle.ts
nav_order: 9
title: ammo/components/ammo-raycast-vehicle.component.ts
nav_order: 6
parent: Modules
---

## gg-3d-raycast-vehicle overview
## ammo-raycast-vehicle.component overview

---

<h2 class="text-delta">Table of contents</h2>

- [utils](#utils)
- [Gg3dRaycastVehicle (class)](#gg3draycastvehicle-class)
- [AmmoRaycastVehicleComponent (class)](#ammoraycastvehiclecomponent-class)
- [addToWorld (method)](#addtoworld-method)
- [addWheel (method)](#addwheel-method)
- [setSteering (method)](#setsteering-method)
Expand All @@ -30,13 +30,13 @@ parent: Modules

# utils

## Gg3dRaycastVehicle (class)
## AmmoRaycastVehicleComponent (class)

**Signature**

```ts
export declare class Gg3dRaycastVehicle {
constructor(protected readonly world: Gg3dPhysicsWorld, public chassisBody: Ammo.btRigidBody)
export declare class AmmoRaycastVehicleComponent {
constructor(protected readonly world: AmmoWorldComponent, public chassisBody: Ammo.btRigidBody)
}
```

Expand All @@ -45,7 +45,7 @@ export declare class Gg3dRaycastVehicle {
**Signature**

```ts
addToWorld(world: Gg3dPhysicsWorld)
addToWorld(world: Gg3dWorld<IVisualScene3dComponent, AmmoWorldComponent>)
```

### addWheel (method)
Expand Down
Loading

0 comments on commit c064241

Please sign in to comment.