Here's the script:
tool
extends EditorScenePostImport
var mdt
var preset={}
var presetMatch
func post_import(_scene):
mdt = MeshDataTool.new()
presetMatch = RegEx.new()
presetMatch.compile("-(\\w+)=(-?[0-9.]+)")
getCollisionNode(_scene,_scene)
print ("*** done ***")
return _scene
func getCollisionNode(node,scene):
var delList = {}
for N in node.get_children():
if N.get_child_count() > 0:
#print("["+N.get_name()+"]")
getCollisionNode(N,scene)
if N.is_type("CollisionObject"):
setBodyPresets(N)
#print(" "+N.get_type())
# build collision shapes "box" for "walls" when material contains "-collide" flag in name
# * mesh has to consist of tris (check on blender collada export)
# * "wall" surface "quad" consists of 2 tris
# * "bottom" tri is required to have 2pts of equal height (y-value)
if (N.get_type()=="MeshInstance"):
var has_collide = false
var m = N.get_mesh()
for s in range(0,m.get_surface_count()):
if (m.surface_get_name(s).find("-collide")>=0):
if m.surface_get_primitive_type(s) == Mesh.PRIMITIVE_TRIANGLES:
has_collide=true
else:
print("Error: wrong surface type "+String(m.surface_get_primitive_type(s))+
" for "+m.surface_get_name(s)+". Required: PRIMITIVE_TRIANGLES")
if has_collide: #collide material found
var cscount=0
delList[N.get_name()+"_collision"]="StaticBody"
var sb=StaticBody.new()
sb.set_name(N.get_name()+"_collision")
N.add_child(sb)
sb.set_owner(scene)
setBodyPresets(sb)
#PRIMITIVE_TRIANGLES = 4
for s in range(0,m.get_surface_count()):
if (m.surface_get_name(s).find("-collide")>=0):
var newname=m.surface_get_name(s).replace("-collide","")
mdt.create_from_surface(m, s)
for f in range(mdt.get_face_count()):
var v=[]
var cwid = 0.5 #width of collision shape (away from normal)
for vc in range(3):
v.append(mdt.get_vertex(mdt.get_face_vertex(f,vc)))
var minY=min(v[0].y,min(v[1].y,v[2].y))
var maxY=max(v[0].y,max(v[1].y,v[2].y))
var vxz=[]
for vc in range(3): #make array of 2 verts at minY
if abs(v[vc].y-minY)<0.01:
vxz.append(v[vc])
if vxz.size()==2: #not "upper rectangle" (which has only 1 vert at minY)
var norm=mdt.get_face_normal(f)
#y-values are the same so this is length in xz plane
var xzVec=vxz[1]-vxz[0]
var lenxz=xzVec.length()
var midxzOffs=xzVec/2
var cshape = CollisionShape.new()
var bshape = BoxShape.new()
#node.add_shape(bshape,N.get_transform())
cscount=cscount+1
cshape.set_name(newname+"_"+String(cscount))
cshape.set_shape(bshape)
sb.add_child(cshape)
#you need to set_owner to scene root to make a node visible in editor
cshape.set_owner(scene)
bshape.set_extents(Vector3(cwid/2,(maxY-minY)/2,lenxz/2))
#center of new shape
cshape.set_translation(Vector3(vxz[0].x+midxzOffs.x-(norm.x*(cwid/2)),
minY+((maxY-minY)/2),vxz[0].z+midxzOffs.z-(norm.z*(cwid/2))))
cshape.rotate_y(Vector2(xzVec.x,xzVec.z).angle_to(Vector2(0,-1)))
mdt.clear()
# Create collision shape for a box-object inside a -colonly (StaticBody) or -rigid (RigidBody) parent
# * object name contains "-colbox"
# * client object rotation is not transferred (if any)
# * shape has extents and position of box
# * removes automatically created shapes ("shape","SphereShape","BoxShape")
if (N.get_type()=="MeshInstance") and (N.get_name().find("-colbox")>=0):
print(node.get_name()+" ->"+N.get_name())
var newname=N.get_name().replace("-colbox","")
if node.is_type("CollisionObject"): #parent can deal with collision shapes
print(" is collision")
var aab=N.get_aabb()
var cshape=null
var bshape=null
#reuse/update shape if already exists, does this ever happen?
if node.has_node(newname) and (N.get_type()=="CollisionShape"):
cshape = node.get_child(newname)
bshape = cshape.get_shape()
print(" update existing")
else:
cshape = CollisionShape.new()
bshape = BoxShape.new()
#node.add_shape(bshape,N.get_transform())
cshape.set_name(newname)
cshape.set_shape(bshape)
node.add_child(cshape)
#you need to set_owner to scene root to make a node visible in editor
node.get_node(newname).set_owner(scene)
#cshape.set_owner(node) #required? think not
bshape.set_extents(aab.size/2)
#no rotation transferred
print("Pos: "+String(aab.pos)+" End: "+String(aab.end))
#pos + size/2 seems to work good as long as pos is min of (pos,end)
#todo: move basis/rotation over
cshape.set_translation(N.get_translation()+((aab.pos+(aab.size/2))))
node.remove_child(N) #maybe not required when free/queue_free is used
N.queue_free()
#remove shapes which are auto-added to rigidbodies(-rigid)/staticbodies (-colonly)
#they're replaced by colbox shapes
delList["shape"]="CollisionShape"
delList["SphereShape"]="CollisionShape"
delList["BoxShape"]="CollisionShape"
#Objects with -preset in name allow to initialize selected presets (i.e. for RigidBody/StaticBody)
#Example name: "Cube-preset-mask=1024-layers=3-mass=1000"
if (N.get_type()=="MeshInstance") and (N.get_name().find("-preset")>=0): #preset values
#print("get presets")
presetMatch.find(N.get_name())
var matchStart=0
var prevMatchStart=0
while presetMatch.find(N.get_name(),matchStart)>=0:
matchStart=presetMatch.get_capture_start(0)+presetMatch.get_capture(0).length()
if prevMatchStart==matchStart:#check for endless loop
print("error in regexpmatch endless loop")
break
else:
prevMatchStart=matchStart
preset[presetMatch.get_capture(1)]=presetMatch.get_capture(2)
print("preset: "+presetMatch.get_capture(1)+"<=>"+presetMatch.get_capture(2))
delList[N.get_name()]=null
#delete child nodes after iteration (to not confuse object iteration)
for i in delList:
delchild(node,i,delList[i])
#init selected presets if set
func setBodyPresets(node):
if node.is_type("PhysicsBody") or node.is_type("Area"):
if preset.has("mask"):
node.set_collision_mask(int(preset["mask"]))
if preset.has("layers"):
node.set_layer_mask(int(preset["layers"]))
if node.is_type("RigidBody"):
if preset.has("mass"):
node.set_mass(float(preset["mass"]))
if preset.has("friction"):
node.set_friction(float(preset["friction"]))
if preset.has("linear_damp"):
node.set_linear_damp(float(preset["linear_damp"]))
if preset.has("angular_damp"):
node.set_angular_damp(float(preset["angular_damp"]))
func delchild(node,childid,type=null):
if node.has_node(childid):
var c=node.get_node(childid)
if (type==null) or (c.get_type()==type):
node.remove_child(c)
c.queue_free()