From 03f170e150f2847aa00ca0713bd4f0ac0f71db63 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Thu, 30 Oct 2025 08:22:21 -0500 Subject: [PATCH] Create mesh-based physics for ship and optimize planet geometry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update Ship class to use CONVEX_HULL physics from ship1.glb - Find geometry mesh from loaded GLB and create physics from it - Move physics creation to initialize() after mesh loads - Add fallback to BOX shape if mesh not found - Fix ship position setter for async initialization - Add null check for physics body - Set transform position directly if body doesn't exist yet - Prevents crash when position set before mesh loads - Optimize planet vertex count for performance - Reduce sphere segments from 32 to 12 - ~144 vertices vs ~1024 vertices per planet - Planets are background objects, lower poly acceptable - Update ship1.glb model 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- public/ship1.glb | Bin 10416 -> 10416 bytes src/levelDeserializer.ts | 6 +++-- src/scoreboard.ts | 22 ++++++++-------- src/ship.ts | 54 +++++++++++++++++++++++++++++++-------- 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/public/ship1.glb b/public/ship1.glb index ab61fbaca3fe04951bf690fe3f88186fbf9c1849..af17b15354d749960496f746bbc6e82f0fc36f65 100644 GIT binary patch delta 29 kcmdlGxFK-E97X|C6JsMY3o{D?Q*$Fz151m|YZzJ70gKuQIRF3v delta 29 kcmdlGxFK-E97X|SOCtkIV-pig3u8kA0}IQ|YZzJ70gImqGXMYp diff --git a/src/levelDeserializer.ts b/src/levelDeserializer.ts index 6d9e4ea..6986f0f 100644 --- a/src/levelDeserializer.ts +++ b/src/levelDeserializer.ts @@ -138,9 +138,11 @@ export class LevelDeserializer { const sunPosition = this.arrayToVector3(this.config.sun.position); for (const planetConfig of this.config.planets) { + // Use fewer segments for better performance - planets are background objects + // 16 segments = ~256 vertices vs 32 segments = ~1024 vertices const planet = MeshBuilder.CreateSphere(planetConfig.name, { diameter: planetConfig.diameter, - segments: 32 + segments: 12 // Reduced from 32 for performance }, this.scene); const planetPosition = this.arrayToVector3(planetConfig.position); @@ -156,7 +158,7 @@ export class LevelDeserializer { // Create lightmap with bright light pointing toward sun const lightmap = createSphereLightmap( planetConfig.name + "-lightmap", - 512, // texture size + 256, // texture size DefaultScene.MainScene, toSun, // bright light from sun direction 1, // bright intensity diff --git a/src/scoreboard.ts b/src/scoreboard.ts index e080592..515f7d7 100644 --- a/src/scoreboard.ts +++ b/src/scoreboard.ts @@ -21,11 +21,7 @@ export class Scoreboard { private _done = false; public readonly onScoreObservable: Observable = new Observable(); constructor() { - DefaultScene.MainScene.onNewMeshAddedObservable.add((mesh) => { - if (mesh.id == 'RightUpperDisplay') { - this.initialize(); - } - }); + this.initialize(); } public get done() { return this._done; @@ -39,18 +35,20 @@ export class Scoreboard { private initialize() { const scene = DefaultScene.MainScene; - const parent = scene.getMeshById('RightUpperDisplay'); + const parent = scene.getNodeById('ship'); + console.log('Scoreboard parent:', parent); + console.log('Initializing scoreboard'); const scoreboard = MeshBuilder.CreatePlane("scoreboard", {width: 1, height: 1}, scene); scoreboard.renderingGroupId = 3; const material = new StandardMaterial("scoreboard", scene); scoreboard.parent =parent; - scoreboard.position.x = -.76; - scoreboard.position.y = 4.19; - scoreboard.position.z = .53; - scoreboard.rotation.x = Angle.FromDegrees(104).radians(); - scoreboard.rotation.z = Math.PI; - scoreboard.scaling = new Vector3(.3, .3, .3); + + scoreboard.position.y = 1.05; + scoreboard.position.z = 2.1; + scoreboard.visibility = .5; + + scoreboard.scaling = new Vector3(.4, .4, .4); const advancedTexture = AdvancedDynamicTexture.CreateForMesh(scoreboard, 512, 512); advancedTexture.background = "black"; diff --git a/src/ship.ts b/src/ship.ts index 0bff303..a9e13a0 100644 --- a/src/ship.ts +++ b/src/ship.ts @@ -123,6 +123,14 @@ export class Ship { public set position(newPosition: Vector3) { const body = this._ship.physicsBody; + + // Physics body might not exist yet if called before initialize() completes + if (!body) { + // Just set position directly on transform node + this._ship.position.copyFrom(newPosition); + return; + } + body.disablePreStep = false; body.transformNode.position.copyFrom(newPosition); DefaultScene.MainScene.onAfterRenderObservable.addOnce(() => { @@ -150,17 +158,9 @@ export class Ship { //const landingLight = new SpotLight("landingLight", new Vector3(0, 0, 0), new Vector3(0, -.5, .5), 1.5, .5, DefaultScene.MainScene); // landingLight.parent = this._ship; // landingLight.position.z = 5; - const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.BOX, { - mass: 100, - extents: new Vector3(4, 4, 7.4), - center: new Vector3(0, 1, 1.8) - }, DefaultScene.MainScene); - agg.body.setMotionType(PhysicsMotionType.DYNAMIC); - agg.body.setLinearDamping(.1); - agg.body.setAngularDamping(.2); - agg.body.setAngularVelocity(new Vector3(0, 0, 0)); - agg.body.setCollisionCallbackEnabled(true); + // Physics will be set up after mesh loads in initialize() + this.setupKeyboard(); this.setupMouse(); this._controllerObservable.add(this.controllerCallback); @@ -201,6 +201,40 @@ export class Ship { shipMesh.id = "shipMesh"; shipMesh.name = "shipMesh"; shipMesh.parent = this._ship; + + // Create physics aggregate based on the loaded mesh + // Find the actual geometry mesh (usually meshes[1] or a child) + //const geometryMesh = importMesh.meshes.find(m => m instanceof Mesh && m.getTotalVertices() > 0) as Mesh; + const geo = shipMesh.getChildMeshes()[0] + if (geo) { + + + // Create physics aggregate on the ship TransformNode using the mesh shape + const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.CONVEX_HULL, { + mass: 100, + mesh: (geo as Mesh) // Use the actual ship geometry + }, DefaultScene.MainScene); + + agg.body.setMotionType(PhysicsMotionType.DYNAMIC); + agg.body.setLinearDamping(.1); + agg.body.setAngularDamping(.2); + agg.body.setAngularVelocity(new Vector3(0, 0, 0)); + agg.body.setCollisionCallbackEnabled(true); + } else { + console.warn("No geometry mesh found in ship1.glb, falling back to box shape"); + // Fallback to box shape if mesh not found + const agg = new PhysicsAggregate(this._ship, PhysicsShapeType.BOX, { + mass: 100, + extents: new Vector3(4, 4, 7.4), + center: new Vector3(0, 1, 1.8) + }, DefaultScene.MainScene); + + agg.body.setMotionType(PhysicsMotionType.DYNAMIC); + agg.body.setLinearDamping(.1); + agg.body.setAngularDamping(.2); + agg.body.setAngularVelocity(new Vector3(0, 0, 0)); + agg.body.setCollisionCallbackEnabled(true); + } //shipMesh.rotation.y = Angle.FromDegrees(90).radians(); //shipMesh.rotation.y = Math.PI; //shipMesh.position.y = 1;