LDraw + Unity。 我如何产生乐高

一切即将到来! 我叫Grisha,是CGDevs的创始人。 假期临近,有人已经打扮好了圣诞树,吃了橘子,充满了新年的心情。 但是今天不是这个。 今天,我们将讨论一种叫做LDraw的奇妙格式,以及我实现并上传到OpenSource的Unity插件。 与往常一样,链接到项目的链接和文章的源代码。 如果您像我一样爱乐高-欢迎来到Cat。



LDraw格式

让我们从LDraw开始吧? LDraw是LEGO CAD程序的开放标准,允许用户创建LEGO模型和场景。 通常,您可以使用各种程序和插件来可视化LDraw(例如,有一个Blender插件)。

格式本身有充分的文档,我们将讨论其最新版本,或者说是1.0.2。

LDraw是一种文本格式,其文件必须使用UTF-8编码创建。 该格式支持的文件必须具有ldr,dat或mdp扩展名。 文件的每一行都是负责特定功能的单独命令。

格式的一个重要细节是右手坐标系(Y指向上)-我们将在以后的单元上下文中进一步讨论细节,以及格式是递归的(大多数文件都包含其他文件的指示)。



LDraw命令

通常,可以在官方文档中找到此信息,但让我们在Unity上下文中进行一些了解。 LDraw格式总共支持6种命令。

0. 注释或meta命令是特殊的命令,我们几乎不会在插件中碰到。 示例: 0 !META command additional parameters

1. 链接到文件 。 其实,最难整合和有趣的团队。 看起来像1 colour xyzabcdefghi file ,其中的参数是TRS矩阵(有关TRS的更多信息,请参见本文 )。 在单位形式下

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

2. 线条 -在Unity中不使用,必须在CAD系统中用某种颜色强调边缘。

3.4。 三角形和正方形 。 这些命令非常简单,但是有一个重要的细微差别,因为LDraw格式不是为3D建模而设计的,因此其中的三角形和正方形的绕过未标准化。 这很重要,因为该单位取决于三角形的圆角,确定计算出的法线的方向,以及三角形的哪一侧是背面,哪一侧是正面(这对于绘制和剔除也很重要)

命令示例:
三角形3 colour x1 y1 z1 x2 y2 z2 x3 y3 z3
正方形4 colour x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4

5. 可选行 -也未使用。



LDraw中的颜色

正如您在负责渲染的大多数团队中所看到的那样,颜色紧随命令类型之后。 颜色在这两篇文章www.ldraw.org/article/299.htmlwww.ldraw.org/article/547.html中都有详细记录 ,但让我们谈谈在实现过程中遇到的功能。 在这里,有必要多谈一些格式和所谓的“范围”格式。 格式有3种文件。

DAT-实际上,这些是已经组装零件或一些基本零件的基本元素。 如果您不渲染单个细节,则其中指示的颜色并不重要。 最常见的是官方标准的标准颜色。

就颜色而言, LDR是最有趣的事情,作用域在其中起作用。 规则很简单,尽管该站点描述了复杂的语言。 如果从一个ldr引用另一个,请忽略根目录中指定的颜色。

例如,文件30051-1-X-wing Fighter-Mini.mpd的一部分(上图中的X-wing):

例子
 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 


在所有dat文件中,我们都考虑了指定的颜色,并在命令1 72 0 -8 -70 1 0 0 0 1 0 0 0 1 1 30051-Nose.ldr-忽略72,并使用文件30051-Nose.ldr中的值。

MDP是一个模型文件,大多数情况下它包含几个ldr文件的描述。 就颜色而言,它也不是很重要。 解析时我们唯一要考虑的是FILE元命令。



LDraw中的模型

LDraw格式的最好之处在于,他在乐高粉丝中有很多粉丝。 可以在官方网站omr.ldraw.org上找到许多有趣的工具包,但除此之外,可以在单独的论坛上找到许多工具包。

我们讨论了格式,现在是时候谈谈Unity插件了。



Unity插件

该插件提供了基于LDraw文件生成3D模型的功能。 您可以在文章的图片中看到结果。 重要:如果设备较弱,最好在“演示”文件夹中仅打开迷你场景。 模型没有经过优化,并且总是会产生背面。

现在让我们谈谈实现。 目前,以上大多数功能均受支持。

也许最重要的特征之一是不同的坐标系。 问题在于格式是右手坐标系,而Unity是左手坐标系。 从本质上讲,这意味着所有匝数和TRS矩阵均无法正常工作。 负Y很容易被击败-我们反映相对于Vector3.up的所有坐标,并获得必要的坐标(乘以-1)。 但是在TRS矩阵的情况下,一切都更加复杂。 由于格式是递归的,因此根本无法反映矩阵,因为Matrix.Identity将在任何地方都变成一个反射矩阵,并且每个嵌套都将沿Y轴反映我们的模型,这将导致显示不正确(如果保持正比例)。 到目前为止,我做出了一个错误的决定,即允许使用负数比例,在以后的版本中将需要重做。

第二个特征是三角形的方向。 对于四边形,已经意识到三角形看起来是一种方式:

正方形的准备代码
 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]]); } } 


但是在这里,根据格式确定三角形原则上应指向哪个方向是明确的-这是一项不平凡的任务。 因此,现在总是生成双方。

另外,由于格式是递归的,因此Unity分层系统变得空前方便。

使用两种方法中的递归,我们生成所需的网格并应用TRS(可以在上一篇文章中找到实现),因此我们可以以方便的格式获得所有必需的偏移量:

在舞台上生成模型的方法
 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); } } 


结果,我们得到了如此漂亮的可视化效果:





有关更多详细信息,请参见Github上存储库

总的来说,关于插件的开发有很多想法,我想介绍如下功能:

  1. 平滑某些形状
  2. 仅正面生成
  3. 构造器并将模型上传回LDraw格式
  4. 用于具有次表面散射的塑料的较冷着色器(通常使用正确的材料集)
  5. 为光照贴图打开UV
  6. 优化模型(现在大多数模型包含500k +,例如埃菲尔铁塔的模型为280万个多边形)

但是目前,该插件允许您在Unity3d中使用Lego的模型,这非常酷。 (本文的所有图像都是使用插件制作的)。所有项目代码均以MIT许可发布,但我建议您查看LDraw资源上特定型号的许可。

感谢您的关注,希望您对自己有所了解,并且对格式和插件感兴趣! 如果有时间,我将继续发展它,并将很乐意为您解决这一难题。

Source: https://habr.com/ru/post/zh-CN433364/


All Articles