Skip to content

Commit

Permalink
Merge pull request #11 from BarthPaleologue/WIP
Browse files Browse the repository at this point in the history
Instance performance fixes
  • Loading branch information
BarthPaleologue authored Jan 15, 2024
2 parents 437ac39 + 092abb6 commit c99e889
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 28 deletions.
14 changes: 12 additions & 2 deletions src/ts/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand All @@ -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) {
Expand Down
14 changes: 14 additions & 0 deletions src/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
30 changes: 15 additions & 15 deletions src/ts/planemos/telluricPlanemo/terrain/chunks/planetChunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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<number>();

Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TransformNode> | 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) {
Expand All @@ -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;
Expand All @@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/ts/starSystem/starSystemController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit c99e889

Please sign in to comment.