Skip to content

Commit 12bca64

Browse files
author
Roberto De Ioris
committed
added collada_factory.py
1 parent 471b315 commit 12bca64

File tree

1 file changed

+142
-0
lines changed

1 file changed

+142
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from unreal_engine.classes import PyFactory, StaticMesh, Object, Class
2+
3+
import unreal_engine as ue
4+
5+
from collada import Collada
6+
7+
from unreal_engine.structs import StaticMeshSourceModel, MeshBuildSettings
8+
from unreal_engine import FRawMesh
9+
import numpy
10+
11+
from unreal_engine import FVector, FRotator
12+
from unreal_engine import SWindow, SVerticalBox, SHorizontalBox, SButton, SRotatorInputBox
13+
from unreal_engine.enums import EHorizontalAlignment
14+
15+
from unreal_engine.classes import Material
16+
from unreal_engine.structs import Rotator, StaticMaterial
17+
18+
class ColladaImportOptions(Object):
19+
20+
DefaultRotation = Rotator
21+
DefaultMaterial = Material
22+
23+
class ColladaFactory(PyFactory):
24+
25+
ImportOptions = ColladaImportOptions()
26+
27+
def __init__(self):
28+
# inform the editor that this class is able to import assets
29+
self.bEditorImport = True
30+
# register the .dae extension as supported
31+
self.Formats = ['dae;Collada']
32+
# set the UClass this UFactory will generate
33+
self.SupportedClass = StaticMesh
34+
ue.log(dir(self))
35+
36+
37+
def open_collada_wizard(self):
38+
39+
def cancel_import():
40+
self.wizard.request_destroy()
41+
42+
def confirm_import():
43+
self.do_import = True
44+
self.wizard.request_destroy()
45+
46+
self.wizard = SWindow(title='Collada Import Options', modal=True, sizing_rule=1)(
47+
SVerticalBox()
48+
(
49+
ue.create_detail_view(self.ImportOptions),
50+
auto_height=True,
51+
padding = 10
52+
)
53+
(
54+
SHorizontalBox()
55+
(
56+
SButton(text='Cancel', on_clicked=cancel_import, h_align = EHorizontalAlignment.HAlign_Center)
57+
)
58+
(
59+
SButton(text='Import', on_clicked=confirm_import, h_align = EHorizontalAlignment.HAlign_Center)
60+
),
61+
auto_height=True,
62+
padding = 4,
63+
),
64+
65+
)
66+
self.wizard.add_modal()
67+
68+
69+
# this functions starts with an uppercase letter, so it will be visible to the UE system
70+
# not required obviously, but it will be a good example
71+
def FixMeshData(self):
72+
# move from collada system (y on top) to ue4 one (z on top, forward decreases over viewer)
73+
for i in range(0, len(self.vertices), 3):
74+
xv, yv, zv = self.vertices[i], self.vertices[i+1], self.vertices[i+2]
75+
# invert forward
76+
vec = FVector(zv * -1, xv, yv) * self.ImportOptions.DefaultRotation
77+
self.vertices[i] = vec.x
78+
self.vertices[i+1] = vec.y
79+
self.vertices[i+2] = vec.z
80+
xn, yn, zn = self.normals[i], self.normals[i+1], self.normals[i+2]
81+
nor = FVector(zn * -1, xn, yn) * self.ImportOptions.DefaultRotation
82+
# invert forward
83+
self.normals[i] = nor.x
84+
self.normals[i+1] = nor.y
85+
self.normals[i+2] = nor.z
86+
87+
# fix uvs from 0 on bottom to 0 on top
88+
for i, uv in enumerate(self.uvs):
89+
if i % 2 != 0:
90+
self.uvs[i] = 1 - uv
91+
92+
def PyFactoryCreateFile(self, uclass: Class, parent: Object, name: str, filename: str) -> Object:
93+
# load the collada file
94+
dae = Collada(filename)
95+
ue.log_warning(dae)
96+
97+
self.do_import = False
98+
self.open_collada_wizard()
99+
100+
if not self.do_import:
101+
return None
102+
103+
104+
# create a new UStaticMesh with the specified name and parent
105+
static_mesh = StaticMesh(name, parent)
106+
107+
# prepare a new model with the specified build settings
108+
source_model = StaticMeshSourceModel(BuildSettings=MeshBuildSettings(bRecomputeNormals=False, bRecomputeTangents=True, bUseMikkTSpace=True, bBuildAdjacencyBuffer=True, bRemoveDegenerates=True))
109+
110+
# extract vertices, uvs and normals from the da file (numpy.ravel will flatten the arrays to simple array of floats)
111+
triset = dae.geometries[0].primitives[0]
112+
self.vertices = numpy.ravel(triset.vertex[triset.vertex_index])
113+
# take the first uv channel (there could be multiple channels, like the one for lightmapping)
114+
self.uvs = numpy.ravel(triset.texcoordset[0][triset.texcoord_indexset[0]])
115+
self.normals = numpy.ravel(triset.normal[triset.normal_index])
116+
117+
# fix mesh data
118+
self.FixMeshData()
119+
120+
# create a new mesh, FRawMesh is an ptopmized wrapper exposed by the python plugin. read: no reflection involved
121+
mesh = FRawMesh()
122+
# assign vertices
123+
mesh.set_vertex_positions(self.vertices)
124+
# uvs are required
125+
mesh.set_wedge_tex_coords(self.uvs)
126+
# normals are optionals
127+
mesh.set_wedge_tangent_z(self.normals)
128+
129+
# assign indices (not optimized, just return the list of triangles * 3...)
130+
mesh.set_wedge_indices(numpy.arange(0, len(triset) * 3))
131+
132+
# assign the FRawMesh to the LOD0 (the model we created before)
133+
mesh.save_to_static_mesh_source_model(source_model)
134+
135+
# assign LOD0 to the SataticMesh and build it
136+
static_mesh.SourceModels = [source_model]
137+
static_mesh.static_mesh_build()
138+
static_mesh.static_mesh_create_body_setup()
139+
140+
static_mesh.StaticMaterials = [StaticMaterial(MaterialInterface=self.ImportOptions.DefaultMaterial, MaterialSlotName='Main')]
141+
142+
return static_mesh

0 commit comments

Comments
 (0)