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
This commit is contained in:
Matteo De Carlo
2024-01-06 19:33:34 +01:00
parent 66b60d1630
commit 5ad0f9ff32
2 changed files with 107 additions and 67 deletions
+32 -17
View File
@@ -27,30 +27,45 @@ PYBIND11_MAKE_OPAQUE(wi::scene::TransformComponent);
PYBIND11_MAKE_OPAQUE(wi::scene::ObjectComponent);
static wi::Application app;
static sdl2::sdlsystem_ptr_t sdl_system { nullptr, nullptr };
static sdl2::window_ptr_t sdl_window { nullptr, nullptr };
void init_wicked(py::module_& mod)
{
//TODO we can probably initialize less stuff
static bool INIT_DONE = false;
sdl2::sdlsystem_ptr_t system = sdl2::make_sdlsystem(SDL_INIT_VIDEO);
if (!system) {
throw sdl2::SDLError("Error creating SDL2 system");
}
mod.def("init", []()
{
if (INIT_DONE) return;
INIT_DONE = true;
sdl2::window_ptr_t window = sdl2::make_window("WickedBlenderExporter",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
50, 50,
SDL_WINDOW_HIDDEN | SDL_WINDOW_VULKAN);
if (!window) {
throw sdl2::SDLError("Error creating window");
}
//TODO we can probably initialize less stuff
sdl_system = sdl2::make_sdlsystem(SDL_INIT_VIDEO);
app.SetWindow(window.get());
wi::renderer::SetShaderSourcePath(WICKED_ROOT_DIR"/WickedEngine/shaders/");
wi::renderer::SetShaderPath(WICKED_ROOT_DIR"/build/BlenderExporter/shaders/");
if (!sdl_system) {
throw sdl2::SDLError("Error creating SDL2 system");
}
wi::initializer::InitializeComponentsImmediate();
sdl_window = sdl2::make_window("WickedBlenderExporter",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
50, 50,
SDL_WINDOW_HIDDEN | SDL_WINDOW_VULKAN);
if (!sdl_window) {
throw sdl2::SDLError("Error creating window");
}
app.SetWindow(sdl_window.get());
wi::renderer::SetShaderSourcePath(WICKED_ROOT_DIR"/WickedEngine/shaders/");
wi::renderer::SetShaderPath(WICKED_ROOT_DIR"/build/BlenderExporter/shaders/");
//wi::initializer::InitializeComponentsImmediate();
app.Initialize();
});
mod.def("deinit", [](){
//TODO INIT_DONE = false;
});
}
void export_primitives(py::module_& mod)
@@ -1,10 +1,17 @@
#!/usr/bin/env python3
import bpy
import os
import ctypes
from mathutils import Vector, Quaternion, Matrix
import os
import sys
importpath=os.path.dirname(os.path.realpath(__file__))
sys.path.append(importpath)
import pywickedengine as wi
bl_info = {
"name": "Wicked wiscene Extension",
@@ -20,32 +27,31 @@ bl_info = {
}
def WIVector3(blvector3: Vector):
import pywickedengine as wi
def WIVector3(blvector3: Vector) -> wi.XMFLOAT3:
return wi.XMFLOAT3(blvector3[0], blvector3[2], blvector3[1])
def WIQuaternion(q: Quaternion):
import pywickedengine as wi
def WIQuaternion(q: Quaternion) -> wi.XMFLOAT4:
# [W X Y Z] -> [X Y Z W] -> [X Z Y -W]
# q2=[q1.x,q1.z,q1.y,q1.w]
return wi.XMFLOAT4(q[1], q[3], q[2], -q[0])
def wicked_add_mesh(item, scene, root):
print(f"Adding {item.name} mesh ...")
import pywickedengine as wi
entity = scene.Entity_CreateObject(item.name)
def wicked_add_mesh(obj, scene, root):
print(f"Adding {obj.name} mesh ...")
mesh = obj.data
entity = scene.Entity_CreateObject(obj.name)
print(f'{obj.name}={entity}')
object = scene.objects().GetComponent(entity)
scene.meshes().Create(entity)
#entity = scene.Entity_CreateMesh(item.name)
#entity = scene.Entity_CreateMesh(obj.name)
#scene.Component_Attach(entity, root) #TODO this creates a self referencing entity, getting the loading screen stuck in an infintie while loop
mesh = scene.meshes().GetComponent(entity)
wimesh = scene.meshes().GetComponent(entity)
transform = scene.transforms().GetComponent(entity)
transform.translation_local = WIVector3(item.location)
transform.scale_local = WIVector3(item.scale)
if item.rotation_mode == 'QUATERNION':
transform.rotation_local = WIQuaternion(item.rotation_quaternion)
transform.translation_local = WIVector3(obj.location)
transform.scale_local = WIVector3(obj.scale)
if obj.rotation_mode == 'QUATERNION':
transform.rotation_local = WIQuaternion(obj.rotation_quaternion)
else:
transform.rotation_local = WIQuaternion(item.rotation_euler.to_matrix().to_quaternion())
transform.rotation_local = WIQuaternion(obj.rotation_euler.to_matrix().to_quaternion())
transform.UpdateTransform()
object.meshID = entity
@@ -55,69 +61,87 @@ def wicked_add_mesh(item, scene, root):
#vertices[i].co # position data
#vertices[i].undeformed_co # position data - no deforming modifiers applied
for mat in mesh.materials:
pass
#TODO create a subset per material
# create only one submesh for now
#TODO later create a submesh for each material (or similar)
subset = wi.MeshComponent_MeshSubset()
material = scene.materials().GetComponent(subset.materialID)
vertex_offset = len(mesh.vertex_positions)
vertex_offset = len(wimesh.vertex_positions)
print("Generating vertex data")
mesh.calc_loop_triangles()
#After using mesh.calc_normals_split() you access the normals with loop.normal
mesh.calc_normals_split()
vertices_map = {}
vertices_array = []
#index_remap = [0,1,-1]
index_remap = [0,0,0]
# append vertices
print(f"Adding vertex data (len={len(item.data.vertices)})")
for vertex in item.data.vertices:
mesh.vertex_positions.append(WIVector3(vertex.co))
mesh.vertex_normals.append(WIVector3(vertex.normal))
mesh.vertex_uvset_0.append(wi.XMFLOAT2(0,0))
print("Adding index data")
for face in item.data.loop_triangles:
#print("Polygon index: %d, length: %d" % (face.index, 3))
for triangle in mesh.loop_triangles:
face_index = mesh.loop_triangle_polygons[triangle.index].value
face = mesh.polygons[face_index]
#print(f'triangle[{triangle.index}]{triangle}={face_index} SmoothGroup={smooth_groups[face_index]}')
offset=0
for i in face.loops:
ix = item.data.loops[i + index_remap[offset]].vertex_index
mesh.indices.append(ix)
#print(" Vertex: %d %r" % (item.data.loops[i].vertex_index, item.data.vertices[ix].co))
#print(" UV: %r" % item.data.uv_layers.active.data[i].uv)
for i in triangle.loops:
i += index_remap[offset]
ix = mesh.loops[i].vertex_index
normal = mesh.loops[i].normal
uv = mesh.uv_layers.active.data[i].uv
assert len(uv) == 2
v = (ix, WIVector3(normal), wi.XMFLOAT2(uv[0], uv[1]))
if v in vertices_map:
newix = vertices_map[v]
else:
newix = len(vertices_array)
vertices_array.append(v)
vertices_map[v] = newix
wimesh.indices.append(newix)
offset +=1
#verts_in_face = face.vertices[:]
#print("face index ", face.index)
#print("normal ", face.normal)
#for vert in verts_in_face:
# print("vertex coords ", item.data.vertices[vert].co)
if len(mesh.vertex_normals) == 0 and len(mesh.vertex_positions) > 0:
for _ in range(len(mesh.vertex_positions)):
mesh.vertex_normals.append(wi.XMFLOAT3(0,0,0))
mesh.ComputeNormals(wi.MeshComponentComputeNormals.SMOOTH_FAST);
print("Adding vertex data")
for i, normal, uv in vertices_array:
vertex = mesh.vertices[i]
wimesh.vertex_positions.append(WIVector3(vertex.co))
wimesh.vertex_normals.append(normal)
wimesh.vertex_uvset_0.append(uv)
if len(wimesh.vertex_normals) == 0 and len(wimesh.vertex_positions) > 0:
print("Generating missing normal data")
for _ in range(len(wimesh.vertex_positions)):
wimesh.vertex_normals.append(wi.XMFLOAT3(0,0,0))
wimesh.ComputeNormals(wi.MeshComponentComputeNormals.SMOOTH_FAST);
subset.materialID = scene.materials().GetEntity(0)#max(0, face.material))
subset.indexOffset = 0
subset.indexCount = len(mesh.indices)
mesh.subsets.append(subset)
subset.indexCount = len(wimesh.indices)
wimesh.subsets.append(subset)
print(f"Finished adding {item.name} mesh")
print(f"Finished adding {obj.name} mesh")
def create_wiscene(scene_name):
import pywickedengine as wi
### Load Scene
scene = wi.Scene()
rootEntity = wi.CreateEntity()
print(f'rootEntity({scene_name})={rootEntity}')
scene.transforms().Create(rootEntity)
scene.names().Create(rootEntity)
scene.names().GetComponent(rootEntity).name = scene_name
#TODO materials
for mat in bpy.data.materials:
pass
# Meshes
print("Exporting meshes")
for item in bpy.data.objects:
print(f"Element in scene {item.name}")
match item.type:
for obj in bpy.data.objects:
print(f"Element in scene {obj.name}")
match obj.type:
case 'MESH':
wicked_add_mesh(item, scene, rootEntity)
wicked_add_mesh(obj, scene, rootEntity)
print("Finished exporting meshes")
#TODO armatures
@@ -145,7 +169,6 @@ def create_wiscene(scene_name):
def write_wiscene(context, filepath, dump_to_header: bool):
print("running write_wiscene...")
import pywickedengine as wi
filename = os.path.splitext(filepath)[0]
scene_name = os.path.basename(filename)
@@ -218,6 +241,7 @@ def menu_func_export(self, context):
# Register and add to the "file selector" menu (required to use F3 search "Text Export Operator" for quick access).
def register():
wi.init()
bpy.utils.register_class(ExportWiScene)
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
@@ -225,6 +249,7 @@ def register():
def unregister():
bpy.utils.unregister_class(ExportWiScene)
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
wi.deinit()
if __name__ == "__main__":