Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3D physics vehicle #7479

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b2e3cbf
WIP 3D vehicle
D8H Mar 17, 2025
6776dd5
Upgrade to Jolt 0.34.0
D8H Mar 18, 2025
d04b679
Fix car forward direction.
D8H Mar 18, 2025
18be12b
Allow to offset the shape and the center of mass.
D8H Mar 20, 2025
ff2fede
Fix wheel settings.
D8H Mar 20, 2025
1a0c01c
Try to find playable settings.
D8H Mar 21, 2025
667a450
Remove useless code.
D8H Mar 21, 2025
72cb1ce
Add a steering speed.
D8H Mar 21, 2025
7653a53
Add more gravity.
D8H Mar 21, 2025
aae0f90
Use the default gear ratios.
D8H Apr 1, 2025
00fabeb
Add stick controls.
D8H Apr 2, 2025
005379a
Optimize memory usage.
D8H Apr 2, 2025
b008e1d
Handle size changes.
D8H Apr 2, 2025
02857c9
Fix some memory leaks.
D8H Apr 3, 2025
560b3ef
Update to Jolt.js 0.35.0
D8H Apr 3, 2025
dc5e1bc
Fix a memory leak when creating bodies.
D8H Apr 4, 2025
02d8627
Fix warnings about characters not handling mass center offset.
D8H Apr 4, 2025
9595b06
Fix a crash when a car is destroyed.
D8H Apr 4, 2025
2c6764d
Add some properties.
D8H Apr 4, 2025
ee0013d
Add properties for gears and wheels.
D8H Apr 7, 2025
456c5de
Add properties for 4 wheels drive.
D8H Apr 8, 2025
0e937be
Add an icon.
D8H Apr 8, 2025
2c769c9
Add brake properties.
D8H Apr 8, 2025
52d6d61
Use a cylinder cast.
D8H Apr 9, 2025
c578712
Fix character memory.
D8H Apr 9, 2025
f900c20
Add override annotations.
D8H Apr 9, 2025
eafa5f4
Add instructions for dashboard and turbo.
D8H Apr 9, 2025
cad7027
Move the mass override to the Physics behavior
D8H Apr 10, 2025
7f24106
Add todo
D8H Apr 11, 2025
94fd09c
Rename the behavior from Vehicle to Car
D8H Apr 13, 2025
d4eb707
Use template property values for steering
D8H Apr 13, 2025
2f6b885
Remove a todo.
D8H Apr 13, 2025
3012f3d
Remove logs.
D8H Apr 13, 2025
a41d02d
Fix some memory leaks.
D8H Apr 14, 2025
aced0f0
Fix another memory leak.
D8H Apr 14, 2025
5fb856e
Fix a memory crash.
D8H Apr 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,067 changes: 1,048 additions & 19 deletions Extensions/Physics3DBehavior/JsExtension.js

Large diffs are not rendered by default.

135 changes: 110 additions & 25 deletions Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,11 +283,11 @@ namespace gdjs {
gdjs.registerRuntimeSceneUnloadedCallback(function (runtimeScene) {
const physics3DSharedData = runtimeScene.physics3DSharedData;
if (physics3DSharedData) {
Jolt.destroy(physics3DSharedData.jolt);
Jolt.destroy(physics3DSharedData.contactListener);
Jolt.destroy(physics3DSharedData._tempVec3);
Jolt.destroy(physics3DSharedData._tempRVec3);
Jolt.destroy(physics3DSharedData._tempQuat);
Jolt.destroy(physics3DSharedData.jolt);
runtimeScene.physics3DSharedData = null;
}
});
Expand All @@ -302,18 +302,25 @@ namespace gdjs {
fixedRotation: boolean;
private shape: string;
private shapeOrientation: string;
private shapeDimensionA: any;
private shapeDimensionB: any;
private shapeDimensionC: any;
private shapeDimensionA: float;
private shapeDimensionB: float;
private shapeDimensionC: float;
private shapeOffsetX: float;
private shapeOffsetY: float;
shapeOffsetZ: float;
private massCenterOffsetX: float;
private massCenterOffsetY: float;
private massCenterOffsetZ: float;
private density: float;
massOverride: float;
friction: float;
restitution: float;
linearDamping: float;
angularDamping: float;
gravityScale: float;
private layers: integer;
private masks: integer;
private shapeScale: number = 1;
shapeScale: number = 1;

/**
* Array containing the beginning of contacts reported by onContactBegin. Each contact
Expand Down Expand Up @@ -348,7 +355,10 @@ namespace gdjs {
/**
* When set to `true` the shape will be recreated before the next physics step.
*/
private _needToRecreateShape: boolean = false;
_needToRecreateShape: boolean = false;

_shapeHalfWidth: float = 0;
_shapeHalfHeight: float = 0;
/**
* Used by {@link gdjs.PhysicsCharacter3DRuntimeBehavior} to convert coordinates.
*/
Expand Down Expand Up @@ -391,7 +401,14 @@ namespace gdjs {
this.shapeDimensionA = behaviorData.shapeDimensionA;
this.shapeDimensionB = behaviorData.shapeDimensionB;
this.shapeDimensionC = behaviorData.shapeDimensionC;
this.shapeOffsetX = behaviorData.shapeOffsetX || 0;
this.shapeOffsetY = behaviorData.shapeOffsetY || 0;
this.shapeOffsetZ = behaviorData.shapeOffsetZ || 0;
this.massCenterOffsetX = behaviorData.massCenterOffsetX || 0;
this.massCenterOffsetY = behaviorData.massCenterOffsetY || 0;
this.massCenterOffsetZ = behaviorData.massCenterOffsetZ || 0;
this.density = behaviorData.density;
this.massOverride = behaviorData.massOverride;
this.friction = behaviorData.friction;
this.restitution = behaviorData.restitution;
this.linearDamping = Math.max(0, behaviorData.linearDamping);
Expand Down Expand Up @@ -424,7 +441,7 @@ namespace gdjs {
return tempQuat;
}

updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
override updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {
if (oldBehaviorData.bullet !== newBehaviorData.bullet) {
this.setBullet(newBehaviorData.bullet);
}
Expand Down Expand Up @@ -477,7 +494,7 @@ namespace gdjs {
return true;
}

getNetworkSyncData(): Physics3DNetworkSyncData {
override getNetworkSyncData(): Physics3DNetworkSyncData {
let bodyProps;
if (this._body) {
const position = this._body.GetPosition();
Expand Down Expand Up @@ -528,7 +545,9 @@ namespace gdjs {
};
}

updateFromNetworkSyncData(networkSyncData: Physics3DNetworkSyncData) {
override updateFromNetworkSyncData(
networkSyncData: Physics3DNetworkSyncData
) {
super.updateFromNetworkSyncData(networkSyncData);

const behaviorSpecificProps = networkSyncData.props;
Expand Down Expand Up @@ -608,15 +627,15 @@ namespace gdjs {
}
}

onDeActivate() {
override onDeActivate() {
this._sharedData.removeFromBehaviorsList(this);
this.bodyUpdater.destroyBody();
this._contactsEndedThisFrame.length = 0;
this._contactsStartedThisFrame.length = 0;
this._currentContacts.length = 0;
}

onActivate() {
override onActivate() {
this._sharedData.addToBehaviorsList(this);

this._contactsEndedThisFrame.length = 0;
Expand All @@ -625,12 +644,45 @@ namespace gdjs {
this.updateBodyFromObject();
}

onDestroy() {
override onDestroy() {
this._destroyedDuringFrameLogic = true;
this.onDeActivate();
}

createShape(): Jolt.Shape {
if (
this.massCenterOffsetX === 0 &&
this.massCenterOffsetY === 0 &&
this.massCenterOffsetZ === 0
) {
return this.createShapeWithoutMassCenterOffset();
}
const rotatedShapeSettings =
this._createShapeSettingsWithoutMassCenterOffset();
const shapeScale = this.shapeScale * this._sharedData.worldInvScale;
const offsetCenterShapeSettings =
new Jolt.OffsetCenterOfMassShapeSettings(
this.getVec3(
this.massCenterOffsetX * shapeScale,
this.massCenterOffsetY * shapeScale,
this.massCenterOffsetZ * shapeScale
),
rotatedShapeSettings
);
const shape = offsetCenterShapeSettings.Create().Get();
Jolt.destroy(offsetCenterShapeSettings);
return shape;
}

createShapeWithoutMassCenterOffset(): Jolt.Shape {
const rotatedShapeSettings =
this._createShapeSettingsWithoutMassCenterOffset();
const shape = rotatedShapeSettings.Create().Get();
Jolt.destroy(rotatedShapeSettings);
return shape;
}

private _createShapeSettingsWithoutMassCenterOffset(): Jolt.RotatedTranslatedShapeSettings {
let width = this.owner3D.getWidth() * this._sharedData.worldInvScale;
let height = this.owner3D.getHeight() * this._sharedData.worldInvScale;
let depth = this.owner3D.getDepth() * this._sharedData.worldInvScale;
Expand Down Expand Up @@ -676,6 +728,8 @@ namespace gdjs {
convexRadius
);
quat = this.getQuat(0, 0, 0, 1);
this._shapeHalfWidth = boxWidth / 2;
this._shapeHalfHeight = boxHeight / 2;
this._shapeHalfDepth = boxDepth / 2;
} else if (this.shape === 'Capsule') {
const radius =
Expand All @@ -691,8 +745,12 @@ namespace gdjs {
radius
);
quat = this._getShapeOrientationQuat();
this._shapeHalfWidth =
this.shapeOrientation === 'X' ? capsuleDepth / 2 : radius;
this._shapeHalfHeight =
this.shapeOrientation === 'Y' ? capsuleDepth / 2 : radius;
this._shapeHalfDepth =
this.shapeOrientation !== 'Z' ? radius : capsuleDepth / 2;
this.shapeOrientation === 'Z' ? capsuleDepth / 2 : radius;
} else if (this.shape === 'Cylinder') {
const radius =
shapeDimensionA > 0
Expand All @@ -713,8 +771,12 @@ namespace gdjs {
convexRadius
);
quat = this._getShapeOrientationQuat();
this._shapeHalfWidth =
this.shapeOrientation === 'X' ? cylinderDepth / 2 : radius;
this._shapeHalfHeight =
this.shapeOrientation === 'Y' ? cylinderDepth / 2 : radius;
this._shapeHalfDepth =
this.shapeOrientation !== 'Z' ? radius : cylinderDepth / 2;
this.shapeOrientation === 'Z' ? cylinderDepth / 2 : radius;
} else {
// Create a 'Sphere' by default.
const radius =
Expand All @@ -725,19 +787,20 @@ namespace gdjs {
: onePixel;
shapeSettings = new Jolt.SphereShapeSettings(radius);
quat = this.getQuat(0, 0, 0, 1);
this._shapeHalfWidth = radius;
this._shapeHalfHeight = radius;
this._shapeHalfDepth = radius;
}
shapeSettings.mDensity = this.density;
const rotatedShape = new Jolt.RotatedTranslatedShapeSettings(
this.getVec3(0, 0, 0),
return new Jolt.RotatedTranslatedShapeSettings(
this.getVec3(
this.shapeOffsetX * shapeScale,
this.shapeOffsetY * shapeScale,
this.shapeOffsetZ * shapeScale
),
quat,
shapeSettings
)
.Create()
.Get();

Jolt.destroy(shapeSettings);
return rotatedShape;
);
}

private _getShapeOrientationQuat(): Jolt.Quat {
Expand Down Expand Up @@ -822,7 +885,7 @@ namespace gdjs {
: this.masks;
}

doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
override doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
// Step the world if not done this frame yet.
// Don't step at the first frame to allow events to handle overlapping objects.
if (
Expand All @@ -836,7 +899,9 @@ namespace gdjs {
}
}

doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {
override doStepPostEvents(
instanceContainer: gdjs.RuntimeInstanceContainer
) {
// Reset world step to update next frame
this._sharedData.stepped = false;
}
Expand Down Expand Up @@ -1100,6 +1165,18 @@ namespace gdjs {
this._needToRecreateShape = true;
}

getMassOverride(): float {
return this.massOverride;
}

setMassOverride(mass: float): void {
if (this.massOverride === mass) {
return;
}
this.massOverride = mass;
this._needToRecreateBody = true;
}

getFriction(): float {
return this.friction;
}
Expand Down Expand Up @@ -1765,7 +1842,9 @@ namespace gdjs {
destroyBody(): void;
}

export class DefaultBodyUpdater {
export class DefaultBodyUpdater
implements gdjs.Physics3DRuntimeBehavior.BodyUpdater
{
behavior: gdjs.Physics3DRuntimeBehavior;

constructor(behavior: gdjs.Physics3DRuntimeBehavior) {
Expand Down Expand Up @@ -1801,6 +1880,12 @@ namespace gdjs {
bodyCreationSettings.mLinearDamping = behavior.linearDamping;
bodyCreationSettings.mAngularDamping = behavior.angularDamping;
bodyCreationSettings.mGravityFactor = behavior.gravityScale;
if (behavior.massOverride > 0) {
bodyCreationSettings.mOverrideMassProperties =
Jolt.EOverrideMassProperties_CalculateInertia;
bodyCreationSettings.mMassPropertiesOverride.mMass =
behavior.massOverride;
}

const bodyInterface = _sharedData.bodyInterface;
const body = bodyInterface.CreateBody(bodyCreationSettings);
Expand Down
Loading