c# unity3d assimp

c# - Malla reflejada y exportación en tiempo de ejecución errónea del mapa UV



unity3d assimp (1)

EDITAR: Entonces, después de un breve contacto con el desarrollador de Assim, me señalaron el proceso de importación. Cuando tomé el código de otra persona, no pensé en buscar esa parte:

using (var importer = new AssimpContext()) { scene = importer.ImportFile(file, PostProcessSteps.Triangulate | PostProcessSteps.FlipUVs | PostProcessSteps.JoinIdenticalVertices); }

FlipUVs hace exactamente lo que dice, se voltea en el eje y por lo que el origen ahora es la esquina superior izquierda. Así que ahora puedo obtener el modelo con una malla UV adecuada pero reflejada. Al establecer el objeto principal con la escala x = -1, se vuelve a la normalidad y se ve bien, pero supongo que no es así. Entonces sigo buscando.

Ver la imagen, hay dos modelos de grúas. El de la izquierda se carga en el tiempo de ejecución a través de la serialización y la reconstrucción, mientras que el de la derecha es el original que simplemente se arrastró a la escena. La serialización ocurre con la biblioteca Assimp.

El piso se crea primero y parece obtener el mapa correcto de uv. Mientras que los otros elementos se equivocan mapa uv. Aunque estoy imprimiendo los valores de los mapas uv y parecen coincidir con el original como deberían.

Así es como serializar, esta es la clase Mesh de Assimp, no la clase Unity Mesh, la serialización de la aplicación es una aplicación de Windows construida en UWP:

private static void SerializeMeshes(BinaryWriter writer, IEnumerable<Mesh> meshes) { foreach (Mesh mesh in meshes) { ICollection<int> triangles = MeshLoadTriangles(mesh); MeshSerializeHeader(writer, mesh.Name, mesh.VertexCount, triangles.Count, mesh.MaterialIndex); MeshSerializeVertices(writer, mesh.Vertices); MeshSerializeUVCoordinate(writer, mesh.TextureCoordinateChannels); MeshSerializeTriangleIndices(writer, triangles); } } private static void MeshSerializeUVCoordinate(BinaryWriter writer, List<Vector3D>[] textureCoordinateChannels) { // get first channel and serialize to writer. Discard z channel // This is Vector3D since happening outside Unity List<Vector3D> list = textureCoordinateChannels[0]; foreach (Vector3D v in list) { float x = v.X; float y = v.Y; writer.Write(x); writer.Write(y); } } private static void MeshSerializeVertices(BinaryWriter writer, IEnumerable<Vector3D> vertices) { foreach (Vector3D vertex in vertices) { Vector3D temp = vertex; writer.Write(temp.X); writer.Write(temp.Y); writer.Write(temp.Z); } } private static void MeshSerializeTriangleIndices(BinaryWriter writer, IEnumerable<int> triangleIndices) { foreach (int index in triangleIndices) { writer.Write(index); } }

Y este es el proceso de inversión:

private static void DeserializeMeshes(BinaryReader reader, SceneGraph scene) { MeshData[] meshes = new MeshData[scene.meshCount]; for (int i = 0; i < scene.meshCount; i++) { meshes[i] = new MeshData(); MeshReadHeader(reader, meshes[i]); MeshReadVertices(reader, meshes[i]); MeshReadUVCoordinate(reader, meshes[i]); MeshReadTriangleIndices(reader, meshes[i]); } scene.meshes = meshes as IEnumerable<MeshData>; } private static void MeshReadUVCoordinate(BinaryReader reader, MeshData meshData) { bool hasUv = reader.ReadBoolean(); if(hasUv == false) { return; } Vector2[] uvs = new Vector2[meshData.vertexCount]; for (int i = 0; i < uvs.Length; i++) { uvs[i] = new Vector2(); uvs[i].x = reader.ReadSingle(); uvs[i].y = reader.ReadSingle(); } meshData.uvs = uvs; } private static void MeshReadHeader(BinaryReader reader, MeshData meshData) { meshData.name = reader.ReadString(); meshData.vertexCount = reader.ReadInt32(); meshData.triangleCount = reader.ReadInt32(); meshData.materialIndex = reader.ReadInt32(); } private static void MeshReadVertices(BinaryReader reader, MeshData meshData) { Vector3[] vertices = new Vector3[meshData.vertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i] = new Vector3(); vertices[i].x = reader.ReadSingle(); vertices[i].y = reader.ReadSingle(); vertices[i].z = reader.ReadSingle(); } meshData.vertices = vertices; } private static void MeshReadTriangleIndices(BinaryReader reader, MeshData meshData) { int[] triangleIndices = new int[meshData.triangleCount]; for (int i = 0; i < triangleIndices.Length; i++) { triangleIndices[i] = reader.ReadInt32(); } meshData.triangles = triangleIndices; }

MeshData es solo un contenedor temporal con los valores deserializados del fbx. Entonces, se crean mallas:

private static Mesh[] CreateMeshes(SceneGraph scene) { Mesh[] meshes = new Mesh[scene.meshCount]; int index = 0; foreach (MeshData meshData in scene.meshes) { meshes[index] = new Mesh(); Vector3[] vec = meshData.vertices; meshes[index].vertices = vec; meshes[index].triangles = meshData.triangles; meshes[index].uv = meshData.uvs; meshes[index].normals = meshData.normals; meshes[index].RecalculateNormals(); index++; } return meshes; }

No veo ninguna razón en el código que dé como resultado este tipo de comportamiento, diría que sería un error si los valores fueran incorrectos.

Puedo ver que los archivos fbx que tengo están usando quad en lugar de triángulo para la indexación.

¿Podría ser que Assimp no va bien con esto?


No resolví el problema de forma adecuada desde Assimp.

La solución básica que utilizamos fue escalar negativamente el eje que se volteó en la transformación del objeto.

Una solución más apropiada habría sido alimentar todos los vértices a una matriz en el lado de la Unidad para que resuelva la posición de los vértices correctamente.

  • Obtener lista de vértices
  • el vértice foreach se multiplica con la matriz de rotación
  • Asignar matriz a malla
  • Usa malla para renderizar