@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,164 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Body3DOptions,
|
|
3
|
+
BodyShape3DDescriptor,
|
|
4
|
+
IGg3dBodyFactory,
|
|
5
|
+
Point3,
|
|
6
|
+
Point4,
|
|
7
|
+
Shape3DDescriptor,
|
|
8
|
+
} from '@gg-web-engine/core';
|
|
9
|
+
import { Gg3dBody } from './bodies/gg-3d-body';
|
|
10
|
+
import { Gg3dPhysicsWorld } from './gg-3d-physics-world';
|
|
11
|
+
import Ammo from 'ammojs-typed';
|
|
12
|
+
import { Gg3dTrigger } from './bodies/gg-3d-trigger';
|
|
13
|
+
|
|
14
|
+
export class Gg3dBodyFactory implements IGg3dBodyFactory<Gg3dBody, Gg3dTrigger> {
|
|
15
|
+
constructor(private readonly world: Gg3dPhysicsWorld) {}
|
|
16
|
+
|
|
17
|
+
createRigidBody(
|
|
18
|
+
descriptor: BodyShape3DDescriptor,
|
|
19
|
+
transform?: {
|
|
20
|
+
position?: Point3;
|
|
21
|
+
rotation?: Point4;
|
|
22
|
+
},
|
|
23
|
+
): Gg3dBody {
|
|
24
|
+
return this.createRigidBodyFromShape(this.createShape(descriptor.shape), descriptor.body, transform);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
createTrigger(
|
|
28
|
+
descriptor: Shape3DDescriptor,
|
|
29
|
+
transform?: {
|
|
30
|
+
position?: Point3;
|
|
31
|
+
rotation?: Point4;
|
|
32
|
+
},
|
|
33
|
+
): Gg3dTrigger {
|
|
34
|
+
return this.createTriggerFromShape(this.createShape(descriptor), transform);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private createShape(descriptor: Shape3DDescriptor): Ammo.btCollisionShape {
|
|
38
|
+
switch (descriptor.shape) {
|
|
39
|
+
case 'BOX':
|
|
40
|
+
return new this.world.ammo.btBoxShape(
|
|
41
|
+
new this.world.ammo.btVector3(
|
|
42
|
+
descriptor.dimensions.x / 2,
|
|
43
|
+
descriptor.dimensions.y / 2,
|
|
44
|
+
descriptor.dimensions.z / 2,
|
|
45
|
+
),
|
|
46
|
+
);
|
|
47
|
+
case 'CAPSULE':
|
|
48
|
+
return new this.world.ammo.btCapsuleShapeZ(descriptor.radius, descriptor.centersDistance);
|
|
49
|
+
case 'CYLINDER':
|
|
50
|
+
return new this.world.ammo.btCylinderShapeZ(
|
|
51
|
+
new this.world.ammo.btVector3(descriptor.radius, descriptor.radius, descriptor.height / 2),
|
|
52
|
+
);
|
|
53
|
+
case 'CONE':
|
|
54
|
+
return new this.world.ammo.btConeShapeZ(descriptor.radius, descriptor.height);
|
|
55
|
+
case 'SPHERE':
|
|
56
|
+
return new this.world.ammo.btSphereShape(descriptor.radius);
|
|
57
|
+
case 'COMPOUND':
|
|
58
|
+
const compoundShape: Ammo.btCollisionShape = new this.world.ammo.btCompoundShape();
|
|
59
|
+
for (const item of descriptor.children) {
|
|
60
|
+
const subShape = this.createShape(item.shape);
|
|
61
|
+
if (!subShape) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const subShapeTransform = new this.world.ammo.btTransform();
|
|
65
|
+
const pos = item.position || { x: 0, y: 0, z: 0 };
|
|
66
|
+
const rot = item.rotation || { x: 0, y: 0, z: 0, w: 1 };
|
|
67
|
+
subShapeTransform.setOrigin(new this.world.ammo.btVector3(pos.x, pos.y, pos.z));
|
|
68
|
+
subShapeTransform.setRotation(new this.world.ammo.btQuaternion(rot.x, rot.y, rot.z, rot.w));
|
|
69
|
+
(compoundShape as Ammo.btCompoundShape).addChildShape(subShapeTransform, subShape);
|
|
70
|
+
}
|
|
71
|
+
return compoundShape;
|
|
72
|
+
case 'CONVEX_HULL':
|
|
73
|
+
const shape = new this.world.ammo.btConvexHullShape();
|
|
74
|
+
const tmpVector = new this.world.ammo.btVector3();
|
|
75
|
+
for (const v of descriptor.vertices) {
|
|
76
|
+
tmpVector.setValue(v.x, v.y, v.z);
|
|
77
|
+
shape.addPoint(tmpVector);
|
|
78
|
+
}
|
|
79
|
+
this.world.ammo.destroy(tmpVector);
|
|
80
|
+
shape.recalcLocalAabb();
|
|
81
|
+
return shape;
|
|
82
|
+
case 'MESH':
|
|
83
|
+
const mesh = new this.world.ammo.btTriangleMesh(true, true);
|
|
84
|
+
const tmpVectors: [Ammo.btVector3, Ammo.btVector3, Ammo.btVector3] = [
|
|
85
|
+
new this.world.ammo.btVector3(),
|
|
86
|
+
new this.world.ammo.btVector3(),
|
|
87
|
+
new this.world.ammo.btVector3(),
|
|
88
|
+
];
|
|
89
|
+
for (const f of descriptor.faces) {
|
|
90
|
+
for (let j = 0; j < 3; j++) {
|
|
91
|
+
tmpVectors[j].setValue(
|
|
92
|
+
descriptor.vertices[f[0]].x,
|
|
93
|
+
descriptor.vertices[f[1]].y,
|
|
94
|
+
descriptor.vertices[f[2]].z,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
mesh.addTriangle(...tmpVectors, true);
|
|
98
|
+
}
|
|
99
|
+
tmpVectors.forEach(v => {
|
|
100
|
+
this.world.ammo.destroy(v);
|
|
101
|
+
});
|
|
102
|
+
return new this.world.ammo.btBvhTriangleMeshShape(mesh, false, true);
|
|
103
|
+
}
|
|
104
|
+
throw new Error(`Shape "${(descriptor as any).shape}" not implemented for Ammo.js`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public createRigidBodyFromShape(
|
|
108
|
+
shape: Ammo.btCollisionShape,
|
|
109
|
+
options: Partial<Body3DOptions>,
|
|
110
|
+
transform?: { position?: Point3; rotation?: Point4 },
|
|
111
|
+
): Gg3dBody {
|
|
112
|
+
if (options.dynamic === false) {
|
|
113
|
+
options.mass = 0;
|
|
114
|
+
}
|
|
115
|
+
const pos = transform?.position || { x: 0, y: 0, z: 0 };
|
|
116
|
+
const rot = transform?.rotation || { x: 0, y: 0, z: 0, w: 1 };
|
|
117
|
+
const ammo = this.world.ammo;
|
|
118
|
+
const ammoTransform = new ammo.btTransform();
|
|
119
|
+
ammoTransform.setOrigin(new ammo.btVector3(pos.x, pos.y, pos.z));
|
|
120
|
+
ammoTransform.setRotation(new ammo.btQuaternion(rot.x, rot.y, rot.z, rot.w));
|
|
121
|
+
const motionState = new ammo.btDefaultMotionState(ammoTransform);
|
|
122
|
+
const localInertia = new ammo.btVector3(0, 0, 0);
|
|
123
|
+
shape.calculateLocalInertia(options.mass || 0, localInertia);
|
|
124
|
+
const environmentBodyCI = new ammo.btRigidBodyConstructionInfo(options.mass || 0, motionState, shape, localInertia);
|
|
125
|
+
if (options.friction) {
|
|
126
|
+
environmentBodyCI.set_m_friction(options.friction);
|
|
127
|
+
environmentBodyCI.set_m_rollingFriction(options.friction);
|
|
128
|
+
}
|
|
129
|
+
if (options.restitution) {
|
|
130
|
+
environmentBodyCI.set_m_restitution(options.restitution);
|
|
131
|
+
}
|
|
132
|
+
const body = new this.world.ammo.btRigidBody(environmentBodyCI);
|
|
133
|
+
return new Gg3dBody(this.world, body);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
public createTriggerFromShape(
|
|
137
|
+
shape: Ammo.btCollisionShape,
|
|
138
|
+
transform?: { position?: Point3; rotation?: Point4 },
|
|
139
|
+
): Gg3dTrigger {
|
|
140
|
+
const ghostObject = new this.world.ammo.btPairCachingGhostObject();
|
|
141
|
+
ghostObject.setCollisionShape(shape);
|
|
142
|
+
ghostObject.setCollisionFlags(ghostObject.getCollisionFlags() | 4); // 4 is a CF_NO_CONTACT_RESPONSE collision flag
|
|
143
|
+
ghostObject
|
|
144
|
+
.getWorldTransform()
|
|
145
|
+
.setOrigin(
|
|
146
|
+
new this.world.ammo.btVector3(
|
|
147
|
+
transform?.position?.x || 0,
|
|
148
|
+
transform?.position?.y || 0,
|
|
149
|
+
transform?.position?.z || 0,
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
ghostObject
|
|
153
|
+
.getWorldTransform()
|
|
154
|
+
.setRotation(
|
|
155
|
+
new this.world.ammo.btQuaternion(
|
|
156
|
+
transform?.rotation?.x || 0,
|
|
157
|
+
transform?.rotation?.y || 0,
|
|
158
|
+
transform?.rotation?.z || 0,
|
|
159
|
+
transform?.rotation?.w || 1,
|
|
160
|
+
),
|
|
161
|
+
);
|
|
162
|
+
return new Gg3dTrigger(this.world, ghostObject);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { Gg3dWorld, GgDebugPhysicsDrawer, IGg3dPhysicsWorld, Point3, Point4 } from '@gg-web-engine/core';
|
|
2
|
+
import Ammo, * as AmmoModule from 'ammojs-typed';
|
|
3
|
+
import { Gg3dBodyFactory } from './gg-3d-body-factory';
|
|
4
|
+
import { Gg3dBodyLoader } from './gg-3d-body-loader';
|
|
5
|
+
import { AmmoDebugger, AmmoDebugMode } from '../ammo-debugger';
|
|
6
|
+
|
|
7
|
+
export class Gg3dPhysicsWorld implements IGg3dPhysicsWorld {
|
|
8
|
+
private _factory: Gg3dBodyFactory | null = null;
|
|
9
|
+
public get factory(): Gg3dBodyFactory {
|
|
10
|
+
if (!this._factory) {
|
|
11
|
+
throw new Error('Ammo world not initialized');
|
|
12
|
+
}
|
|
13
|
+
return this._factory;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private _loader: Gg3dBodyLoader | null = null;
|
|
17
|
+
public get loader(): Gg3dBodyLoader {
|
|
18
|
+
if (!this._loader) {
|
|
19
|
+
throw new Error('Ammo world not initialized');
|
|
20
|
+
}
|
|
21
|
+
return this._loader;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private _gravity: Point3 = { x: 0, y: 0, z: -9.82 };
|
|
25
|
+
public get gravity(): Point3 {
|
|
26
|
+
return this._gravity;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public set gravity(value: Point3) {
|
|
30
|
+
this._gravity = value;
|
|
31
|
+
if (this.gravityVector) {
|
|
32
|
+
this.gravityVector.setValue(value.x, value.y, value.z);
|
|
33
|
+
this._dynamicAmmoWorld?.setGravity(this.gravityVector);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private _timeScale: number = 1;
|
|
38
|
+
public get timeScale(): number {
|
|
39
|
+
return this._timeScale;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public set timeScale(value: number) {
|
|
43
|
+
this._timeScale = value;
|
|
44
|
+
}
|
|
45
|
+
private _debugger: AmmoDebugger | null = null;
|
|
46
|
+
private _debugDrawer: GgDebugPhysicsDrawer<Point3, Point4> | null = null;
|
|
47
|
+
|
|
48
|
+
get physicsDebugViewActive(): boolean {
|
|
49
|
+
return !!this._debugger;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private ammoInstance: typeof Ammo | undefined;
|
|
53
|
+
|
|
54
|
+
public get ammo(): typeof Ammo {
|
|
55
|
+
if (this.ammoInstance) {
|
|
56
|
+
return this.ammoInstance;
|
|
57
|
+
}
|
|
58
|
+
throw 'Physics world not initialized!';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public get dynamicAmmoWorld(): Ammo.btDiscreteDynamicsWorld | undefined {
|
|
62
|
+
return this._dynamicAmmoWorld;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private collisionConfiguration: Ammo.btDefaultCollisionConfiguration | undefined;
|
|
66
|
+
private dispatcher: Ammo.btCollisionDispatcher | undefined;
|
|
67
|
+
private ghostPairCallback: Ammo.btGhostPairCallback | undefined;
|
|
68
|
+
private broadphase: Ammo.btBroadphaseInterface | undefined;
|
|
69
|
+
private solver: Ammo.btSequentialImpulseConstraintSolver | undefined;
|
|
70
|
+
private gravityVector: Ammo.btVector3 | undefined;
|
|
71
|
+
private _dynamicAmmoWorld: Ammo.btDiscreteDynamicsWorld | undefined;
|
|
72
|
+
|
|
73
|
+
async init(): Promise<void> {
|
|
74
|
+
this.ammoInstance = await AmmoModule.default.bind(AmmoModule)();
|
|
75
|
+
this.collisionConfiguration = new this.ammo.btDefaultCollisionConfiguration();
|
|
76
|
+
this.dispatcher = new this.ammo.btCollisionDispatcher(this.collisionConfiguration);
|
|
77
|
+
this.broadphase = new this.ammo.btDbvtBroadphase();
|
|
78
|
+
this.ghostPairCallback = new this.ammo.btGhostPairCallback();
|
|
79
|
+
this.solver = new this.ammo.btSequentialImpulseConstraintSolver();
|
|
80
|
+
this.gravityVector = new this.ammo.btVector3(this._gravity.x, this._gravity.y, this._gravity.z);
|
|
81
|
+
|
|
82
|
+
this._dynamicAmmoWorld = new this.ammo.btDiscreteDynamicsWorld(
|
|
83
|
+
this.dispatcher,
|
|
84
|
+
this.broadphase,
|
|
85
|
+
this.solver,
|
|
86
|
+
this.collisionConfiguration,
|
|
87
|
+
);
|
|
88
|
+
this._dynamicAmmoWorld.getPairCache().setInternalGhostPairCallback(this.ghostPairCallback);
|
|
89
|
+
this._dynamicAmmoWorld.setGravity(this.gravityVector);
|
|
90
|
+
this._factory = new Gg3dBodyFactory(this);
|
|
91
|
+
this._loader = new Gg3dBodyLoader(this);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
simulate(delta: number): void {
|
|
95
|
+
this._dynamicAmmoWorld?.stepSimulation((this._timeScale * delta) / 1000, 100, this._timeScale * 0.01);
|
|
96
|
+
if (this._debugger) {
|
|
97
|
+
this._debugger.update();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
startDebugger(world: Gg3dWorld, drawer: GgDebugPhysicsDrawer<Point3, Point4>): void {
|
|
102
|
+
if (!this._debugger) {
|
|
103
|
+
this._debugger = new AmmoDebugger(this, drawer);
|
|
104
|
+
this.dynamicAmmoWorld?.setDebugDrawer(this._debugger.ammoInstance);
|
|
105
|
+
}
|
|
106
|
+
this._debugger!.setDebugMode(AmmoDebugMode.DrawWireframe);
|
|
107
|
+
this._debugDrawer = drawer;
|
|
108
|
+
this._debugDrawer.addToWorld(world.visualScene);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
stopDebugger(world: Gg3dWorld): void {
|
|
112
|
+
if (this._debugger) {
|
|
113
|
+
this.dynamicAmmoWorld?.setDebugDrawer(null!);
|
|
114
|
+
this.ammo.destroy(this._debugger.ammoInstance);
|
|
115
|
+
this._debugger = null;
|
|
116
|
+
}
|
|
117
|
+
if (this._debugDrawer) {
|
|
118
|
+
this._debugDrawer!.removeFromWorld(world.visualScene);
|
|
119
|
+
this._debugDrawer!.dispose();
|
|
120
|
+
this._debugDrawer = null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
dispose(): void {
|
|
125
|
+
this.ammo.destroy(this._dynamicAmmoWorld);
|
|
126
|
+
this.ammo.destroy(this.solver);
|
|
127
|
+
this.ammo.destroy(this.broadphase);
|
|
128
|
+
this.ammo.destroy(this.dispatcher);
|
|
129
|
+
this.ammo.destroy(this.collisionConfiguration);
|
|
130
|
+
if (this._debugger) {
|
|
131
|
+
this.ammo.destroy(this._debugger.ammoInstance);
|
|
132
|
+
}
|
|
133
|
+
this._dynamicAmmoWorld =
|
|
134
|
+
this.solver =
|
|
135
|
+
this.broadphase =
|
|
136
|
+
this.dispatcher =
|
|
137
|
+
this.collisionConfiguration =
|
|
138
|
+
this.ammoInstance =
|
|
139
|
+
undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IGg3dRaycastVehicle,
|
|
3
|
+
WheelOptions,
|
|
4
|
+
SuspensionOptions,
|
|
5
|
+
Point3,
|
|
6
|
+
Point4,
|
|
7
|
+
Gg3dRaycastVehicleEntity,
|
|
8
|
+
} from '@gg-web-engine/core';
|
|
9
|
+
import Ammo from 'ammojs-typed';
|
|
10
|
+
import { Gg3dBody } from './bodies/gg-3d-body';
|
|
11
|
+
import { Gg3dPhysicsWorld } from './gg-3d-physics-world';
|
|
12
|
+
export class Gg3dRaycastVehicle extends Gg3dBody implements IGg3dRaycastVehicle {
|
|
13
|
+
public readonly nativeVehicle: Ammo.btRaycastVehicle;
|
|
14
|
+
public readonly vehicleTuning: Ammo.btVehicleTuning = new this.ammo.btVehicleTuning();
|
|
15
|
+
protected readonly wheelDirectionCS0: Ammo.btVector3 = new this.ammo.btVector3(0, 0, -1);
|
|
16
|
+
protected readonly wheelAxleCS: Ammo.btVector3 = new this.ammo.btVector3(1, 0, 0);
|
|
17
|
+
|
|
18
|
+
public entity: Gg3dRaycastVehicleEntity | null = null;
|
|
19
|
+
|
|
20
|
+
constructor(protected readonly world: Gg3dPhysicsWorld, public chassisBody: Ammo.btRigidBody) {
|
|
21
|
+
super(world, chassisBody);
|
|
22
|
+
this.nativeVehicle = new this.ammo.btRaycastVehicle(
|
|
23
|
+
this.vehicleTuning,
|
|
24
|
+
this.chassisBody,
|
|
25
|
+
new this.ammo.btDefaultVehicleRaycaster(world.dynamicAmmoWorld!),
|
|
26
|
+
);
|
|
27
|
+
this.nativeVehicle.setCoordinateSystem(0, 2, 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get wheelSpeed(): number {
|
|
31
|
+
// FIXME not correct (shows chassis speed in world)
|
|
32
|
+
return this.nativeVehicle.getCurrentSpeedKmHour() / 3.6;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
addToWorld(world: Gg3dPhysicsWorld) {
|
|
36
|
+
if (world != this.world) {
|
|
37
|
+
throw new Error('Ammo raycast vehicle cannot be shared between different worlds');
|
|
38
|
+
}
|
|
39
|
+
// TODO parked cars can be deactivated until we start handling them. Needs explicit activation call
|
|
40
|
+
this.chassisBody.setActivationState(4); // btCollisionObject::DISABLE_DEACTIVATION
|
|
41
|
+
world.dynamicAmmoWorld?.addRigidBody(this.chassisBody);
|
|
42
|
+
this.world.dynamicAmmoWorld!.addAction(this.nativeVehicle);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
addWheel(options: WheelOptions, suspensionOptions: SuspensionOptions): void {
|
|
46
|
+
const wheelInfo = this.nativeVehicle.addWheel(
|
|
47
|
+
new this.ammo.btVector3(options.position.x, options.position.y, options.position.z * 2),
|
|
48
|
+
this.wheelDirectionCS0,
|
|
49
|
+
this.wheelAxleCS,
|
|
50
|
+
suspensionOptions.restLength,
|
|
51
|
+
options.tyre_radius,
|
|
52
|
+
this.vehicleTuning,
|
|
53
|
+
options.isFront,
|
|
54
|
+
);
|
|
55
|
+
wheelInfo.set_m_suspensionStiffness(suspensionOptions.stiffness);
|
|
56
|
+
wheelInfo.set_m_wheelsDampingRelaxation(suspensionOptions.damping);
|
|
57
|
+
wheelInfo.set_m_wheelsDampingCompression(suspensionOptions.compression);
|
|
58
|
+
wheelInfo.set_m_frictionSlip(options.frictionSlip);
|
|
59
|
+
wheelInfo.set_m_rollInfluence(options.rollInfluence);
|
|
60
|
+
wheelInfo.set_m_maxSuspensionTravelCm(options.maxTravel * 100);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setSteering(wheelIndex: number, steering: number): void {
|
|
64
|
+
this.nativeVehicle.setSteeringValue(steering, wheelIndex);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
applyEngineForce(wheelIndex: number, force: number): void {
|
|
68
|
+
this.nativeVehicle.applyEngineForce(force, wheelIndex);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
applyBrake(wheelIndex: number, force: number): void {
|
|
72
|
+
this.nativeVehicle.setBrake(force, wheelIndex);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
isWheelTouchesGround(wheelIndex: number): boolean {
|
|
76
|
+
return this.nativeVehicle.getWheelInfo(wheelIndex).get_m_raycastInfo().get_m_groundObject() > 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getWheelTransform(wheelIndex: number): { position: Point3; rotation: Point4 } {
|
|
80
|
+
// FIXME when set to `true`, wheels are jittering, but it was not the case in old sandbox app. Check why
|
|
81
|
+
this.nativeVehicle.updateWheelTransform(wheelIndex, false);
|
|
82
|
+
const transform = this.nativeVehicle.getWheelTransformWS(wheelIndex);
|
|
83
|
+
const origin = transform.getOrigin();
|
|
84
|
+
const quaternion = transform.getRotation();
|
|
85
|
+
return {
|
|
86
|
+
position: { x: origin.x(), y: origin.y(), z: origin.z() },
|
|
87
|
+
rotation: { x: quaternion.x(), y: quaternion.y(), z: quaternion.z(), w: quaternion.w() },
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
resetSuspension(): void {
|
|
92
|
+
this.nativeVehicle.resetSuspension();
|
|
93
|
+
for (let i = 0; i < this.nativeVehicle.getNumWheels(); i++) {
|
|
94
|
+
this.nativeVehicle.updateWheelTransform(i, true);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/index.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';
|