diff --git a/src/ts/assets.ts b/src/ts/assets.ts index 8702aa67b..d133bfd7b 100644 --- a/src/ts/assets.ts +++ b/src/ts/assets.ts @@ -45,6 +45,8 @@ import { Sound } from "@babylonjs/core/Audio/sound"; import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder"; import { ProceduralTexture } from "@babylonjs/core/Materials/Textures/Procedurals/proceduralTexture"; +import { createButterfly } from "./proceduralAssets/butterfly/butterfly"; +import { createGrassBlade } from "./proceduralAssets/grass/grassBlade"; export class Assets { static IS_READY = false; @@ -78,6 +80,9 @@ export class Assets { public static Tree: Mesh; public static ScatterCube: Mesh; + public static Butterfly: Mesh; + public static GrassBlade: Mesh; + public static OuchSound: Sound; public static EngineRunningSound: Sound; @@ -183,7 +188,6 @@ export class Assets { Assets.Tree.position.y = -1; Assets.Tree.scaling.scaleInPlace(3); Assets.Tree.bakeCurrentTransformIntoVertices(); - Assets.Tree.isVisible = false; const treeMaterial = new StandardMaterial("treeMaterial", scene); @@ -198,8 +202,14 @@ export class Assets { Assets.Tree.material = treeMaterial; + Assets.Tree.isVisible = false; + console.log("Tree loaded"); - } + }; + + Assets.Butterfly = createButterfly(scene); + + Assets.GrassBlade = createGrassBlade(scene, 3); const ouchSoundTask = Assets.manager.addBinaryFileTask("ouchSoundTask", ouchSound); ouchSoundTask.onSuccess = function (task) { diff --git a/src/ts/index.ts b/src/ts/index.ts index 33be4aecf..208247dec 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -214,6 +214,20 @@ if (aresAtmosphere) { document.addEventListener("keydown", (e) => { if (engine.isPaused()) return; + if(e.key === "x") { + let nbVertices = 0; + let nbInstances = 0; + planet.sides.forEach(side => { + side.executeOnEveryChunk((chunk) => { + nbVertices += Settings.VERTEX_RESOLUTION * Settings.VERTEX_RESOLUTION; + chunk.instancePatches.forEach(patch => { + nbInstances += patch.getNbInstances(); + }); + }); + }); + console.log("Vertices", nbVertices, "Instances", nbInstances); + } + if (e.key === "y") { if (starSystemView.scene.getActiveController() === spaceshipController) { console.log("disembark"); diff --git a/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts b/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts index 1144456d1..ba3d83683 100644 --- a/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts +++ b/src/ts/planemos/telluricPlanemo/terrain/chunks/chunkTree.ts @@ -164,7 +164,7 @@ export class ChunkTree { const distanceToNodeSquared = Vector3.DistanceSquared(chunkApproxPosition, observerPositionW); const subdivisionDistanceThreshold = Settings.CHUNK_RENDER_DISTANCE_MULTIPLIER * (this.rootChunkLength / 2 ** walked.length); - const deletionDistanceThreshold = 20e3 + 1.5 * subdivisionDistanceThreshold; + const deletionDistanceThreshold = 10e3 + Settings.CHUNK_RENDER_DISTANCE_MULTIPLIER * (this.rootChunkLength / 2 ** (walked.length - 1)); // the 1.5 is to avoid creation/deletion oscillations if (distanceToNodeSquared > deletionDistanceThreshold ** 2 && walked.length >= this.minDepth && tree instanceof Array) { diff --git a/src/ts/planemos/telluricPlanemo/terrain/chunks/planetChunk.ts b/src/ts/planemos/telluricPlanemo/terrain/chunks/planetChunk.ts index dde47b285..c62a1e72e 100644 --- a/src/ts/planemos/telluricPlanemo/terrain/chunks/planetChunk.ts +++ b/src/ts/planemos/telluricPlanemo/terrain/chunks/planetChunk.ts @@ -17,9 +17,7 @@ import { CollisionMask } from "../../../../settings"; import { isSizeOnScreenEnough } from "../../../../utils/isObjectVisibleOnScreen"; import { Camera } from "@babylonjs/core/Cameras/camera"; import { IPatch } from "../instancePatch/iPatch"; -import { createGrassBlade } from "../../../../proceduralAssets/grass/grassBlade"; import { TelluricPlanemoModel } from "../../telluricPlanemoModel"; -import { createButterfly } from "../../../../proceduralAssets/butterfly/butterfly"; import { BoundingSphere } from "../../../../bodies/common"; import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh"; @@ -38,7 +36,7 @@ export class PlanetChunk implements Transformable, BoundingSphere { private readonly parent: TransformNode; - private readonly instancePatches: IPatch[] = []; + readonly instancePatches: IPatch[] = []; readonly onDestroyPhysicsShapeObservable = new Observable(); @@ -114,14 +112,14 @@ export class PlanetChunk implements Transformable, BoundingSphere { // The following is a code snippet to use the approximate normals of the mesh instead of // the analytic normals. This is useful for debugging purposes /*if(!analyticNormal) { - this.mesh.createNormals(true); - const normals = this.mesh.getVerticesData(VertexBuffer.NormalKind); - if (normals === null) throw new Error("Mesh has no normals"); - for(let i = 0; i < normals.length; i++) { - normals[i] = -normals[i]; - } - this.mesh.setVerticesData(VertexBuffer.NormalKind, normals); - }*/ + this.mesh.createNormals(true); + const normals = this.mesh.getVerticesData(VertexBuffer.NormalKind); + if (normals === null) throw new Error("Mesh has no normals"); + for(let i = 0; i < normals.length; i++) { + normals[i] = -normals[i]; + } + this.mesh.setVerticesData(VertexBuffer.NormalKind, normals); +}*/ this.mesh.freezeNormals(); if (this.depth > 3) { @@ -137,21 +135,23 @@ export class PlanetChunk implements Transformable, BoundingSphere { this.onRecieveVertexDataObservable.notifyObservers(); + if (instancesMatrixBuffer.length === 0) return; + const rockPatch = new ThinInstancePatch(this.parent, randomDownSample(alignedInstancesMatrixBuffer, 3200)); rockPatch.createInstances(Assets.Rock); this.instancePatches.push(rockPatch); - if(this.planetModel.physicalProperties.pressure > 0 && this.planetModel.physicalProperties.oceanLevel > 0) { + if (this.planetModel.physicalProperties.pressure > 0 && this.planetModel.physicalProperties.oceanLevel > 0) { const treePatch = new ThinInstancePatch(this.parent, randomDownSample(instancesMatrixBuffer, 4800)); treePatch.createInstances(Assets.Tree); this.instancePatches.push(treePatch); const butterflyPatch = new ThinInstancePatch(this.parent, randomDownSample(instancesMatrixBuffer, 800)); - butterflyPatch.createInstances(createButterfly(this.mesh.getScene())); + butterflyPatch.createInstances(Assets.Butterfly); this.instancePatches.push(butterflyPatch); const grassPatch = new ThinInstancePatch(this.parent, instancesMatrixBuffer); - grassPatch.createInstances(createGrassBlade(this.mesh.getScene(), 3)); + grassPatch.createInstances(Assets.GrassBlade); this.instancePatches.push(grassPatch); } } @@ -231,7 +231,7 @@ export class PlanetChunk implements Transformable, BoundingSphere { const closestPointToCamera = this.getTransform().getAbsolutePosition().add(chunkToCameraDir.scale(this.getBoundingRadius())); const conservativeSphereNormal = closestPointToCamera.subtract(this.parent.getAbsolutePosition()).normalizeToNew(); const observerToCenter = camera.globalPosition.subtract(this.parent.getAbsolutePosition()).normalizeToNew(); - if(Vector3.Dot(observerToCenter, conservativeSphereNormal) < 0) { + if (Vector3.Dot(observerToCenter, conservativeSphereNormal) < 0) { this.mesh.setEnabled(false); return; } diff --git a/src/ts/planemos/telluricPlanemo/terrain/instancePatch/matrixBuffer.ts b/src/ts/planemos/telluricPlanemo/terrain/instancePatch/matrixBuffer.ts index 0516590e3..b243beeae 100644 --- a/src/ts/planemos/telluricPlanemo/terrain/instancePatch/matrixBuffer.ts +++ b/src/ts/planemos/telluricPlanemo/terrain/instancePatch/matrixBuffer.ts @@ -67,3 +67,16 @@ export function decomposeModelMatrix(matrix: Float32Array, position: Vector3, ro rotation.copyFrom(Quaternion.FromRotationMatrix(rotationMatrix)); } + + +export function applyTransformationToBuffer(transformation: Matrix, matrixBuffer: Float32Array): Float32Array { + const nbMatrices = Math.floor(matrixBuffer.length / 16); + const result = new Float32Array(matrixBuffer.length); + for (let i = 0; i < nbMatrices; i++) { + const index = i * 16; + const matrix = Matrix.FromArray(matrixBuffer, index); + matrix.multiplyToRef(transformation, matrix); + matrix.copyToArray(result, index); + } + return result; +} \ No newline at end of file diff --git a/src/ts/planemos/telluricPlanemo/terrain/instancePatch/thinInstancePatch.ts b/src/ts/planemos/telluricPlanemo/terrain/instancePatch/thinInstancePatch.ts index 0fb5b2b34..d6f187d9e 100644 --- a/src/ts/planemos/telluricPlanemo/terrain/instancePatch/thinInstancePatch.ts +++ b/src/ts/planemos/telluricPlanemo/terrain/instancePatch/thinInstancePatch.ts @@ -2,17 +2,22 @@ import { Mesh } from "@babylonjs/core/Meshes/mesh"; import "@babylonjs/core/Meshes/thinInstanceMesh"; import { TransformNode } from "@babylonjs/core/Meshes/transformNode"; import { IPatch } from "./iPatch"; -import { createSquareMatrixBuffer } from "./matrixBuffer"; +import { applyTransformationToBuffer, createSquareMatrixBuffer } from "./matrixBuffer"; import { Vector3 } from "@babylonjs/core/Maths/math.vector"; +import { Observer } from "@babylonjs/core"; export class ThinInstancePatch implements IPatch { private baseMesh: Mesh | null = null; readonly matrixBuffer: Float32Array; + readonly rawMatrixBuffer: Float32Array; + readonly parent: TransformNode; + private parentObserver: Observer | null = null; constructor(parent: TransformNode, matrixBuffer: Float32Array) { this.parent = parent; - this.matrixBuffer = matrixBuffer; + this.rawMatrixBuffer = matrixBuffer; + this.matrixBuffer = applyTransformationToBuffer(parent.computeWorldMatrix(), this.rawMatrixBuffer); } public static CreateSquare(parent: TransformNode, position: Vector3, size: number, resolution: number) { @@ -22,6 +27,7 @@ export class ThinInstancePatch implements IPatch { public clearInstances(): void { if (this.baseMesh === null) return; + this.parent.onAfterWorldMatrixUpdateObservable.remove(this.parentObserver); this.baseMesh.thinInstanceCount = 0; this.baseMesh.dispose(); this.baseMesh = null; @@ -35,14 +41,24 @@ export class ThinInstancePatch implements IPatch { this.baseMesh = baseMesh.clone(); this.baseMesh.makeGeometryUnique(); - this.baseMesh.parent = this.parent; - //this.baseMesh.computeWorldMatrix(true); - //this.baseMesh.parent = null; + let oldPosition = this.parent.getAbsolutePosition().clone(); + this.parentObserver = this.parent.onAfterWorldMatrixUpdateObservable.add(() => { + const newPosition = this.parent.getAbsolutePosition(); + if (newPosition.equals(oldPosition)) return; + oldPosition = newPosition.clone(); + this.syncWithParent(); + }); this.baseMesh.isVisible = true; this.baseMesh.thinInstanceSetBuffer("matrix", this.matrixBuffer, 16); } + public syncWithParent(): void { + if(this.baseMesh === null) throw new Error("Tried to sync with parent but no base mesh was set."); + this.matrixBuffer.set(applyTransformationToBuffer(this.parent.computeWorldMatrix(), this.rawMatrixBuffer)); + this.baseMesh.thinInstanceBufferUpdated("matrix"); + } + public getNbInstances(): number { if (this.baseMesh === null) return 0; return this.baseMesh.thinInstanceCount; diff --git a/src/ts/starSystem/starSystemController.ts b/src/ts/starSystem/starSystemController.ts index 9cf09631e..dabee3bc8 100644 --- a/src/ts/starSystem/starSystemController.ts +++ b/src/ts/starSystem/starSystemController.ts @@ -423,11 +423,6 @@ export class StarSystemController { const displacementTranslation = controller.getTransform().getAbsolutePosition().negate(); this.translateEverythingNow(displacementTranslation); translate(controller.getTransform(), displacementTranslation); - } else { - // FIXME: this is necessary to update every world matrix, it could be done better - const displacementTranslation = Vector3.Zero(); - this.translateEverythingNow(displacementTranslation); - translate(controller.getTransform(), displacementTranslation); } this.updateShaders(deltaTime);