def pointInTri2D(v, v1, v2, v3): global dict_matrix key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face. ''' # BOUNDS CHECK xmin= 1000000 ymin= 1000000 xmax= -1000000 ymax= -1000000 for i in (0,2,4): x= key[i] y= key[i+1] if xmax<x: xmax= x if ymax<y: ymax= y if xmin>x: xmin= x if ymin>y: ymin= y x= v.x y= v.y if x<xmin or x>xmax or y < ymin or y > ymax: return False # Done with bounds check ''' try: mtx = dict_matrix[key] if not mtx: return False except: side1 = v2 - v1 side2 = v3 - v1 nor = side1.cross(side2) l1 = [side1[0], side1[1], side1[2]] l2 = [side2[0], side2[1], side2[2]] l3 = [nor[0], nor[1], nor[2]] mtx = Matrix(l1, l2, l3) # Zero area 2d tri, even tho we throw away zerop area faces # the projection UV can result in a zero area UV. if not mtx.determinant(): dict_matrix[key] = None return False mtx.invert() dict_matrix[key] = mtx uvw = (v - v1) * mtx return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
def pointInTri2D(v, v1, v2, v3): global dict_matrix key = v1.x, v1.y, v2.x, v2.y, v3.x, v3.y # Commented because its slower to do teh bounds check, we should realy cache the bounds info for each face. """ # BOUNDS CHECK xmin= 1000000 ymin= 1000000 xmax= -1000000 ymax= -1000000 for i in (0,2,4): x= key[i] y= key[i+1] if xmax<x: xmax= x if ymax<y: ymax= y if xmin>x: xmin= x if ymin>y: ymin= y x= v.x y= v.y if x<xmin or x>xmax or y < ymin or y > ymax: return False # Done with bounds check """ try: mtx = dict_matrix[key] if not mtx: return False except: side1 = v2 - v1 side2 = v3 - v1 nor = side1.cross(side2) l1 = [side1[0], side1[1], side1[2]] l2 = [side2[0], side2[1], side2[2]] l3 = [nor[0], nor[1], nor[2]] mtx = Matrix(l1, l2, l3) # Zero area 2d tri, even tho we throw away zerop area faces # the projection UV can result in a zero area UV. if not mtx.determinant(): dict_matrix[key] = None return False mtx.invert() dict_matrix[key] = mtx uvw = (v - v1) * mtx return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
def parse_rot(self, trash): i = self.i - 1 ob = self.objlist[-1] rot = self.lines[i].split(' ', 1)[1] rot = map(float, rot.split()) matrix = Matrix(rot[:3], rot[3:6], rot[6:]) ob.rot = matrix size = matrix.scalePart() # vector ob.size = size
def MatrixrotationOnly(mm, object): try: sx = 1 / abs(object.SizeX) sy = 1 / abs(object.SizeY) sz = 1 / abs(object.SizeZ) return Matrix([mm[0][0] * sx, mm[0][1] * sx, mm[0][2] * sx, 0], [mm[1][0] * sy, mm[1][1] * sy, mm[1][2] * sy, 0], [mm[2][0] * sz, mm[2][1] * sz, mm[2][2] * sz, 0], [0, 0, 0, 1]) except: # Normals are screwed by zero scale - just return anything return Matrix().identity().resize4x4()
def VectoMat(vec): a3 = vec.__copy__().normalize() up = Vector(0, 0, 1) if abs(a3.dot(up)) == 1.0: up = Vector(0, 1, 0) a1 = a3.cross(up).normalize() a2 = a3.cross(a1) return Matrix([a1[0], a1[1], a1[2]], [a2[0], a2[1], a2[2]], [a3[0], a3[1], a3[2]])
def addattach(scene, name, matrix): if name.startswith('attachpt_'): name=name[9:] if not name: name='AttachPoint' obj=Object.New('Empty', name) scene.objects.link(obj) obj.layers=[1] obj.addProperty('name', obj.name) # Need to convert between right and left handed coordinate systems. obj.setMatrix(RotationMatrix(-90,4,'x')*matrix*Matrix([1,0,0,0],[0,0,1,0],[0,-1,0,0],[0,0,0,1])) obj.LocY=-obj.LocY return obj
def saveBR2(filename): if not filename.lower().endswith('.br2'): filename += '.br2' if not BPyMessages.Warning_SaveOver(filename): return print "Start BR2 Export..." Blender.Window.WaitCursor(1) file = open( filename, 'wb') scene = Blender.Scene.GetCurrent() allObjects = scene.objects filedata = "// Nexus Buddy BR2 - Exported from Blender for import to Nexus Buddy 2\n" modelObs = {} modelMeshes = {} # will need list of these for multi-skeleton boneIds = {} for object in allObjects: if object.type == 'Armature': modelObs[object.name] = object if object.type == 'Mesh': print "Getting parent for mesh: %s" % object.name parentArmOb = BPyObject.getObjectArmature(object) if not parentArmOb.name in modelMeshes: modelMeshes[parentArmOb.name] = [] modelMeshes[parentArmOb.name].append(object) for modelObName in modelObs.keys(): # Write Skeleton filedata += "skeleton\n" armOb = modelObs[modelObName] armature = armOb.data # Calc bone depths and sort boneDepths = [] for bone in armature.bones.values(): boneDepth = getBoneTreeDepth(bone, 0) boneDepths.append((bone, boneDepth)) boneDepths = sorted(boneDepths, key=lambda k: k[0].name) boneDepths = sorted(boneDepths, key=lambda k: k[1]) sortedBones = boneDepths for boneid, boneTuple in enumerate(sortedBones): boneIds[boneTuple[0].name] = boneid boneIds[armOb.name] = -1 # Add entry for World Bone # Write World Bone filedata += '%d "%s" %d ' % (0, armOb.name, -1) filedata += '%.8f %.8f %.8f ' % (0.0, 0.0, 0.0) filedata += '%.8f %.8f %.8f %.8f ' % (0.0, 0.0, 0.0, 1.0) filedata += '%.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f\n' % (1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0) for boneid, boneTuple in enumerate(sortedBones): bone = boneTuple[0] boneDepth = boneTuple[1] #boneid = boneid + 1 # World bone is zero position, orientationQuat = getTranslationOrientation(bone) # Get Inverse World Matrix for bone t = bone.matrix['ARMATURESPACE'].copy().invert() invWorldMatrix = Matrix([t[0][1], -t[0][0], t[0][2], t[0][3]], [t[1][1], -t[1][0], t[1][2], t[1][3]], [t[2][1], -t[2][0], t[2][2], t[2][3]], [t[3][1], -t[3][0], t[3][2], t[3][3]]) outputBoneName = bone.name if len(outputBoneName) == 29: for item in armOb.getAllProperties(): if (("B_" + outputBoneName) == item.getName()): outputBoneName = item.getData() print 'Decode Bone Name: "%s" >' % item.getName() print ' "%s"' % item.getData() break filedata += '%d "%s" ' % (boneid + 1, outputBoneName) # Adjust bone ids + 1 as zero is the World Bone parentBoneId = 0 if bone.hasParent(): parentBoneId = boneIds[bone.parent.name] + 1 # Adjust bone ids + 1 as zero is the World Bone filedata += '%d ' % parentBoneId filedata +='%.8f %.8f %.8f ' % (position[0], position[1] , position[2]) filedata +='%.8f %.8f %.8f %.8f ' % (orientationQuat[1], orientationQuat[2], orientationQuat[3], orientationQuat[0]) # GR2 uses x,y,z,w for Quaternions rather than w,x,y,z filedata += '%.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f %.8f' % ( invWorldMatrix[0][0], invWorldMatrix[0][1], invWorldMatrix[0][2], invWorldMatrix[0][3], invWorldMatrix[1][0], invWorldMatrix[1][1], invWorldMatrix[1][2], invWorldMatrix[1][3], invWorldMatrix[2][0], invWorldMatrix[2][1], invWorldMatrix[2][2], invWorldMatrix[2][3], invWorldMatrix[3][0], invWorldMatrix[3][1], invWorldMatrix[3][2], invWorldMatrix[3][3]) #End of bone line filedata += "\n" filedata += 'meshes:%d\n' % len(modelMeshes[modelObName]) for meshObject in modelMeshes[modelObName]: mesh = meshObject.data meshName = meshObject.name filedata += 'mesh:"%s"\n' % meshName parentArmOb = BPyObject.getObjectArmature(meshObject) # Fetch long mesh names from Armature properties if len(meshName) == 19: for item in parentArmOb.getAllProperties(): if ("M_" + meshName == item.getName()): meshName = item.getData() print 'Decode Mesh Name: %s > %s' % (item.getName(), item.getData()) break #file.write('meshname:%s\n' % meshName) #file.write('parent Arm:%s\n' % parentArmOb) weights = meshNormalizedWeights(mesh) vertexBoneWeights = {} for boneName in boneIds.keys(): vgroupDataForBone = getBoneWeights(boneName, weights) #file.write('bone:%s vg:%s\n' % (boneName, vgroupDataForBone)) for vgData in vgroupDataForBone: vertexId = vgData[0] weight = vgData[1] if not vertexId in vertexBoneWeights: vertexBoneWeights[vertexId] = [] vertexBoneWeights[vertexId].append((boneName, weight)) #file.write('vert:%d bone:%s \n' % (vertexId, (boneName, weight))) grannyVertexBoneWeights = {} for vertId in vertexBoneWeights.keys(): #file.write('vert:%d ' % vertId) rawBoneIdWeightTuples = [] firstBoneId = 0 for i in range(max(4,len(vertexBoneWeights[vertId]))): if i < len(vertexBoneWeights[vertId]): vertexBoneWeightTuple = vertexBoneWeights[vertId][i] boneName = vertexBoneWeightTuple[0] rawBoneIdWeightTuples.append((boneIds[boneName] + 1, vertexBoneWeightTuple[1])) if i == 0: firstBoneId = boneIds[boneName] + 1 else: rawBoneIdWeightTuples.append((firstBoneId, 0)) # Sort bone mappings by weight highest to lowest sortedBoneIdWeightTuples = sorted(rawBoneIdWeightTuples, key=lambda rawBoneIdWeightTuple: rawBoneIdWeightTuple[1], reverse=True) #if len(vertexBoneWeights[vertId]) > 4: # print "len(vertexBoneWeights[vertId]): %s" % len(vertexBoneWeights[vertId]) # print "sortedBoneIdWeightTuples: %s" % sortedBoneIdWeightTuples # Pick first four highest weighted bones boneIdsList = [] rawBoneWeightsList = [] for i in range(4): boneIdsList.append(sortedBoneIdWeightTuples[i][0]) rawBoneWeightsList.append(sortedBoneIdWeightTuples[i][1]) rawWeightTotal = 0 for weight in rawBoneWeightsList: rawWeightTotal = rawWeightTotal + weight boneWeightsList = [] for weight in rawBoneWeightsList: calcWeight = round(255 * weight / rawWeightTotal) boneWeightsList.append(calcWeight) # Ensure that total of vertex bone weights is 255 runningTotal = 0 for i, weight in enumerate(boneWeightsList): runningTotal = runningTotal + weight if runningTotal > 255: boneWeightsList[i] = weight - 1 break if runningTotal < 255: boneWeightsList[0] = boneWeightsList[0] + 1 runningTotal = 0 for i, weight in enumerate(boneWeightsList): runningTotal = runningTotal + weight if runningTotal != 255: raise "Error: Vertex bone weights do not total 255!" if not vertId in grannyVertexBoneWeights: grannyVertexBoneWeights[vertId] = [] grannyVertexBoneWeights[vertId] = (boneIdsList, boneWeightsList) #file.write('%s %s ' % (boneIdsList, boneWeightsList)) #file.write("\n") position, orientationQuat = getTranslationOrientation(meshObject) #file.write('position:%.8f %.8f %.8f\n' % (position[0], position[1], position[2])) #file.write('orientationQuat:%.8f %.8f %.8f %.8f\n' % (orientationQuat[1], orientationQuat[2], orientationQuat[3], orientationQuat[0])) #file.write(meshName+"\n") filedata += "vertices\n" # Determine unique vertex/UVs for output uniqueVertSet = set() uniqueVertUVIndexes = {} uniqueVertUVs = [] currentVertUVIndex = 0 currentTriangleId = 0 triangleVertUVIndexes = [] for triangle in mesh.faces: vertIds = [v.index for v in triangle] vertIds = tuple(vertIds) triangleVertUVIndexes.append([]) for i, uv in enumerate(triangle.uv): vertexId = vertIds[i] uvt = tuple(uv) vertSig = '%i|%.8f|%.8f' % (vertexId, uvt[0], uvt[1]) if vertSig in uniqueVertSet: triangleVertUVIndex = uniqueVertUVIndexes[vertSig] else: uniqueVertSet.add(vertSig) uniqueVertUVIndexes[vertSig] = currentVertUVIndex uniqueVertUVs.append((vertexId, uvt[0], uvt[1])) triangleVertUVIndex = currentVertUVIndex currentVertUVIndex = currentVertUVIndex + 1 triangleVertUVIndexes[currentTriangleId].append(triangleVertUVIndex) currentTriangleId = currentTriangleId + 1 meshVerts = {} for i,vert in enumerate(mesh.verts): meshVerts[i] = vert # Write Vertices for uniqueVertUV in uniqueVertUVs: index = uniqueVertUV[0] vert = meshVerts[index] vertCoord = tuple(vert.co) vertNormal = tuple(vert.no) filedata +='%.8f %.8f %.8f ' % (vertCoord[0] + position[0], vertCoord[1] + position[1], vertCoord[2] + position[2]) filedata +='%.8f %.8f %.8f ' % vertNormal filedata +='%.8f %.8f ' % (uniqueVertUV[1], 1 - uniqueVertUV[2]) if index in grannyVertexBoneWeights: vBoneWeightTuple = grannyVertexBoneWeights[index] else: raise "Error: Mesh has unweighted vertices!" #vBoneWeightTuple = ([-1,-1,-1,-1],[-1,-1,-1,-1]) # Unweighted vertex - raise error filedata +='%d %d %d %d ' % (vBoneWeightTuple[0][0], vBoneWeightTuple[0][1],vBoneWeightTuple[0][2],vBoneWeightTuple[0][3]) # Bone Ids filedata +='%d %d %d %d\n' % (vBoneWeightTuple[1][0], vBoneWeightTuple[1][1],vBoneWeightTuple[1][2],vBoneWeightTuple[1][3]) # Bone Weights # Write Triangles filedata += "triangles\n" for triangle in triangleVertUVIndexes: #filedata += '%i %i %i\n' % tuple(triangle) filedata += '%i %i %i\n' % (triangle[0],triangle[1],triangle[2]) filedata += "end" file.write(filedata) file.close() Blender.Window.WaitCursor(0) print "End BR2 Export."
def main(): scn = bpy.data.scenes.active ob = scn.objects.active if not ob or ob.type != 'Mesh': return is_editmode = Window.EditMode() if is_editmode: Window.EditMode(0) mousedown_wait() # so the menu items clicking dosnt trigger the mouseclick Window.DrawProgressBar(0.0, '') Window.DrawProgressBar(0.1, '(1 of 3) Click on a face corner') # wait for a click mouse_buttons = Window.GetMouseButtons() while not mouse_buttons & LMB: sys.sleep(10) mouse_buttons = Window.GetMouseButtons() # Allow for RMB cancel if mouse_buttons & RMB: return while mouse_buttons & LMB: sys.sleep(10) mouse_buttons = Window.GetMouseButtons() Window.DrawProgressBar(0.2, '(2 of 3 ) Click confirms the U coords') mousedown_wait() obmat = ob.matrixWorld screen_x, screen_y = Window.GetMouseCoords() mouseInView, OriginA, DirectionA = mouseViewRay(screen_x, screen_y, obmat) if not mouseInView or not OriginA: return me = ob.getData(mesh=1) # Get the face under the mouse face_click, isect, side = BPyMesh.pickMeshRayFace(me, OriginA, DirectionA) if not face_click: return proj_z_component = face_click.no if not face_click: return # Find the face vertex thats closest to the mouse, # this vert will be used as the corner to map from. best_v = None best_length = 10000000 vi1 = None for i, v in enumerate(face_click.v): l = (v.co - isect).length if l < best_length: best_v = v best_length = l vi1 = i # now get the 2 edges in the face that connect to v # we can work it out fairly easerly if len(face_click) == 4: if vi1 == 0: vi2, vi3 = 3, 1 elif vi1 == 1: vi2, vi3 = 0, 2 elif vi1 == 2: vi2, vi3 = 1, 3 elif vi1 == 3: vi2, vi3 = 2, 0 else: if vi1 == 0: vi2, vi3 = 2, 1 elif vi1 == 1: vi2, vi3 = 0, 2 elif vi1 == 2: vi2, vi3 = 1, 0 face_corner_main = face_click.v[vi1].co face_corner_a = face_click.v[vi2].co face_corner_b = face_click.v[vi3].co line_a_len = (face_corner_a - face_corner_main).length line_b_len = (face_corner_b - face_corner_main).length orig_cursor = Window.GetCursorPos() Window.SetCursorPos(face_corner_main.x, face_corner_main.y, face_corner_main.z) SHIFT = Window.Qual.SHIFT MODE = 0 # firstclick, 1, secondclick mouse_buttons = Window.GetMouseButtons() project_mat = Matrix([0, 0, 0], [0, 0, 0], [0, 0, 0]) def get_face_coords(f): f_uv = f.uv return [(v.co - face_corner_main, f_uv[i]) for i, v in enumerate(f.v)] if me.faceUV == False: me.faceUV = True coords = [(co, uv) for f in me.faces if f.sel for co, uv in get_face_coords(f)] coords_orig = [uv.copy() for co, uv in coords] USE_MODIFIER = using_modifier(ob) while 1: if mouse_buttons & LMB: if MODE == 0: mousedown_wait() Window.DrawProgressBar( 0.8, '(3 of 3 ) Click confirms the V coords') MODE = 1 # second click # Se we cont continually set the length and get float error proj_y_component_orig = proj_y_component.copy() else: break elif mouse_buttons & RMB: # Restore old uvs for i, uv_orig in enumerate(coords_orig): coords[i][1][:] = uv_orig break mouse_buttons = Window.GetMouseButtons() screen_x, screen_y = Window.GetMouseCoords() mouseInView, OriginA, DirectionA = mouseViewRay( screen_x, screen_y, obmat) if not mouseInView: continue # Do a ray tri intersection, not clipped by the tri new_isect = Intersect(face_corner_main, face_corner_a, face_corner_b, DirectionA, OriginA, False) new_isect_alt = new_isect + DirectionA * 0.0001 # The distance from the mouse cursor ray vector to the edge line_isect_a_pair = LineIntersect(new_isect, new_isect_alt, face_corner_main, face_corner_a) line_isect_b_pair = LineIntersect(new_isect, new_isect_alt, face_corner_main, face_corner_b) # SHIFT to flip the axis. is_shift = Window.GetKeyQualifiers() & SHIFT if MODE == 0: line_dist_a = (line_isect_a_pair[0] - line_isect_a_pair[1]).length line_dist_b = (line_isect_b_pair[0] - line_isect_b_pair[1]).length if line_dist_a < line_dist_b: proj_x_component = face_corner_a - face_corner_main y_axis_length = line_b_len x_axis_length = (line_isect_a_pair[1] - face_corner_main).length else: proj_x_component = face_corner_b - face_corner_main y_axis_length = line_a_len x_axis_length = (line_isect_b_pair[1] - face_corner_main).length proj_y_component = proj_x_component.cross(proj_z_component) proj_y_component.length = 1 / y_axis_length proj_x_component.length = 1 / x_axis_length if is_shift: proj_x_component.negate() else: proj_y_component[:] = proj_y_component_orig if line_dist_a < line_dist_b: proj_y_component.length = 1 / (line_isect_a_pair[1] - new_isect).length else: proj_y_component.length = 1 / (line_isect_b_pair[1] - new_isect).length if is_shift: proj_y_component.negate() # Use the existing matrix to make a new 3x3 projecton matrix project_mat[0][:] = -proj_y_component project_mat[1][:] = -proj_x_component project_mat[2][:] = proj_z_component # Apply the projection matrix for proj_co, uv in coords: uv[:] = (project_mat * proj_co)[0:2] if USE_MODIFIER: me.update() Window.Redraw(Window.Types.VIEW3D) Window.SetCursorPos(*orig_cursor) if is_editmode: Window.EditMode(1) Window.RedrawAll()
def mouseViewRay(screen_x, screen_y, localMatrix=None, useMid = False): # Constant function variables p = mouseViewRay.p d = mouseViewRay.d for win3d in Window.GetScreenInfo(Window.Types.VIEW3D): # we search all 3dwins for the one containing the point (screen_x, screen_y) (could be the mousecoords for example) win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices'] # calculate a few geometric extents for this window win_mid_x = (win_max_x + win_min_x + 1.0) * 0.5 win_mid_y = (win_max_y + win_min_y + 1.0) * 0.5 win_size_x = (win_max_x - win_min_x + 1.0) * 0.5 win_size_y = (win_max_y - win_min_y + 1.0) * 0.5 #useMid is for projecting the coordinates when we subdivide the screen into bins if useMid: # == True screen_x = win_mid_x screen_y = win_mid_y # if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one... if (win_max_x > screen_x > win_min_x) and ( win_max_y > screen_y > win_min_y): # first we handle all pending events for this window (otherwise the matrices might come out wrong) Window.QHandle(win3d['id']) # now we get a few matrices for our window... # sorry - i cannot explain here what they all do # - if you're not familiar with all those matrices take a look at an introduction to OpenGL... pm = Window.GetPerspMatrix() # the prespective matrix pmi = Matrix(pm); pmi.invert() # the inverted perspective matrix if (1.0 - epsilon < pmi[3][3] < 1.0 + epsilon): # pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5) hms = mouseViewRay.hms ortho_d = mouseViewRay.ortho_d # ortho mode: is a bit strange - actually there's no definite location of the camera ... # but the camera could be displaced anywhere along the viewing direction. ortho_d.x, ortho_d.y, ortho_d.z = Window.GetViewVector() ortho_d.w = 0 # all rays are parallel in ortho mode - so the direction vector is simply the viewing direction #hms.x, hms.y, hms.z, hms.w = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0 hms[:] = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0 # these are the homogenious screencoords of the point (screen_x, screen_y) ranging from -1 to +1 p=(hms*pmi) + (1000*ortho_d) p.resize3D() d[:] = ortho_d[:3] # Finally we shift the position infinitely far away in # the viewing direction to make sure the camera if outside the scene # (this is actually a hack because this function # is used in sculpt_mesh to initialize backface culling...) else: # PERSPECTIVE MODE: here everything is well defined - all rays converge at the camera's location vmi = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix fp = mouseViewRay.fp dx = pm[3][3] * (((screen_x-win_min_x)/win_size_x)-1.0) - pm[3][0] dy = pm[3][3] * (((screen_y-win_min_y)/win_size_y)-1.0) - pm[3][1] fp[:] = \ pmi[0][0]*dx+pmi[1][0]*dy,\ pmi[0][1]*dx+pmi[1][1]*dy,\ pmi[0][2]*dx+pmi[1][2]*dy # fp is a global 3dpoint obtained from "unprojecting" the screenspace-point (screen_x, screen_y) #- figuring out how to calculate this took me quite some time. # The calculation of dxy and fp are simplified versions of my original code #- so it's almost impossible to explain what's going on geometrically... sorry p[:] = vmi[3][:3] # the camera's location in global 3dcoords can be read directly from the inverted viewmatrix #d.x, d.y, d.z =normalize_v3(sub_v3v3(p, fp)) d[:] = p.x-fp.x, p.y-fp.y, p.z-fp.z #print 'd', d, 'p', p, 'fp', fp # the direction vector is simply the difference vector from the virtual camera's position #to the unprojected (screenspace) point fp # Do we want to return a direction in object's localspace? if localMatrix: localInvMatrix = Matrix(localMatrix) localInvMatrix.invert() localInvMatrix_notrans = localInvMatrix.rotationPart() p = p * localInvMatrix d = d * localInvMatrix # normalize_v3 # remove the translation from d d.x -= localInvMatrix[3][0] d.y -= localInvMatrix[3][1] d.z -= localInvMatrix[3][2] d.normalize() ''' # Debugging me = Blender.Mesh.New() me.verts.extend([p[0:3]]) me.verts.extend([(p-d)[0:3]]) me.edges.extend([0,1]) ob = Blender.Scene.GetCurrent().objects.new(me) ''' return True, p, d # Origin, Direction # Mouse is not in any view, return None. return False, None, None
if TEXTURES_DIR[-1] != dirsep: TEXTURES_DIR = "%s%s" % (TEXTURES_DIR, dirsep) if oldtexdir != TEXTURES_DIR: update_registry() VERBOSE = True rd = Registry.GetKey('General', True) if rd: if rd.has_key('verbose'): VERBOSE = rd['verbose'] errmsg = "" # Matrix to align ac3d's coordinate system with Blender's one, # it's a -90 degrees rotation around the x axis: AC_TO_BLEND_MATRIX = Matrix([1, 0, 0], [0, 0, 1], [0, -1, 0]) AC_WORLD = 0 AC_GROUP = 1 AC_POLY = 2 AC_LIGHT = 3 AC_OB_TYPES = { 'world': AC_WORLD, 'group': AC_GROUP, 'poly': AC_POLY, 'light': AC_LIGHT } AC_OB_BAD_TYPES_LIST = [] # to hold references to unknown (wrong) ob types def inform(msg):
def file_callback (filename): try: bgl=file(filename,'rb') except: Draw.PupMenu("ERROR%%t|Can't open %s" % filename) return bgldir=dirname(filename) guid=None friendly=None texnames=[] # list of texture file names matlist=[] # fs9 materials mats=[] # list of Blender Materials inde=[] vert=[] tran=[] amap=[] # fsx map scen=[] # (child, peer, matrix, parent) data={} # (material, vert, inde, scene) by LOD plat={} # (surface, vertices) by scene atta=[] # (name, scene) attobjs=[] atteffects=[] partcount=0 Window.WaitCursor(1) Window.DrawProgressBar(0, "Opening ...") try: (c,size,endmdl)=container(bgl,0) assert (c=='RIFF') assert (bgl.read(4) in ['MDL9','MDLX']) while bgl.tell()<endmdl: (c,size,end1)=container(bgl,1) if c=='MDLG': # FSX guid guid='{%x-%x-%x-%x%x-%x%x%x%x%x%x}' % unpack('<IHH8B',bgl.read(size)) if debug: print guid elif c=='MDLH': if size==36: # FS9 header (size,reserved,reserved,radius,reserved,reserved,reserved,reserved,reserved)=unpack('<9I', bgl.read(size)) if debug: print radius else: bgl.seek(size,1) elif c=='MDLN': # FSX friendly name friendly=bgl.read(size).strip('\0').strip() if debug: print friendly elif c=='MDLD': # FSX data while bgl.tell()<end1: Window.DrawProgressBar(float(bgl.tell())/(endmdl+endmdl), "Reading ...") (c,size,end2)=container(bgl,2) if c=='TEXT': texnames=[bgl.read(64).strip('\0').strip() for i in range(0,size,64)] elif c=='MATE': mats.extend([getmaterialx(bgl.read(120), bgldir, texnames) for i in range(0,size,120)]) elif c=='INDE': # reverse order of vertices in each triangle for i in range(size/6): t=list(unpack('<3H', bgl.read(6))) t.reverse() inde.extend(t) elif c=='VERB': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='VERT': vert.append([tuple([round(i,VROUND) for i in unpack('<8f',bgl.read(32))]) for j in range(0,size,32)]) else: bgl.seek(size,1) elif c=='TRAN': for i in range(0,size,64): tran.append(Matrix(*[unpack('<4f',bgl.read(16)) for j in range(4)])) elif c=='AMAP': for i in range(0,size,8): (a,b)=unpack('<2I',bgl.read(8)) amap.append(b) elif c=='SCEN': # Assumed to be after TRAN and AMAP sections count=size/8 for i in range(count): (child,peer,offset,unk)=unpack('<4h',bgl.read(8)) thismatrix=tran[amap[offset/8]] scen.append((child,peer,thismatrix,-1)) elif c=='LODT': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='LODE': (lod,)=unpack('<I', bgl.read(4)) while bgl.tell()<end3: (c,size,end4)=container(bgl,4) if c=='PART': (typ,sceneg,material,verb,voff,vcount,ioff,icount,mouserect)=unpack('<9I', bgl.read(36)) if debug: print lod, typ,sceneg,material,verb,voff,vcount,ioff,icount,mouserect assert (typ==1) # TRIANGLE_LIST if not lod in data: data[lod]=[] data[lod].append((mats[material], vert[verb][voff:voff+vcount], inde[ioff:ioff+icount], sceneg)) partcount+=1 else: bgl.seek(size,1) else: bgl.seek(size,1) elif c=='PLAL': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='PLAT': (surface,sceneg,numvert,v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z)=unpack('<3I9f', bgl.read(48)) assert (numvert==3) #print (surface,scene,numvert,v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z) if not sceneg in plat: plat[sceneg]={} plat[sceneg][((round(v2x,VROUND),round(v2y,VROUND),round(v2z,VROUND)),(round(v1x,VROUND),round(v1y,VROUND),round(v1z,VROUND)),(round(v0x,VROUND),round(v0y,VROUND),round(v0z,VROUND)))]=surface else: bgl.seek(size,1) elif c=='REFL': while bgl.tell()<end2: (c,size,end3)=container(bgl,3) if c=='REFP': (sceneg,size)=unpack('<II', bgl.read(8)) atta.append((bgl.read(size).strip('\0').strip(),sceneg)) else: bgl.seek(size,1) elif c=='ATTO': while bgl.tell()<end2: (unk,flags,size)=unpack('<IHH', bgl.read(8)) d=bgl.read(size) if flags==2: # Attached object attobjs.append((d[40:-5], '{%x-%x-%x-%x%x-%x%x%x%x%x%x}' % unpack('<IHH8B', d[20:36]))) elif flags==4: # Attached effect p=d[100:-5].split('\0') # params, attachpt atteffects.append((p[1], d[20:100].strip(' \0'), p[0])) elif debug: print "Unknown attach %d:\n%s" % (flags, d) else: bgl.seek(size,1) elif c=='EXTE': # FS9 data while bgl.tell()<end1: Window.DrawProgressBar(float(bgl.tell())/(endmdl+endmdl), "Reading ...") (c,size,end2)=container(bgl,2) if c=='TEXT': (bglop,count,reserved)=unpack('<HHI', bgl.read(8)) assert(bglop==0xb7) texnames=[unpack('<4I', bgl.read(16)) + (bgl.read(64).strip('\0').strip(),) for i in range(count)] assert (bgl.read(2)=='\x22\0') # return elif c=='MATE': (bglop,count,reserved)=unpack('<HHI', bgl.read(8)) assert(bglop==0xb6) matlist.extend([unpack('<17f', bgl.read(17*4)) for i in range(count)]) assert (bgl.read(2)=='\x22\0') # return elif c=='VERT': (bglop,count,reserved)=unpack('<HHI', bgl.read(8)) assert(bglop==0xb5) vert.extend([tuple([round(i,VROUND) for i in unpack('<8f',bgl.read(32))]) for j in range(count)]) assert (bgl.read(2)=='\x22\0') # return elif c=='BGL ': code=bgl.read(size) lods=[0] lodno=0 while lodno<len(lods): ip=0 lod=lods[lodno] sceneg=0 curmat=None stack=[] if debug: print "LOD >", lod while True: (bglop,)=unpack('<H', code[ip:ip+2]) if debug: print "%s%04x: %02x" % (' '*len(stack), ip, bglop) ip+=2 # mostly just opcodes from BGLFP.doc if bglop==0x0d: # BGL_JUMP (offset,)=unpack('<h', code[ip:ip+2]) ip=ip-2+offset elif bglop==0x22: # BGLOP_RETURN ip=stack.pop() elif bglop==0x23: # BGLOP_CALL (offset,)=unpack('<h', code[ip:ip+2]) stack.append(ip+2) ip=ip-2+offset elif bglop==0x24: # BGLOP_IFIN1 ip+=8 # assume true elif bglop==0x39: # BGLOP_IFMASK ip+=6 # assume true elif bglop==0x5f: # BGLOP_IFSIZEV (offset,r,pixels)=unpack('<hHH', code[ip:ip+6]) newlod=int(0.5+radius*2475.0/r) if newlod not in lods: lods.append(newlod) if lod<newlod: ip=ip-2+offset else: ip+=6 elif bglop==0x88: # BGLOP_JUMP_32 (offset,)=unpack('<i', code[ip:ip+4]) ip=ip-2+offset elif bglop==0x89: # BGLOP_JUMP_32 (offset,)=unpack('<i', code[ip:ip+4]) assert (offset==-1) ip=ip+4 elif bglop==0x8a: # BGLOP_CALL_32 (offset,)=unpack('<i', code[ip:ip+4]) stack.append(ip+4) ip=ip-2+offset elif bglop==0xa7: # BGLOP_SPRITE_VICALL ip+=20 # ignore elif bglop==0xb3: # BGLOP_IFINF1 ip+=14 # assume true elif bglop==0xb8: # BGLOP_SET_MATERIAL (m,t)=unpack('<hh', code[ip:ip+4]) ip+=4 curmat=getmaterial9(bgldir, m, matlist, t, texnames) elif bglop==0xb9: # BGLOP_DRAW_TRILIST (voff,vcount,icount)=unpack('<3H', code[ip:ip+6]) ip+=6 inde=unpack('<%dH' % icount, code[ip:ip+2*icount]) ip+=2*icount if debug: print "DATA:", lod, sceneg, voff,vcount,icount if not lod in data: data[lod]=[] data[lod].append((curmat, vert[voff:voff+vcount], inde, sceneg)) partcount+=1 elif bglop==0xbd: # BGLOP_END break elif bglop==0xc4: # BGLOP_SET_MATRIX_INDIRECT (sceneg,)=unpack('<H', code[ip:ip+2]) ip+=2 else: assert 0 lodno+=1 # Shift LODs up lods.sort() lods.append(100) for i in range(len(lods)-1,0,-1): data[lods[i]]=data[lods[i-1]] data[lods[0]].pop() elif c=='TRAN': for i in range(0,size,64): tran.append(Matrix(*[unpack('<4f',bgl.read(16)) for j in range(4)])) if debug: print i/64 print tran[i/64] elif c=='ANIC': anicbase=bgl.tell() amap=bgl.read(size) elif c=='SCEN': # Assumed to be after TRAN and ANIC sections (count,)=unpack('<H', bgl.read(2)) scen=[None for i in range(count)] for i in range(count): (n,child,peer,size,offset)=unpack('<4hi', bgl.read(12)) offset=bgl.tell()-12+offset-anicbase if size==6: # Static (bglop,src,dst)=unpack('<3H', amap[offset:offset+6]) assert (bglop==0xc6) thismatrix=tran[src] else: # Animation (x,y,z,dst)=unpack('<3fh', amap[offset+size-14:offset+size]) thismatrix=TranslationMatrix(Vector(x,y,z,0)) scen[n]=(child,peer,thismatrix,-1) elif c=='PLAT': (count,)=unpack('<I', bgl.read(4)) s=[] for i in range(count): (sceneg,offset,numvert,surface)=unpack('<HhHH', bgl.read(8)) assert (numvert==3) # triangle s.append((sceneg,surface)) # Assumes in order so can ignore offset for i in range(count): (sceneg,surface)=s[i] (v0x,v0y,v0z,v1x,v1y,v1z,v2x,v2y,v2z)=unpack('9f', bgl.read(36)) if not sceneg in plat: plat[sceneg]={} plat[sceneg][((round(v0x,VROUND),round(v0y,VROUND),round(v0z,VROUND)),(round(v1x,VROUND),round(v1y,VROUND),round(v1z,VROUND)),(round(v2x,VROUND),round(v2y,VROUND),round(v2z,VROUND)))]=surface elif c=='ATTA': (count,)=unpack('<I', bgl.read(4)) s=[] for i in range(count): (sceneg,offset)=unpack('<Hh', bgl.read(4)) s.append((sceneg)) # Assumes in order so can ignore offset for i in range(count): name='' while True: c=bgl.read(1) if c=='\0': break name=name+c atta.append((name.strip(),s[i])) elif c=='ATTO': # same as FSX while bgl.tell()<end2: (unk,flags,size)=unpack('<IHH', bgl.read(8)) d=bgl.read(size) if flags==2: # Attached object attobjs.append((d[40:-5], '{%x-%x-%x-%x%x-%x%x%x%x%x%x}' % unpack('<IHH8B', d[20:36]))) elif flags==4: # Attached effect p=d[100:-5].split('\0') # params, attachpt atteffects.append((p[1], d[20:100].strip(' \0'), p[0])) elif debug: print "Unknown attach %d:\n%s" % (flags, d) else: bgl.seek(size,1) else: bgl.seek(size,1) bgl.close() # Invert Child/Peer pointers to get parents for i in range(len(scen)): (child, peer, thismatrix, parent)=scen[i] if child!=-1: # child's parent is me (xchild, xpeer, xmatrix, xparent)=scen[child] scen[child]=(xchild, xpeer, xmatrix, i) if peer!=-1: # peer's parent is my parent assert (peer>i) (xchild, xpeer, xmatrix, xparent)=scen[peer] scen[peer]=(xchild, xpeer, xmatrix, parent) if debug: print "TRAN Matrices", len(tran) for i in range(len(tran)): print i print tran[i] #print "Animation map", len(amap) #for i in range(len(amap)): # print i, '->', amap[i] print "Scene Graph", len(scen) for i in range(len(scen)): (child, peer, thismatrix, parent)=scen[i] print i, child, peer, parent print thismatrix scene=Scene.GetCurrent() lods=data.keys() lods.sort() lods.reverse() partno=0.0 for layer in range(len(lods)): for (material, vert, inde, sceneg) in data[lods[layer]]: Window.DrawProgressBar(0.5+partno/(partcount+partcount), "Adding ...") (child, peer, finalmatrix, parent)=scen[sceneg] #print lods[layer] #print sceneg, child, peer, parent while parent!=-1: (child, peer, thismatrix, parent)=scen[parent] finalmatrix=finalmatrix*thismatrix #print finalmatrix if not layer and sceneg in plat: adddata(scene, layer+1, material, vert, inde, finalmatrix, plat[sceneg]) else: adddata(scene, layer+1, material, vert, inde, finalmatrix) partno+=1 if debug: for (sceneg,verts) in plat.iteritems(): if verts: print "Unallocated platforms: sceneg=%d, %d:" % (sceneg, len(verts.keys())) for v in verts.keys(): for vt in v: print "%.4f %.4f %.4f" % vt print # Attach points attachpoints={} for (name, sceneg) in atta: (child, peer, finalmatrix, parent)=scen[sceneg] while parent!=-1: (child, peer, thismatrix, parent)=scen[parent] finalmatrix=finalmatrix*thismatrix attachpoints[name]=addattach(scene, name, finalmatrix) for (name, obj) in attobjs: attachpoints[name].addProperty('guid', obj) for (name, effect, params) in atteffects: attachpoints[name].addProperty('effectName', effect) if params: attachpoints[name].addProperty('effectParams', params) addprops(scene, lods) Window.DrawProgressBar(1, "Finished") Window.WaitCursor(0) except: bgl.close() Window.DrawProgressBar(1, "ERROR") Window.WaitCursor(0) Draw.PupMenu("ERROR%%t|Can't read %s - is this a FSX MDL format file?" % filename)
def adddata(scene, layer, material, vert, inde, matrix, plat=None): # Need to convert between right and left handed coordinate systems. matrix=matrix*Matrix([1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]) # This results in a negatively scaled matrix, which causes problems # for face normals. So separate out and apply scale&rotation parts. tran=TranslationMatrix(matrix.translationPart()) matrix[3]=[0,0,0,1] # detect back-to-back duplicate faces facelookup={} newvert=[[]] for i in range(0, len(inde), 3): face=[(vert[inde[j]][0], vert[inde[j]][1], vert[inde[j]][2]) for j in range(i+2,i-1,-1)] # duplicate faces may be rotated - eg Oil_Rig_Sample if tuple(face) in facelookup or (face[1],face[2],face[0]) in facelookup or (face[2],face[0],face[1]) in facelookup: # back-to-back duplicate - start new mesh newvert.append([]) facelookup={} face.reverse() facelookup[tuple(face)]=True newvert[-1].extend([vert[inde[j]] for j in range(i,i+3)]) for vert in newvert: mesh=Mesh.New('Mesh') mesh.mode &= ~(Mesh.Modes.TWOSIDED|Mesh.Modes.AUTOSMOOTH) mesh.mode |= Mesh.Modes.NOVNORMALSFLIP mesh.materials+=[material] mesh.verts.extend([v[0:3] for v in vert]) mesh.faces.extend([[i,i+1,i+2] for i in range(0, len(vert), 3)], ignoreDups=True) mesh.faceUV=True mtex=material.getTextures()[0] if mtex: image=mtex.tex.image else: image=None surface=None i=0 for face in mesh.faces: face.mode &= ~(Mesh.FaceModes.TWOSIDE|Mesh.FaceModes.TILES) if image: face.mode|=Mesh.FaceModes.TEX face.image=image face.uv=[Vector(v[6],1-v[7]) for v in [vert[j] for j in range(i,i+3)]] # smooth if vertex normals are different face.smooth=not (vert[i][3:6]==vert[i+1][3:6]==vert[i+2][3:6]) # is this a platform? if plat: v=(vert[i][:3], vert[i+1][:3], vert[i+2][:3]) #for vt in v: print "%.4f %.4f %.4f" % vt #print if v in plat: face.mode&=~Mesh.FaceModes.DYNAMIC surface=plat.pop(v) else: face.mode |= Mesh.FaceModes.DYNAMIC else: face.mode |= Mesh.FaceModes.DYNAMIC i+=3 ob = Object.New('Mesh') ob.link(mesh) scene.objects.link(ob) ob.layers=[layer] ob.setMatrix(tran) if surface!=None: ob.addProperty('surfaceType', SURFACES[surface]) # following must be after object linked to scene mesh.transform(matrix) mesh.sel=True mesh.remDoubles(0.001) # 1/10mm mesh.sel=True mesh.calcNormals() # calculate vertex normals mesh.sel=False
def mouseViewRay(screen_x, screen_y, localMatrix=None, useMid=False): # Constant function variables p = mouseViewRay.p d = mouseViewRay.d for win3d in Window.GetScreenInfo( Window.Types.VIEW3D ): # we search all 3dwins for the one containing the point (screen_x, screen_y) (could be the mousecoords for example) win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices'] # calculate a few geometric extents for this window win_mid_x = (win_max_x + win_min_x + 1.0) * 0.5 win_mid_y = (win_max_y + win_min_y + 1.0) * 0.5 win_size_x = (win_max_x - win_min_x + 1.0) * 0.5 win_size_y = (win_max_y - win_min_y + 1.0) * 0.5 #useMid is for projecting the coordinates when we subdivide the screen into bins if useMid: # == True screen_x = win_mid_x screen_y = win_mid_y # if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one... if (win_max_x > screen_x > win_min_x) and (win_max_y > screen_y > win_min_y): # first we handle all pending events for this window (otherwise the matrices might come out wrong) Window.QHandle(win3d['id']) # now we get a few matrices for our window... # sorry - i cannot explain here what they all do # - if you're not familiar with all those matrices take a look at an introduction to OpenGL... pm = Window.GetPerspMatrix() # the prespective matrix pmi = Matrix(pm) pmi.invert() # the inverted perspective matrix if (1.0 - epsilon < pmi[3][3] < 1.0 + epsilon): # pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5) hms = mouseViewRay.hms ortho_d = mouseViewRay.ortho_d # ortho mode: is a bit strange - actually there's no definite location of the camera ... # but the camera could be displaced anywhere along the viewing direction. ortho_d.x, ortho_d.y, ortho_d.z = Window.GetViewVector() ortho_d.w = 0 # all rays are parallel in ortho mode - so the direction vector is simply the viewing direction #hms.x, hms.y, hms.z, hms.w = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0 hms[:] = (screen_x - win_mid_x) / win_size_x, ( screen_y - win_mid_y) / win_size_y, 0.0, 1.0 # these are the homogenious screencoords of the point (screen_x, screen_y) ranging from -1 to +1 p = (hms * pmi) + (1000 * ortho_d) p.resize3D() d[:] = ortho_d[:3] # Finally we shift the position infinitely far away in # the viewing direction to make sure the camera if outside the scene # (this is actually a hack because this function # is used in sculpt_mesh to initialize backface culling...) else: # PERSPECTIVE MODE: here everything is well defined - all rays converge at the camera's location vmi = Matrix(Window.GetViewMatrix()) vmi.invert() # the inverse viewing matrix fp = mouseViewRay.fp dx = pm[3][3] * (( (screen_x - win_min_x) / win_size_x) - 1.0) - pm[3][0] dy = pm[3][3] * (( (screen_y - win_min_y) / win_size_y) - 1.0) - pm[3][1] fp[:] = \ pmi[0][0]*dx+pmi[1][0]*dy,\ pmi[0][1]*dx+pmi[1][1]*dy,\ pmi[0][2]*dx+pmi[1][2]*dy # fp is a global 3dpoint obtained from "unprojecting" the screenspace-point (screen_x, screen_y) #- figuring out how to calculate this took me quite some time. # The calculation of dxy and fp are simplified versions of my original code #- so it's almost impossible to explain what's going on geometrically... sorry p[:] = vmi[3][:3] # the camera's location in global 3dcoords can be read directly from the inverted viewmatrix #d.x, d.y, d.z =normalize_v3(sub_v3v3(p, fp)) d[:] = p.x - fp.x, p.y - fp.y, p.z - fp.z #print 'd', d, 'p', p, 'fp', fp # the direction vector is simply the difference vector from the virtual camera's position #to the unprojected (screenspace) point fp # Do we want to return a direction in object's localspace? if localMatrix: localInvMatrix = Matrix(localMatrix) localInvMatrix.invert() localInvMatrix_notrans = localInvMatrix.rotationPart() p = p * localInvMatrix d = d * localInvMatrix # normalize_v3 # remove the translation from d d.x -= localInvMatrix[3][0] d.y -= localInvMatrix[3][1] d.z -= localInvMatrix[3][2] d.normalize() ''' # Debugging me = Blender.Mesh.New() me.verts.extend([p[0:3]]) me.verts.extend([(p-d)[0:3]]) me.edges.extend([0,1]) ob = Blender.Scene.GetCurrent().objects.new(me) ''' return True, p, d # Origin, Direction # Mouse is not in any view, return None. return False, None, None