Tudo com a vinda! Meu nome é Grisha e sou o fundador da CGDevs. As férias estão chegando, alguém já vestiu uma árvore de Natal, comeu tangerina e está totalmente carregado com o clima do Ano Novo. Mas hoje não é sobre isso. Hoje falaremos sobre um formato maravilhoso chamado LDraw e sobre o plug-in para Unity, que eu implementei e enviei para o OpenSource. O link para o projeto e o código-fonte do artigo, como sempre, estão anexados. Se você ama Lego tanto quanto eu, seja bem-vindo ao gato.
Formato LDrawVamos começar com o que é LDraw?
LDraw é um padrão aberto para programas LEGO CAD que permite aos usuários criar modelos e cenas LEGO. Em geral, existem vários programas e plugins com os quais você pode visualizar o LDraw (por exemplo, existe um plugin para o Blender).
O formato em si está bem documentado e falaremos sobre sua versão mais recente, ou melhor, sobre 1.0.2.
LDraw é um formato de texto cujos arquivos devem ser criados com a codificação UTF-8. Os arquivos suportados pelo formato devem ter a extensão ldr, dat ou mdp. Cada linha do arquivo é um comando separado responsável por uma função específica.
Um detalhe importante do formato é o sistema de coordenadas para a mão direita (Y é direcionado para cima) - discutiremos mais detalhadamente mais adiante no contexto da unidade, bem como o fato de o formato ser recursivo (a maioria dos arquivos contém uma indicação de outros arquivos).
Comandos LDrawEm geral, essas informações podem ser encontradas na
documentação oficial , mas vamos olhar um pouco no contexto do Unity. No total, o formato LDraw suporta 6 tipos de comandos.
0. Um
comentário ou um comando meta são comandos especiais que dificilmente tocaremos no plugin. Exemplo:
0 !META command additional parameters
1.
Link para o arquivo . Na verdade, a equipe mais difícil de integrar e interessante. Parece -
1 colour xyzabcdefghi file
, em que os parâmetros são a matriz TRS (mais sobre TRS pode ser encontrado
neste artigo ). No contexto da unidade no formulário
/ adg 0 \ | beh 0 | | cfi 0 | \ xyz 1 /
2.
Linha - não utilizada no caso do Unity, é necessário enfatizar as arestas com uma determinada cor nos sistemas CAD.
3.4
Triângulo e quadrado . Os comandos são bastante simples, mas há uma nuance importante, já que o formato LDraw não foi projetado para modelagem 3D, o desvio de triângulos e quadrados nele não é padronizado. Isso é importante porque a unidade, dependendo da travessia do triângulo, determina a direção do normal calculado, bem como de que lado do triângulo está a parte de trás e qual é a frente (o que também é importante para o desenho e seleção).
Exemplo de comando:
Triângulo -
3 colour x1 y1 z1 x2 y2 z2 x3 y3 z3
Quadrado -
4 colour x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4
5.
Linha opcional - também não usada.
Cores no LDrawComo você pode ver na maioria das equipes responsáveis pela renderização, a cor vem imediatamente após o tipo de comando. As cores estão bem documentadas nesses dois artigos
www.ldraw.org/article/299.html e
www.ldraw.org/article/547.html , mas
vamos falar sobre os recursos que encontrei durante a implementação. Aqui vale a pena falar um pouco mais sobre os formatos e o chamado formato "Escopo". Existem 3 tipos de arquivos no formato.
DAT - de fato, esses são os elementos básicos a partir dos quais as peças já estão montadas, ou algumas peças básicas. Se você não renderizar detalhes individuais, a cor indicada neles não é importante. Na maioria das vezes, existem cores padrão do padrão oficial.
LDR é a coisa mais interessante em termos de cores e onde o Scope desempenha um papel. A regra é bastante simples, embora o site tenha descrito linguagens complexas. Se você se referir a outro de um ldr, ignore a cor especificada na raiz.
Por exemplo, parte do arquivo 30051-1 - X-wing Fighter - Mini.mpd (X-wing na imagem acima):
Exemplo 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
Em todos os arquivos dat, levamos em consideração a cor especificada e no comando
1 72 0 -8 -70 1 0 0 0 1 0 0 0 1 30051 - Nose.ldr - ignore 72 e use os valores do arquivo
30051 - Nose.ldr .
MDP é um arquivo de modelo, na maioria das vezes contém uma descrição de vários arquivos ldr. Em termos de cor, também não é muito importante. A única coisa que levamos em consideração ao analisar é o meta-comando
FILE .
Modelos no LDrawA melhor parte do formato LDraw é que ele tem muitos fãs entre os fãs de lego. Muitos kits interessantes podem ser encontrados no site oficial
omr.ldraw.org , mas além disso, muitos podem ser encontrados em fóruns separados.
Conversamos sobre o formato, agora é hora de falar um pouco sobre o plugin do Unity.
Plugin para UnityO plug-in fornece a capacidade de gerar modelos 3D com base em arquivos LDraw. Você pode ver os resultados nas fotos do artigo. Importante: se você tiver um dispositivo fraco, é melhor abrir apenas mini cenas na pasta Demo. Os modelos não são otimizados e sempre geram um backface.
Agora vamos falar um pouco sobre implementação. No momento, a maioria dos itens acima é suportada.
Uma das características talvez mais importantes são os diferentes sistemas de coordenadas. O problema é que o formato é um sistema de coordenadas para destros, enquanto o Unity é um sistema de coordenadas para canhotos. O que isso significa, em essência, que todas as curvas e a matriz TRS não funcionarão corretamente. Y negativo é fácil de superar - refletimos todas as coordenadas relativas ao Vector3.up e obtemos as necessárias (multiplique por -1). Mas no caso da matriz TRS, tudo é mais complicado. Como o formato é recursivo, é simplesmente impossível refletir a matriz, pois Matrix.Identity se tornará uma matriz de reflexão em todos os lugares e cada aninhamento refletirá nosso modelo ao longo do eixo Y, o que levará a uma exibição incorreta (se você mantiver uma escala positiva). Até agora, cheguei a uma decisão incorreta na forma de permitir uma escala negativa, que precisará ser refeita em versões futuras.
O segundo recurso é a orientação dos triângulos. Para quads, percebe-se que os triângulos parecem de uma maneira:
Código de Preparação para Quadrados 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]]); } }
Mas aqui é inequívoco determinar, com base no formato, em que direção os triângulos devem ser direcionados em princípio - uma tarefa não trivial. Por esse motivo, os dois lados são sempre gerados agora.
Além disso, devido ao fato de o formato ser recursivo, o sistema hierárquico do Unity foi útil como nunca antes.
Usando recursão em dois métodos, geramos as malhas necessárias e aplicamos o TRS (a implementação pode ser encontrada
no artigo anterior ) e, portanto, obtemos todas as compensações necessárias em um formato conveniente:
Métodos para gerar um modelo no palco 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); } }
E, como resultado, obtemos visualizações tão bonitas:


Veja o
repositório no Github para mais detalhes.
Em geral, existem muitas idéias sobre o desenvolvimento do plugin. Quero apresentar recursos como:
- Suavizando algumas formas
- Somente geração de face frontal
- Construtor e upload de modelos de volta ao formato LDraw
- O shader mais frio para plástico com espalhamento subterrâneo (e o conjunto correto de materiais em geral)
- Desembrulhe os UV para mapas de luz
- Otimização de modelos (agora a maioria consiste em 500k + e, por exemplo, o modelo da torre Eiffel é de 2,8 milhões de polígonos)
Mas, no momento, o plug-in permite que você use modelos da Lego no Unity3d, o que é bem legal. (Todas as imagens do artigo foram criadas usando o plug-in) Todo o
código do projeto é publicado sob a licença MIT, mas aconselho que você olhe a licença para modelos específicos nos recursos do LDraw.
Obrigado por sua atenção, espero que você tenha aprendido algo novo e esteja interessado no formato e no plug-in! Se houver tempo, continuarei a desenvolvê-lo e ficarei feliz em ajudar nessa questão difícil.