@gg-web-engine/ammo 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/ammo-debugger.d.ts +38 -0
- package/dist/ammo-debugger.js +80 -0
- package/dist/ammo-utils.d.ts +2 -0
- package/dist/ammo-utils.js +5 -0
- package/dist/impl/bodies/base-ammo-gg-body.d.ts +24 -0
- package/dist/impl/bodies/base-ammo-gg-body.js +59 -0
- package/dist/impl/bodies/gg-3d-body.d.ts +14 -0
- package/dist/impl/bodies/gg-3d-body.js +43 -0
- package/dist/impl/bodies/gg-3d-trigger.d.ts +22 -0
- package/dist/impl/bodies/gg-3d-trigger.js +71 -0
- package/dist/impl/gg-3d-body-factory.d.ts +26 -0
- package/dist/impl/gg-3d-body-factory.js +111 -0
- package/dist/impl/gg-3d-body-loader.d.ts +6 -0
- package/dist/impl/gg-3d-body-loader.js +11 -0
- package/dist/impl/gg-3d-physics-world.d.ts +34 -0
- package/dist/impl/gg-3d-physics-world.js +154 -0
- package/dist/impl/gg-3d-raycast-vehicle.d.ts +26 -0
- package/dist/impl/gg-3d-raycast-vehicle.js +70 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/package.json +41 -0
- package/src/ammo-debugger.ts +104 -0
- package/src/ammo-utils.ts +3 -0
- package/src/impl/bodies/base-ammo-gg-body.ts +76 -0
- package/src/impl/bodies/gg-3d-body.ts +48 -0
- package/src/impl/bodies/gg-3d-trigger.ts +82 -0
- package/src/impl/gg-3d-body-factory.ts +164 -0
- package/src/impl/gg-3d-body-loader.ts +8 -0
- package/src/impl/gg-3d-physics-world.ts +141 -0
- package/src/impl/gg-3d-raycast-vehicle.ts +97 -0
- package/src/index.ts +6 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.Gg3dPhysicsWorld = void 0;
|
|
36
|
+
const AmmoModule = __importStar(require("ammojs-typed"));
|
|
37
|
+
const gg_3d_body_factory_1 = require("./gg-3d-body-factory");
|
|
38
|
+
const gg_3d_body_loader_1 = require("./gg-3d-body-loader");
|
|
39
|
+
const ammo_debugger_1 = require("../ammo-debugger");
|
|
40
|
+
class Gg3dPhysicsWorld {
|
|
41
|
+
constructor() {
|
|
42
|
+
this._factory = null;
|
|
43
|
+
this._loader = null;
|
|
44
|
+
this._gravity = { x: 0, y: 0, z: -9.82 };
|
|
45
|
+
this._timeScale = 1;
|
|
46
|
+
this._debugger = null;
|
|
47
|
+
this._debugDrawer = null;
|
|
48
|
+
}
|
|
49
|
+
get factory() {
|
|
50
|
+
if (!this._factory) {
|
|
51
|
+
throw new Error('Ammo world not initialized');
|
|
52
|
+
}
|
|
53
|
+
return this._factory;
|
|
54
|
+
}
|
|
55
|
+
get loader() {
|
|
56
|
+
if (!this._loader) {
|
|
57
|
+
throw new Error('Ammo world not initialized');
|
|
58
|
+
}
|
|
59
|
+
return this._loader;
|
|
60
|
+
}
|
|
61
|
+
get gravity() {
|
|
62
|
+
return this._gravity;
|
|
63
|
+
}
|
|
64
|
+
set gravity(value) {
|
|
65
|
+
var _a;
|
|
66
|
+
this._gravity = value;
|
|
67
|
+
if (this.gravityVector) {
|
|
68
|
+
this.gravityVector.setValue(value.x, value.y, value.z);
|
|
69
|
+
(_a = this._dynamicAmmoWorld) === null || _a === void 0 ? void 0 : _a.setGravity(this.gravityVector);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
get timeScale() {
|
|
73
|
+
return this._timeScale;
|
|
74
|
+
}
|
|
75
|
+
set timeScale(value) {
|
|
76
|
+
this._timeScale = value;
|
|
77
|
+
}
|
|
78
|
+
get physicsDebugViewActive() {
|
|
79
|
+
return !!this._debugger;
|
|
80
|
+
}
|
|
81
|
+
get ammo() {
|
|
82
|
+
if (this.ammoInstance) {
|
|
83
|
+
return this.ammoInstance;
|
|
84
|
+
}
|
|
85
|
+
throw 'Physics world not initialized!';
|
|
86
|
+
}
|
|
87
|
+
get dynamicAmmoWorld() {
|
|
88
|
+
return this._dynamicAmmoWorld;
|
|
89
|
+
}
|
|
90
|
+
init() {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
this.ammoInstance = yield AmmoModule.default.bind(AmmoModule)();
|
|
93
|
+
this.collisionConfiguration = new this.ammo.btDefaultCollisionConfiguration();
|
|
94
|
+
this.dispatcher = new this.ammo.btCollisionDispatcher(this.collisionConfiguration);
|
|
95
|
+
this.broadphase = new this.ammo.btDbvtBroadphase();
|
|
96
|
+
this.ghostPairCallback = new this.ammo.btGhostPairCallback();
|
|
97
|
+
this.solver = new this.ammo.btSequentialImpulseConstraintSolver();
|
|
98
|
+
this.gravityVector = new this.ammo.btVector3(this._gravity.x, this._gravity.y, this._gravity.z);
|
|
99
|
+
this._dynamicAmmoWorld = new this.ammo.btDiscreteDynamicsWorld(this.dispatcher, this.broadphase, this.solver, this.collisionConfiguration);
|
|
100
|
+
this._dynamicAmmoWorld.getPairCache().setInternalGhostPairCallback(this.ghostPairCallback);
|
|
101
|
+
this._dynamicAmmoWorld.setGravity(this.gravityVector);
|
|
102
|
+
this._factory = new gg_3d_body_factory_1.Gg3dBodyFactory(this);
|
|
103
|
+
this._loader = new gg_3d_body_loader_1.Gg3dBodyLoader(this);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
simulate(delta) {
|
|
107
|
+
var _a;
|
|
108
|
+
(_a = this._dynamicAmmoWorld) === null || _a === void 0 ? void 0 : _a.stepSimulation((this._timeScale * delta) / 1000, 100, this._timeScale * 0.01);
|
|
109
|
+
if (this._debugger) {
|
|
110
|
+
this._debugger.update();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
startDebugger(world, drawer) {
|
|
114
|
+
var _a;
|
|
115
|
+
if (!this._debugger) {
|
|
116
|
+
this._debugger = new ammo_debugger_1.AmmoDebugger(this, drawer);
|
|
117
|
+
(_a = this.dynamicAmmoWorld) === null || _a === void 0 ? void 0 : _a.setDebugDrawer(this._debugger.ammoInstance);
|
|
118
|
+
}
|
|
119
|
+
this._debugger.setDebugMode(ammo_debugger_1.AmmoDebugMode.DrawWireframe);
|
|
120
|
+
this._debugDrawer = drawer;
|
|
121
|
+
this._debugDrawer.addToWorld(world.visualScene);
|
|
122
|
+
}
|
|
123
|
+
stopDebugger(world) {
|
|
124
|
+
var _a;
|
|
125
|
+
if (this._debugger) {
|
|
126
|
+
(_a = this.dynamicAmmoWorld) === null || _a === void 0 ? void 0 : _a.setDebugDrawer(null);
|
|
127
|
+
this.ammo.destroy(this._debugger.ammoInstance);
|
|
128
|
+
this._debugger = null;
|
|
129
|
+
}
|
|
130
|
+
if (this._debugDrawer) {
|
|
131
|
+
this._debugDrawer.removeFromWorld(world.visualScene);
|
|
132
|
+
this._debugDrawer.dispose();
|
|
133
|
+
this._debugDrawer = null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
dispose() {
|
|
137
|
+
this.ammo.destroy(this._dynamicAmmoWorld);
|
|
138
|
+
this.ammo.destroy(this.solver);
|
|
139
|
+
this.ammo.destroy(this.broadphase);
|
|
140
|
+
this.ammo.destroy(this.dispatcher);
|
|
141
|
+
this.ammo.destroy(this.collisionConfiguration);
|
|
142
|
+
if (this._debugger) {
|
|
143
|
+
this.ammo.destroy(this._debugger.ammoInstance);
|
|
144
|
+
}
|
|
145
|
+
this._dynamicAmmoWorld =
|
|
146
|
+
this.solver =
|
|
147
|
+
this.broadphase =
|
|
148
|
+
this.dispatcher =
|
|
149
|
+
this.collisionConfiguration =
|
|
150
|
+
this.ammoInstance =
|
|
151
|
+
undefined;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.Gg3dPhysicsWorld = Gg3dPhysicsWorld;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { IGg3dRaycastVehicle, WheelOptions, SuspensionOptions, Point3, Point4, Gg3dRaycastVehicleEntity } from '@gg-web-engine/core';
|
|
2
|
+
import Ammo from 'ammojs-typed';
|
|
3
|
+
import { Gg3dBody } from './bodies/gg-3d-body';
|
|
4
|
+
import { Gg3dPhysicsWorld } from './gg-3d-physics-world';
|
|
5
|
+
export declare class Gg3dRaycastVehicle extends Gg3dBody implements IGg3dRaycastVehicle {
|
|
6
|
+
protected readonly world: Gg3dPhysicsWorld;
|
|
7
|
+
chassisBody: Ammo.btRigidBody;
|
|
8
|
+
readonly nativeVehicle: Ammo.btRaycastVehicle;
|
|
9
|
+
readonly vehicleTuning: Ammo.btVehicleTuning;
|
|
10
|
+
protected readonly wheelDirectionCS0: Ammo.btVector3;
|
|
11
|
+
protected readonly wheelAxleCS: Ammo.btVector3;
|
|
12
|
+
entity: Gg3dRaycastVehicleEntity | null;
|
|
13
|
+
constructor(world: Gg3dPhysicsWorld, chassisBody: Ammo.btRigidBody);
|
|
14
|
+
get wheelSpeed(): number;
|
|
15
|
+
addToWorld(world: Gg3dPhysicsWorld): void;
|
|
16
|
+
addWheel(options: WheelOptions, suspensionOptions: SuspensionOptions): void;
|
|
17
|
+
setSteering(wheelIndex: number, steering: number): void;
|
|
18
|
+
applyEngineForce(wheelIndex: number, force: number): void;
|
|
19
|
+
applyBrake(wheelIndex: number, force: number): void;
|
|
20
|
+
isWheelTouchesGround(wheelIndex: number): boolean;
|
|
21
|
+
getWheelTransform(wheelIndex: number): {
|
|
22
|
+
position: Point3;
|
|
23
|
+
rotation: Point4;
|
|
24
|
+
};
|
|
25
|
+
resetSuspension(): void;
|
|
26
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Gg3dRaycastVehicle = void 0;
|
|
4
|
+
const gg_3d_body_1 = require("./bodies/gg-3d-body");
|
|
5
|
+
class Gg3dRaycastVehicle extends gg_3d_body_1.Gg3dBody {
|
|
6
|
+
constructor(world, chassisBody) {
|
|
7
|
+
super(world, chassisBody);
|
|
8
|
+
this.world = world;
|
|
9
|
+
this.chassisBody = chassisBody;
|
|
10
|
+
this.vehicleTuning = new this.ammo.btVehicleTuning();
|
|
11
|
+
this.wheelDirectionCS0 = new this.ammo.btVector3(0, 0, -1);
|
|
12
|
+
this.wheelAxleCS = new this.ammo.btVector3(1, 0, 0);
|
|
13
|
+
this.entity = null;
|
|
14
|
+
this.nativeVehicle = new this.ammo.btRaycastVehicle(this.vehicleTuning, this.chassisBody, new this.ammo.btDefaultVehicleRaycaster(world.dynamicAmmoWorld));
|
|
15
|
+
this.nativeVehicle.setCoordinateSystem(0, 2, 1);
|
|
16
|
+
}
|
|
17
|
+
get wheelSpeed() {
|
|
18
|
+
// FIXME not correct (shows chassis speed in world)
|
|
19
|
+
return this.nativeVehicle.getCurrentSpeedKmHour() / 3.6;
|
|
20
|
+
}
|
|
21
|
+
addToWorld(world) {
|
|
22
|
+
var _a;
|
|
23
|
+
if (world != this.world) {
|
|
24
|
+
throw new Error('Ammo raycast vehicle cannot be shared between different worlds');
|
|
25
|
+
}
|
|
26
|
+
// TODO parked cars can be deactivated until we start handling them. Needs explicit activation call
|
|
27
|
+
this.chassisBody.setActivationState(4); // btCollisionObject::DISABLE_DEACTIVATION
|
|
28
|
+
(_a = world.dynamicAmmoWorld) === null || _a === void 0 ? void 0 : _a.addRigidBody(this.chassisBody);
|
|
29
|
+
this.world.dynamicAmmoWorld.addAction(this.nativeVehicle);
|
|
30
|
+
}
|
|
31
|
+
addWheel(options, suspensionOptions) {
|
|
32
|
+
const wheelInfo = this.nativeVehicle.addWheel(new this.ammo.btVector3(options.position.x, options.position.y, options.position.z * 2), this.wheelDirectionCS0, this.wheelAxleCS, suspensionOptions.restLength, options.tyre_radius, this.vehicleTuning, options.isFront);
|
|
33
|
+
wheelInfo.set_m_suspensionStiffness(suspensionOptions.stiffness);
|
|
34
|
+
wheelInfo.set_m_wheelsDampingRelaxation(suspensionOptions.damping);
|
|
35
|
+
wheelInfo.set_m_wheelsDampingCompression(suspensionOptions.compression);
|
|
36
|
+
wheelInfo.set_m_frictionSlip(options.frictionSlip);
|
|
37
|
+
wheelInfo.set_m_rollInfluence(options.rollInfluence);
|
|
38
|
+
wheelInfo.set_m_maxSuspensionTravelCm(options.maxTravel * 100);
|
|
39
|
+
}
|
|
40
|
+
setSteering(wheelIndex, steering) {
|
|
41
|
+
this.nativeVehicle.setSteeringValue(steering, wheelIndex);
|
|
42
|
+
}
|
|
43
|
+
applyEngineForce(wheelIndex, force) {
|
|
44
|
+
this.nativeVehicle.applyEngineForce(force, wheelIndex);
|
|
45
|
+
}
|
|
46
|
+
applyBrake(wheelIndex, force) {
|
|
47
|
+
this.nativeVehicle.setBrake(force, wheelIndex);
|
|
48
|
+
}
|
|
49
|
+
isWheelTouchesGround(wheelIndex) {
|
|
50
|
+
return this.nativeVehicle.getWheelInfo(wheelIndex).get_m_raycastInfo().get_m_groundObject() > 0;
|
|
51
|
+
}
|
|
52
|
+
getWheelTransform(wheelIndex) {
|
|
53
|
+
// FIXME when set to `true`, wheels are jittering, but it was not the case in old sandbox app. Check why
|
|
54
|
+
this.nativeVehicle.updateWheelTransform(wheelIndex, false);
|
|
55
|
+
const transform = this.nativeVehicle.getWheelTransformWS(wheelIndex);
|
|
56
|
+
const origin = transform.getOrigin();
|
|
57
|
+
const quaternion = transform.getRotation();
|
|
58
|
+
return {
|
|
59
|
+
position: { x: origin.x(), y: origin.y(), z: origin.z() },
|
|
60
|
+
rotation: { x: quaternion.x(), y: quaternion.y(), z: quaternion.z(), w: quaternion.w() },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
resetSuspension() {
|
|
64
|
+
this.nativeVehicle.resetSuspension();
|
|
65
|
+
for (let i = 0; i < this.nativeVehicle.getNumWheels(); i++) {
|
|
66
|
+
this.nativeVehicle.updateWheelTransform(i, true);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.Gg3dRaycastVehicle = Gg3dRaycastVehicle;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './impl/bodies/gg-3d-body';
|
|
2
|
+
export * from './impl/bodies/gg-3d-trigger';
|
|
3
|
+
export * from './impl/gg-3d-body-factory';
|
|
4
|
+
export * from './impl/gg-3d-body-loader';
|
|
5
|
+
export * from './impl/gg-3d-physics-world';
|
|
6
|
+
export * from './impl/gg-3d-raycast-vehicle';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./impl/bodies/gg-3d-body"), exports);
|
|
18
|
+
__exportStar(require("./impl/bodies/gg-3d-trigger"), exports);
|
|
19
|
+
__exportStar(require("./impl/gg-3d-body-factory"), exports);
|
|
20
|
+
__exportStar(require("./impl/gg-3d-body-loader"), exports);
|
|
21
|
+
__exportStar(require("./impl/gg-3d-physics-world"), exports);
|
|
22
|
+
__exportStar(require("./impl/gg-3d-raycast-vehicle"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gg-web-engine/ammo",
|
|
3
|
+
"version": "0.0.01",
|
|
4
|
+
"description": "An attempt to create open source game engine for browser",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
|
+
"prettier-format": "prettier --config ../core/.prettierrc 'src/**/*.ts' --write",
|
|
10
|
+
"prepublish": "tsc",
|
|
11
|
+
"build": "tsc"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/AndyGura/gg-web-engine.git"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"game-development",
|
|
19
|
+
"game-engine",
|
|
20
|
+
"game-engine-framework",
|
|
21
|
+
"gamedev"
|
|
22
|
+
],
|
|
23
|
+
"author": "AndyGura",
|
|
24
|
+
"license": "Apache-2.0",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/AndyGura/gg-web-engine/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/AndyGura/gg-web-engine#readme",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@gg-web-engine/core": "^0.0.1",
|
|
31
|
+
"ammojs-typed": "^1.0.6",
|
|
32
|
+
"fs-web": "^1.0.1",
|
|
33
|
+
"mini-signals": "^1.2.0",
|
|
34
|
+
"path-browserify": "^1.0.1",
|
|
35
|
+
"rxjs": "~7.5.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"prettier": "^2.8.6",
|
|
39
|
+
"typescript": "~4.7.2"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import Ammo from 'ammojs-typed';
|
|
2
|
+
import { GgDebugPhysicsDrawer, Point3, Point4 } from '@gg-web-engine/core';
|
|
3
|
+
import { Gg3dPhysicsWorld } from './impl/gg-3d-physics-world';
|
|
4
|
+
|
|
5
|
+
export const DebugBufferSize = 3 * 1000000;
|
|
6
|
+
|
|
7
|
+
export enum AmmoDebugMode {
|
|
8
|
+
NoDebug = 0,
|
|
9
|
+
DrawWireframe = 1,
|
|
10
|
+
DrawAabb = 2,
|
|
11
|
+
DrawFeaturesText = 4,
|
|
12
|
+
DrawContactPoints = 8,
|
|
13
|
+
NoDeactivation = 16,
|
|
14
|
+
NoHelpText = 32,
|
|
15
|
+
DrawText = 64,
|
|
16
|
+
ProfileTimings = 128,
|
|
17
|
+
EnableSatComparison = 256,
|
|
18
|
+
DisableBulletLCP = 512,
|
|
19
|
+
EnableCCD = 1024,
|
|
20
|
+
DrawConstraints = 1 << 11, //2048
|
|
21
|
+
DrawConstraintLimits = 1 << 12, //4096
|
|
22
|
+
FastWireframe = 1 << 13, //8192
|
|
23
|
+
DrawNormals = 1 << 14, //16384
|
|
24
|
+
MAX_DEBUG_DRAW_MODE = 0xffffffff,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const deserializeVector = function (ammo: typeof Ammo, vec: Ammo.btVector3): Point3 {
|
|
28
|
+
if (!isNaN(+vec)) {
|
|
29
|
+
const heap = ammo.HEAPF32;
|
|
30
|
+
return {
|
|
31
|
+
x: heap[+vec / 4],
|
|
32
|
+
y: heap[(+vec + 4) / 4],
|
|
33
|
+
z: heap[(+vec + 8) / 4],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return { x: vec.x(), y: vec.y(), z: vec.z() };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class AmmoDebugger implements Ammo.btIDebugDraw {
|
|
40
|
+
private debugMode: AmmoDebugMode = AmmoDebugMode.DrawWireframe;
|
|
41
|
+
|
|
42
|
+
constructor(private readonly world: Gg3dPhysicsWorld, private readonly drawer: GgDebugPhysicsDrawer<Point3, Point4>) {
|
|
43
|
+
this.ammoInstance = new this.world.ammo.DebugDrawer();
|
|
44
|
+
this.ammoInstance.drawLine = this.drawLine.bind(this);
|
|
45
|
+
this.ammoInstance.drawContactPoint = this.drawContactPoint.bind(this);
|
|
46
|
+
this.ammoInstance.reportErrorWarning = this.reportErrorWarning.bind(this);
|
|
47
|
+
this.ammoInstance.draw3dText = this.draw3dText.bind(this);
|
|
48
|
+
this.ammoInstance.setDebugMode = this.setDebugMode.bind(this);
|
|
49
|
+
this.ammoInstance.getDebugMode = this.getDebugMode.bind(this);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public readonly ammoInstance: Ammo.btIDebugDraw;
|
|
53
|
+
|
|
54
|
+
draw3dText(location: Ammo.btVector3, textString: string): void {
|
|
55
|
+
console.log('DRAWER DEBUG', 'draw3dtext');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
drawContactPoint(
|
|
59
|
+
pointOnB: Ammo.btVector3,
|
|
60
|
+
normalOnB: Ammo.btVector3,
|
|
61
|
+
distance: number,
|
|
62
|
+
lifeTime: number,
|
|
63
|
+
color: Ammo.btVector3,
|
|
64
|
+
): void {
|
|
65
|
+
this.drawer.drawContactPoint(
|
|
66
|
+
deserializeVector(this.world.ammo, pointOnB),
|
|
67
|
+
deserializeVector(this.world.ammo, normalOnB),
|
|
68
|
+
deserializeVector(this.world.ammo, color),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
drawLine(from: Ammo.btVector3, to: Ammo.btVector3, color: Ammo.btVector3): void {
|
|
73
|
+
this.drawer.drawLine(
|
|
74
|
+
deserializeVector(this.world.ammo, from),
|
|
75
|
+
deserializeVector(this.world.ammo, to),
|
|
76
|
+
deserializeVector(this.world.ammo, color),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getDebugMode(): number {
|
|
81
|
+
return this.debugMode;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
update(): void {
|
|
85
|
+
this.world.dynamicAmmoWorld?.debugDrawWorld();
|
|
86
|
+
this.drawer.update();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setDebugFlags(flags: AmmoDebugMode[]): void {
|
|
90
|
+
let res = 0;
|
|
91
|
+
for (const flag of flags) {
|
|
92
|
+
res = res | flag;
|
|
93
|
+
}
|
|
94
|
+
this.setDebugMode(res);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setDebugMode(debugMode: number): void {
|
|
98
|
+
this.debugMode = debugMode;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
reportErrorWarning(warningString: string): void {
|
|
102
|
+
console.log('DRAWER DEBUG', 'reportErrorWarning', warningString);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { IGg3dBody, Point3, Point4, Gg3dEntity } from '@gg-web-engine/core';
|
|
2
|
+
import { Gg3dPhysicsWorld } from '../gg-3d-physics-world';
|
|
3
|
+
import Ammo from 'ammojs-typed';
|
|
4
|
+
import { ammoId } from '../../ammo-utils';
|
|
5
|
+
|
|
6
|
+
export abstract class BaseAmmoGGBody<T extends Ammo.btCollisionObject> implements IGg3dBody {
|
|
7
|
+
protected static nativeBodyReverseMap: Map<number, IGg3dBody> = new Map<number, IGg3dBody>();
|
|
8
|
+
|
|
9
|
+
protected get ammo(): typeof Ammo {
|
|
10
|
+
return this.world.ammo;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public get position(): Point3 {
|
|
14
|
+
const origin = this.nativeBody.getWorldTransform().getOrigin();
|
|
15
|
+
return { x: origin.x(), y: origin.y(), z: origin.z() };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public set position(value: Point3) {
|
|
19
|
+
const transform = this.nativeBody.getWorldTransform();
|
|
20
|
+
transform.setOrigin(new this.ammo.btVector3(value.x, value.y, value.z));
|
|
21
|
+
this.nativeBody.setWorldTransform(transform);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public get rotation(): Point4 {
|
|
25
|
+
const quaternion = this.nativeBody.getWorldTransform().getRotation();
|
|
26
|
+
return { x: quaternion.x(), y: quaternion.y(), z: quaternion.z(), w: quaternion.w() };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public set rotation(value: Point4) {
|
|
30
|
+
const transform = this.nativeBody.getWorldTransform();
|
|
31
|
+
transform.setRotation(new this.ammo.btQuaternion(value.x, value.y, value.z, value.w));
|
|
32
|
+
this.nativeBody.setWorldTransform(transform);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public get scale(): Point3 {
|
|
36
|
+
// hmm, is it even possible to be different?
|
|
37
|
+
return { x: 1, y: 1, z: 1 };
|
|
38
|
+
}
|
|
39
|
+
get nativeBody(): T {
|
|
40
|
+
return this._nativeBody;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
set nativeBody(value: T) {
|
|
44
|
+
if (value == this._nativeBody) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
BaseAmmoGGBody.nativeBodyReverseMap.delete(ammoId(this._nativeBody));
|
|
48
|
+
this._nativeBody = value;
|
|
49
|
+
BaseAmmoGGBody.nativeBodyReverseMap.set(ammoId(value), this);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
public name: string = '';
|
|
53
|
+
|
|
54
|
+
abstract entity: Gg3dEntity | null;
|
|
55
|
+
|
|
56
|
+
protected constructor(protected readonly world: Gg3dPhysicsWorld, protected _nativeBody: T) {
|
|
57
|
+
BaseAmmoGGBody.nativeBodyReverseMap.set(ammoId(this.nativeBody), this);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
abstract clone(): BaseAmmoGGBody<T>;
|
|
61
|
+
|
|
62
|
+
abstract addToWorld(world: Gg3dPhysicsWorld): void;
|
|
63
|
+
|
|
64
|
+
abstract removeFromWorld(world: Gg3dPhysicsWorld): void;
|
|
65
|
+
|
|
66
|
+
dispose(): void {
|
|
67
|
+
try {
|
|
68
|
+
this.ammo.destroy(this.nativeBody);
|
|
69
|
+
} catch {
|
|
70
|
+
// pass
|
|
71
|
+
}
|
|
72
|
+
BaseAmmoGGBody.nativeBodyReverseMap.delete(ammoId(this.nativeBody));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
abstract resetMotion(): void;
|
|
76
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Gg3dPhysicsWorld } from '../gg-3d-physics-world';
|
|
2
|
+
import Ammo from 'ammojs-typed';
|
|
3
|
+
import { BaseAmmoGGBody } from './base-ammo-gg-body';
|
|
4
|
+
import { Gg3dEntity } from '@gg-web-engine/core';
|
|
5
|
+
|
|
6
|
+
export class Gg3dBody extends BaseAmmoGGBody<Ammo.btRigidBody> {
|
|
7
|
+
public entity: Gg3dEntity | null = null;
|
|
8
|
+
|
|
9
|
+
constructor(protected readonly world: Gg3dPhysicsWorld, protected _nativeBody: Ammo.btRigidBody) {
|
|
10
|
+
super(world, _nativeBody);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
clone(): Gg3dBody {
|
|
14
|
+
return this.world.factory.createRigidBodyFromShape(
|
|
15
|
+
this._nativeBody.getCollisionShape(),
|
|
16
|
+
{
|
|
17
|
+
dynamic: !this._nativeBody.isStaticObject(),
|
|
18
|
+
mass: 5, // FIXME how to get mass??
|
|
19
|
+
friction: this._nativeBody.getFriction(),
|
|
20
|
+
restitution: this._nativeBody.getRestitution(),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
position: this.position,
|
|
24
|
+
rotation: this.rotation,
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
addToWorld(world: Gg3dPhysicsWorld): void {
|
|
30
|
+
if (world != this.world) {
|
|
31
|
+
throw new Error('Ammo bodies cannot be shared between different worlds');
|
|
32
|
+
}
|
|
33
|
+
world.dynamicAmmoWorld?.addRigidBody(this.nativeBody);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
removeFromWorld(world: Gg3dPhysicsWorld): void {
|
|
37
|
+
world.dynamicAmmoWorld?.removeRigidBody(this.nativeBody);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
resetMotion(): void {
|
|
41
|
+
const emptyVector = new this.ammo.btVector3();
|
|
42
|
+
this.nativeBody.setLinearVelocity(emptyVector);
|
|
43
|
+
this.nativeBody.setAngularVelocity(emptyVector);
|
|
44
|
+
this.nativeBody.clearForces();
|
|
45
|
+
this.nativeBody.updateInertiaTensor();
|
|
46
|
+
this.ammo.destroy(emptyVector);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Gg3dTriggerEntity, GgPositionable3dEntity, IGg3dTrigger } from '@gg-web-engine/core';
|
|
2
|
+
import { Gg3dPhysicsWorld } from '../gg-3d-physics-world';
|
|
3
|
+
import { filter, map, Observable, Subject } from 'rxjs';
|
|
4
|
+
import Ammo from 'ammojs-typed';
|
|
5
|
+
import { BaseAmmoGGBody } from './base-ammo-gg-body';
|
|
6
|
+
import { ammoId } from '../../ammo-utils';
|
|
7
|
+
|
|
8
|
+
export class Gg3dTrigger extends BaseAmmoGGBody<Ammo.btPairCachingGhostObject> implements IGg3dTrigger {
|
|
9
|
+
public entity: Gg3dTriggerEntity | null = null;
|
|
10
|
+
|
|
11
|
+
get onEntityEntered(): Observable<GgPositionable3dEntity> {
|
|
12
|
+
return this.onEnter$.pipe(
|
|
13
|
+
map(b => BaseAmmoGGBody.nativeBodyReverseMap.get(b)?.entity),
|
|
14
|
+
filter(x => !!x && x instanceof GgPositionable3dEntity),
|
|
15
|
+
) as Observable<GgPositionable3dEntity>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get onEntityLeft(): Observable<GgPositionable3dEntity | null> {
|
|
19
|
+
return this.onLeft$.pipe(
|
|
20
|
+
map(b => BaseAmmoGGBody.nativeBodyReverseMap.get(b)?.entity || null),
|
|
21
|
+
) as Observable<GgPositionable3dEntity | null>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
constructor(protected readonly world: Gg3dPhysicsWorld, protected _nativeBody: Ammo.btPairCachingGhostObject) {
|
|
25
|
+
super(world, _nativeBody);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private readonly onEnter$: Subject<number> = new Subject<number>();
|
|
29
|
+
private readonly onLeft$: Subject<number> = new Subject<number>();
|
|
30
|
+
private readonly overlaps: Set<Ammo.btCollisionObject> = new Set<Ammo.btCollisionObject>();
|
|
31
|
+
|
|
32
|
+
checkOverlaps(): void {
|
|
33
|
+
const numOverlappingObjects = this.nativeBody.getNumOverlappingObjects();
|
|
34
|
+
const newOverlaps = new Set(
|
|
35
|
+
new Array(numOverlappingObjects).fill(null).map((_, i) => this.nativeBody.getOverlappingObject(i)),
|
|
36
|
+
);
|
|
37
|
+
for (const overlap of this.overlaps.keys()) {
|
|
38
|
+
if (!newOverlaps.has(overlap)) {
|
|
39
|
+
this.overlaps.delete(overlap);
|
|
40
|
+
this.onLeft$.next(ammoId(overlap));
|
|
41
|
+
} else {
|
|
42
|
+
newOverlaps.delete(overlap);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const newOverlap of newOverlaps) {
|
|
46
|
+
this.overlaps.add(newOverlap);
|
|
47
|
+
this.onEnter$.next(ammoId(newOverlap));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clone(): Gg3dTrigger {
|
|
52
|
+
return this.world.factory.createTriggerFromShape(this._nativeBody.getCollisionShape(), {
|
|
53
|
+
position: this.position,
|
|
54
|
+
rotation: this.rotation,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
addToWorld(world: Gg3dPhysicsWorld) {
|
|
59
|
+
if (world != this.world) {
|
|
60
|
+
throw new Error('Ammo triggers cannot be shared between different worlds');
|
|
61
|
+
}
|
|
62
|
+
world.dynamicAmmoWorld?.addCollisionObject(this.nativeBody);
|
|
63
|
+
this.overlaps.clear();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
removeFromWorld(world: Gg3dPhysicsWorld): void {
|
|
67
|
+
for (const body of this.overlaps) {
|
|
68
|
+
this.onLeft$.next(ammoId(body));
|
|
69
|
+
}
|
|
70
|
+
this.overlaps.clear();
|
|
71
|
+
world.dynamicAmmoWorld?.removeCollisionObject(this.nativeBody);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
dispose(): void {
|
|
75
|
+
super.dispose();
|
|
76
|
+
this.overlaps.clear();
|
|
77
|
+
this.onEnter$.complete();
|
|
78
|
+
this.onLeft$.complete();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
resetMotion(): void {}
|
|
82
|
+
}
|