Skip to content

Commit 5ad0f9f

Browse files
proper normals, uv and better init
- correct normal reading from Blender, which respects per face smoothing setting - exporting UVs - engine initialization separated into a init function
1 parent 66b60d1 commit 5ad0f9f

File tree

2 files changed

+111
-71
lines changed

2 files changed

+111
-71
lines changed

BlenderExporter/python_bindings.cpp

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,45 @@ PYBIND11_MAKE_OPAQUE(wi::scene::TransformComponent);
2727
PYBIND11_MAKE_OPAQUE(wi::scene::ObjectComponent);
2828

2929
static wi::Application app;
30+
static sdl2::sdlsystem_ptr_t sdl_system { nullptr, nullptr };
31+
static sdl2::window_ptr_t sdl_window { nullptr, nullptr };
3032

3133
void init_wicked(py::module_& mod)
3234
{
33-
//TODO we can probably initialize less stuff
34-
35-
sdl2::sdlsystem_ptr_t system = sdl2::make_sdlsystem(SDL_INIT_VIDEO);
36-
if (!system) {
37-
throw sdl2::SDLError("Error creating SDL2 system");
38-
}
39-
40-
sdl2::window_ptr_t window = sdl2::make_window("WickedBlenderExporter",
41-
SDL_WINDOWPOS_CENTERED,
42-
SDL_WINDOWPOS_CENTERED,
43-
50, 50,
44-
SDL_WINDOW_HIDDEN | SDL_WINDOW_VULKAN);
45-
if (!window) {
46-
throw sdl2::SDLError("Error creating window");
47-
}
48-
49-
app.SetWindow(window.get());
50-
wi::renderer::SetShaderSourcePath(WICKED_ROOT_DIR"/WickedEngine/shaders/");
51-
wi::renderer::SetShaderPath(WICKED_ROOT_DIR"/build/BlenderExporter/shaders/");
52-
53-
wi::initializer::InitializeComponentsImmediate();
35+
static bool INIT_DONE = false;
36+
37+
mod.def("init", []()
38+
{
39+
if (INIT_DONE) return;
40+
INIT_DONE = true;
41+
42+
//TODO we can probably initialize less stuff
43+
sdl_system = sdl2::make_sdlsystem(SDL_INIT_VIDEO);
44+
45+
if (!sdl_system) {
46+
throw sdl2::SDLError("Error creating SDL2 system");
47+
}
48+
49+
sdl_window = sdl2::make_window("WickedBlenderExporter",
50+
SDL_WINDOWPOS_CENTERED,
51+
SDL_WINDOWPOS_CENTERED,
52+
50, 50,
53+
SDL_WINDOW_HIDDEN | SDL_WINDOW_VULKAN);
54+
if (!sdl_window) {
55+
throw sdl2::SDLError("Error creating window");
56+
}
57+
58+
app.SetWindow(sdl_window.get());
59+
wi::renderer::SetShaderSourcePath(WICKED_ROOT_DIR"/WickedEngine/shaders/");
60+
wi::renderer::SetShaderPath(WICKED_ROOT_DIR"/build/BlenderExporter/shaders/");
61+
62+
//wi::initializer::InitializeComponentsImmediate();
63+
app.Initialize();
64+
});
65+
66+
mod.def("deinit", [](){
67+
//TODO INIT_DONE = false;
68+
});
5469
}
5570

5671
void export_primitives(py::module_& mod)

BlenderExporter/wicked_blender_exporter/__init__.py

Lines changed: 75 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
#!/usr/bin/env python3
22

3+
34
import bpy
45
import os
56
import ctypes
67
from mathutils import Vector, Quaternion, Matrix
78

9+
import os
10+
import sys
11+
importpath=os.path.dirname(os.path.realpath(__file__))
12+
sys.path.append(importpath)
13+
import pywickedengine as wi
14+
815

916
bl_info = {
1017
"name": "Wicked wiscene Extension",
@@ -20,32 +27,31 @@
2027
}
2128

2229

23-
def WIVector3(blvector3: Vector):
24-
import pywickedengine as wi
30+
def WIVector3(blvector3: Vector) -> wi.XMFLOAT3:
2531
return wi.XMFLOAT3(blvector3[0], blvector3[2], blvector3[1])
2632

27-
def WIQuaternion(q: Quaternion):
28-
import pywickedengine as wi
33+
def WIQuaternion(q: Quaternion) -> wi.XMFLOAT4:
2934
# [W X Y Z] -> [X Y Z W] -> [X Z Y -W]
3035
# q2=[q1.x,q1.z,q1.y,−q1.w]
3136
return wi.XMFLOAT4(q[1], q[3], q[2], -q[0])
3237

33-
def wicked_add_mesh(item, scene, root):
34-
print(f"Adding {item.name} mesh ...")
35-
import pywickedengine as wi
36-
entity = scene.Entity_CreateObject(item.name)
38+
def wicked_add_mesh(obj, scene, root):
39+
print(f"Adding {obj.name} mesh ...")
40+
mesh = obj.data
41+
entity = scene.Entity_CreateObject(obj.name)
42+
print(f'{obj.name}={entity}')
3743
object = scene.objects().GetComponent(entity)
3844
scene.meshes().Create(entity)
39-
#entity = scene.Entity_CreateMesh(item.name)
45+
#entity = scene.Entity_CreateMesh(obj.name)
4046
#scene.Component_Attach(entity, root) #TODO this creates a self referencing entity, getting the loading screen stuck in an infintie while loop
41-
mesh = scene.meshes().GetComponent(entity)
47+
wimesh = scene.meshes().GetComponent(entity)
4248
transform = scene.transforms().GetComponent(entity)
43-
transform.translation_local = WIVector3(item.location)
44-
transform.scale_local = WIVector3(item.scale)
45-
if item.rotation_mode == 'QUATERNION':
46-
transform.rotation_local = WIQuaternion(item.rotation_quaternion)
49+
transform.translation_local = WIVector3(obj.location)
50+
transform.scale_local = WIVector3(obj.scale)
51+
if obj.rotation_mode == 'QUATERNION':
52+
transform.rotation_local = WIQuaternion(obj.rotation_quaternion)
4753
else:
48-
transform.rotation_local = WIQuaternion(item.rotation_euler.to_matrix().to_quaternion())
54+
transform.rotation_local = WIQuaternion(obj.rotation_euler.to_matrix().to_quaternion())
4955
transform.UpdateTransform()
5056
object.meshID = entity
5157

@@ -55,69 +61,87 @@ def wicked_add_mesh(item, scene, root):
5561
#vertices[i].co # position data
5662
#vertices[i].undeformed_co # position data - no deforming modifiers applied
5763

64+
for mat in mesh.materials:
65+
pass
66+
#TODO create a subset per material
5867
# create only one submesh for now
59-
#TODO later create a submesh for each material (or similar)
6068
subset = wi.MeshComponent_MeshSubset()
6169
material = scene.materials().GetComponent(subset.materialID)
62-
vertex_offset = len(mesh.vertex_positions)
70+
vertex_offset = len(wimesh.vertex_positions)
71+
72+
print("Generating vertex data")
73+
mesh.calc_loop_triangles()
74+
#After using mesh.calc_normals_split() you access the normals with loop.normal
75+
mesh.calc_normals_split()
76+
77+
vertices_map = {}
78+
vertices_array = []
6379

6480
#index_remap = [0,1,-1]
6581
index_remap = [0,0,0]
6682

67-
# append vertices
68-
print(f"Adding vertex data (len={len(item.data.vertices)})")
69-
for vertex in item.data.vertices:
70-
mesh.vertex_positions.append(WIVector3(vertex.co))
71-
mesh.vertex_normals.append(WIVector3(vertex.normal))
72-
mesh.vertex_uvset_0.append(wi.XMFLOAT2(0,0))
73-
74-
print("Adding index data")
75-
for face in item.data.loop_triangles:
76-
#print("Polygon index: %d, length: %d" % (face.index, 3))
83+
for triangle in mesh.loop_triangles:
84+
face_index = mesh.loop_triangle_polygons[triangle.index].value
85+
face = mesh.polygons[face_index]
86+
#print(f'triangle[{triangle.index}]{triangle}={face_index} SmoothGroup={smooth_groups[face_index]}')
7787
offset=0
78-
for i in face.loops:
79-
ix = item.data.loops[i + index_remap[offset]].vertex_index
80-
mesh.indices.append(ix)
81-
#print(" Vertex: %d %r" % (item.data.loops[i].vertex_index, item.data.vertices[ix].co))
82-
#print(" UV: %r" % item.data.uv_layers.active.data[i].uv)
88+
for i in triangle.loops:
89+
i += index_remap[offset]
90+
ix = mesh.loops[i].vertex_index
91+
normal = mesh.loops[i].normal
92+
uv = mesh.uv_layers.active.data[i].uv
93+
assert len(uv) == 2
94+
v = (ix, WIVector3(normal), wi.XMFLOAT2(uv[0], uv[1]))
95+
if v in vertices_map:
96+
newix = vertices_map[v]
97+
else:
98+
newix = len(vertices_array)
99+
vertices_array.append(v)
100+
vertices_map[v] = newix
101+
wimesh.indices.append(newix)
83102
offset +=1
84-
#verts_in_face = face.vertices[:]
85-
#print("face index ", face.index)
86-
#print("normal ", face.normal)
87-
#for vert in verts_in_face:
88-
# print("vertex coords ", item.data.vertices[vert].co)
89103

90-
if len(mesh.vertex_normals) == 0 and len(mesh.vertex_positions) > 0:
91-
for _ in range(len(mesh.vertex_positions)):
92-
mesh.vertex_normals.append(wi.XMFLOAT3(0,0,0))
93-
mesh.ComputeNormals(wi.MeshComponentComputeNormals.SMOOTH_FAST);
104+
print("Adding vertex data")
105+
for i, normal, uv in vertices_array:
106+
vertex = mesh.vertices[i]
107+
wimesh.vertex_positions.append(WIVector3(vertex.co))
108+
wimesh.vertex_normals.append(normal)
109+
wimesh.vertex_uvset_0.append(uv)
110+
111+
if len(wimesh.vertex_normals) == 0 and len(wimesh.vertex_positions) > 0:
112+
print("Generating missing normal data")
113+
for _ in range(len(wimesh.vertex_positions)):
114+
wimesh.vertex_normals.append(wi.XMFLOAT3(0,0,0))
115+
wimesh.ComputeNormals(wi.MeshComponentComputeNormals.SMOOTH_FAST);
94116

95117
subset.materialID = scene.materials().GetEntity(0)#max(0, face.material))
96118
subset.indexOffset = 0
97-
subset.indexCount = len(mesh.indices)
98-
mesh.subsets.append(subset)
119+
subset.indexCount = len(wimesh.indices)
120+
wimesh.subsets.append(subset)
99121

100-
print(f"Finished adding {item.name} mesh")
122+
print(f"Finished adding {obj.name} mesh")
101123

102124

103125
def create_wiscene(scene_name):
104-
import pywickedengine as wi
105126
### Load Scene
106127
scene = wi.Scene()
107128
rootEntity = wi.CreateEntity()
129+
print(f'rootEntity({scene_name})={rootEntity}')
108130
scene.transforms().Create(rootEntity)
109131
scene.names().Create(rootEntity)
110132
scene.names().GetComponent(rootEntity).name = scene_name
111133

112134
#TODO materials
135+
for mat in bpy.data.materials:
136+
pass
113137

114138
# Meshes
115139
print("Exporting meshes")
116-
for item in bpy.data.objects:
117-
print(f"Element in scene {item.name}")
118-
match item.type:
140+
for obj in bpy.data.objects:
141+
print(f"Element in scene {obj.name}")
142+
match obj.type:
119143
case 'MESH':
120-
wicked_add_mesh(item, scene, rootEntity)
144+
wicked_add_mesh(obj, scene, rootEntity)
121145
print("Finished exporting meshes")
122146

123147
#TODO armatures
@@ -145,7 +169,6 @@ def create_wiscene(scene_name):
145169

146170
def write_wiscene(context, filepath, dump_to_header: bool):
147171
print("running write_wiscene...")
148-
import pywickedengine as wi
149172

150173
filename = os.path.splitext(filepath)[0]
151174
scene_name = os.path.basename(filename)
@@ -218,13 +241,15 @@ def menu_func_export(self, context):
218241

219242
# Register and add to the "file selector" menu (required to use F3 search "Text Export Operator" for quick access).
220243
def register():
244+
wi.init()
221245
bpy.utils.register_class(ExportWiScene)
222246
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
223247

224248

225249
def unregister():
226250
bpy.utils.unregister_class(ExportWiScene)
227251
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
252+
wi.deinit()
228253

229254

230255
if __name__ == "__main__":

0 commit comments

Comments
 (0)