+#
+# Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
+#
+
import bpy, math, gpu
import cProfile
from ctypes import *
_fields_ = [("co",c_float*3),
( "q",c_float*4),
( "s",c_float*3),
+ ("sub_uid",c_uint32), # dont use
("submesh_start",c_uint32),
("submesh_count",c_uint32),
("classtype",c_uint32),
_fields_ = [("identifier",c_uint32),
("version",c_uint32),
("file_length",c_uint32),
+ ("pad0",c_uint32),
+
("vertex_count",c_uint32),
("vertex_offset",c_uint32),
("anim_count",c_uint32),
("anim_offset",c_uint32),
-
+
+ ("strings_length",c_uint32),
("strings_offset",c_uint32),
+
+ ("entdata_length",c_uint32),
("entdata_offset",c_uint32),
- ("animdata_offset",c_uint32)
- ]
+
+ ("keyframe_count",c_uint32),
+ ("keyframe_offset",c_uint32)]
+
class mdl_animation(Structure):
_pack_ = 1
class classtype_route(Structure):
_pack_ = 1
- _fields_ = [("pstr_name",c_uint32),
- ("id_start",c_uint32),
+ _fields_ = [("id_start",c_uint32),
("colour",c_float*3)]
class classtype_skin(Structure):
("angle_limits",(c_float*3)*2),
("hitbox",(c_float*3)*2)]
+class classtype_achievement_box(Structure):
+ _pack_ = 1
+ _fields_ = [("pstr_name",c_uint32),
+ ("trigger",c_uint32)]
+
+class classtype_audio(Structure):
+ _pack_ = 1
+ _fields_ = [("pstr_file",c_uint32),
+ ("flags",c_uint32),
+ ("volume",c_float)]
+
# Exporter
# ==============================================================================
header.node_count = 0
header.material_count = 0
header.file_length = 0
+
+ header.strings_length = 0
+ header.entdata_length = 0
+ header.keyframe_count = 0
mesh_cache = {}
string_cache = {}
indice_buffer = []
node_buffer = []
entdata_buffer = []
- entdata_length = 0
anim_buffer = []
- animdata_length = 0
animdata_buffer = []
def emplace_string( s ):
def emplace_material( mat ):
nonlocal material_cache, material_buffer
+ if mat == None:
+ return 0
+
if mat.name in material_cache:
return material_cache[mat.name]
_extendb( tree, b, d+1 )
for obj1 in n.children:
- _extend( tree, obj1, d+1 )
+ nonlocal collection
+ for c1 in obj1.users_collection:
+ if c1 == collection:
+ _extend( tree, obj1, d+1 )
+ break
p["children"] += [tree]
graph_lookup[n] = tree
can_use_cache = True
for mod in obj.modifiers:
- if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP':
+ if mod.type == 'DATA_TRANSFER' or mod.type == 'SHRINKWRAP' or \
+ mod.type == 'BOOLEAN' or mod.type == 'CURVE' or \
+ mod.type == 'ARRAY':
can_use_cache = False
if mod.type == 'ARMATURE':
# WEight groups
#
if armature_def:
- weight_groups = sorted( data.vertices[vi].groups, key = \
+ src_groups = [_ for _ in data.vertices[vi].groups \
+ if obj.vertex_groups[_.group].name in \
+ armature_def['bones']]
+
+ weight_groups = sorted( src_groups, key = \
lambda a: a.weight, reverse=True )
tot = 0.0
for ml in range(3):
# Process entity data
# ==================================================================
- node.offset = entdata_length
+ node.offset = header.entdata_length
if classtype != 'k_classtype_none':
disptype = classtype
node.classtype = 12
armature = armature_def['obj']
- entdata_length += sizeof( classtype_skin )
+ header.entdata_length += sizeof( classtype_skin )
skin = classtype_skin()
skin.skeleton = armature.cv_data.uid
elif classtype == 'k_classtype_skeleton':
node.classtype = 11
- entdata_length += sizeof( classtype_skeleton )
+ header.entdata_length += sizeof( classtype_skeleton )
skeleton = classtype_skeleton()
armature_def = graph_lookup[obj]
anim = mdl_animation()
anim.pstr_name = emplace_string( NLAStrip.action.name )
anim.rate = 30.0
- anim.offset = animdata_length
+ anim.offset = header.keyframe_count
anim.length = anim_end-anim_start
# Export the fucking keyframes
for pb in armature.pose.bones:
if pb.name == bone_name:
rb = armature.data.bones[ bone_name ]
-
- loc, rot, sca = pb.matrix_basis.decompose()
+
+ # relative bone matrix
+ if rb.parent is not None:
+ offset_mtx = rb.parent.matrix_local
+ offset_mtx = offset_mtx.inverted_safe() @ \
+ rb.matrix_local
+
+ inv_parent = pb.parent.matrix @ offset_mtx
+ inv_parent.invert_safe()
+ fpm = inv_parent @ pb.matrix
+ else:
+ bone_mtx = rb.matrix.to_4x4()
+ local_inv = rb.matrix_local.inverted_safe()
+ fpm = bone_mtx @ local_inv @ pb.matrix
+
+ loc, rot, sca = fpm.decompose()
# local position
- vp = rb.matrix @ loc
- final_pos = Vector(( vp[0], vp[2], -vp[1] ))
+ final_pos = Vector(( loc[0], loc[2], -loc[1] ))
# rotation
lc_m = pb.matrix_channel.to_3x3()
kf.s[2] = sca[1]
animdata_buffer += [kf]
- animdata_length += sizeof(mdl_keyframe)
+ header.keyframe_count += 1
break
anim_buffer += [anim]
elif classtype == 'k_classtype_bone':
node.classtype = 10
- entdata_length += sizeof( classtype_bone )
+ header.entdata_length += sizeof( classtype_bone )
bone = classtype_bone()
bone.deform = node_def['deform']
elif classtype == 'k_classtype_gate':
node.classtype = 1
- entdata_length += sizeof( classtype_gate )
+ header.entdata_length += sizeof( classtype_gate )
gate = classtype_gate()
gate.target = 0
elif classtype == 'k_classtype_block':
node.classtype = 2
- entdata_length += sizeof( classtype_block )
+ header.entdata_length += sizeof( classtype_block )
source = obj.data.cv_data
block.bbx[1][2] = -source.v0[1]
entdata_buffer += [block]
+ elif classtype == 'k_classtype_achievement_box':
+ node.classtype = 13
+
+ header.entdata_length += sizeof( classtype_achievement_box )
+ ach = classtype_achievement_box()
+ ach.pstr_name = emplace_string( obj.cv_data.strp )
+ ach.trigger = 0
+
+ if obj.cv_data.target != None:
+ ach.trigger = obj.cv_data.target.cv_data.uid
+
+ entdata_buffer += [ach]
+
+ elif classtype == 'k_classtype_audio':
+ node.classtype = 14
+
+ header.entdata_length += sizeof( classtype_audio )
+ aud = classtype_audio()
+ aud.pstr_file = emplace_string( obj.cv_data.strp )
+ aud.flags = obj.cv_data.intp
+ aud.volume = obj.cv_data.fltp
+
+ entdata_buffer += [aud]
+
elif classtype == 'k_classtype_spawn':
node.classtype = 3
elif classtype == 'k_classtype_car_path':
node.classtype = 5
- entdata_length += sizeof( classtype_car_path )
+ header.entdata_length += sizeof( classtype_car_path )
pn = classtype_car_path()
pn.target = 0
target = obj.instance_collection
node.classtype = 6
- entdata_length += sizeof( classtype_instance )
+ header.entdata_length += sizeof( classtype_instance )
inst = classtype_instance()
inst.pstr_file = emplace_string( F"models/{target.name}.mdl" )
elif classtype == 'k_classtype_route_node':
node.classtype = 8
- entdata_length += sizeof( classtype_route_node )
+ header.entdata_length += sizeof( classtype_route_node )
rn = classtype_route_node()
if obj.cv_data.target != None:
elif classtype == 'k_classtype_route':
node.classtype = 9
- entdata_length += sizeof( classtype_route )
+ header.entdata_length += sizeof( classtype_route )
r = classtype_route()
- r.pstr_name = emplace_string("not-implemented")
r.colour[0] = obj.cv_data.colour[0]
r.colour[1] = obj.cv_data.colour[1]
r.colour[2] = obj.cv_data.colour[2]
node_buffer += [node]
# Write data arrays
- #
- header.anim_count = len(anim_buffer)
+ # TODO: 8 BYTE ALIGNMENT
print( "Writing data" )
fpos = sizeof(header)
header.material_offset = fpos
fpos += sizeof(mdl_material)*header.material_count
- print( F"Animation count: {header.anim_count}" )
+ print( F"Animation count: {len(anim_buffer)}" )
+ header.anim_count = len(anim_buffer)
header.anim_offset = fpos
fpos += sizeof(mdl_animation)*header.anim_count
- print( F"Entdata length: {entdata_length}" )
+ print( F"Entdata length: {header.entdata_length}" )
header.entdata_offset = fpos
- fpos += entdata_length
+ fpos += header.entdata_length
+
+ print( F"Strings length: {len(strings_buffer)}" )
+ header.strings_offset = fpos
+ header.strings_length = len(strings_buffer)
+ fpos += header.strings_length
+
+ # Optional array things
+ print( F"Keyframe count: {header.keyframe_count}" )
+ header.keyframe_offset = fpos
+ fpos += sizeof(mdl_keyframe)*header.keyframe_count
print( F"Vertex count: {header.vertex_count}" )
header.vertex_offset = fpos
header.indice_offset = fpos
fpos += sizeof(c_uint32)*header.indice_count
- print( F"Keyframe count: {animdata_length}" )
- header.animdata_offset = fpos
- fpos += animdata_length
-
- print( F"Strings length: {len(strings_buffer)}" )
- header.strings_offset = fpos
- fpos += len(strings_buffer)
-
header.file_length = fpos
path = F"/home/harry/Documents/carve/models_src/{collection_name}.mdl"
fp.write( bytearray(a) )
for ed in entdata_buffer:
fp.write( bytearray(ed) )
+
+ fp.write( strings_buffer )
+
+ for kf in animdata_buffer:
+ fp.write( bytearray(kf) )
+
for v in vertex_buffer:
fp.write( bytearray(v) )
for i in indice_buffer:
fp.write( bytearray(i) )
- for kf in animdata_buffer:
- fp.write( bytearray(kf) )
- fp.write( strings_buffer )
fp.close()
print( F"Completed {collection_name}.mdl" )
for obj in bpy.context.collection.objects:
if obj.type == 'ARMATURE':
for bone in obj.data.bones:
- if bone.cv_data.collider:
+ if bone.cv_data.collider and obj.data.pose_position == 'REST':
c = bone.head_local
a = bone.cv_data.v0
b = bone.cv_data.v1
obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5))
drawbline( obj.location, p1, sw,sw2 )
+ elif obj.cv_data.classtype == 'k_classtype_achievement_box':
+ a = Vector((-1,-1,-1))
+ b = Vector((1,1,1))
+
+ 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 += [(0,1,0,1),(0,1,0,1)]
+
+ if obj.cv_data.target != None:
+ vs = [None]*2
+ vs[0] = obj.location
+ vs[1] = obj.cv_data.target.location
+ indices = [(0,1)]
+ 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 += [(0,1,1,1),(0,1,1,1)]
+
elif obj.cv_data.classtype == 'k_classtype_block':
a = obj.data.cv_data.v0
class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
uid: bpy.props.IntProperty( name="" )
+ strp: bpy.props.StringProperty( name="strp" )
+ intp: bpy.props.IntProperty( name="intp" )
+ fltp: bpy.props.FloatProperty( name="fltp" )
+
target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \
poll=cv_poll_target )
target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \
('k_classtype_route', "k_classtype_route", "", 9 ),
('k_classtype_bone',"k_classtype_bone","",10),
('k_classtype_SKELETON', "","", 11 ),
- ('k_classtype_SKIN',"","",12)
+ ('k_classtype_SKIN',"","",12),
+ ('k_classtype_achievement_box',"k_classtype_achievement_box","",13),
+ ('k_classtype_audio',"k_classtype_audio","",14),
])
class CV_BONE_SETTINGS(bpy.types.PropertyGroup):
v0: bpy.props.FloatVectorProperty(name="v0",size=3)
v1: bpy.props.FloatVectorProperty(name="v1",size=3)
+ con0: bpy.props.BoolProperty(name="Constriant 0",default=False)
mins: bpy.props.FloatVectorProperty(name="mins",size=3)
maxs: bpy.props.FloatVectorProperty(name="maxs",size=3)
- con0: bpy.props.BoolProperty(name="Constriant 0",default=False)
- c0: bpy.props.FloatVectorProperty(name="dir",size=3)
- s0: bpy.props.FloatVectorProperty(name="limits",size=3)
-
- con1: bpy.props.BoolProperty(name="Constriant 1",default=False)
- c1: bpy.props.FloatVectorProperty(name="dir",size=3)
- s1: bpy.props.FloatVectorProperty(name="limits",size=3)
-
class CV_BONE_PANEL(bpy.types.Panel):
bl_label="Bone Config"
bl_idname="SCENE_PT_cv_bone"
_.layout.prop( bone.cv_data, "v1" )
_.layout.label( text="Angle Limits" )
+ _.layout.prop( bone.cv_data, "con0" )
_.layout.prop( bone.cv_data, "mins" )
_.layout.prop( bone.cv_data, "maxs" )
-
- _.layout.prop( bone.cv_data, "con0" )
- _.layout.prop( bone.cv_data, "c0" )
- _.layout.prop( bone.cv_data, "s0" )
-
- _.layout.prop( bone.cv_data, "con1" )
- _.layout.prop( bone.cv_data, "c1" )
- _.layout.prop( bone.cv_data, "s1" )
class CV_SCENE_SETTINGS(bpy.types.PropertyGroup):
use_hidden: bpy.props.BoolProperty( name="use hidden", default=False )
mesh = active_object.data
_.layout.label( text=F"(i) Data is stored in {mesh.name}" )
_.layout.prop( mesh.cv_data, "v0" )
+ elif active_object.cv_data.classtype == 'k_classtype_achievement_box':
+ _.layout.prop( active_object.cv_data, "strp" )
+ _.layout.prop( active_object.cv_data, "target" )
+ elif active_object.cv_data.classtype == 'k_classtype_audio':
+ _.layout.prop( active_object.cv_data, "strp" )
+ _.layout.prop( active_object.cv_data, "intp" )
+ _.layout.prop( active_object.cv_data, "fltp" )
class CV_INTERFACE(bpy.types.Panel):
bl_idname = "VIEW3D_PT_carve"