LDraw + Unidad. Cómo generé Lego

Todo con la venida! Mi nombre es Grisha y soy el fundador de CGDevs. Las vacaciones están a la vuelta de la esquina, alguien ya se vistió con un árbol de Navidad, comió mandarinas y está totalmente cargado del estado de ánimo de Año Nuevo. Pero hoy no se trata de eso. Hoy hablaremos sobre un formato maravilloso llamado LDraw y sobre el complemento para Unity, que implementé y subí a OpenSource. Se adjunta un enlace al proyecto y el código fuente del artículo, como siempre. Si amas a Lego tanto como a mí, bienvenido a Cat.



Formato LDraw

Comencemos con lo que es LDraw? LDraw es un estándar abierto para programas LEGO CAD que permite a los usuarios crear modelos y escenas LEGO. En general, hay varios programas y complementos con los que puede visualizar LDraw (por ejemplo, hay un complemento para Blender).

El formato en sí está bien documentado y hablaremos de su última versión, o más bien, de 1.0.2.

LDraw es un formato de texto cuyos archivos deben crearse con codificación UTF-8. Los archivos compatibles con el formato deben tener la extensión ldr, dat o mdp. Cada línea del archivo es un comando separado responsable de una función específica.

Un detalle importante del formato es el sistema de coordenadas diestro (Y se dirige hacia arriba): discutiremos con más detalle más adelante en el contexto de la unidad, así como el hecho de que el formato es recursivo (la mayoría de los archivos contienen una indicación de otros archivos).



Comandos LDraw

En general, esta información se puede encontrar en la documentación oficial , pero veamos un poco en el contexto de Unity. En total, el formato LDraw admite 6 tipos de comandos.

0. Un comentario o un metacomando son comandos especiales que difícilmente tocaremos en el complemento. Ejemplo: 0 !META command additional parameters

1. Enlace al archivo . De hecho, el equipo más difícil de integrar e interesante. Parece que es 1 colour xyzabcdefghi file , donde los parámetros son la matriz TRS (puede encontrar más información sobre TRS en este artículo ). En el contexto de la unidad en la forma

 / adg 0 \ | beh 0 | | cfi 0 | \ xyz 1 / 

2. Línea : no se utiliza en el caso de Unity, es necesario enfatizar los bordes con un cierto color en los sistemas CAD.

3.4. Triángulo y cuadrado . Los comandos son bastante simples, pero hay un matiz importante, ya que el formato LDraw no está diseñado para el modelado 3D, la omisión de triángulos y cuadrados no está estandarizada. Esto es importante, porque la unidad, dependiendo de la ronda del triángulo, determina la dirección de la normal calculada, así como qué lado del triángulo es la parte posterior y cuál es el frente (que también es importante para dibujar y seleccionar)

Ejemplo de comando:
Triángulo - 3 colour x1 y1 z1 x2 y2 z2 x3 y3 z3
Cuadrado - 4 colour x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4

5. Línea opcional : tampoco utilizada.



Colores en LDraw

Como puede ver en la mayoría de los equipos responsables del renderizado, el color viene inmediatamente después del tipo de comando. Los colores están bien documentados en estos dos artículos www.ldraw.org/article/299.html y www.ldraw.org/article/547.html , pero hablemos de las características que encontré durante la implementación. Aquí vale la pena hablar un poco más sobre los formatos y el llamado formato "Alcance". Hay 3 tipos de archivos en el formato.

DAT : de hecho, estos son los elementos básicos a partir de los cuales las partes ya están ensambladas, o algunas partes básicas. Si no presenta detalles individuales, el color indicado en ellos no es importante. La mayoría de las veces hay colores estándar del estándar oficial.

LDR es lo más interesante en términos de colores, y donde Scope juega un papel. La regla es bastante simple, aunque el sitio describe lenguajes complejos. Si se refiere a otro de un ldr, ignore el color especificado en la raíz.

Por ejemplo, parte del archivo 30051-1 - X-wing Fighter - Mini.mpd (X-wing en la imagen de arriba):

Ejemplo
 1 71 -10 0 50 0 0 1 0 1 0 -1 0 0 60470a.dat 1 71 10 0 50 0 0 -1 0 1 0 1 0 0 60470a.dat 0 STEP 1 19 0 8 50 0 0 -1 0 1 0 1 0 0 4032b.dat 0 STEP 0 ROTSTEP 35 55 0 ABS 1 19 0 -16 0 0 0 -1 0 1 0 1 0 0 3623.dat 1 72 0 -16 50 0 0 -1 0 1 0 1 0 0 3022.dat 0 STEP 1 72 0 -8 -70 1 0 0 0 1 0 0 0 1 30051 - Nose.ldr 


En todos los archivos de datos, tenemos en cuenta el color especificado y en el comando 1 72 0 -8-70 1 0 0 0 1 0 0 0 1 30051 - Nose.ldr - ignora 72 y usamos los valores del archivo 30051 - Nose.ldr .

MDP es un archivo modelo, la mayoría de las veces contiene una descripción de varios archivos ldr. En términos de color, tampoco es muy importante. Lo único que tenemos en cuenta al analizar es el metacomando FILE .



Modelos en LDraw

La mejor parte del formato LDraw es que tiene muchos fanáticos entre los fanáticos de lego. Se pueden encontrar muchos kits interesantes en el sitio web oficial omr.ldraw.org , pero además de esto, muchos se pueden encontrar en foros separados.

Hablamos sobre el formato, ahora es el momento de hablar un poco sobre el complemento para Unity.



Plugin para la unidad

El complemento proporciona la capacidad de generar modelos 3D basados ​​en archivos LDraw. Puedes ver los resultados en las imágenes del artículo. Importante: si tiene un dispositivo débil, es mejor abrir solo mini escenas en la carpeta Demo. Los modelos no están optimizados y siempre generan una cara posterior.

Ahora hablemos un poco sobre la implementación. Por el momento, la mayor parte de lo anterior es compatible.

Una de las características quizás más importantes son los diferentes sistemas de coordenadas. El problema es que el formato es un sistema de coordenadas diestro, mientras que Unity es un sistema de coordenadas zurdo. Lo que esto significa, en esencia, es que todos los giros y la matriz TRS no funcionarán correctamente. La Y negativa es fácil de superar: reflejamos todas las coordenadas relativas a Vector3.up y obtenemos las necesarias (multiplicar por -1). Pero en el caso de la matriz TRS, todo es más complicado. Dado que el formato es recursivo, es simplemente imposible reflejar la matriz, ya que Matrix.Identity se convertirá en una matriz de reflexión en todas partes y cada anidamiento reflejará nuestro modelo a lo largo del eje Y, lo que conducirá a una visualización incorrecta (si mantiene una escala positiva). Hasta ahora, he tomado una decisión incorrecta en la forma de permitir una escala negativa, que deberá rehacerse en futuras versiones.

La segunda característica es la orientación de los triángulos. Para los quads, se da cuenta de que los triángulos se ven de una manera:

Código de preparación para cuadrados
 public override void PrepareMeshData(List<int> triangles, List<Vector3> verts) { var v = _Verts; var nA = Vector3.Cross(v[1] - v[0], v[2] - v[0]); var nB = Vector3.Cross(v[1] - v[0], v[2] - v[0]); var vertLen = verts.Count; triangles.AddRange(new[] { vertLen + 1, vertLen + 2, vertLen, vertLen + 1, vertLen + 3, vertLen + 2 }); var indexes = Vector3.Dot(nA, nB) > 0 ? new int[] {0, 1, 3, 2} : new int[] {0, 1, 2, 3}; for (int i = 0; i < indexes.Length; i++) { verts.Add(v[indexes[i]]); } } 


Pero aquí es inequívoco determinar, en función del formato, en qué dirección deben dirigirse los triángulos en principio, una tarea no trivial. Por esta razón, ambos lados siempre se generan ahora.

Además, debido a que el formato es recursivo, el sistema jerárquico de Unity fue útil como nunca antes.

Usando la recursividad en dos métodos, generamos las mallas que necesitamos y aplicamos TRS (la implementación se puede encontrar en el artículo anterior ), y así obtenemos todas las compensaciones necesarias en un formato conveniente:

Métodos para generar un modelo en el escenario.
 public class LDrawModel { public GameObject CreateMeshGameObject(Matrix4x4 trs, Material mat = null, Transform parent = null) { if (_Commands.Count == 0) return null; GameObject go = new GameObject(_Name); var triangles = new List<int>(); var verts = new List<Vector3>(); for (int i = 0; i < _Commands.Count; i++) { var sfCommand = _Commands[i] as LDrawSubFile; if (sfCommand == null) { _Commands[i].PrepareMeshData(triangles, verts); } else { sfCommand.GetModelGameObject(go.transform); } } if (mat != null) { var childMrs = go.transform.GetComponentsInChildren<MeshRenderer>(); foreach (var meshRenderer in childMrs) { meshRenderer.material = mat; } } if (verts.Count > 0) { var visualGO = new GameObject("mesh"); visualGO.transform.SetParent(go.transform); var mf = visualGO.AddComponent<MeshFilter>(); mf.sharedMesh = PrepareMesh(verts, triangles); var mr = visualGO.AddComponent<MeshRenderer>(); if (mat != null) { mr.sharedMaterial = mat; } } go.transform.ApplyLocalTRS(trs); go.transform.SetParent(parent); return go; } } public class LDrawSubFile : LDrawCommand { public void GetModelGameObject(Transform parent) { _Model.CreateMeshGameObject(_Matrix, GetMaterial(), parent); } } 


Y como resultado, obtenemos visualizaciones tan hermosas:





Vea el repositorio en Github para más detalles.

En general, hay muchas ideas sobre el desarrollo del complemento, quiero introducir funcionalidades como:

  1. Alisar algunas formas
  2. Generación frontal solamente
  3. Constructor y subir modelos a formato LDraw
  4. El sombreador más frío para plástico con dispersión subsuperficial (y el conjunto correcto de materiales en general)
  5. Desenvuelva UV para mapas de luz
  6. Optimización de modelos (ahora la mayoría de ellos consisten en 500k +, y por ejemplo, el modelo de la torre Eiffel es de 2.8 millones de polígonos)

Pero por el momento, el complemento le permite usar modelos de Lego en Unity3d, lo cual es bastante bueno. (Todas las imágenes del artículo se hicieron con el complemento). Todo el código del proyecto se publica bajo la licencia MIT, pero le aconsejo que mire la licencia para modelos específicos en recursos LDraw.

Gracias por su atención, ¡espero que haya aprendido algo nuevo para usted y le interese el formato y el complemento! Si hay tiempo, continuaré desarrollándolo y estaré encantado de ayudarlo en este difícil asunto.

Source: https://habr.com/ru/post/es433364/


All Articles