From 992b79f8b703f06677ca38a856b24f0bab7698bc Mon Sep 17 00:00:00 2001 From: RandomityGuy <31925790+RandomityGuy@users.noreply.github.com> Date: Sat, 30 Mar 2024 13:49:05 +0530 Subject: [PATCH] dts optimization: share node transforms --- src/DtsObject.hx | 296 +++++++++++++++++++++---------------- src/MarbleWorld.hx | 1 + src/PreviewWorld.hx | 1 + src/shapes/AntiGravity.hx | 1 + src/shapes/Blast.hx | 1 + src/shapes/DuctFan.hx | 1 + src/shapes/EasterEgg.hx | 1 + src/shapes/Gem.hx | 1 + src/shapes/GemBeam.hx | 1 + src/shapes/Helicopter.hx | 1 + src/shapes/MegaMarble.hx | 1 + src/shapes/Sign.hx | 1 + src/shapes/SignCaution.hx | 1 + src/shapes/SignPlain.hx | 1 + src/shapes/SmallDuctFan.hx | 1 + src/shapes/SuperJump.hx | 1 + src/shapes/SuperSpeed.hx | 1 + src/shapes/TimeTravel.hx | 1 + 18 files changed, 189 insertions(+), 124 deletions(-) diff --git a/src/DtsObject.hx b/src/DtsObject.hx index 94b5cc05..8850585a 100644 --- a/src/DtsObject.hx +++ b/src/DtsObject.hx @@ -97,6 +97,12 @@ class DtsObject extends GameObject { var lastSequenceKeyframes:Map = new Map(); var doSequenceOnce:Bool; var doSequenceOnceBeginTime:Float; + var sharedNodeTransforms:Bool = false; + + static var sharedGraphNodesMap:Map> = []; + + var sharedGraphNodes:Array = []; + var isSharedGraphNodesRoot = false; var graphNodes:Array = []; var dirtyTransforms:Array = []; @@ -358,6 +364,16 @@ class DtsObject extends GameObject { rootObject = new Object(this); + if (this.sharedNodeTransforms) { + if (sharedGraphNodesMap.exists(this.identifier)) { + sharedGraphNodes = sharedGraphNodesMap.get(this.identifier); + isSharedGraphNodesRoot = false; + } else { + sharedGraphNodesMap.set(this.identifier, graphNodes.concat([this.rootObject])); + isSharedGraphNodesRoot = true; + } + } + for (i in rootNodesIdx) { rootObject.addChild(this.graphNodes[i]); } @@ -1143,142 +1159,158 @@ class DtsObject extends GameObject { continue; var t = (actualKeyframe - keyframeLow) % 1; - if (rot > 0) { - for (i in 0...this.dts.nodes.length) { - var affected = ((1 << i) & rot) != 0; + if (rot > 0 || trans > 0 || scale > 0) { + if ((!this.sharedNodeTransforms || this.isSharedGraphNodesRoot)) { + if (rot > 0) { + for (i in 0...this.dts.nodes.length) { + var affected = ((1 << i) & rot) != 0; - if (affected) { - var rot1 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeLow]; - var rot2 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeHigh]; + if (affected) { + var rot1 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeLow]; + var rot2 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeHigh]; - var q1 = new Quat(-rot1.x, rot1.y, rot1.z, -rot1.w); - q1.normalize(); - q1.conjugate(); + var q1 = new Quat(-rot1.x, rot1.y, rot1.z, -rot1.w); + q1.normalize(); + q1.conjugate(); - var q2 = new Quat(-rot2.x, rot2.y, rot2.z, -rot2.w); - q2.normalize(); - q2.conjugate(); + var q2 = new Quat(-rot2.x, rot2.y, rot2.z, -rot2.w); + q2.normalize(); + q2.conjugate(); - var quat = new Quat(); - quat.slerp(q1, q2, t); - quat.normalize(); + var quat = new Quat(); + quat.slerp(q1, q2, t); + quat.normalize(); - this.graphNodes[i].getRotationQuat().load(quat); - this.graphNodes[i].posChanged = true; - propagateDirtyFlags(i); - affectedCount++; - // quaternions.push(quat); - } else { - var rotation = this.dts.defaultRotations[i]; - var quat = new Quat(-rotation.x, rotation.y, rotation.z, -rotation.w); - quat.normalize(); - quat.conjugate(); - this.graphNodes[i].getRotationQuat().load(quat); - this.graphNodes[i].posChanged = true; - // quaternions.push(quat); + this.graphNodes[i].getRotationQuat().load(quat); + this.graphNodes[i].posChanged = true; + propagateDirtyFlags(i); + affectedCount++; + // quaternions.push(quat); + } else { + var rotation = this.dts.defaultRotations[i]; + var quat = new Quat(-rotation.x, rotation.y, rotation.z, -rotation.w); + quat.normalize(); + quat.conjugate(); + this.graphNodes[i].getRotationQuat().load(quat); + this.graphNodes[i].posChanged = true; + // quaternions.push(quat); + } + } } - } - } - affectedCount = 0; - if (trans > 0) { - for (i in 0...this.dts.nodes.length) { - var affected = ((1 << i) & trans) != 0; + affectedCount = 0; + if (trans > 0) { + for (i in 0...this.dts.nodes.length) { + var affected = ((1 << i) & trans) != 0; - if (affected) { - var trans1 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeLow]; - var trans2 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeHigh]; + if (affected) { + var trans1 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeLow]; + var trans2 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeHigh]; - var v1 = new Vector(-trans1.x, trans1.y, trans1.z); - var v2 = new Vector(-trans2.x, trans2.y, trans2.z); - var trans = Util.lerpThreeVectors(v1, v2, t); - this.graphNodes[i].setPosition(trans.x, trans.y, trans.z); - propagateDirtyFlags(i); - affectedCount++; - // translations.push(Util.lerpThreeVectors(v1, v2, t)); - } else { - var translation = this.dts.defaultTranslations[i]; - var trans = new Vector(-translation.x, translation.y, translation.z); - this.graphNodes[i].setPosition(trans.x, trans.y, trans.z); - // translations.push(); + var v1 = new Vector(-trans1.x, trans1.y, trans1.z); + var v2 = new Vector(-trans2.x, trans2.y, trans2.z); + var trans = Util.lerpThreeVectors(v1, v2, t); + this.graphNodes[i].setPosition(trans.x, trans.y, trans.z); + propagateDirtyFlags(i); + affectedCount++; + // translations.push(Util.lerpThreeVectors(v1, v2, t)); + } else { + var translation = this.dts.defaultTranslations[i]; + var trans = new Vector(-translation.x, translation.y, translation.z); + this.graphNodes[i].setPosition(trans.x, trans.y, trans.z); + // translations.push(); + } + } } - } - } - affectedCount = 0; - if (scale > 0) { - if (sequence.flags & 1 > 0) { // Uniform scales - for (i in 0...this.dts.nodes.length) { - var affected = ((1 << i) & scale) != 0; + affectedCount = 0; + if (scale > 0) { + if (sequence.flags & 1 > 0) { // Uniform scales + for (i in 0...this.dts.nodes.length) { + var affected = ((1 << i) & scale) != 0; - if (affected) { - var scale1 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; - var scale2 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; + if (affected) { + var scale1 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; + var scale2 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; - var v1 = new Vector(scale1, scale1, scale1); - var v2 = new Vector(scale2, scale2, scale2); + var v1 = new Vector(scale1, scale1, scale1); + var v2 = new Vector(scale2, scale2, scale2); - var scaleVec = Util.lerpThreeVectors(v1, v2, t); - this.graphNodes[i].scaleX = scaleVec.x; - this.graphNodes[i].scaleY = scaleVec.y; - this.graphNodes[i].scaleZ = scaleVec.z; - affectedCount++; - propagateDirtyFlags(i); - } else { - this.graphNodes[i].scaleX = 1; - this.graphNodes[i].scaleY = 1; - this.graphNodes[i].scaleZ = 1; + var scaleVec = Util.lerpThreeVectors(v1, v2, t); + this.graphNodes[i].scaleX = scaleVec.x; + this.graphNodes[i].scaleY = scaleVec.y; + this.graphNodes[i].scaleZ = scaleVec.z; + affectedCount++; + propagateDirtyFlags(i); + } else { + this.graphNodes[i].scaleX = 1; + this.graphNodes[i].scaleY = 1; + this.graphNodes[i].scaleZ = 1; + } + } + } + + if (sequence.flags & 2 > 0) { // `Aligned` scales + for (i in 0...this.dts.nodes.length) { + var affected = ((1 << i) & scale) != 0; + + if (affected) { + var scale1 = this.dts.nodeAlignedScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; + var scale2 = this.dts.nodeAlignedScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; + + var v1 = new Vector(scale1.x, scale1.y, scale1.z); + var v2 = new Vector(scale2.x, scale2.y, scale2.z); + + var scaleVec = Util.lerpThreeVectors(v1, v2, t); + this.graphNodes[i].scaleX = scaleVec.x; + this.graphNodes[i].scaleY = scaleVec.y; + this.graphNodes[i].scaleZ = scaleVec.z; + affectedCount++; + propagateDirtyFlags(i); + } else { + this.graphNodes[i].scaleX = 1; + this.graphNodes[i].scaleY = 1; + this.graphNodes[i].scaleZ = 1; + } + } + } + + if (sequence.flags & 4 > 0) { // Arbitrary scales + for (i in 0...this.dts.nodes.length) { + var affected = ((1 << i) & scale) != 0; + + if (affected) { + var scale1 = this.dts.nodeArbitraryScaleFactors[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; + var scale2 = this.dts.nodeArbitraryScaleFactors[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; + + var v1 = new Vector(scale1.x, scale1.y, scale1.z); + var v2 = new Vector(scale2.x, scale2.y, scale2.z); + + var scaleVec = Util.lerpThreeVectors(v1, v2, t); + this.graphNodes[i].scaleX = scaleVec.x; + this.graphNodes[i].scaleY = scaleVec.y; + this.graphNodes[i].scaleZ = scaleVec.z; + affectedCount++; + propagateDirtyFlags(i); + } else { + this.graphNodes[i].scaleX = 1; + this.graphNodes[i].scaleY = 1; + this.graphNodes[i].scaleZ = 1; + } + } } } } - - if (sequence.flags & 2 > 0) { // `Aligned` scales + if (this.sharedNodeTransforms && !this.isSharedGraphNodesRoot) { for (i in 0...this.dts.nodes.length) { - var affected = ((1 << i) & scale) != 0; - - if (affected) { - var scale1 = this.dts.nodeAlignedScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; - var scale2 = this.dts.nodeAlignedScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; - - var v1 = new Vector(scale1.x, scale1.y, scale1.z); - var v2 = new Vector(scale2.x, scale2.y, scale2.z); - - var scaleVec = Util.lerpThreeVectors(v1, v2, t); - this.graphNodes[i].scaleX = scaleVec.x; - this.graphNodes[i].scaleY = scaleVec.y; - this.graphNodes[i].scaleZ = scaleVec.z; - affectedCount++; - propagateDirtyFlags(i); - } else { - this.graphNodes[i].scaleX = 1; - this.graphNodes[i].scaleY = 1; - this.graphNodes[i].scaleZ = 1; - } - } - } - - if (sequence.flags & 4 > 0) { // Arbitrary scales - for (i in 0...this.dts.nodes.length) { - var affected = ((1 << i) & scale) != 0; - - if (affected) { - var scale1 = this.dts.nodeArbitraryScaleFactors[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; - var scale2 = this.dts.nodeArbitraryScaleFactors[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; - - var v1 = new Vector(scale1.x, scale1.y, scale1.z); - var v2 = new Vector(scale2.x, scale2.y, scale2.z); - - var scaleVec = Util.lerpThreeVectors(v1, v2, t); - this.graphNodes[i].scaleX = scaleVec.x; - this.graphNodes[i].scaleY = scaleVec.y; - this.graphNodes[i].scaleZ = scaleVec.z; - affectedCount++; - propagateDirtyFlags(i); - } else { - this.graphNodes[i].scaleX = 1; - this.graphNodes[i].scaleY = 1; - this.graphNodes[i].scaleZ = 1; - } + var node = this.graphNodes[i]; + var shared = this.sharedGraphNodes[i]; + node.setPosition(shared.x, shared.y, shared.z); + node.qRot.load(shared.qRot); + node.scaleX = shared.scaleX; + node.scaleY = shared.scaleY; + node.scaleZ = shared.scaleZ; + // this.graphNodes[i].setTransform(this.sharedGraphNodes[i].getTransform()); } } } @@ -1422,14 +1454,25 @@ class DtsObject extends GameObject { } if (this.ambientRotate) { - var spinAnimation = new Quat(); - spinAnimation.initRotateAxis(0, 0, -1, timeState.timeSinceLoad * this.ambientSpinFactor); + if ((!this.sharedNodeTransforms || this.isSharedGraphNodesRoot)) { + var spinAnimation = new Quat(); + spinAnimation.initRotateAxis(0, 0, -1, timeState.timeSinceLoad * this.ambientSpinFactor); - // var orientation = this.getRotationQuat(); - // spinAnimation.multiply(orientation, spinAnimation); + // var orientation = this.getRotationQuat(); + // spinAnimation.multiply(orientation, spinAnimation); - this.rootObject.getRotationQuat().load(spinAnimation); - this.rootObject.posChanged = true; + this.rootObject.getRotationQuat().load(spinAnimation); + this.rootObject.posChanged = true; + } + if (this.sharedNodeTransforms && !this.isSharedGraphNodesRoot) { + var node = this.rootObject; + var shared = this.sharedGraphNodes[this.sharedGraphNodes.length - 1]; + node.setPosition(shared.x, shared.y, shared.z); + node.qRot.load(shared.qRot); + node.scaleX = shared.scaleX; + node.scaleY = shared.scaleY; + node.scaleZ = shared.scaleZ; + } } for (i in 0...this.colliders.length) { @@ -1529,6 +1572,11 @@ class DtsObject extends GameObject { this.level = null; boundingCollider = null; colliders = null; + sharedGraphNodes = null; this.dtsResource.release(); } + + public static function disposeShared() { + sharedGraphNodesMap = []; + } } diff --git a/src/MarbleWorld.hx b/src/MarbleWorld.hx index fb0969d9..e7373bf5 100644 --- a/src/MarbleWorld.hx +++ b/src/MarbleWorld.hx @@ -2327,6 +2327,7 @@ class MarbleWorld extends Scheduler { dtsObject.dispose(); } dtsObjects = null; + DtsObject.disposeShared(); powerUps = []; for (trigger in this.triggers) { trigger.dispose(); diff --git a/src/PreviewWorld.hx b/src/PreviewWorld.hx index 9e37d326..9adbbeae 100644 --- a/src/PreviewWorld.hx +++ b/src/PreviewWorld.hx @@ -301,6 +301,7 @@ class PreviewWorld extends Scheduler { for (marb in marbles) { marb.dispose(); } + DtsObject.disposeShared(); interiors = []; dtsObjects = []; marbles = []; diff --git a/src/shapes/AntiGravity.hx b/src/shapes/AntiGravity.hx index eebad1d9..b4e0ec52 100644 --- a/src/shapes/AntiGravity.hx +++ b/src/shapes/AntiGravity.hx @@ -21,6 +21,7 @@ class AntiGravity extends PowerUp { this.autoUse = true; this.useInstancing = true; this.animateSubObjectOpacities = true; + this.sharedNodeTransforms = true; if (MisParser.parseBoolean(element.permanent)) norespawn = true; if (norespawn) diff --git a/src/shapes/Blast.hx b/src/shapes/Blast.hx index 210431ad..9bb2642b 100644 --- a/src/shapes/Blast.hx +++ b/src/shapes/Blast.hx @@ -17,6 +17,7 @@ class Blast extends PowerUp { this.pickUpName = "a Blast powerup"; this.ambientRotate = false; this.autoUse = true; + this.sharedNodeTransforms = true; } public override function init(level:MarbleWorld, onFinish:Void->Void) { diff --git a/src/shapes/DuctFan.hx b/src/shapes/DuctFan.hx index 1f060368..d1ab7fef 100644 --- a/src/shapes/DuctFan.hx +++ b/src/shapes/DuctFan.hx @@ -15,6 +15,7 @@ class DuctFan extends ForceObject { this.dtsPath = "data/shapes/hazards/ductfan.dts"; this.isCollideable = true; this.isTSStatic = false; + this.sharedNodeTransforms = true; this.identifier = "DuctFan"; this.forceDatas = [ { diff --git a/src/shapes/EasterEgg.hx b/src/shapes/EasterEgg.hx index ef3f6067..f838a6f0 100644 --- a/src/shapes/EasterEgg.hx +++ b/src/shapes/EasterEgg.hx @@ -15,6 +15,7 @@ class EasterEgg extends PowerUp { this.identifier = "EasterEgg"; this.pickUpName = "Easter Egg"; this.autoUse = true; + this.sharedNodeTransforms = true; } public function pickUp(marble:Marble):Bool { diff --git a/src/shapes/Gem.hx b/src/shapes/Gem.hx index e5ef8869..4402ff04 100644 --- a/src/shapes/Gem.hx +++ b/src/shapes/Gem.hx @@ -26,6 +26,7 @@ class Gem extends DtsObject { this.isBoundingBoxCollideable = true; pickedUp = false; useInstancing = true; + this.sharedNodeTransforms = true; showSequences = false; // Gems actually have an animation for the little shiny thing, but the actual game ignores that. I get it, it was annoying as hell. var GEM_COLORS = ["red"]; diff --git a/src/shapes/GemBeam.hx b/src/shapes/GemBeam.hx index aa6e9bd2..cd608ae8 100644 --- a/src/shapes/GemBeam.hx +++ b/src/shapes/GemBeam.hx @@ -12,6 +12,7 @@ class GemBeam extends DtsObject { this.isTSStatic = false; this.identifier = "GemBeam"; this.useInstancing = true; + this.sharedNodeTransforms = true; this.animateSubObjectOpacities = true; } diff --git a/src/shapes/Helicopter.hx b/src/shapes/Helicopter.hx index 6069047b..98720747 100644 --- a/src/shapes/Helicopter.hx +++ b/src/shapes/Helicopter.hx @@ -17,6 +17,7 @@ class Helicopter extends PowerUp { this.isTSStatic = false; this.showSequences = false; this.identifier = "Helicopter"; + this.sharedNodeTransforms = true; this.pickUpName = "a Gyrocopter powerup"; } diff --git a/src/shapes/MegaMarble.hx b/src/shapes/MegaMarble.hx index ba329160..d1cbea23 100644 --- a/src/shapes/MegaMarble.hx +++ b/src/shapes/MegaMarble.hx @@ -16,6 +16,7 @@ class MegaMarble extends PowerUp { this.isTSStatic = false; this.showSequences = true; this.identifier = "MegaMarble"; + this.sharedNodeTransforms = true; this.pickUpName = "a Mega-Marble powerup"; } diff --git a/src/shapes/Sign.hx b/src/shapes/Sign.hx index 04346214..d3af36a7 100644 --- a/src/shapes/Sign.hx +++ b/src/shapes/Sign.hx @@ -10,6 +10,7 @@ class Sign extends DtsObject { this.dtsPath = "data/shapes/signs/sign.dts"; this.isCollideable = true; this.useInstancing = true; + this.sharedNodeTransforms = true; var d = ""; if (element.datablock.toLowerCase() != 'arrow') { diff --git a/src/shapes/SignCaution.hx b/src/shapes/SignCaution.hx index b31890f7..bf9c7791 100644 --- a/src/shapes/SignCaution.hx +++ b/src/shapes/SignCaution.hx @@ -10,6 +10,7 @@ class SignCaution extends DtsObject { this.dtsPath = "data/shapes/signs/cautionsign.dts"; this.isCollideable = true; this.useInstancing = true; + this.sharedNodeTransforms = true; var type = element.datablock.substring("SignCaution".length).toLowerCase(); switch (type) { diff --git a/src/shapes/SignPlain.hx b/src/shapes/SignPlain.hx index aa6e4f57..07891b62 100644 --- a/src/shapes/SignPlain.hx +++ b/src/shapes/SignPlain.hx @@ -10,6 +10,7 @@ class SignPlain extends DtsObject { this.isCollideable = true; this.useInstancing = true; + this.sharedNodeTransforms = true; // Determine the direction to show var direction = element.datablock.substring("Arrow".length).toLowerCase(); diff --git a/src/shapes/SmallDuctFan.hx b/src/shapes/SmallDuctFan.hx index 0dc41287..f08da242 100644 --- a/src/shapes/SmallDuctFan.hx +++ b/src/shapes/SmallDuctFan.hx @@ -15,6 +15,7 @@ class SmallDuctFan extends ForceObject { this.dtsPath = "data/shapes/hazards/ductfan.dts"; this.isCollideable = true; this.isTSStatic = false; + this.sharedNodeTransforms = true; this.identifier = "DuctFan"; this.forceDatas = [ { diff --git a/src/shapes/SuperJump.hx b/src/shapes/SuperJump.hx index 50a78e03..8536d85b 100644 --- a/src/shapes/SuperJump.hx +++ b/src/shapes/SuperJump.hx @@ -49,6 +49,7 @@ class SuperJump extends PowerUp { this.identifier = "SuperJump"; this.pickUpName = "a Super Jump powerup"; this.showSequences = false; + this.sharedNodeTransforms = true; sjEmitterParticleData = new ParticleData(); sjEmitterParticleData.identifier = "superJumpParticle"; sjEmitterParticleData.texture = ResourceLoader.getResource("data/particles/twirl.png", ResourceLoader.getTexture, this.textureResources); diff --git a/src/shapes/SuperSpeed.hx b/src/shapes/SuperSpeed.hx index 468df760..edd00cb2 100644 --- a/src/shapes/SuperSpeed.hx +++ b/src/shapes/SuperSpeed.hx @@ -50,6 +50,7 @@ class SuperSpeed extends PowerUp { this.identifier = "SuperSpeed"; this.pickUpName = "a Super Speed powerup"; this.useInstancing = true; + this.sharedNodeTransforms = true; ssEmitterParticleData = new ParticleData(); ssEmitterParticleData.identifier = "superSpeedParticle"; ssEmitterParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources); diff --git a/src/shapes/TimeTravel.hx b/src/shapes/TimeTravel.hx index 487cacc0..ce76c953 100644 --- a/src/shapes/TimeTravel.hx +++ b/src/shapes/TimeTravel.hx @@ -16,6 +16,7 @@ class TimeTravel extends PowerUp { this.dtsPath = "data/shapes/items/timetravel.dts"; this.isCollideable = false; this.isTSStatic = false; + this.sharedNodeTransforms = true; this.identifier = "TimeTravel"; if (element.timebonus != null) {