2012-07-04 10 views
5

Sto tentando di caricare una scena da un file in Three.js (formato personalizzato, non uno supportato da Three.js). Questo particolare formato descrive un grafico di scena in cui ogni nodo dell'albero presenta una trasformazione specificata come matrice 4x4. Il processo per spingere in Three.js simile a questa:Utilizzo delle matrici per trasformare il grafico di scena Three.js

// Yeah, this is javascript-like psuedocode 
function processNodes(srcNode, parentThreeObj) { 
    for(child in srcNode.children) { 
     var threeObj = new THREE.Object3D(); 

     // This line is the problem 
     threeObj.applyMatrix(threeMatrixFromSrcMatrix(child.matrix)); 

     for(mesh in child.meshes) { 
      var threeMesh = threeMeshFromSrcMesh(mesh); 
      threeObj.add(threeMesh); 
     } 

     parentThreeObj.add(threeObj); 

     processNodes(child, threeObj); // And recurse! 
    } 
} 

O almeno questo è quello che mi piacerebbe che fosse. Come ho sottolineato, la linea applyMatrix non funziona come mi aspetterei. La maggior parte della scena sembra a posto, ma alcuni elementi che sono stati ruotati non sono allineati correttamente (mentre altri sono, è strano).

Guardando attraverso il caricatore COLLADA (che fa approssimativamente la stessa cosa che sto tentando di fare) sembra che siano decompose the matrix in una traslazione/rotazione/scala e si applicano singolarmente. Ho provato che al posto del applyMatrix mostrato sopra:

var props = threeMatrixFromSrcMatrix(child.matrix).decompose(); 
threeObj.useQuaternion = true; 
threeObj.position = props[ 0 ]; 
threeObj.quaternion = props[ 1 ]; 
threeObj.scale = props[ 2 ]; 

Questo, ancora una volta, produce una scena in cui la maggior parte elementi sono nel posto giusto ma ingrana che in precedenza erano disallinea ora sono state trasformate in oblio qualche parte e non più sembra affatto. Quindi alla fine non è meglio dello applyMatrix dall'alto.

Guardando attraverso diverse discussioni online sull'argomento, sembra che il modo consigliato di utilizzare le matrici per le proprie trasformazioni consiste nell'applicarle direttamente alla geometria, non ai nodi, quindi l'ho provato costruendo manualmente la matrice di trasformazione in questo modo:

function processNodes(srcNode, parentThreeObj, parentMatrix) { 
    for(child in srcNode.children) { 
     var threeObj = new THREE.Object3D(); 

     var childMatrix = threeMatrixFromSrcMatrix(child.matrix); 
     var objMatrix = THREE.Matrix4(); 
     objMatrix.multiply(parentMatrix, childMatrix); 

     for(mesh in child.meshes) { 
      var threeMesh = threeMeshFromSrcMesh(mesh); 
      threeMesh.geometry.applyMatrix(objMatrix); 
      threeObj.add(threeMesh); 
     } 

     parentThreeObj.add(threeObj); 

     processNodes(child, threeObj, objMatrix); // And recurse! 
    } 
} 

Questo produce effettivamente i risultati corretti! (meno alcune stranezze con le normali, ma posso capirlo). È grandioso, ma il problema è che ora abbiamo effettivamente appiattito la gerarchia delle scene: cambiare la trasformazione su un genitore produrrà risultati inaspettati sui bambini perché il pieno trasformare lo stack è ora "cotto" nelle mesh. In questo caso è una perdita inaccettabile di informazioni sulla scena.

Quindi, come si può dire a Three.js di fare la stessa logica, ma al punto appropriato nel grafico della scena?

(Scusate, mi amerebbero di inviare alcuni esempi di codice in tensione ma che non è, purtroppo, un'opzione in questo caso.)

risposta

5

È possibile utilizzare matrixAutoUpdate = false per saltare roba posizione/scala/rotazione delle Three.js scenegraph. Quindi imposta object.matrix sulla matrice che vuoi e tutto dovrebbe essere dandy (beh, viene ancora moltiplicato per le matrici dei nodi principali, quindi se stai utilizzando matrici di modelview assolute devi hackerare il metodo updateMatrixWorld su Object3D.)

object.matrixAutoUpdate = false; 
object.matrix = myMatrix; 

Ora, se si desidera applicare una matrice di trasformazione personalizzata in cima al Tre.js position/scale/rotation stuff, è necessario modificare Object3D # updateMatrix per essere qualcosa di simile.

THREE.Object3D.prototype._updateMatrix = THREE.Object3D.prototype.updateMatrix; 
THREE.Object3D.prototype.updateMatrix = function() { 
    this._updateMatrix(); 
    if (this.customMatrix != null) 
    this.matrix.multiply(this.customMatrix); 
}; 

Vedi https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L209

+0

Stessa risposta, un minuto di distanza. :) Grazie comunque, però! Il bit updateMatrix è molto interessante. – Toji

5

Sigh ...

Altered Qualia pointed out the solution su Twitter in pochi minuti di me postando questo.

È una semplice soluzione a una riga: basta impostare matrixAutoUpdate su false sulle istanze Object3D e il primo esempio di codice funziona come previsto.

threeObj.matrixAutoUpdate = false; // This fixes it 
threeObj.applyMatrix(threeMatrixFromSrcMatrix(child.matrix)); 

E 'sempre le piccole cose stupide che si ottiene ...

Problemi correlati