-import bpy, math
+import bpy, math, gpu
from ctypes import *
+from gpu_extras.batch import batch_for_shader
+
+bl_info = {
+ "name":"Carve exporter",
+ "author": "Harry Godden (hgn)",
+ "version": (0,1),
+ "blender":(3,1,0),
+ "location":"Export",
+ "descriptin":"",
+ "warning":"",
+ "wiki_url":"",
+ "category":"Import/Export",
+}
class model(Structure):
_pack_ = 1
_fields_ = [("identifier",c_uint32),
("vertex_count",c_uint32),
("indice_count",c_uint32),
- ("layer_count",c_uint32)]
-
-class sdf_primative(Structure):
- _pack_ = 1
- _fields_ = [("origin",c_float*4),
- ("info",c_float*4)]
+ ("layer_count",c_uint32),
+ ("marker_count",c_uint32)]
class submodel(Structure):
_pack_ = 1
("vertex_count",c_uint32),
("bbx",(c_float*3)*2),
("pivot",c_float*3),
- ("sdf",sdf_primative),
- ("sdf_type",c_int32),
+ ("q",c_float*4),
+ ("name",c_char*32),
+ ("material",c_char*32)]
+
+class classtype_gate(Structure):
+ _pack_ = 1
+ _fields_ = [("target",c_uint32)]
+
+class marker(Structure):
+ _pack_ = 1
+ _fields_ = [("co",c_float*3),
+ ( "q",c_float*4),
+ ( "s",c_float*3),
+ ("classtype",c_uint32),
+ ("offset",c_uint32),
("name",c_char*32)]
class model_vert(Structure):
("colour",c_float*4),
("uv",c_float*2)]
-def fixed_string(dest,string):
- return
- for i in range(len(string)):
- dest[i] = string[i]
+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]
+
+ 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]
def write_model(name):
fp = open(F"/home/harry/Documents/carve/models/{name}.mdl", "wb")
header.vertex_count = 0
header.indice_count = 0
header.layer_count = 0
-
+ header.marker_count = 1
+
+ mesh_cache = {}
layers = []
vertex_buffer = []
indice_buffer = []
+ 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 == 'MESH':
+ 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
+
+ classtype = obj.cv_data.classtype
+
+ if classtype == 'k_classtype_gate':
+ mk.classtype = 1
+ entdata_offset += sizeof( classtype_gate )
+
+ gate = classtype_gate()
+ gate.target = 0
+ if obj.cv_data.target != None:
+ gate.target = obj.cv_data.target.cv_data.uid
+
+ entdata_structs += [gate]
+
+ elif classtype == 'k_thingummybob':
+ pass
+
+ markers += [mk]
+ header.marker_count += 1
+
+ elif 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
+ 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()
data.calc_normals_split()
- sm = submodel()
- sm.indice_start = header.indice_count
- sm.vertex_start = header.vertex_count
- sm.vertex_count = len(data.vertices)
- sm.indice_count = len(data.loop_triangles)*3
- sm.sdf_type = 0
- sm.pivot[0] = obj.matrix_world.translation[0]
- sm.pivot[1] = obj.matrix_world.translation[2]
- sm.pivot[2] = -obj.matrix_world.translation[1]
-
- for i in range(3):
- sm.bbx[0][i] = 999999
- sm.bbx[1][i] = -999999
-
- if F"{obj.name}.sdf_cone" in bpy.data.objects:
- cone = bpy.data.objects[F"{obj.name}.sdf_cone"]
- sm.sdf.origin[0] = cone.location[0]
- sm.sdf.origin[1] = cone.location[2] + cone.scale[1]*2.0
- sm.sdf.origin[2] = -cone.location[1]
- sm.sdf.origin[3] = 0.0
-
- lo = cone.scale[0]
- la = cone.scale[1]*2.0
- lh = math.sqrt(lo*lo+la*la)
-
- sm.sdf.info[0] = lo
- sm.sdf.info[1] = la
- sm.sdf.info[2] = lo/lh
- sm.sdf.info[3] = la/lh
-
- sm.sdf_type = 1
-
- sm.name = obj.name.encode('utf-8')
-
- for vert in data.vertices:
- v = model_vert()
- v.co[0] = vert.co[0]
- v.co[1] = vert.co[2]
- v.co[2] = -vert.co[1]
- v.colour[0] = 1.0
- v.colour[1] = 1.0
- v.colour[2] = 1.0
- v.colour[3] = 1.0
- vertex_buffer += [v]
-
+ mat_list = data.materials if len(data.materials) > 0 else [default_mat]
+ for material_id, mat in enumerate(mat_list):
+ mref = {}
+
+ sm = submodel()
+ 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 )
+
for i in range(3):
- sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
- sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
-
- for l in data.loops:
- pvert = vertex_buffer[l.vertex_index + sm.vertex_start]
- norm = l.normal
- pvert.norm[0] = norm[0]
- pvert.norm[1] = norm[2]
- pvert.norm[2] = -norm[1]
-
- #if data.vertex_colors:
- # colour = data.vertex_colors.active.data[ l.index ].color
- # pvert.colour[0] = colour[0]
-
- if data.uv_layers:
- uv = data.uv_layers.active.data[ l.index ].uv
- pvert.uv[0] = uv[0]
- pvert.uv[1] = uv[1]
+ 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
+ #
+ for tri_index, tri in enumerate(data.loop_triangles):
+ if tri.material_index != material_id:
+ continue
+
+ for j in range(3):
+ vert = data.vertices[tri.vertices[j]]
+ li = tri.loops[j]
+
+ co = vert.co
+ norm = data.loops[li].normal
+ uv = (0,0)
+ colour = (1,1,1,1)
+ if data.uv_layers:
+ 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(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.co[0] = co[0]
+ v.co[1] = co[2]
+ v.co[2] = -co[1]
+ v.norm[0] = norm[0]
+ v.norm[1] = norm[2]
+ v.norm[2] = -norm[1]
+ v.uv[0] = uv[0]
+ v.uv[1] = uv[1]
+ v.colour[0] = colour[0]
+ v.colour[1] = colour[1]
+ v.colour[2] = colour[2]
+ v.colour[3] = colour[3]
+ vertex_buffer += [v]
- for tri in data.loop_triangles:
- indice_buffer += [c_uint32(tri.vertices[_]) for _ in range(3)]
+ for i in range(3):
+ sm.bbx[0][i] = min( sm.bbx[0][i], v.co[i] )
+ sm.bbx[1][i] = max( sm.bbx[1][i], v.co[i] )
- layers += [sm]
- header.layer_count += 1
- header.vertex_count += sm.vertex_count
- header.indice_count += sm.indice_count
+ sm.indice_count += 1
+
+ if sm.vertex_count == 0:
+ for j in range(2):
+ for i in range(3):
+ sm.bbx[j][i] = 0
+
+ layers += [sm]
+ header.layer_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]}" )
+
+ mref['material'] = sm.material
+ ref['sm'] += [mref]
fp.write( bytearray( header ) )
for l in layers:
fp.write( bytearray(l) )
+ for m in markers:
+ fp.write( bytearray(m) )
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.close()
-write_model( "test" )
-write_model( "free_dev" )
-write_model( "char_dev" )
-write_model( "skydome" )
-write_model( "cement_r1" )
+# Clicky clicky GUI
+# ------------------------------------------------------------------------------
+
+cv_view_draw_handler = None
+cv_view_shader = gpu.shader.from_builtin('3D_SMOOTH_COLOR')
+
+def cv_draw():
+ global cv_view_shader
+ cv_view_shader.bind()
+ gpu.state.depth_mask_set(False)
+ gpu.state.line_width_set(2.0)
+ gpu.state.face_culling_set('BACK')
+ gpu.state.depth_test_set('NONE')
+ gpu.state.blend_set('ADDITIVE')
+
+ verts = []
+ colours = []
+
+ for obj in bpy.context.collection.all_objects:
+ if obj.cv_data.classtype == 'k_classtype_gate':
+ if obj.cv_data.target != None:
+ p0 = obj.location
+ p1 = obj.cv_data.target.location
+ 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)]
+
+ lines = batch_for_shader(\
+ cv_view_shader, 'LINES', \
+ { "pos":verts, "color":colours })
+
+ lines.draw( cv_view_shader )
+
+def cv_poll_target(scene, obj):
+ if obj == bpy.context.active_object:
+ return False
+ if obj.cv_data.classtype == 'k_classtype_none':
+ return False
+ return True
+
+class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
+ uid: bpy.props.IntProperty( name="" )
+
+ target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \
+ poll=cv_poll_target )
+
+ classtype: bpy.props.EnumProperty(
+ name="Format",
+ items = [
+ ('k_classtype_none', "k_classtype_none", "", 0),
+ ('k_classtype_gate', "k_classtype_gate", "", 1),
+ ])
+
+class CV_OBJ_PANEL(bpy.types.Panel):
+ bl_label="Entity Config"
+ bl_idname="SCENE_PT_cv_entity"
+ bl_space_type='PROPERTIES'
+ bl_region_type='WINDOW'
+ bl_context="object"
+
+ def draw(_,context):
+ 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" )
+
+class CV_INTERFACE(bpy.types.Panel):
+ bl_idname = "VIEW3D_PT_carve"
+ bl_label = "Carve"
+ bl_space_type = 'VIEW_3D'
+ bl_region_type = 'UI'
+ bl_category = "Carve"
+
+ def draw(_, context):
+ layout = _.layout
+ layout.operator( "carve.compile_all" )
+
+class CV_COMPILE(bpy.types.Operator):
+ bl_idname="carve.compile_all"
+ bl_label="Compile All"
+
+ def execute(_,context):
+ for col in bpy.data.collections["export"].children:
+ write_model( col.name )
+
+ return {'FINISHED'}
+
+classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE]
+
+def register():
+ global cv_view_draw_handler
+
+ for c in classes:
+ bpy.utils.register_class(c)
+
+ bpy.types.Object.cv_data = bpy.props.PointerProperty(type=CV_OBJ_SETTINGS)
+ cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
+ cv_draw,(),'WINDOW','POST_VIEW')
+
+def unregister():
+ global cv_view_draw_handler
+
+ for c in classes:
+ bpy.utils.unregister_class(c)
-for col in bpy.data.collections["export"].children:
- write_model( col.name )
+ bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')