Skip to content

Commit 13c8063

Browse files
author
Roberto De Ioris
authored
Update WritingAColladaFactoryWithPython.md
1 parent a6a9413 commit 13c8063

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

‎tutorials/WritingAColladaFactoryWithPython.md‎

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,97 @@ obviously we still have not added data to our StaticMesh, so in our Content Brow
9292

9393
![Empty mesh](https://github.com/20tab/UnrealEnginePython/blob/master/tutorials/WritingAColladaFactoryWithPython_Assets/empty_mesh.png)
9494

95+
## Bulding the new mesh
96+
97+
The following part is a bit complex and requires heavy understanding of the UE4 internals.
98+
99+
Just read the code comments, we now need to extract the data in the dae file (vertices, uvs and normals) and build a LOD (Level of Detail) for the StaticMesh (a StaticMesh can have multiple LOD's, each one is a different Mesh).
100+
101+
In this case we use a single LOD (the LOD0). Pay attention to the data manipulation (using numpy). The collada format has different convention in respect to UE4. We flip UVs vertically and we swap axis to have Z on top (instead of y)
102+
103+
```python
104+
from unreal_engine.classes import PyFactory, StaticMesh, Object, Class
105+
106+
import unreal_engine as ue
107+
108+
from collada import Collada
109+
110+
from unreal_engine.structs import StaticMeshSourceModel, MeshBuildSettings
111+
from unreal_engine import FRawMesh
112+
import numpy
113+
114+
class ColladaFactory(PyFactory):
115+
116+
def __init__(self):
117+
# inform the editor that this class is able to import assets
118+
self.bEditorImport = True
119+
# register the .dae extension as supported
120+
self.Formats = ['dae;Collada']
121+
# set the UClass this UFactory will generate
122+
self.SupportedClass = StaticMesh
123+
124+
def PyFactoryCreateFile(self, uclass: Class, parent: Object, name: str, filename: str) -> Object:
125+
# load the collada file
126+
dae = Collada(filename)
127+
ue.log_warning(dae)
128+
# create a new UStaticMesh with the specified name and parent
129+
static_mesh = StaticMesh(name, parent)
130+
131+
# prepare a new model with the specified build settings
132+
source_model = StaticMeshSourceModel(BuildSettings=MeshBuildSettings(bRecomputeNormals=False, bRecomputeTangents=True, bUseMikkTSpace=True, bBuildAdjacencyBuffer=True, bRemoveDegenerates=True))
133+
134+
# extract vertices, uvs and normals from the da file (numpy.ravel will flatten the arrays to simple array of floats)
135+
triset = dae.geometries[0].primitives[0]
136+
vertices = numpy.ravel(triset.vertex[triset.vertex_index])
137+
# take the first uv channel (there could be multiple channels, like the one for lightmapping)
138+
uvs = numpy.ravel(triset.texcoordset[0][triset.texcoord_indexset[0]])
139+
normals = numpy.ravel(triset.normal[triset.normal_index])
140+
141+
# move from collada system (y on top) to ue4 one (z on top, forward decreases over viewer)
142+
for i in range(0, len(vertices), 3):
143+
xv, yv, zv = vertices[i], vertices[i+1], vertices[i+2]
144+
# invert forward
145+
vertices[i] = zv * -1
146+
vertices[i+1] = xv
147+
vertices[i+2] = yv
148+
xn, yn, zn = normals[i], normals[i+1], normals[i+2]
149+
# invert forward
150+
normals[i] = zn * -1
151+
normals[i+1] = xn
152+
normals[i+2] = yn
153+
154+
# fix uvs from 0 on bottom to 0 on top
155+
for i, uv in enumerate(uvs):
156+
if i % 2 != 0:
157+
uvs[i] = 1 - uv
158+
159+
# create a new mesh, FRawMesh is an ptopmized wrapper exposed by the python plugin. read: no reflection involved
160+
mesh = FRawMesh()
161+
# assign vertices
162+
mesh.set_vertex_positions(vertices)
163+
# uvs are required
164+
mesh.set_wedge_tex_coords(uvs)
165+
# normals are optionals
166+
mesh.set_wedge_tangent_z(normals)
167+
168+
# assign indices (not optimized, just return the list of triangles * 3...)
169+
mesh.set_wedge_indices(numpy.arange(0, len(triset) * 3))
170+
171+
# assign the FRawMesh to the LOD0 (the model we created before)
172+
mesh.save_to_static_mesh_source_model(source_model)
173+
174+
# assign LOD0 to the SataticMesh and build it
175+
static_mesh.SourceModels = [source_model]
176+
static_mesh.static_mesh_build()
177+
static_mesh.static_mesh_create_body_setup()
178+
179+
return static_mesh
180+
```
181+
182+
re-run the script and import the same file again (by clicking Import in the Content Browser), if all goes well you will end with the duck mesh:
183+
184+
![The Duck](https://github.com/20tab/UnrealEnginePython/blob/master/tutorials/WritingAColladaFactoryWithPython_Assets/the_duck.png)
185+
95186
## Adding a GUI to the importer: The Slate API
96187

97188
## Persistent importer options: more subclassing

0 commit comments

Comments
 (0)