X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=blender_export.py;h=6fadae28decb6428fee5dc197795724c75884b17;hb=4c49b11c317034277be4d117c5d2847ef2f3492b;hp=16d1f381e201f2c9625c335690a4404134d38150;hpb=9c85e110fa8b965195438d96625ff9753af362a6;p=carveJwlIkooP6JGAAIwe30JlM.git diff --git a/blender_export.py b/blender_export.py index 16d1f38..6fadae2 100644 --- a/blender_export.py +++ b/blender_export.py @@ -1,4 +1,5 @@ import bpy, math, gpu +import cProfile from ctypes import * from mathutils import * from gpu_extras.batch import batch_for_shader @@ -75,7 +76,8 @@ class mdl_header(Structure): class classtype_gate(Structure): _pack_ = 1 - _fields_ = [("target",c_uint32)] + _fields_ = [("target",c_uint32), + ("dims",c_float*3)] class classtype_block(Structure): _pack_ = 1 @@ -89,14 +91,37 @@ class classtype_water(Structure): _pack_ = 1 _fields_ = [("temp",c_uint32)] +class classtype_car_path(Structure): + _pack_ = 1 + _fields_ = [("target",c_uint32), + ("target1",c_uint32)] + +class classtype_instance(Structure): + _pack_ = 1 + _fields_ = [("pstr_file",c_uint32)] + +class classtype_capsule(Structure): + _pack_ = 1 + _fields_ = [("height",c_float), + ("radius",c_float)] + +class classtype_route_node(Structure): + _pack_ = 1 + _fields_ = [("target",c_uint32), + ("target1",c_uint32)] + +class classtype_route(Structure): + _pack_ = 1 + _fields_ = [("pstr_name",c_uint32), + ("id_start",c_uint32), + ("colour",c_float*3)] + # Exporter # ============================================================================== def write_model(name): print( F"Create mode {name}" ) - collection = bpy.data.collections[name] - header = mdl_header() header.identifier = 0xABCD0000 header.version = 0 @@ -172,6 +197,8 @@ def write_model(name): # Do exporting # print( " assigning ids" ) + collection = bpy.data.collections[name] + header.node_count = 1 for obj in collection.all_objects: obj.cv_data.uid = header.node_count @@ -203,11 +230,7 @@ def write_model(name): 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': + if classtype == 'k_classtype_gate': node.classtype = 1 entdata_length += sizeof( classtype_gate ) @@ -216,6 +239,15 @@ def write_model(name): if obj.cv_data.target != None: gate.target = obj.cv_data.target.cv_data.uid + if obj.type == 'MESH': + gate.dims[0] = obj.data.cv_data.v0[0] + gate.dims[1] = obj.data.cv_data.v0[1] + gate.dims[2] = obj.data.cv_data.v0[2] + else: + gate.dims[0] = obj.cv_data.v0[0] + gate.dims[1] = obj.cv_data.v0[1] + gate.dims[2] = obj.cv_data.v0[2] + entdata_buffer += [gate] elif classtype == 'k_classtype_block': @@ -227,10 +259,11 @@ def write_model(name): 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[0][2] = -source.v1[1] + block.bbx[1][0] = source.v1[0] block.bbx[1][1] = source.v1[2] - block.bbx[1][2] = -source.v1[1] + block.bbx[1][2] = -source.v0[1] entdata_buffer += [block] elif classtype == 'k_classtype_spawn': @@ -238,6 +271,60 @@ def write_model(name): elif classtype == 'k_classtype_water': node.classtype = 4 + elif classtype == 'k_classtype_car_path': + node.classtype = 5 + entdata_length += sizeof( classtype_car_path ) + + pn = classtype_car_path() + pn.target = 0 + pn.target1 = 0 + + if obj.cv_data.target != None: + pn.target = obj.cv_data.target.cv_data.uid + if obj.cv_data.target1 != None: + pn.target1 = obj.cv_data.target1.cv_data.uid + + entdata_buffer += [pn] + elif obj.is_instancer: + target = obj.instance_collection + + node.classtype = 6 + entdata_length += sizeof( classtype_instance ) + + inst = classtype_instance() + inst.pstr_file = emplace_string( F"models/{target.name}.mdl" ) + entdata_buffer += [inst] + elif classtype == 'k_classtype_capsule': + node.classtype = 7 + elif classtype == 'k_classtype_route_node': + node.classtype = 8 + entdata_length += sizeof( classtype_route_node ) + + rn = classtype_route_node() + if obj.cv_data.target != None: + rn.target = obj.cv_data.target.cv_data.uid + if obj.cv_data.target1 != None: + rn.target1 = obj.cv_data.target1.cv_data.uid + + entdata_buffer += [rn] + elif classtype == 'k_classtype_route': + node.classtype = 9 + 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] + + if obj.cv_data.target != None: + r.id_start = obj.cv_data.target.cv_data.uid + + entdata_buffer += [r] + + # classtype == 'k_classtype_none': + else: + node.classtype = 0 + node.offset = 0 # Process meshes # @@ -247,8 +334,15 @@ def write_model(name): if obj.type == 'MESH': default_mat = c_uint32(69) default_mat.name = "" - - if obj.data.name in mesh_cache: + + # Dont use the cache if we have modifiers that affect the normals + # + use_cache = True + for mod in obj.modifiers: + if mod.type == 'DATA_TRANSFER': + use_cache = False + + if use_cache and obj.data.name in mesh_cache: ref = mesh_cache[obj.data.name] node.submesh_start = ref.submesh_start node.submesh_count = ref.submesh_count @@ -296,11 +390,21 @@ def write_model(name): 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)) + TOLERENCE = 4 + m = float(10**TOLERENCE) + + key = (int(co[0]*m+0.5),\ + int(co[1]*m+0.5),\ + int(co[2]*m+0.5),\ + int(norm[0]*m+0.5),\ + int(norm[1]*m+0.5),\ + int(norm[2]*m+0.5),\ + int(uv[0]*m+0.5),\ + int(uv[1]*m+0.5),\ + int(colour[0]*m+0.5),\ + int(colour[1]*m+0.5),\ + int(colour[2]*m+0.5),\ + int(colour[3]*m+0.5)) if key in boffa: indice_buffer += [boffa[key]] @@ -406,20 +510,130 @@ def cv_draw(): 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') + gpu.state.depth_test_set('LESS') + gpu.state.blend_set('NONE') verts = [] colours = [] + #def drawbezier(p0,h0,p1,h1,c0,c1): + # nonlocal verts, colours + + # verts += [p0] + # verts += [h0] + # colours += [(0.5,0.5,0.5,1.0),(0.5,0.5,0.5,1)] + # verts += [p1] + # verts += [h1] + # colours += [(1.0,1.0,1,1),(1,1,1,1)] + # + # last = p0 + # for i in range(10): + # t = (i+1)/10 + # a0 = 1-t + + # tt = t*t + # ttt = tt*t + # p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0 + # verts += [(last[0],last[1],last[2])] + # verts += [(p[0],p[1],p[2])] + # colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)] + # last = p + + course_count = 0 + + def drawbhandle(obj, direction, colour): + nonlocal verts, colours + p0 = obj.location + h0 = obj.matrix_world @ Vector((0,direction,0)) + verts += [p0] + verts += [h0] + colours += [colour,colour] + + def drawbezier(p0,h0,p1,h1,c0,c1): + nonlocal verts, colours + + last = p0 + for i in range(10): + t = (i+1)/10 + a0 = 1-t + + tt = t*t + ttt = tt*t + p=ttt*p1+(3*tt-3*ttt)*h1+(3*ttt-6*tt+3*t)*h0+(3*tt-ttt-3*t+1)*p0 + verts += [(last[0],last[1],last[2])] + verts += [(p[0],p[1],p[2])] + colours += [c0*a0+c1*(1-a0),c0*a0+c1*(1-a0)] + last = p + + def drawsbpath(o0,o1,c0,c1,s0,s1): + nonlocal course_count + + offs = ((course_count % 2)*2-1) * course_count * 0.02 + + p0 = o0.matrix_world @ Vector((offs, 0,0)) + h0 = o0.matrix_world @ Vector((offs, s0,0)) + p1 = o1.matrix_world @ Vector((offs, 0,0)) + h1 = o1.matrix_world @ Vector((offs,-s1,0)) + drawbezier(p0,h0,p1,h1,c0,c1) + + def drawbpath(o0,o1,c0,c1): + drawsbpath(o0,o1,c0,c1,1.0,1.0) + + def drawbline(p0,p1,c0,c1): + nonlocal verts, colours + verts += [p0,p1] + colours += [c0,c1] + for obj in bpy.context.collection.objects: + if obj.cv_data.classtype == 'k_classtype_gate': + if obj.type == 'MESH': + dims = obj.data.cv_data.v0 + else: + dims = obj.cv_data.v0 + + vs = [None]*9 + c = Vector((0,0,dims[2])) + + vs[0] = obj.matrix_world @ Vector((-dims[0],0.0,-dims[1]+dims[2])) + vs[1] = obj.matrix_world @ Vector((-dims[0],0.0, dims[1]+dims[2])) + vs[2] = obj.matrix_world @ Vector(( dims[0],0.0, dims[1]+dims[2])) + vs[3] = obj.matrix_world @ Vector(( dims[0],0.0,-dims[1]+dims[2])) + vs[4] = obj.matrix_world @ (c+Vector((-1,0,-2))) + vs[5] = obj.matrix_world @ (c+Vector((-1,0, 2))) + vs[6] = obj.matrix_world @ (c+Vector(( 1,0, 2))) + vs[7] = obj.matrix_world @ (c+Vector((-1,0, 0))) + vs[8] = obj.matrix_world @ (c+Vector(( 1,0, 0))) + + indices = [(0,1),(1,2),(2,3),(3,0),(4,5),(5,6),(7,8)] + + 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)] + + sw = (0.4,0.4,0.4,0.2) + if obj.cv_data.target != None: + drawbline( obj.location, obj.cv_data.target.location, sw,sw ) + + elif obj.cv_data.classtype == 'k_classtype_route_node': + sw = Vector((0.4,0.4,0.4,0.2)) + sw2 = Vector((1.5,0.2,0.2,0.0)) 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)] + drawbpath( obj, obj.cv_data.target, sw, sw ) + if obj.cv_data.target1 != None: + drawbpath( obj, obj.cv_data.target1, sw, sw ) + + drawbhandle( obj, 1.0, (0.8,0.8,0.8,1.0) ) + drawbhandle( obj, -1.0, (0.4,0.4,0.4,1.0) ) + + p1 = obj.location+ \ + obj.matrix_world.to_quaternion() @ Vector((0,0,-6+1.5)) + drawbline( obj.location, p1, sw,sw2 ) + + elif obj.cv_data.classtype == 'k_classtype_block': a = obj.data.cv_data.v0 b = obj.data.cv_data.v1 @@ -444,6 +658,141 @@ def cv_draw(): verts += [(v1[0],v1[1],v1[2])] colours += [(1,1,0,1),(1,1,0,1)] + elif obj.cv_data.classtype == 'k_classtype_capsule': + h = obj.data.cv_data.v0[0] + r = obj.data.cv_data.v0[1] + + vs = [None]*10 + vs[0] = obj.matrix_world @ Vector((0.0,0.0, h*0.5 )) + vs[1] = obj.matrix_world @ Vector((0.0,0.0,-h*0.5 )) + vs[2] = obj.matrix_world @ Vector(( r,0.0, h*0.5-r)) + vs[3] = obj.matrix_world @ Vector(( -r,0.0, h*0.5-r)) + vs[4] = obj.matrix_world @ Vector(( r,0.0,-h*0.5+r)) + vs[5] = obj.matrix_world @ Vector(( -r,0.0,-h*0.5+r)) + vs[6] = obj.matrix_world @ Vector((0.0, r , h*0.5-r)) + vs[7] = obj.matrix_world @ Vector((0.0,-r , h*0.5-r)) + vs[8] = obj.matrix_world @ Vector((0.0, r ,-h*0.5+r)) + vs[9] = obj.matrix_world @ Vector((0.0,-r ,-h*0.5+r)) + + indices = [(0,1),(2,3),(4,5),(6,7),(8,9)] + + 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.5,1,0,1),(0.5,1,0,1)] + + elif obj.cv_data.classtype == 'k_classtype_spawn': + vs = [None]*4 + vs[0] = obj.matrix_world @ Vector((0,0,0)) + vs[1] = obj.matrix_world @ Vector((0,2,0)) + vs[2] = obj.matrix_world @ Vector((0.5,1,0)) + vs[3] = obj.matrix_world @ Vector((-0.5,1,0)) + indices = [(0,1),(1,2),(1,3)] + 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_route': + 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)] + + stack = [None]*64 + stack_i = [0]*64 + stack[0] = obj.cv_data.target + si = 1 + loop_complete = False + + while si > 0: + if stack_i[si-1] == 2: + si -= 1 + continue + + if si == 0: # Loop failed to complete + break + + node = stack[si-1] + + targets = [None,None] + targets[0] = node.cv_data.target + + if node.cv_data.classtype == 'k_classtype_route_node': + targets[1] = node.cv_data.target1 + + nextnode = targets[stack_i[si-1]] + stack_i[si-1] += 1 + + if nextnode != None: # branch + if nextnode == stack[0]: # Loop completed + loop_complete = True + break + + valid=True + for sj in range(si): + if stack[sj] == nextnode: # invalidated path + valid=False + break + + if valid: + stack_i[si] = 0 + stack[si] = nextnode + si += 1 + continue + + if loop_complete: + course_colours = [Vector((0,0.8,0.2,1.0)), \ + Vector((0,0.3,0.9,1.0)), \ + Vector((0.4,0.0,0.8,1.0)),\ + Vector((0.5,0.8,0.0,1.0)),\ + Vector((0.0,0.7,0.6,1.0)),\ + Vector((0.2,0.9,0.5,1.0)) ] + + cc = course_colours[ course_count % len(course_colours) ] + + for sj in range(si): + sk = (sj+1)%si + + if stack[sj].cv_data.classtype == 'k_classtype_gate' and \ + stack[sk].cv_data.classtype == 'k_classtype_gate': + dist = (stack[sj].location-stack[sk].location).magnitude + drawsbpath( stack[sj], stack[sk], cc*0.4, cc, dist, dist ) + + else: + drawbpath( stack[sj], stack[sk], cc, cc ) + + course_count += 1 + + elif obj.cv_data.classtype == 'k_classtype_car_path': + v0 = obj.matrix_world.to_quaternion() @ Vector((0,1,0)) + c0 = Vector((v0.x*0.5+0.5, v0.y*0.5+0.5, 0.0, 1.0)) + drawbhandle( obj, 1.0, (0.9,0.9,0.9,1.0) ) + + if obj.cv_data.target != None: + v1 = obj.cv_data.target.matrix_world.to_quaternion()@Vector((0,1,0)) + c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0)) + + drawbhandle( obj.cv_data.target, -1.0, (0.5,0.5,0.5,1.0) ) + drawbpath( obj, obj.cv_data.target, c0, c1 ) + + if obj.cv_data.target1 != None: + v1 = obj.cv_data.target1.matrix_world.to_quaternion()@Vector((0,1,0)) + c1 = Vector((v1.x*0.5+0.5, v1.y*0.5+0.5, 0.0, 1.0)) + + drawbhandle( obj.cv_data.target1, -1.0, (0.5,0.5,0.5,1.0) ) + drawbpath( obj, obj.cv_data.target1, c0, c1 ) + lines = batch_for_shader(\ cv_view_shader, 'LINES', \ { "pos":verts, "color":colours }) @@ -468,6 +817,11 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup): target: bpy.props.PointerProperty( type=bpy.types.Object, name="target", \ poll=cv_poll_target ) + target1: bpy.props.PointerProperty( type=bpy.types.Object, name="target1", \ + poll=cv_poll_target ) + + colour: bpy.props.FloatVectorProperty(name="colour",subtype='COLOR',\ + min=0.0,max=1.0) classtype: bpy.props.EnumProperty( name="Format", @@ -476,7 +830,11 @@ class CV_OBJ_SETTINGS(bpy.types.PropertyGroup): ('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) + ('k_classtype_water', "k_classtype_water", "", 4), + ('k_classtype_car_path', "k_classtype_car_path", "", 5), + ('k_classtype_capsule', "k_classtype_capsule", "", 7 ), + ('k_classtype_route_node', "k_classtype_route_node", "", 8 ), + ('k_classtype_route', "k_classtype_route", "", 9 ) ]) class CV_OBJ_PANEL(bpy.types.Panel): @@ -493,6 +851,20 @@ class CV_OBJ_PANEL(bpy.types.Panel): if active_object.cv_data.classtype == 'k_classtype_gate': _.layout.prop( active_object.cv_data, "target" ) + + 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_car_path' or \ + active_object.cv_data.classtype == 'k_classtype_route_node': + _.layout.prop( active_object.cv_data, "target" ) + _.layout.prop( active_object.cv_data, "target1" ) + + elif active_object.cv_data.classtype == 'k_classtype_route': + _.layout.prop( active_object.cv_data, "target" ) + _.layout.prop( active_object.cv_data, "colour" ) + elif active_object.cv_data.classtype == 'k_classtype_block': mesh = active_object.data @@ -501,6 +873,10 @@ class CV_OBJ_PANEL(bpy.types.Panel): _.layout.prop( mesh.cv_data, "v1" ) _.layout.prop( mesh.cv_data, "v2" ) _.layout.prop( mesh.cv_data, "v3" ) + elif active_object.cv_data.classtype == 'k_classtype_capsule': + mesh = active_object.data + _.layout.label( text=F"(i) Data is stored in {mesh.name}" ) + _.layout.prop( mesh.cv_data, "v0" ) class CV_INTERFACE(bpy.types.Panel): bl_idname = "VIEW3D_PT_carve" @@ -513,13 +889,19 @@ class CV_INTERFACE(bpy.types.Panel): layout = _.layout layout.operator( "carve.compile_all" ) +def test_compile(): + for col in bpy.data.collections["export"].children: + write_model( col.name ) + 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 ) + test_compile() + #cProfile.runctx("test_compile()",globals(),locals(),sort=1) + #for col in bpy.data.collections["export"].children: + # write_model( col.name ) return {'FINISHED'}