2012-07-04 31 views
5

Estoy intentando cargar una escena de un archivo en Three.js (formato personalizado, no uno que admite Three.js). Este formato particular describe un gráfico de escena donde cada nodo en el árbol tiene una transformación especificada como una matriz 4x4. El proceso para empujar en Three.js ve algo como esto:Uso de matrices para transformar el gráfico de escena 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 al menos eso es lo que me gustaría que fuera. Como señalé, la línea applyMatrix no funciona de la manera que yo esperaría. La mayoría de la escena se ve bien, pero ciertos elementos que se han girado no están alineados correctamente (mientras que otros son, es extraño).

Mirando a través del cargador COLLADA (que hace aproximadamente lo mismo que estoy tratando de hacer) parece que ellos decompose the matrix en una trasladar/rotar/escalar y aplicar cada uno individualmente. Traté de que en lugar de la applyMatrix mostrado arriba

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

Esto, una vez más, produce una escena en la que la mayoría de los elementos están en el lugar correcto, pero mallas que antes estaban mal alineado ahora se han transformado en el olvido en algún lugar y ya no se aparecer en absoluto. Entonces, al final, esto no es mejor que el applyMatrix desde arriba.

Mirando a través de varias discusiones en línea sobre el tema, parece que la forma recomendada de utilizar matrices para sus transformaciones es aplicarlas directamente a la geometría, no a los nodos, así que intenté construyendo manualmente la matriz de transformación así:

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! 
    } 
} 

¡Esto realmente da los resultados correctos! (sin algunas peculiaridades con las normales, pero puedo descifrarlo). Eso es genial, pero el problema es que ahora hemos aplastado la jerarquía de escenas: cambiar la transformación en una matriz producirá resultados inesperados en los hijos porque la totalidad la pila de transformación ahora está "horneada" en las mallas. En este caso, esa es una pérdida de información inaceptable sobre la escena.

Entonces, ¿cómo puede uno decirle a Three.js que haga la misma lógica, pero en el punto apropiado en el gráfico de escena?

(Lo siento, me encantaría publicar algunos ejemplos de código en vivo, pero eso no es por desgracia una opción en este caso.)

Respuesta

5

Puede utilizar matrixAutoUpdate = false saltarse las cosas Three.js scenegraph posición/escala/rotación. A continuación, establezca object.matrix en la matriz que desee y todo debe ser elegante (bueno, aún se multiplica por matrices de nodo padre, por lo que si está utilizando matrices de modelos absolutas, debe piratear el método updateMatrixWorld en Object3D.)

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

Ahora, si desea tener una matriz de transformación personalizada aplicada sobre los Tres.js cosas de posición/escala/rotación, necesitas editar Object3D # updateMatrix para que sea algo así como.

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

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

+0

La misma respuesta, a un minuto. :) ¡Gracias de todos modos! El bit de updateMatrix es muy interesante. – Toji

5

suspiro ...

Altered Qualia pointed out the solution en Twitter en cuestión de minutos de yo publicando esto.

Es una solución simple de una sola línea: simplemente configure matrixAutoUpdate como falso en las instancias de Object3D y la primera muestra de código funciona según lo previsto.

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

Siempre las pequeñas cosas tontas que le consiguen ...

Cuestiones relacionadas