dts optimization: share node transforms

This commit is contained in:
RandomityGuy 2024-03-30 13:49:05 +05:30
parent ac85f49705
commit afe4969887
18 changed files with 189 additions and 124 deletions

View file

@ -97,6 +97,12 @@ class DtsObject extends GameObject {
var lastSequenceKeyframes:Map<Sequence, Float> = new Map(); var lastSequenceKeyframes:Map<Sequence, Float> = new Map();
var doSequenceOnce:Bool; var doSequenceOnce:Bool;
var doSequenceOnceBeginTime:Float; var doSequenceOnceBeginTime:Float;
var sharedNodeTransforms:Bool = false;
static var sharedGraphNodesMap:Map<String, Array<Object>> = [];
var sharedGraphNodes:Array<Object> = [];
var isSharedGraphNodesRoot = false;
var graphNodes:Array<Object> = []; var graphNodes:Array<Object> = [];
var dirtyTransforms:Array<Bool> = []; var dirtyTransforms:Array<Bool> = [];
@ -358,6 +364,16 @@ class DtsObject extends GameObject {
rootObject = new Object(this); 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) { for (i in rootNodesIdx) {
rootObject.addChild(this.graphNodes[i]); rootObject.addChild(this.graphNodes[i]);
} }
@ -1143,142 +1159,158 @@ class DtsObject extends GameObject {
continue; continue;
var t = (actualKeyframe - keyframeLow) % 1; var t = (actualKeyframe - keyframeLow) % 1;
if (rot > 0) { if (rot > 0 || trans > 0 || scale > 0) {
for (i in 0...this.dts.nodes.length) { if ((!this.sharedNodeTransforms || this.isSharedGraphNodesRoot)) {
var affected = ((1 << i) & rot) != 0; if (rot > 0) {
for (i in 0...this.dts.nodes.length) {
var affected = ((1 << i) & rot) != 0;
if (affected) { if (affected) {
var rot1 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeLow]; var rot1 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeLow];
var rot2 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeHigh]; var rot2 = this.dts.nodeRotations[sequence.baseRotation + sequence.numKeyFrames * affectedCount + keyframeHigh];
var q1 = new Quat(-rot1.x, rot1.y, rot1.z, -rot1.w); var q1 = new Quat(-rot1.x, rot1.y, rot1.z, -rot1.w);
q1.normalize(); q1.normalize();
q1.conjugate(); q1.conjugate();
var q2 = new Quat(-rot2.x, rot2.y, rot2.z, -rot2.w); var q2 = new Quat(-rot2.x, rot2.y, rot2.z, -rot2.w);
q2.normalize(); q2.normalize();
q2.conjugate(); q2.conjugate();
var quat = new Quat(); var quat = new Quat();
quat.slerp(q1, q2, t); quat.slerp(q1, q2, t);
quat.normalize(); quat.normalize();
this.graphNodes[i].getRotationQuat().load(quat); this.graphNodes[i].getRotationQuat().load(quat);
this.graphNodes[i].posChanged = true; this.graphNodes[i].posChanged = true;
propagateDirtyFlags(i); propagateDirtyFlags(i);
affectedCount++; affectedCount++;
// quaternions.push(quat); // quaternions.push(quat);
} else { } else {
var rotation = this.dts.defaultRotations[i]; var rotation = this.dts.defaultRotations[i];
var quat = new Quat(-rotation.x, rotation.y, rotation.z, -rotation.w); var quat = new Quat(-rotation.x, rotation.y, rotation.z, -rotation.w);
quat.normalize(); quat.normalize();
quat.conjugate(); quat.conjugate();
this.graphNodes[i].getRotationQuat().load(quat); this.graphNodes[i].getRotationQuat().load(quat);
this.graphNodes[i].posChanged = true; this.graphNodes[i].posChanged = true;
// quaternions.push(quat); // quaternions.push(quat);
}
}
} }
}
}
affectedCount = 0; affectedCount = 0;
if (trans > 0) { if (trans > 0) {
for (i in 0...this.dts.nodes.length) { for (i in 0...this.dts.nodes.length) {
var affected = ((1 << i) & trans) != 0; var affected = ((1 << i) & trans) != 0;
if (affected) { if (affected) {
var trans1 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeLow]; var trans1 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeLow];
var trans2 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeHigh]; var trans2 = this.dts.nodeTranslations[sequence.baseTranslation + sequence.numKeyFrames * affectedCount + keyframeHigh];
var v1 = new Vector(-trans1.x, trans1.y, trans1.z); var v1 = new Vector(-trans1.x, trans1.y, trans1.z);
var v2 = new Vector(-trans2.x, trans2.y, trans2.z); var v2 = new Vector(-trans2.x, trans2.y, trans2.z);
var trans = Util.lerpThreeVectors(v1, v2, t); var trans = Util.lerpThreeVectors(v1, v2, t);
this.graphNodes[i].setPosition(trans.x, trans.y, trans.z); this.graphNodes[i].setPosition(trans.x, trans.y, trans.z);
propagateDirtyFlags(i); propagateDirtyFlags(i);
affectedCount++; affectedCount++;
// translations.push(Util.lerpThreeVectors(v1, v2, t)); // translations.push(Util.lerpThreeVectors(v1, v2, t));
} else { } else {
var translation = this.dts.defaultTranslations[i]; var translation = this.dts.defaultTranslations[i];
var trans = new Vector(-translation.x, translation.y, translation.z); var trans = new Vector(-translation.x, translation.y, translation.z);
this.graphNodes[i].setPosition(trans.x, trans.y, trans.z); this.graphNodes[i].setPosition(trans.x, trans.y, trans.z);
// translations.push(); // translations.push();
}
}
} }
}
}
affectedCount = 0; affectedCount = 0;
if (scale > 0) { if (scale > 0) {
if (sequence.flags & 1 > 0) { // Uniform scales if (sequence.flags & 1 > 0) { // Uniform scales
for (i in 0...this.dts.nodes.length) { for (i in 0...this.dts.nodes.length) {
var affected = ((1 << i) & scale) != 0; var affected = ((1 << i) & scale) != 0;
if (affected) { if (affected) {
var scale1 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; var scale1 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow];
var scale2 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; var scale2 = this.dts.nodeUniformScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh];
var v1 = new Vector(scale1, scale1, scale1); var v1 = new Vector(scale1, scale1, scale1);
var v2 = new Vector(scale2, scale2, scale2); var v2 = new Vector(scale2, scale2, scale2);
var scaleVec = Util.lerpThreeVectors(v1, v2, t); var scaleVec = Util.lerpThreeVectors(v1, v2, t);
this.graphNodes[i].scaleX = scaleVec.x; this.graphNodes[i].scaleX = scaleVec.x;
this.graphNodes[i].scaleY = scaleVec.y; this.graphNodes[i].scaleY = scaleVec.y;
this.graphNodes[i].scaleZ = scaleVec.z; this.graphNodes[i].scaleZ = scaleVec.z;
affectedCount++; affectedCount++;
propagateDirtyFlags(i); propagateDirtyFlags(i);
} else { } else {
this.graphNodes[i].scaleX = 1; this.graphNodes[i].scaleX = 1;
this.graphNodes[i].scaleY = 1; this.graphNodes[i].scaleY = 1;
this.graphNodes[i].scaleZ = 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 (this.sharedNodeTransforms && !this.isSharedGraphNodesRoot) {
if (sequence.flags & 2 > 0) { // `Aligned` scales
for (i in 0...this.dts.nodes.length) { for (i in 0...this.dts.nodes.length) {
var affected = ((1 << i) & scale) != 0; var node = this.graphNodes[i];
var shared = this.sharedGraphNodes[i];
if (affected) { node.setPosition(shared.x, shared.y, shared.z);
var scale1 = this.dts.nodeAlignedScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeLow]; node.qRot.load(shared.qRot);
var scale2 = this.dts.nodeAlignedScales[sequence.baseScale + sequence.numKeyFrames * affectedCount + keyframeHigh]; node.scaleX = shared.scaleX;
node.scaleY = shared.scaleY;
var v1 = new Vector(scale1.x, scale1.y, scale1.z); node.scaleZ = shared.scaleZ;
var v2 = new Vector(scale2.x, scale2.y, scale2.z); // this.graphNodes[i].setTransform(this.sharedGraphNodes[i].getTransform());
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;
}
} }
} }
} }
@ -1422,14 +1454,25 @@ class DtsObject extends GameObject {
} }
if (this.ambientRotate) { if (this.ambientRotate) {
var spinAnimation = new Quat(); if ((!this.sharedNodeTransforms || this.isSharedGraphNodesRoot)) {
spinAnimation.initRotateAxis(0, 0, -1, timeState.timeSinceLoad * this.ambientSpinFactor); var spinAnimation = new Quat();
spinAnimation.initRotateAxis(0, 0, -1, timeState.timeSinceLoad * this.ambientSpinFactor);
// var orientation = this.getRotationQuat(); // var orientation = this.getRotationQuat();
// spinAnimation.multiply(orientation, spinAnimation); // spinAnimation.multiply(orientation, spinAnimation);
this.rootObject.getRotationQuat().load(spinAnimation); this.rootObject.getRotationQuat().load(spinAnimation);
this.rootObject.posChanged = true; 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) { for (i in 0...this.colliders.length) {
@ -1529,6 +1572,11 @@ class DtsObject extends GameObject {
this.level = null; this.level = null;
boundingCollider = null; boundingCollider = null;
colliders = null; colliders = null;
sharedGraphNodes = null;
this.dtsResource.release(); this.dtsResource.release();
} }
public static function disposeShared() {
sharedGraphNodesMap = [];
}
} }

View file

@ -2326,6 +2326,7 @@ class MarbleWorld extends Scheduler {
dtsObject.dispose(); dtsObject.dispose();
} }
dtsObjects = null; dtsObjects = null;
DtsObject.disposeShared();
powerUps = []; powerUps = [];
for (trigger in this.triggers) { for (trigger in this.triggers) {
trigger.dispose(); trigger.dispose();

View file

@ -301,6 +301,7 @@ class PreviewWorld extends Scheduler {
for (marb in marbles) { for (marb in marbles) {
marb.dispose(); marb.dispose();
} }
DtsObject.disposeShared();
interiors = []; interiors = [];
dtsObjects = []; dtsObjects = [];
marbles = []; marbles = [];

View file

@ -21,6 +21,7 @@ class AntiGravity extends PowerUp {
this.autoUse = true; this.autoUse = true;
this.useInstancing = true; this.useInstancing = true;
this.animateSubObjectOpacities = true; this.animateSubObjectOpacities = true;
this.sharedNodeTransforms = true;
if (MisParser.parseBoolean(element.permanent)) if (MisParser.parseBoolean(element.permanent))
norespawn = true; norespawn = true;
if (norespawn) if (norespawn)

View file

@ -17,6 +17,7 @@ class Blast extends PowerUp {
this.pickUpName = "a Blast powerup"; this.pickUpName = "a Blast powerup";
this.ambientRotate = false; this.ambientRotate = false;
this.autoUse = true; this.autoUse = true;
this.sharedNodeTransforms = true;
} }
public override function init(level:MarbleWorld, onFinish:Void->Void) { public override function init(level:MarbleWorld, onFinish:Void->Void) {

View file

@ -15,6 +15,7 @@ class DuctFan extends ForceObject {
this.dtsPath = "data/shapes/hazards/ductfan.dts"; this.dtsPath = "data/shapes/hazards/ductfan.dts";
this.isCollideable = true; this.isCollideable = true;
this.isTSStatic = false; this.isTSStatic = false;
this.sharedNodeTransforms = true;
this.identifier = "DuctFan"; this.identifier = "DuctFan";
this.forceDatas = [ this.forceDatas = [
{ {

View file

@ -15,6 +15,7 @@ class EasterEgg extends PowerUp {
this.identifier = "EasterEgg"; this.identifier = "EasterEgg";
this.pickUpName = "Easter Egg"; this.pickUpName = "Easter Egg";
this.autoUse = true; this.autoUse = true;
this.sharedNodeTransforms = true;
} }
public function pickUp(marble:Marble):Bool { public function pickUp(marble:Marble):Bool {

View file

@ -26,6 +26,7 @@ class Gem extends DtsObject {
this.isBoundingBoxCollideable = true; this.isBoundingBoxCollideable = true;
pickedUp = false; pickedUp = false;
useInstancing = true; 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. 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"]; var GEM_COLORS = ["red"];

View file

@ -12,6 +12,7 @@ class GemBeam extends DtsObject {
this.isTSStatic = false; this.isTSStatic = false;
this.identifier = "GemBeam"; this.identifier = "GemBeam";
this.useInstancing = true; this.useInstancing = true;
this.sharedNodeTransforms = true;
this.animateSubObjectOpacities = true; this.animateSubObjectOpacities = true;
} }

View file

@ -17,6 +17,7 @@ class Helicopter extends PowerUp {
this.isTSStatic = false; this.isTSStatic = false;
this.showSequences = false; this.showSequences = false;
this.identifier = "Helicopter"; this.identifier = "Helicopter";
this.sharedNodeTransforms = true;
this.pickUpName = "a Gyrocopter powerup"; this.pickUpName = "a Gyrocopter powerup";
} }

View file

@ -16,6 +16,7 @@ class MegaMarble extends PowerUp {
this.isTSStatic = false; this.isTSStatic = false;
this.showSequences = true; this.showSequences = true;
this.identifier = "MegaMarble"; this.identifier = "MegaMarble";
this.sharedNodeTransforms = true;
this.pickUpName = "a Mega-Marble powerup"; this.pickUpName = "a Mega-Marble powerup";
} }

View file

@ -10,6 +10,7 @@ class Sign extends DtsObject {
this.dtsPath = "data/shapes/signs/sign.dts"; this.dtsPath = "data/shapes/signs/sign.dts";
this.isCollideable = true; this.isCollideable = true;
this.useInstancing = true; this.useInstancing = true;
this.sharedNodeTransforms = true;
var d = ""; var d = "";
if (element.datablock.toLowerCase() != 'arrow') { if (element.datablock.toLowerCase() != 'arrow') {

View file

@ -10,6 +10,7 @@ class SignCaution extends DtsObject {
this.dtsPath = "data/shapes/signs/cautionsign.dts"; this.dtsPath = "data/shapes/signs/cautionsign.dts";
this.isCollideable = true; this.isCollideable = true;
this.useInstancing = true; this.useInstancing = true;
this.sharedNodeTransforms = true;
var type = element.datablock.substring("SignCaution".length).toLowerCase(); var type = element.datablock.substring("SignCaution".length).toLowerCase();
switch (type) { switch (type) {

View file

@ -10,6 +10,7 @@ class SignPlain extends DtsObject {
this.isCollideable = true; this.isCollideable = true;
this.useInstancing = true; this.useInstancing = true;
this.sharedNodeTransforms = true;
// Determine the direction to show // Determine the direction to show
var direction = element.datablock.substring("Arrow".length).toLowerCase(); var direction = element.datablock.substring("Arrow".length).toLowerCase();

View file

@ -15,6 +15,7 @@ class SmallDuctFan extends ForceObject {
this.dtsPath = "data/shapes/hazards/ductfan.dts"; this.dtsPath = "data/shapes/hazards/ductfan.dts";
this.isCollideable = true; this.isCollideable = true;
this.isTSStatic = false; this.isTSStatic = false;
this.sharedNodeTransforms = true;
this.identifier = "DuctFan"; this.identifier = "DuctFan";
this.forceDatas = [ this.forceDatas = [
{ {

View file

@ -49,6 +49,7 @@ class SuperJump extends PowerUp {
this.identifier = "SuperJump"; this.identifier = "SuperJump";
this.pickUpName = "a Super Jump powerup"; this.pickUpName = "a Super Jump powerup";
this.showSequences = false; this.showSequences = false;
this.sharedNodeTransforms = true;
sjEmitterParticleData = new ParticleData(); sjEmitterParticleData = new ParticleData();
sjEmitterParticleData.identifier = "superJumpParticle"; sjEmitterParticleData.identifier = "superJumpParticle";
sjEmitterParticleData.texture = ResourceLoader.getResource("data/particles/twirl.png", ResourceLoader.getTexture, this.textureResources); sjEmitterParticleData.texture = ResourceLoader.getResource("data/particles/twirl.png", ResourceLoader.getTexture, this.textureResources);

View file

@ -50,6 +50,7 @@ class SuperSpeed extends PowerUp {
this.identifier = "SuperSpeed"; this.identifier = "SuperSpeed";
this.pickUpName = "a Super Speed powerup"; this.pickUpName = "a Super Speed powerup";
this.useInstancing = true; this.useInstancing = true;
this.sharedNodeTransforms = true;
ssEmitterParticleData = new ParticleData(); ssEmitterParticleData = new ParticleData();
ssEmitterParticleData.identifier = "superSpeedParticle"; ssEmitterParticleData.identifier = "superSpeedParticle";
ssEmitterParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources); ssEmitterParticleData.texture = ResourceLoader.getResource("data/particles/smoke.png", ResourceLoader.getTexture, this.textureResources);

View file

@ -16,6 +16,7 @@ class TimeTravel extends PowerUp {
this.dtsPath = "data/shapes/items/timetravel.dts"; this.dtsPath = "data/shapes/items/timetravel.dts";
this.isCollideable = false; this.isCollideable = false;
this.isTSStatic = false; this.isTSStatic = false;
this.sharedNodeTransforms = true;
this.identifier = "TimeTravel"; this.identifier = "TimeTravel";
if (element.timebonus != null) { if (element.timebonus != null) {