X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=blender_export.py;h=16d1f381e201f2c9625c335690a4404134d38150;hb=9c85e110fa8b965195438d96625ff9753af362a6;hp=a13e84eb4a95b926018528902d7b26c9aee250b2;hpb=cb16ccb05a796178c879ea8d5091663d215a5217;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/blender_export.py b/blender_export.py index a13e84e..16d1f38 100644 --- a/blender_export.py +++ b/blender_export.py @@ -1,5 +1,6 @@ import bpy, math, gpu from ctypes import * +from mathutils import * from gpu_extras.batch import batch_for_shader bl_info = { @@ -14,168 +15,246 @@ bl_info = { "category":"Import/Export", } -class model(Structure): +class mdl_vert(Structure): _pack_ = 1 - _fields_ = [("identifier",c_uint32), - ("vertex_count",c_uint32), - ("indice_count",c_uint32), - ("layer_count",c_uint32), - ("marker_count",c_uint32)] + _fields_ = [("co",c_float*3), + ("norm",c_float*3), + ("colour",c_float*4), + ("uv",c_float*2)] -class submodel(Structure): +class mdl_submesh(Structure): _pack_ = 1 _fields_ = [("indice_start",c_uint32), ("indice_count",c_uint32), ("vertex_start",c_uint32), ("vertex_count",c_uint32), ("bbx",(c_float*3)*2), - ("pivot",c_float*3), - ("q",c_float*4), - ("name",c_char*32), - ("material",c_char*32)] + ("material_id",c_uint32)] # index into the material array -class classtype_gate(Structure): +class mdl_material(Structure): _pack_ = 1 - _fields_ = [("target",c_uint32)] + _fields_ = [("pstr_name",c_uint32)] -class marker(Structure): +class mdl_node(Structure): _pack_ = 1 _fields_ = [("co",c_float*3), ( "q",c_float*4), ( "s",c_float*3), + ("submesh_start",c_uint32), + ("submesh_count",c_uint32), ("classtype",c_uint32), ("offset",c_uint32), - ("name",c_char*32)] + ("pstr_name",c_uint32)] -class model_vert(Structure): +class mdl_header(Structure): _pack_ = 1 - _fields_ = [("co",c_float*3), - ("norm",c_float*3), - ("colour",c_float*4), - ("uv",c_float*2)] + _fields_ = [("identifier",c_uint32), + ("version",c_uint32), + ("file_length",c_uint32), + ("vertex_count",c_uint32), + ("vertex_offset",c_uint32), + + ("indice_count",c_uint32), + ("indice_offset",c_uint32), + + ("submesh_count",c_uint32), + ("submesh_offset",c_uint32), + + ("material_count",c_uint32), + ("material_offset",c_uint32), + + ("node_count",c_uint32), + ("node_offset",c_uint32), + + ("strings_offset",c_uint32), + ("entdata_offset",c_uint32) + ] + +# Entity types +# ========================================== + +class classtype_gate(Structure): + _pack_ = 1 + _fields_ = [("target",c_uint32)] + +class classtype_block(Structure): + _pack_ = 1 + _fields_ = [("bbx",(c_float*3)*2)] -def submesh_set_transform( sm, obj ): - sm.pivot[0] = obj.matrix_world.translation[0] - sm.pivot[1] = obj.matrix_world.translation[2] - sm.pivot[2] = -obj.matrix_world.translation[1] +class classtype_spawn(Structure): + _pack_ = 1 + _fields_ = [("temp",c_uint32)] + +class classtype_water(Structure): + _pack_ = 1 + _fields_ = [("temp",c_uint32)] - quat = obj.matrix_world.to_quaternion() - sm.q[0] = quat[1] - sm.q[1] = quat[3] - sm.q[2] = -quat[2] - sm.q[3] = quat[0] +# Exporter +# ============================================================================== def write_model(name): - fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb") + print( F"Create mode {name}" ) + collection = bpy.data.collections[name] - header = model() + header = mdl_header() header.identifier = 0xABCD0000 + header.version = 0 header.vertex_count = 0 header.indice_count = 0 - header.layer_count = 0 - header.marker_count = 1 + header.submesh_count = 0 + header.node_count = 0 + header.material_count = 0 + header.file_length = 0 mesh_cache = {} - layers = [] + string_cache = {} + material_cache = {} + + strings_buffer = b'' + + material_buffer = [] + submesh_buffer = [] vertex_buffer = [] indice_buffer = [] + node_buffer = [] + entdata_buffer = [] + entdata_length = 0 + + def emplace_string( s ): + nonlocal string_cache, strings_buffer + + if s in string_cache: + return string_cache[s] + + string_cache[s] = len( strings_buffer ) + strings_buffer += (s+'\0').encode('utf-8') + return string_cache[s] + + def emplace_material( mat ): + nonlocal material_cache, material_buffer + + if mat.name in material_cache: + return material_cache[mat.name] + + material_cache[mat.name] = header.material_count + dest = mdl_material() + dest.pstr_name = emplace_string( mat.name ) + material_buffer += [dest] + + header.material_count += 1 + return material_cache[mat.name] + + # Create root or empty node and materials + # + none_material = c_uint32(69) + none_material.name = "" + emplace_material( none_material ) + + root = mdl_node() + root.co[0] = 0 + root.co[1] = 0 + root.co[2] = 0 + root.q[0] = 0 + root.q[1] = 0 + root.q[2] = 0 + root.q[3] = 1 + root.s[0] = 1 + root.s[1] = 1 + root.s[2] = 1 + root.pstr_name = emplace_string('') + root.submesh_start = 0 + root.submesh_count = 0 + root.offset = 0 + root.classtype = 0 + node_buffer += [root] + + # Do exporting + # + print( " assigning ids" ) + header.node_count = 1 + for obj in collection.all_objects: + obj.cv_data.uid = header.node_count + header.node_count += 1 + + print( " compiling data" ) + for obj in collection.all_objects: + print( F" [{obj.cv_data.uid}/{header.node_count-1}] {obj.name}" ) + + node = mdl_node() + node.co[0] = obj.location[0] + node.co[1] = obj.location[2] + node.co[2] = -obj.location[1] + + # Convert rotation quat to our space type + quat = obj.matrix_world.to_quaternion() + node.q[0] = quat[1] + node.q[1] = quat[3] + node.q[2] = -quat[2] + node.q[3] = quat[0] + + node.s[0] = obj.scale[0] + node.s[1] = obj.scale[2] + node.s[2] = obj.scale[1] + node.pstr_name = emplace_string( obj.name ) + + # Process entity data + # + node.offset = entdata_length + classtype = obj.cv_data.classtype + + if classtype == 'k_classtype_none': + node.classtype = 0 + node.offset = 0 + + elif classtype == 'k_classtype_gate': + node.classtype = 1 + entdata_length += sizeof( classtype_gate ) + + gate = classtype_gate() + gate.target = 0 + if obj.cv_data.target != None: + gate.target = obj.cv_data.target.cv_data.uid - print( F"Create mode {name}" ) - - rootmarker = marker() - rootmarker.co[0] = 0 - rootmarker.co[1] = 0 - rootmarker.co[2] = 0 - rootmarker.q[0] = 0 - rootmarker.q[1] = 0 - rootmarker.q[2] = 0 - rootmarker.q[3] = 1 - rootmarker.s[0] = 1 - rootmarker.s[1] = 1 - rootmarker.s[2] = 1 - rootmarker.name = "".encode('utf-8') - rootmarker.offset = 0 - rootmarker.classtype = 0 - - markers = [ rootmarker ] # aka entities - entdata_structs = [] - entdata_offset = 0 - - entity_count = 1 - - for obj in collection.objects: - if obj.type == 'EMPTY': - obj.cv_data.uid = entity_count - entity_count += 1 - - for obj in collection.objects: - if obj.type == 'EMPTY': - mk = marker() - mk.co[0] = obj.location[0] - mk.co[1] = obj.location[2] - mk.co[2] = -obj.location[1] - - # Convert rotation quat to our space type - quat = obj.matrix_world.to_quaternion() - mk.q[0] = quat[1] - mk.q[1] = quat[3] - mk.q[2] = -quat[2] - mk.q[3] = quat[0] - - mk.s[0] = obj.scale[0] - mk.s[1] = obj.scale[2] - mk.s[2] = obj.scale[1] - mk.name = obj.name.encode('utf-8') - mk.offset = entdata_offset + entdata_buffer += [gate] - classtype = obj.cv_data.classtype + elif classtype == 'k_classtype_block': + node.classtype = 2 + entdata_length += sizeof( classtype_block ) - if classtype == 'k_classtype_gate': - mk.classtype = 1 - entdata_offset += sizeof( classtype_gate ) + source = obj.data.cv_data - gate = classtype_gate() - gate.target = 0 - if obj.cv_data.target != None: - gate.target = obj.cv_data.target.cv_data.uid + block = classtype_block() + block.bbx[0][0] = source.v0[0] + block.bbx[0][1] = source.v0[2] + block.bbx[0][2] = -source.v0[1] + block.bbx[1][0] = source.v1[0] + block.bbx[1][1] = source.v1[2] + block.bbx[1][2] = -source.v1[1] + entdata_buffer += [block] - entdata_structs += [gate] + elif classtype == 'k_classtype_spawn': + node.classtype = 3 - elif classtype == 'k_thingummybob': - pass + elif classtype == 'k_classtype_water': + node.classtype = 4 - markers += [mk] - header.marker_count += 1 + # Process meshes + # + node.submesh_start = header.submesh_count + node.submesh_count = 0 - elif obj.type == 'MESH': + if obj.type == 'MESH': default_mat = c_uint32(69) default_mat.name = "" if obj.data.name in mesh_cache: ref = mesh_cache[obj.data.name] - for material_id, mref in enumerate(ref['sm']): - print(F" Link submesh({ref['users']}) '{obj.name}:{mat.name}'") - - sm = submodel() - sm.indice_start = mref['indice_start'] - sm.indice_count = mref['indice_count'] - sm.vertex_start = mref['vertex_start'] - sm.vertex_count = mref['vertex_count'] - sm.name = obj.name.encode('utf-8') - sm.material = mref['material'] - sm.bbx = mref['bbx'] - submesh_set_transform( sm, obj ) - layers += [sm] - header.layer_count += 1 - - ref['users'] += 1 + node.submesh_start = ref.submesh_start + node.submesh_count = ref.submesh_count + node_buffer += [node] continue - ref = mesh_cache[obj.data.name] = {} - ref['users'] = 0 - ref['sm'] = [] - dgraph = bpy.context.evaluated_depsgraph_get() data = obj.evaluated_get(dgraph).data data.calc_loop_triangles() @@ -185,24 +264,18 @@ def write_model(name): for material_id, mat in enumerate(mat_list): mref = {} - sm = submodel() + sm = mdl_submesh() sm.indice_start = header.indice_count sm.vertex_start = header.vertex_count sm.vertex_count = 0 sm.indice_count = 0 - submesh_set_transform( sm, obj ) + sm.material_id = emplace_material( mat ) for i in range(3): sm.bbx[0][i] = 999999 sm.bbx[1][i] = -999999 - - sm.name = obj.name.encode('utf-8') - sm.material = mat.name.encode('utf-8') - print( F" Creating submesh '{obj.name}:{mat.name}'" ) + boffa = {} - - hit_count = 0 - miss_count = 0 # Write the vertex / indice data # @@ -212,30 +285,33 @@ def write_model(name): for j in range(3): vert = data.vertices[tri.vertices[j]] + li = tri.loops[j] co = vert.co - norm = data.loops[tri.loops[j]].normal + norm = data.loops[li].normal uv = (0,0) + colour = (1,1,1,1) if data.uv_layers: - uv = data.uv_layers.active.data[tri.loops[j]].uv + uv = data.uv_layers.active.data[li].uv + if data.vertex_colors: + colour = data.vertex_colors.active.data[li].color key = (round(co[0],4),round(co[1],4),round(co[2],4),\ round(norm[0],4),round(norm[1],4),round(norm[2],4),\ - round(uv[0],4),round(uv[1],4)) + round(uv[0],4),round(uv[1],4),\ + round(colour[0],4),round(colour[1],4),\ + round(colour[2],4),round(colour[3],4)) if key in boffa: indice_buffer += [boffa[key]] - hit_count += 1 else: - miss_count += 1 index = c_uint32(sm.vertex_count) sm.vertex_count += 1 boffa[key] = index - indice_buffer += [index] - v = model_vert() + v = mdl_vert() v.co[0] = co[0] v.co[1] = co[2] v.co[2] = -co[1] @@ -244,10 +320,10 @@ def write_model(name): v.norm[2] = -norm[1] v.uv[0] = uv[0] v.uv[1] = uv[1] - v.colour[0] = 1.0 - v.colour[1] = 1.0 - v.colour[2] = 1.0 - v.colour[3] = 1.0 + v.colour[0] = colour[0] + v.colour[1] = colour[1] + v.colour[2] = colour[2] + v.colour[3] = colour[3] vertex_buffer += [v] for i in range(3): @@ -261,35 +337,63 @@ def write_model(name): for i in range(3): sm.bbx[j][i] = 0 - layers += [sm] - header.layer_count += 1 + submesh_buffer += [sm] + node.submesh_count += 1 + header.submesh_count += 1 header.vertex_count += sm.vertex_count header.indice_count += sm.indice_count - mref['indice_start'] = sm.indice_start - mref['indice_count'] = sm.indice_count - mref['vertex_start'] = sm.vertex_start - mref['vertex_count'] = sm.vertex_count - mref['bbx'] = sm.bbx - print( F"{sm.bbx[0][0]},{sm.bbx[0][1]},{sm.bbx[0][2]}" ) + mesh_cache[obj.data.name] = node + node_buffer += [node] + + # Write data arrays + # + print( "Writing data" ) + fpos = sizeof(header) + + header.node_offset = fpos + fpos += sizeof(mdl_node)*header.node_count + + header.submesh_offset = fpos + fpos += sizeof(mdl_submesh)*header.submesh_count + + header.material_offset = fpos + fpos += sizeof(mdl_material)*header.material_count - mref['material'] = sm.material - ref['sm'] += [mref] + header.entdata_offset = fpos + fpos += entdata_length + header.vertex_offset = fpos + fpos += sizeof(mdl_vert)*header.vertex_count + + header.indice_offset = fpos + fpos += sizeof(c_uint32)*header.indice_count + + header.strings_offset = fpos + fpos += len(strings_buffer) + + header.file_length = fpos + + fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb") fp.write( bytearray( header ) ) - for l in layers: - fp.write( bytearray(l) ) - for m in markers: - fp.write( bytearray(m) ) + + for node in node_buffer: + fp.write( bytearray(node) ) + for sm in submesh_buffer: + fp.write( bytearray(sm) ) + for mat in material_buffer: + fp.write( bytearray(mat) ) + for ed in entdata_buffer: + fp.write( bytearray(ed) ) for v in vertex_buffer: fp.write( bytearray(v) ) for i in indice_buffer: fp.write( bytearray(i) ) - for ed in entdata_structs: - fp.write( bytearray(ed) ) - + fp.write( strings_buffer ) fp.close() + print( F"Completed {name}.mdl" ) + # Clicky clicky GUI # ------------------------------------------------------------------------------ @@ -308,7 +412,7 @@ def cv_draw(): verts = [] colours = [] - for obj in bpy.context.collection.all_objects: + for obj in bpy.context.collection.objects: if obj.cv_data.classtype == 'k_classtype_gate': if obj.cv_data.target != None: p0 = obj.location @@ -316,6 +420,29 @@ def cv_draw(): verts += [(p0[0],p0[1],p0[2])] verts += [(p1[0],p1[1],p1[2])] colours += [(0,1,0,1.0),(1,0,0,1.0)] + elif obj.cv_data.classtype == 'k_classtype_block': + a = obj.data.cv_data.v0 + b = obj.data.cv_data.v1 + + vs = [None]*8 + vs[0] = obj.matrix_world @ Vector((a[0], a[1], a[2])) + vs[1] = obj.matrix_world @ Vector((a[0], b[1], a[2])) + vs[2] = obj.matrix_world @ Vector((b[0], b[1], a[2])) + vs[3] = obj.matrix_world @ Vector((b[0], a[1], a[2])) + vs[4] = obj.matrix_world @ Vector((a[0], a[1], b[2])) + vs[5] = obj.matrix_world @ Vector((a[0], b[1], b[2])) + vs[6] = obj.matrix_world @ Vector((b[0], b[1], b[2])) + vs[7] = obj.matrix_world @ Vector((b[0], a[1], b[2])) + + indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(6,7),(7,4),\ + (0,4),(1,5),(2,6),(3,7)] + + for l in indices: + v0 = vs[l[0]] + v1 = vs[l[1]] + verts += [(v0[0],v0[1],v0[2])] + verts += [(v1[0],v1[1],v1[2])] + colours += [(1,1,0,1),(1,1,0,1)] lines = batch_for_shader(\ cv_view_shader, 'LINES', \ @@ -330,6 +457,12 @@ def cv_poll_target(scene, obj): return False return True +class CV_MESH_SETTINGS(bpy.types.PropertyGroup): + v0: bpy.props.FloatVectorProperty(name="v0",size=3) + v1: bpy.props.FloatVectorProperty(name="v1",size=3) + v2: bpy.props.FloatVectorProperty(name="v2",size=3) + v3: bpy.props.FloatVectorProperty(name="v3",size=3) + class CV_OBJ_SETTINGS(bpy.types.PropertyGroup): uid: bpy.props.IntProperty( name="" ) @@ -341,6 +474,9 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup): items = [ ('k_classtype_none', "k_classtype_none", "", 0), ('k_classtype_gate', "k_classtype_gate", "", 1), + ('k_classtype_block', "k_classtype_block", "", 2), + ('k_classtype_spawn', "k_classtype_spawn", "", 3), + ('k_classtype_water', "k_classtype_water", "", 4) ]) class CV_OBJ_PANEL(bpy.types.Panel): @@ -354,7 +490,17 @@ class CV_OBJ_PANEL(bpy.types.Panel): active_object = bpy.context.active_object if active_object == None: return _.layout.prop( active_object.cv_data, "classtype" ) - _.layout.prop( active_object.cv_data, "target" ) + + if active_object.cv_data.classtype == 'k_classtype_gate': + _.layout.prop( active_object.cv_data, "target" ) + elif active_object.cv_data.classtype == 'k_classtype_block': + mesh = active_object.data + + _.layout.label( text=F"(i) Data is stored in {mesh.name}" ) + _.layout.prop( mesh.cv_data, "v0" ) + _.layout.prop( mesh.cv_data, "v1" ) + _.layout.prop( mesh.cv_data, "v2" ) + _.layout.prop( mesh.cv_data, "v3" ) class CV_INTERFACE(bpy.types.Panel): bl_idname = "VIEW3D_PT_carve" @@ -377,7 +523,8 @@ class CV_COMPILE(bpy.types.Operator): return {'FINISHED'} -classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE] +classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE,\ + CV_MESH_SETTINGS] def register(): global cv_view_draw_handler @@ -386,6 +533,8 @@ def register(): bpy.utils.register_class(c) bpy.types.Object.cv_data = bpy.props.PointerProperty(type=CV_OBJ_SETTINGS) + bpy.types.Mesh.cv_data = bpy.props.PointerProperty(type=CV_MESH_SETTINGS) + cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\ cv_draw,(),'WINDOW','POST_VIEW')