-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
("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 v4_dot( a, b ):
- return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*a[3]
-
-def v4_length( a ):
- return math.sqrt( v4_dot(a,a) )
-
-def v2_eq( a, b ):
- if abs(a[0]-b[0]) < 0.0001:
- if abs(a[1]-b[1]) < 0.0001:
- return True
- return False
-
-def v3_eq( a, b ):
- if abs(a[0]-b[0]) < 0.0001:
- if abs(a[1]-b[1]) < 0.0001:
- if abs(a[2]-b[2]) < 0.0001:
- return True
- return False
-
-def v4_eq( a, b ):
- if abs(a[0]-b[0]) < 0.0001:
- if abs(a[1]-b[1]) < 0.0001:
- if abs(a[2]-b[2]) < 0.0001:
- if abs(a[3]-b[3]) < 0.0001:
- return True
- return False
-
-def m3x3_mul( a, b, d ):
- a00 = a[0][0]
- a01 = a[0][1]
- a02 = a[0][2]
- a10 = a[1][0]
- a11 = a[1][1]
- a12 = a[1][2]
- a20 = a[2][0]
- a21 = a[2][1]
- a22 = a[2][2]
- b00 = b[0][0]
- b01 = b[0][1]
- b02 = b[0][2]
- b10 = b[1][0]
- b11 = b[1][1]
- b12 = b[1][2]
- b20 = b[2][0]
- b21 = b[2][1]
- b22 = b[2][2]
- d[0][0] = a00*b00 + a10*b01 + a20*b02
- d[0][1] = a01*b00 + a11*b01 + a21*b02
- d[0][2] = a02*b00 + a12*b01 + a22*b02
- d[1][0] = a00*b10 + a10*b11 + a20*b12
- d[1][1] = a01*b10 + a11*b11 + a21*b12
- d[1][2] = a02*b10 + a12*b11 + a22*b12
- d[2][0] = a00*b20 + a10*b21 + a20*b22
- d[2][1] = a01*b20 + a11*b21 + a21*b22
- d[2][2] = a02*b20 + a12*b21 + a22*b22
-
-def q_m3x3( q, d ):
- l = v4_length(q)
- s = 2.0 if l > 0.0 else 0.0
- xx = s*q[0]*q[0]
- xy = s*q[0]*q[1]
- wx = s*q[3]*q[0]
- yy = s*q[1]*q[1]
- yz = s*q[1]*q[2]
- wy = s*q[3]*q[1]
- zz = s*q[2]*q[2]
- xz = s*q[0]*q[2]
- wz = s*q[3]*q[2]
- d[0][0] = 1.0 - yy - zz
- d[1][1] = 1.0 - xx - zz
- d[2][2] = 1.0 - xx - yy
- d[0][1] = xy + wz
- d[1][2] = yz + wx
- d[2][0] = xz + wy
- d[1][0] = xy - wz
- d[2][1] = yz - wx
- d[0][2] = xz - wy
-
-def m3x3_q( m, q ):
- diag = m[0][0] + m[1][1] + m[2][2]
- if diag >= 0.0:
- r = math.sqrt( 1.0 + diag )
- rinv = 0.5 / r
- q[0] = rinv * (m[1][2] - m[2][1])
- q[1] = rinv * (m[2][0] - m[0][2])
- q[2] = rinv * (m[0][1] - m[1][0])
- q[3] = r * 0.5
- elif m[0][0] >= m[1][1] and m[0][0] >= m[2][2]:
- r = math.sqrt( 1.0 - m[1][1] - m[2][2] + m[0][0] )
- rinv = 0.5 / r
- q[0] = r * 0.5
- q[1] = rinv * (m[0][1] + m[1][0])
- q[2] = rinv * (m[0][2] + m[2][0])
- q[3] = rinv * (m[1][2] - m[2][1])
- elif m[1][1] >= m[2][2]:
- r = math.sqrt( 1.0 - m[0][0] - m[2][2] + m[1][1] )
- rinv = 0.5 / r
- q[0] = rinv * (m[0][1] + m[1][0])
- q[1] = r * 0.5
- q[2] = rinv * (m[1][2] + m[2][1])
- q[3] = rinv * (m[2][0] - m[0][2])
- else:
- r = math.sqrt( 1.0 - m[0][0] - m[1][1] + m[2][2] )
- rinv = 0.5 / r
- q[0] = rinv * (m[0][2] + m[2][0])
- q[1] = rinv * (m[1][2] + m[2][1])
- q[2] = r * 0.5
- q[3] = rinv * (m[0][1] - m[1][0])
+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 = []
- markers = []
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 == 'EMPTY':
mk = marker()
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()
- for material_id, mat in enumerate(data.materials):
+ 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
- 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]
+ submesh_set_transform( sm, obj )
for i in range(3):
sm.bbx[0][i] = 999999
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]]
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):
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) )
fp.write( bytearray(v) )
for i in indice_buffer:
fp.write( bytearray(i) )
+ for ed in entdata_structs:
+ fp.write( bytearray(ed) )
fp.close()
-for col in bpy.data.collections["export"].children:
- write_model( col.name )
+# 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)
+
+ bpy.types.SpaceView3D.draw_handler_remove(cv_view_draw_handler,'WINDOW')