fix magnet sound persist, use BVH for interior CD (cleaner, similar perf), cull instances not in frustum, overall FPS improvements

This commit is contained in:
RandomityGuy 2022-12-04 14:41:04 +05:30
parent b7652955b5
commit ba5f06a40d
7 changed files with 226 additions and 21 deletions

View file

@ -1,5 +1,6 @@
package src;
import h3d.prim.Instanced;
import h3d.shader.pbr.PropsValues;
import shaders.Billboard;
import shaders.DtsTexture;
@ -37,8 +38,21 @@ class InstanceManager {
public function update(dt:Float) {
for (meshes in objects) {
for (minfo in meshes) {
var visibleinstances = [];
// Culling
if (minfo.meshbatch != null || minfo.transparencymeshbatch != null) {
for (inst in minfo.instances) {
var objBounds = @:privateAccess cast(minfo.meshbatch.primitive, Instanced).baseBounds.clone();
objBounds.transform(inst.emptyObj.getAbsPos());
if (scene.camera.frustum.hasBounds(objBounds)) {
visibleinstances.push(inst);
}
}
}
// Emit non culled primitives
if (minfo.meshbatch != null) {
var opaqueinstances = minfo.instances.filter(x -> x.gameObject.currentOpacity == 1);
var opaqueinstances = visibleinstances.filter(x -> x.gameObject.currentOpacity == 1);
minfo.meshbatch.begin(opaqueinstances.length);
for (instance in opaqueinstances) { // Draw the opaque shit first
var dtsShader = minfo.meshbatch.material.mainPass.getShader(DtsTexture);
@ -53,7 +67,7 @@ class InstanceManager {
}
}
if (minfo.transparencymeshbatch != null) {
var transparentinstances = minfo.instances.filter(x -> x.gameObject.currentOpacity != 1);
var transparentinstances = visibleinstances.filter(x -> x.gameObject.currentOpacity != 1);
minfo.transparencymeshbatch.begin(transparentinstances.length);
for (instance in transparentinstances) { // Non opaque shit
var dtsShader = minfo.transparencymeshbatch.material.mainPass.getShader(DtsTexture);

View file

@ -248,7 +248,7 @@ class Marble extends GameObject {
// mat.mainPass.culling = None;
if (Settings.optionsSettings.reflectiveMarble) {
this.cubemapRenderer = new CubemapRenderer(level.scene);
this.cubemapRenderer = new CubemapRenderer(level.scene, level.sky);
mat.mainPass.addShader(new MarbleReflection(this.cubemapRenderer.cubemap));
}
}
@ -833,7 +833,7 @@ class Marble extends GameObject {
+ relLocalVel.z * deltaT * 2,
radius * 1.1);
var surfaces = obj.grid == null ? obj.octree.boundingSearch(boundThing).map(x -> cast x) : obj.grid.boundingSearch(boundThing);
var surfaces = obj.bvh == null ? obj.octree.boundingSearch(boundThing).map(x -> cast x) : obj.bvh.boundingSearch(boundThing);
for (surf in surfaces) {
var surface:CollisionSurface = cast surf;
@ -1118,7 +1118,7 @@ class Marble extends GameObject {
boundThing.addSpherePos(localpos.x + relLocalVel.x * dt * 2, localpos.y + relLocalVel.y * dt * 2, localpos.z + relLocalVel.z * dt * 2,
radius * 1.1);
var surfaces = obj.grid == null ? obj.octree.boundingSearch(boundThing).map(x -> cast x) : obj.grid.boundingSearch(boundThing);
var surfaces = obj.bvh == null ? obj.octree.boundingSearch(boundThing).map(x -> cast x) : obj.bvh.boundingSearch(boundThing);
var tform = obj.transform.clone();

View file

@ -20,6 +20,8 @@ import src.ResourceLoaderWorker;
class Sky extends Object {
public var dmlPath:String;
public var cubemap:Texture;
var imageResources:Array<Resource<Image>> = [];
public function new() {
@ -51,6 +53,7 @@ class Sky extends Object {
var shad = new Skybox(texture);
skyMesh.material.mainPass.addShader(shad);
skyMesh.material.mainPass.depthWrite = false;
cubemap = texture;
onFinish();
});
// skyMesh.material.shadows = false;

183
src/collision/BVHTree.hx Normal file
View file

@ -0,0 +1,183 @@
package collision;
import h3d.col.Bounds;
import h3d.Vector;
// https://github.com/Sopiro/DynamicBVH/blob/master/src/aabbtree.ts
@:publicFields
class BVHNode {
var bounds:Bounds;
var objects:Array<CollisionSurface>;
var objectBounds:Bounds; // total bounds for objects stored in THIS node
var left:BVHNode;
var right:BVHNode;
var surfaceArea:Float;
public function new(bounds:Bounds) {
this.bounds = bounds.clone();
surfaceArea = this.bounds.xSize * this.bounds.ySize + this.bounds.xSize * this.bounds.zSize + this.bounds.ySize * this.bounds.zSize;
}
function getSplitCost(objs:Array<{obj:CollisionSurface, centroid:h3d.col.Point}>, axis:Int) {
// Pick best axis to split
switch (axis) {
case 0:
objs.sort((x, y) -> x.centroid.x > y.centroid.x ? 1 : -1);
case 1:
objs.sort((x, y) -> x.centroid.y > y.centroid.y ? 1 : -1);
case 2:
objs.sort((x, y) -> x.centroid.z > y.centroid.z ? 1 : -1);
};
var leftObjects = objs.slice(0, Math.ceil(objs.length / 2));
var rightObjects = objs.slice(Math.ceil(objs.length / 2));
var leftAABB = new Bounds();
var rightAABB = new Bounds();
for (o in leftObjects)
leftAABB.add(o.obj.boundingBox);
for (o in rightObjects)
rightAABB.add(o.obj.boundingBox);
var leftSA = leftAABB.xSize * leftAABB.ySize + leftAABB.xSize * leftAABB.zSize + leftAABB.ySize * leftAABB.zSize;
var rightSA = rightAABB.xSize * rightAABB.ySize + rightAABB.xSize * rightAABB.zSize + rightAABB.ySize * rightAABB.zSize;
var splitCost = leftSA + rightSA;
var bestSplit = {
cost: splitCost,
left: leftObjects,
right: rightObjects,
leftBounds: leftAABB,
rightBounds: rightAABB,
axis: axis
};
return bestSplit;
}
public function split() {
// Splitting first time
// Calculate the centroids of all objects
var objs = objects.map(x -> {
x.generateBoundingBox();
return {obj: x, centroid: x.boundingBox.getCenter()};
});
// Find the best split cost
var costs = [getSplitCost(objs, 0), getSplitCost(objs, 1), getSplitCost(objs, 2)];
costs.sort((x, y) -> x.cost > y.cost ? 1 : -1);
var bestSplit = costs[0];
// Sort the objects according to where they should go
var leftObjs = [];
var rightObjs = [];
var intersectObjs = [];
for (o in bestSplit.left.concat(bestSplit.right)) {
var inleft = bestSplit.leftBounds.containsBounds(o.obj.boundingBox);
var inright = bestSplit.rightBounds.containsBounds(o.obj.boundingBox);
if (inleft && inright) {
intersectObjs.push(o.obj);
} else if (inleft) {
leftObjs.push(o.obj);
} else if (inright) {
rightObjs.push(o.obj);
}
}
// Only one side has objects, egh
if (leftObjs.length == 0 || rightObjs.length == 0) {
var thisobjs = leftObjs.concat(rightObjs).concat(intersectObjs);
this.objects = thisobjs;
this.objectBounds = new Bounds();
for (o in thisobjs)
this.objectBounds.add(o.boundingBox);
return;
}
// Make the child nodes
var leftBounds = new Bounds();
var rightBounds = new Bounds();
for (o in leftObjs)
leftBounds.add(o.boundingBox);
for (o in rightObjs)
rightBounds.add(o.boundingBox);
left = new BVHNode(leftBounds);
right = new BVHNode(rightBounds);
left.objects = leftObjs;
right.objects = rightObjs;
this.objects = intersectObjs;
this.objectBounds = new Bounds();
for (o in intersectObjs)
this.objectBounds.add(o.boundingBox);
left.split();
right.split();
}
public function boundingSearch(searchbox:Bounds) {
if (this.bounds.containsBounds(searchbox) || this.bounds.collide(searchbox)) {
var intersects = [];
if (this.left != null && this.right != null) {
intersects = intersects.concat(this.left.boundingSearch(searchbox));
intersects = intersects.concat(this.right.boundingSearch(searchbox));
}
if (this.objectBounds.collide(searchbox) || this.objectBounds.containsBounds(searchbox)) {
for (o in this.objects) {
if (o.boundingBox.containsBounds(searchbox) || o.boundingBox.collide(searchbox))
intersects.push(o);
}
}
return intersects;
} else {
return [];
}
}
public function rayCast(origin:Vector, direction:Vector) {
var ray = h3d.col.Ray.fromValues(origin.x, origin.y, origin.z, direction.x, direction.y, direction.z);
if (ray.collide(this.bounds)) {
var intersects = [];
if (this.left != null && this.right != null) {
intersects = intersects.concat(this.left.rayCast(origin, direction));
intersects = intersects.concat(this.right.rayCast(origin, direction));
}
if (ray.collide(this.objectBounds)) {
for (o in this.objects) {
if (ray.collide(o.boundingBox))
intersects = intersects.concat(o.rayCast(origin, direction));
}
}
return intersects;
} else {
return [];
}
}
}
class BVHTree {
public var bounds:Bounds;
var surfaces:Array<CollisionSurface> = [];
var root:BVHNode;
public function new(bounds:Bounds) {
this.bounds = bounds.clone();
}
public function insert(surf:CollisionSurface) {
surfaces.push(surf);
}
public function build() {
root = new BVHNode(bounds);
// Add all children
root.objects = this.surfaces;
root.split();
}
public function boundingSearch(searchbox:Bounds) {
return this.root.boundingSearch(searchbox);
}
public function rayCast(origin:Vector, direction:Vector) {
return this.root.rayCast(origin, direction);
}
}

View file

@ -19,7 +19,7 @@ class CollisionEntity implements IOctreeObject {
public var octree:Octree;
public var grid:Grid;
public var bvh:BVHTree;
public var surfaces:Array<CollisionSurface>;
@ -52,13 +52,14 @@ class CollisionEntity implements IOctreeObject {
}
}
// Generates the grid
// Generates the bvh
public function finalize() {
this.generateBoundingBox();
this.grid = new Grid(this.boundingBox);
this.bvh = new BVHTree(this.boundingBox);
for (surface in this.surfaces) {
this.grid.insert(surface);
this.bvh.insert(surface);
}
this.bvh.build();
}
public function setTransform(transform:Matrix) {
@ -102,7 +103,7 @@ class CollisionEntity implements IOctreeObject {
var rStart = rayOrigin.clone();
rStart.transform(invMatrix);
var rDir = rayDirection.transformed3x3(invMatrix);
if (grid == null) {
if (bvh == null) {
var intersections = octree.raycast(rStart, rDir);
var iData:Array<RayIntersectionData> = [];
for (i in intersections) {
@ -113,7 +114,7 @@ class CollisionEntity implements IOctreeObject {
}
return iData;
} else {
var intersections = this.grid.rayCast(rStart, rDir);
var intersections = this.bvh.rayCast(rStart, rDir);
for (i in intersections) {
i.point.transform(transform);
i.normal.transform3x3(transform);
@ -146,7 +147,7 @@ class CollisionEntity implements IOctreeObject {
sphereBounds.addSpherePos(position.x, position.y, position.z, radius * 1.1);
sphereBounds.transform(invMatrix);
sphereBounds.addSpherePos(localPos.x, localPos.y, localPos.z, radius * 1.1);
var surfaces = grid == null ? octree.boundingSearch(sphereBounds).map(x -> cast x) : grid.boundingSearch(sphereBounds);
var surfaces = bvh == null ? octree.boundingSearch(sphereBounds).map(x -> cast x) : bvh.boundingSearch(sphereBounds);
var tform = transform.clone();
// tform.setPosition(tform.getPosition().add(this.velocity.multiply(timeState.dt)));

View file

@ -1,5 +1,6 @@
package shaders;
import src.Sky;
import h3d.Vector;
import h3d.scene.Scene;
import h3d.Engine;
@ -8,18 +9,17 @@ import h3d.mat.Texture;
class CubemapRenderer {
public var cubemap:Texture;
public var sky:Sky;
public var position:Vector;
var camera:Camera;
var scene:Scene;
var nextFaceToRender:Int;
public function new(scene:Scene) {
public function new(scene:Scene, sky:Sky) {
this.scene = scene;
this.cubemap = new Texture(128, 128, [Cube, Dynamic, Target]);
this.sky = sky;
this.cubemap = new Texture(128, 128, [Cube, Dynamic, Target], h3d.mat.Data.TextureFormat.RGB8);
this.camera = new Camera(90, 1, 1, 0.02);
this.position = new Vector();
this.nextFaceToRender = 0;

View file

@ -43,10 +43,14 @@ class Magnet extends ForceObject {
public override function update(timeState:src.TimeState) {
super.update(timeState);
var seffect = this.soundChannel.getEffect(Spatialization);
seffect.position = this.getAbsPos().getPosition();
if (this.soundChannel != null) {
var seffect = this.soundChannel.getEffect(Spatialization);
seffect.position = this.getAbsPos().getPosition();
seffect.fadeDistance = 15;
// seffect.maxDistance = 5;
if (this.soundChannel.pause)
this.soundChannel.pause = false;
if (this.soundChannel.pause)
this.soundChannel.pause = false;
}
}
}