# =============================================================================
#
# Copyright . . . -----, ,----- ,---. .---.
-# 2021-2022 |\ /| | / | | | | /|
+# 2021-2023 |\ /| | / | | | | /|
# | \ / | +-- / +----- +---' | / |
# | \ / | | / | | \ | / |
# | \/ | | / | | \ | / |
#}
#}
+class classtype_nonlocal_gate(classtype_gate):
+#{
+ def encode_obj(_,node,node_def):
+ #{
+ node.classtype = 300
+
+ obj = node_def['obj']
+ _.target = encoder_process_pstr( node_def['obj'].cv_data.strp )
+
+ if obj.type == 'MESH':
+ #{
+ _.dims[0] = obj.data.cv_data.v0[0]
+ _.dims[1] = obj.data.cv_data.v0[1]
+ _.dims[2] = obj.data.cv_data.v0[2]
+ #}
+ else:
+ #{
+ _.dims[0] = obj.cv_data.v0[0]
+ _.dims[1] = obj.cv_data.v0[1]
+ _.dims[2] = obj.cv_data.v0[2]
+ #}
+ #}
+
+ @staticmethod
+ def editor_interface( layout, obj ):
+ #{
+ layout.prop( obj.cv_data, "strp", text="Nonlocal ID" )
+
+ mesh = obj.data
+ layout.label( text=F"(i) Data is stored in {mesh.name}" )
+ layout.prop( mesh.cv_data, "v0", text="Gate dimensions" )
+ #}
+#}
+
# Classtype 3
#
# Purpose: player can reset here, its a safe place
class classtype_bone(Structure):
#{
_pack_ = 1
- _fields_ = [("deform",c_uint32),
+ _fields_ = [("flags",c_uint32),
("ik_target",c_uint32),
("ik_pole",c_uint32),
- ("collider",c_uint32),
- ("use_limits",c_uint32),
- ("angle_limits",(c_float*3)*2),
- ("hitbox",(c_float*3)*2)]
+ ("hitbox",(c_float*3)*2),
+ ("conevx",c_float*3),
+ ("conevy",c_float*3),
+ ("coneva",c_float*3),
+ ("conet",c_float)]
def encode_obj(_, node,node_def):
#{
armature_def = node_def['linked_armature']
obj = node_def['bone']
- _.deform = node_def['deform']
+ _.flags = node_def['deform']
if 'ik_target' in node_def:
#{
+ _.flags |= 0x2
_.ik_target = armature_def['bones'].index( node_def['ik_target'] )
_.ik_pole = armature_def['bones'].index( node_def['ik_pole'] )
#}
# For ragdolls
#
- if obj.cv_data.collider:
+ if obj.cv_data.collider != 'collider_none':
#{
- _.collider = 1
+ if obj.cv_data.collider == 'collider_box':
+ _.flags |= 0x4
+ else:
+ _.flags |= 0x8
+
_.hitbox[0][0] = obj.cv_data.v0[0]
_.hitbox[0][1] = obj.cv_data.v0[2]
_.hitbox[0][2] = -obj.cv_data.v1[1]
if obj.cv_data.con0:
#{
- _.use_limits = 1
- _.angle_limits[0][0] = obj.cv_data.mins[0]
- _.angle_limits[0][1] = obj.cv_data.mins[2]
- _.angle_limits[0][2] = -obj.cv_data.maxs[1]
- _.angle_limits[1][0] = obj.cv_data.maxs[0]
- _.angle_limits[1][1] = obj.cv_data.maxs[2]
- _.angle_limits[1][2] = -obj.cv_data.mins[1]
+ _.flags |= 0x100
+ _.conevx[0] = obj.cv_data.conevx[0]
+ _.conevx[1] = obj.cv_data.conevx[2]
+ _.conevx[2] = -obj.cv_data.conevx[1]
+ _.conevy[0] = obj.cv_data.conevy[0]
+ _.conevy[1] = obj.cv_data.conevy[2]
+ _.conevy[2] = -obj.cv_data.conevy[1]
+ _.coneva[0] = obj.cv_data.coneva[0]
+ _.coneva[1] = obj.cv_data.coneva[2]
+ _.coneva[2] = -obj.cv_data.coneva[1]
+ _.conet = obj.cv_data.conet
#}
#}
#}
#}
#}
+# Classtype 200
+#
+# Purpose: world light
+#
+class classtype_world_light( Structure ):
+#{
+ _pack_ = 1
+ _fields_ = [("type",c_uint32),
+ ("colour",c_float*4),
+ ("angle",c_float),
+ ("range",c_float)]
+
+ def encode_obj(_, node, node_def):
+ #{
+ node.classtype = 200
+
+ obj = node_def['obj']
+ data = obj.data
+ _.colour[0] = data.color[0]
+ _.colour[1] = data.color[1]
+ _.colour[2] = data.color[2]
+ _.colour[3] = data.energy
+ _.range = data.cutoff_distance # this has to be manually set
+ # TODO: At some point, automate a min
+ # threshold value
+
+ if obj.data.type == 'POINT':
+ #{
+ _.type = 0
+ _.angle = 0.0
+ #}
+ elif obj.data.type == 'SPOT':
+ #{
+ _.type = 1
+ _.angle = data.spot_size*0.5
+ #}
+
+ if data.cv_data.bp0:
+ _.type += 2
+ #}
+
+ @staticmethod
+ def editor_interface( layout, obj ):
+ #{
+ pass
+ #}
+#}
+
+# Classtype 201
+#
+# Purpose: lighting settings for world
+#
+class classtype_lighting_info(Structure):
+#{
+ _pack_ = 1
+ _fields_ = [("colours",(c_float*3)*3),
+ ("directions",(c_float*2)*3),
+ ("states",c_uint32*3),
+ ("shadow_spread",c_float),
+ ("shadow_length",c_float),
+ ("ambient",c_float*3)]
+
+ def encode_obj(_, node, node_def):
+ #{
+ node.classtype = 201
+
+ # TODO
+ #}
+
+ @staticmethod
+ def editor_interface( layout, obj ):
+ #{
+ pass
+ #}
+#}
+
class classtype_spawn_link(Structure):
#{
_pack_ = 1
},
"Mix":
{
- "Color1": material_tex_image("tex_diffuse"),
- "Color2": material_tex_image("tex_decal")
+ "A": material_tex_image("tex_diffuse"),
+ "B": material_tex_image("tex_decal")
},
},
"Normal":
if isinstance( link_def, dict ):
#{
- node_link = node.inputs[link]
+ node_link = None
+ for x in node.inputs:
+ #{
+ if isinstance( x, bpy.types.NodeSocketColor ):
+ #{
+ if link == x.name:
+ #{
+ node_link = x
+ break
+ #}
+ #}
+ #}
- if node_link.is_linked:
+ if node_link and node_link.is_linked:
#{
# look for definitions for the connected node type
#
for obj in collection.all_objects:
#{
- if obj.parent: continue
+ #if obj.parent: continue
def _extend( p, n, d ):
#{
+ nonlocal collection
+
uid = _new_uid()
tree = {}
tree["uid"] = uid
tree["obj"] = n
tree["parent"] = p
n.cv_data.uid = uid
-
+
# Descend into amature
#
if n.type == 'ARMATURE':
tree["bones"] = [None] # None is the root transform
tree["ik_count"] = 0
tree["collider_count"] = 0
+ tree["compile_animation"] = collection.cv_data.animations
# Here also collects some information about constraints, ik and
# counts colliders for the armature.
#}
#}
- if n.cv_data.collider:
+ if n.cv_data.collider != 'collider_none':
tree['collider_count'] += 1
btree['deform'] = n.use_deform
#
for obj1 in n.children:
#{
- nonlocal collection
for c1 in obj1.users_collection:
#{
if c1 == collection:
# extra info
node_def['anim_start'] = len(animdata)
node_def['anim_count'] = 0
-
+
+ if not node_def['compile_animation']:
+ #{
+ return
+ #}
+
# Compile anims
#
if obj.animation_data:
#{
obj = node_def['obj']
obj_type = obj.type
- obj_co = obj.location
+ obj_co = obj.matrix_world @ Vector((0,0,0))
if obj_type == 'ARMATURE':
obj_classtype = 'classtype_skeleton'
+ elif obj_type == 'LIGHT':
+ #{
+ obj_classtype = 'classtype_world_light'
+ #}
else:
#{
obj_classtype = obj.cv_data.classtype
cv_draw_lines()
#}
+# Draw axis alligned sphere at position with radius
+#
+def cv_draw_halfsphere( pos, tx, ty, tz, radius, colour ):
+#{
+ global cv_view_verts, cv_view_colours
+
+ ly = pos + tz*radius
+ lx = pos + ty*radius
+ lz = pos + tz*radius
+
+ pi = 3.14159265358979323846264
+
+ for i in range(16):
+ #{
+ t = ((i+1.0) * 1.0/16.0) * pi
+ s = math.sin(t)
+ c = math.cos(t)
+
+ s1 = math.sin(t*2.0)
+ c1 = math.cos(t*2.0)
+
+ py = pos + s*tx*radius + c *tz*radius
+ px = pos + s*tx*radius + c *ty*radius
+ pz = pos + s1*ty*radius + c1*tz*radius
+
+ cv_view_verts += [ px, lx ]
+ cv_view_verts += [ py, ly ]
+ cv_view_verts += [ pz, lz ]
+
+ cv_view_colours += [ colour, colour, colour, colour, colour, colour ]
+
+ ly = py
+ lx = px
+ lz = pz
+ #}
+ cv_draw_lines()
+#}
+
# Draw transformed -1 -> 1 cube
#
def cv_draw_ucube( transform, colour ):
cv_draw_lines()
#}
-# Just the tx because we dont really need ty for this app
+#
#
-def cv_tangent_basis_tx( n, tx ):
+def cv_tangent_basis( n, tx, ty ):
#{
if abs( n[0] ) >= 0.57735027:
#{
#}
tx.normalize()
+ _ty = n.cross( tx )
+
+ ty[0] = _ty[0]
+ ty[1] = _ty[1]
+ ty[2] = _ty[2]
#}
# Draw coloured arrow
n.normalize()
tx = Vector((1,0,0))
- cv_tangent_basis_tx( n, tx )
+ ty = Vector((1,0,0))
+ cv_tangent_basis( n, tx, ty )
cv_view_verts += [p0,p1, midpt+(tx-n)*0.15,midpt, midpt+(-tx-n)*0.15,midpt ]
cv_view_colours += [c0,c0,c0,c0,c0,c0]
cv_draw_lines()
#}
+# Cone and twist limit
+#
+def draw_cone_twist( center, vx, vy, va ):
+#{
+ global cv_view_verts, cv_view_colours
+ axis = vy.cross( vx )
+ axis.normalize()
+
+ size = 0.12
+
+ cv_view_verts += [center, center+va*size]
+ cv_view_colours += [ (1,1,1,1), (1,1,1,1) ]
+
+ for x in range(32):
+ #{
+ t0 = (x/32) * math.tau
+ t1 = ((x+1)/32) * math.tau
+
+ c0 = math.cos(t0)
+ s0 = math.sin(t0)
+ c1 = math.cos(t1)
+ s1 = math.sin(t1)
+
+ p0 = center + (axis + vx*c0 + vy*s0).normalized() * size
+ p1 = center + (axis + vx*c1 + vy*s1).normalized() * size
+
+ col0 = ( abs(c0), abs(s0), 0.0, 1.0 )
+ col1 = ( abs(c1), abs(s1), 0.0, 1.0 )
+
+ cv_view_verts += [center, p0, p0, p1]
+ cv_view_colours += [ (0,0,0,0), col0, col0, col1 ]
+ #}
+
+ cv_draw_lines()
+#}
+
# Draws constraints and stuff for the skeleton. This isnt documented and wont be
#
def draw_skeleton_helpers( obj ):
#{
global cv_view_verts, cv_view_colours
+ if obj.data.pose_position != 'REST':
+ #{
+ return
+ #}
+
for bone in obj.data.bones:
#{
- if bone.cv_data.collider and (obj.data.pose_position == 'REST'):
+ c = bone.head_local
+ a = Vector((bone.cv_data.v0[0], bone.cv_data.v0[1], bone.cv_data.v0[2]))
+ b = Vector((bone.cv_data.v1[0], bone.cv_data.v1[1], bone.cv_data.v1[2]))
+
+ if bone.cv_data.collider == 'collider_box':
#{
- c = bone.head_local
- a = bone.cv_data.v0
- b = bone.cv_data.v1
vs = [None]*8
vs[0]=obj.matrix_world@Vector((c[0]+a[0],c[1]+a[1],c[2]+a[2]))
cv_view_verts += [(v1[0],v1[1],v1[2])]
cv_view_colours += [(0.5,0.5,0.5,0.5),(0.5,0.5,0.5,0.5)]
#}
+ #}
+ elif bone.cv_data.collider == 'collider_capsule':
+ #{
+ v0 = b-a
+ major_axis = 0
+ largest = -1.0
- center = obj.matrix_world @ c
- if bone.cv_data.con0:
+ for i in range(3):
#{
- draw_limit( obj, c, Vector((0,1,0)),Vector((0,0,1)), \
- bone.cv_data.mins[0], bone.cv_data.maxs[0], \
- (1,0,0,1))
- draw_limit( obj, c, Vector((0,0,1)),Vector((1,0,0)), \
- bone.cv_data.mins[1], bone.cv_data.maxs[1], \
- (0,1,0,1))
- draw_limit( obj, c, Vector((1,0,0)),Vector((0,1,0)), \
- bone.cv_data.mins[2], bone.cv_data.maxs[2], \
- (0,0,1,1))
+ if abs(v0[i]) > largest:
+ #{
+ largest = abs(v0[i])
+ major_axis = i
+ #}
#}
+
+ v1 = Vector((0,0,0))
+ v1[major_axis] = 1.0
+
+ tx = Vector((0,0,0))
+ ty = Vector((0,0,0))
+
+ cv_tangent_basis( v1, tx, ty )
+ r = (abs(tx.dot( v0 )) + abs(ty.dot( v0 ))) * 0.25
+ l = v0[ major_axis ] - r*2
+
+ p0 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l*-0.5 )
+ p1 = obj.matrix_world@Vector( c + (a+b)*0.5 + v1*l* 0.5 )
+
+ colour = [0.2,0.2,0.2,1.0]
+ colour[major_axis] = 0.5
+
+ cv_draw_halfsphere( p0, -v1, ty, tx, r, colour )
+ cv_draw_halfsphere( p1, v1, ty, tx, r, colour )
+ cv_draw_line( p0+tx* r, p1+tx* r, colour )
+ cv_draw_line( p0+tx*-r, p1+tx*-r, colour )
+ cv_draw_line( p0+ty* r, p1+ty* r, colour )
+ cv_draw_line( p0+ty*-r, p1+ty*-r, colour )
+ #}
+ else:
+ #{
+ continue
+ #}
+
+ center = obj.matrix_world @ c
+ if bone.cv_data.con0:
+ #{
+ vx = Vector([bone.cv_data.conevx[_] for _ in range(3)])
+ vy = Vector([bone.cv_data.conevy[_] for _ in range(3)])
+ va = Vector([bone.cv_data.coneva[_] for _ in range(3)])
+ draw_cone_twist( center, vx, vy, va )
+
+ #draw_limit( obj, c, Vector((0,0,1)),Vector((0,-1,0)), \
+ # bone.cv_data.mins[0], bone.cv_data.maxs[0], \
+ # (1,0,0,1))
+ #draw_limit( obj, c, Vector((0,-1,0)),Vector((1,0,0)), \
+ # bone.cv_data.mins[1], bone.cv_data.maxs[1], \
+ # (0,1,0,1))
+ #draw_limit( obj, c, Vector((1,0,0)),Vector((0,0,1)), \
+ # bone.cv_data.mins[2], bone.cv_data.maxs[2], \
+ # (0,0,1,1))
#}
#}
#}
v3: bpy.props.FloatVectorProperty(name="v3",size=3)
#}
+class CV_LIGHT_SETTINGS(bpy.types.PropertyGroup):
+#{
+ bp0: bpy.props.BoolProperty( name="bp0" );
+#}
+
+class CV_LIGHT_PANEL(bpy.types.Panel):
+#{
+ bl_label="[Skate Rift]"
+ bl_idname="SCENE_PT_cv_light"
+ bl_space_type='PROPERTIES'
+ bl_region_type='WINDOW'
+ bl_context='data'
+
+ def draw(_,context):
+ #{
+ active_object = context.active_object
+ if active_object == None: return
+
+ if active_object.type != 'LIGHT': return
+
+ data = active_object.data.cv_data
+ _.layout.prop( data, "bp0", text="Only on during night" )
+ #}
+#}
+
class CV_OBJ_SETTINGS(bpy.types.PropertyGroup):
#{
uid: bpy.props.IntProperty( name="" )
('classtype_logic_achievement',"classtype_logic_achievement","",101),
('classtype_logic_relay',"classtype_logic_relay","",102),
('classtype_spawn_link',"classtype_spawn_link","",150),
+ ('classtype_nonlocal_gate', "classtype_nonlocal_gate", "", 300)
])
#}
class CV_BONE_SETTINGS(bpy.types.PropertyGroup):
#{
- collider: bpy.props.BoolProperty(name="Collider",default=False)
+ collider: bpy.props.EnumProperty(
+ name="Collider Type",
+ items = [
+ ('collider_none', "collider_none", "", 0),
+ ('collider_box', "collider_box", "", 1),
+ ('collider_capsule', "collider_capsule", "", 2),
+ ])
+
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)
+
+ conevx: bpy.props.FloatVectorProperty(name="conevx",size=3)
+ conevy: bpy.props.FloatVectorProperty(name="conevy",size=3)
+ coneva: bpy.props.FloatVectorProperty(name="coneva",size=3)
+ conet: bpy.props.FloatProperty(name="conet")
#}
class CV_BONE_PANEL(bpy.types.Panel):
#{
- bl_label="Bone Config"
+ bl_label="[Skate Rift]"
bl_idname="SCENE_PT_cv_bone"
bl_space_type='PROPERTIES'
bl_region_type='WINDOW'
_.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, "conevx" )
+ _.layout.prop( bone.cv_data, "conevy" )
+ _.layout.prop( bone.cv_data, "coneva" )
+ _.layout.prop( bone.cv_data, "conet" )
#}
#}
class CV_COLLECTION_SETTINGS(bpy.types.PropertyGroup):
#{
pack_textures: bpy.props.BoolProperty( name="Pack Textures", default=False )
+ animations: bpy.props.BoolProperty( name="Export animation", default=True)
#}
class CV_MATERIAL_SETTINGS(bpy.types.PropertyGroup):
info = material_info( active_mat )
+ if 'tex_diffuse' in info:
+ #{
+ _.layout.label( icon='INFO', \
+ text=F"{info['tex_diffuse'].name} will be compiled" )
+ #}
+
_.layout.prop( active_mat.cv_data, "shader" )
_.layout.prop( active_mat.cv_data, "surface_prop" )
_.layout.prop( active_mat.cv_data, "collision" )
#{
box.label( text=col.name + ".mdl" )
box.prop( col.cv_data, "pack_textures" )
+ box.prop( col.cv_data, "animations" )
box.operator( "carve.compile_this" )
#}
else:
classes = [CV_OBJ_SETTINGS,CV_OBJ_PANEL,CV_COMPILE,CV_INTERFACE,\
CV_MESH_SETTINGS, CV_SCENE_SETTINGS, CV_BONE_SETTINGS,\
CV_BONE_PANEL, CV_COLLECTION_SETTINGS, CV_COMPILE_THIS,\
- CV_MATERIAL_SETTINGS, CV_MATERIAL_PANEL ]
+ CV_MATERIAL_SETTINGS, CV_MATERIAL_PANEL, CV_LIGHT_SETTINGS,\
+ CV_LIGHT_PANEL]
def register():
#{
bpy.props.PointerProperty(type=CV_COLLECTION_SETTINGS)
bpy.types.Material.cv_data = \
bpy.props.PointerProperty(type=CV_MATERIAL_SETTINGS)
+ bpy.types.Light.cv_data = bpy.props.PointerProperty(type=CV_LIGHT_SETTINGS)
cv_view_draw_handler = bpy.types.SpaceView3D.draw_handler_add(\
cv_draw,(),'WINDOW','POST_VIEW')