------------------------------------------------------------------------------------- utility bluExport "BluFlame v0.2 Export" ( -- Define variables that are visible to all functions in the utility local ostream, tabs = "" local version = 2 local usedMaterials = #() -- Define the GUI interface group "Options" ( checkbox cb_exportSelOnly "Export Selected Only" ) button btn_export "Save As..." width:100 ------------------------------------------------------------------------------------ function computeTangent obj = ( local theMesh = snapshotAsMesh obj local tSpace = #() -- Do we have to flip faces? local flip = false local indices = #(1, 2, 3) if dot (cross obj.transform.row1 obj.transform.row2) obj.transform.row3 <= 0 do ( indices[2] = 3 indices[3] = 2 flip = true ) for nFace = 1 to theMesh.numFaces do ( local face = getFace theMesh nFace local tface = getTVFace theMesh nFace local v1 = getVert theMesh face[indices[1]] local v2 = getVert theMesh face[indices[2]] local v3 = getVert theMesh face[indices[3]] local uv1 = getTVert theMesh tface[indices[1]] local uv2 = getTVert theMesh tface[indices[2]] local uv3 = getTVert theMesh tface[indices[3]] local dV1 = v1 - v2 local dV2 = v1 - v3 local dUV1 = uv1 - uv2 local dUV2 = uv1 - uv3 local area = dUV1.x * dUV2.y - dUV1.y * dUV2.x local sign = if area < 0 then -1 else 1 local tangent = [0,0,1] tangent.x = dV1.x * dUV2.y - dUV1.y * dV2.x tangent.y = dV1.y * dUV2.y - dUV1.y * dV2.y tangent.z = dV1.z * dUV2.y - dUV1.y * dV2.z tangent = (normalize tangent) * sign append tSpace tangent ) delete theMesh return tSpace ) function AddMaterialToList inmaterial = ( local canadd = true for mat in usedMaterials do ( if mat == inmaterial then ( canadd = false break ) ) if canadd then ( append usedMaterials inmaterial ) ) ------------------------------------------------------------------------------------- -- This function exports a bones object to the BLU file. function ExportNodeAnimation node = ( -- animation header writelong ostream 3 -- 3: animation writestring ostream node.name -- position writelong ostream node.position.controller.keys.count #unsigned for i = 1 to node.position.controller.keys.count do ( writefloat ostream (node.position.controller.keys[i].time / 30.0) if node.parent != undefined then trans = in coordsys node.parent [node.pos.x_position.controller.keys[i].value, node.pos.y_position.controller.keys[i].value, node.pos.z_position.controller.keys[i].value] else trans = in coordsys world [node.pos.x_position.controller.keys[i].value, node.pos.y_position.controller.keys[i].value, node.pos.z_position.controller.keys[i].value] writefloat ostream trans.x writefloat ostream trans.y writefloat ostream trans.z ) -- scale writelong ostream node.scale.controller.keys.count #unsigned for i = 1 to node.scale.controller.keys.count do ( writefloat ostream (node.scale.controller.keys[i].time / 30.0) writefloat ostream node.scale.controller.keys[i].value.x writefloat ostream node.scale.controller.keys[i].value.y writefloat ostream node.scale.controller.keys[i].value.z ) -- rotation writelong ostream node.rotation.controller.keys.count #unsigned for i = 1 to node.rotation.controller.keys.count do ( writefloat ostream (node.rotation.controller.keys[i].time / 30.0) writefloat ostream node.rotation.x_rotation.controller.keys[i].value writefloat ostream node.rotation.y_rotation.controller.keys[i].value writefloat ostream node.rotation.z_rotation.controller.keys[i].value ) ) ------------------------------------------------------------------------------------- -- This function exports a bones object to the BLU file. function ExportBone bone = ( -- bone header writelong ostream 2 -- 2: bone writestring ostream bone.name if bone.parent != undefined then writestring ostream bone.parent.name else writestring ostream "" -- Translation (IN PARENT SPACE)-- if bone.parent != undefined then trans = in coordsys parent at time 0.0 bone.pos else trans = in coordsys world at time 0.0 bone.pos writefloat ostream trans.x writefloat ostream trans.y writefloat ostream trans.z print "bone:" print trans -- Rotation (IN PARENT SPACE)-- rot = in coordsys parent bone.rotation at time 0.0 writefloat ostream rot.x writefloat ostream rot.y writefloat ostream rot.z writefloat ostream rot.w -- Scale -- sca = bone.scale at time 0.0 writefloat ostream sca.x writefloat ostream sca.y writefloat ostream sca.z ) ------------------------------------------------------------------------------------- -- This function exports a material object to the BLU file. function ExportMaterial mat = ( -- material header writelong ostream 1 -- 1: material writestring ostream mat.name if classof mat.diffusemap == Bitmaptexture and mat.diffuseMapenable then writestring ostream mat.diffuseMap.bitmap.filename else writestring ostream "" if classof mat.specularmap == Bitmaptexture and mat.specularmapenable then writestring ostream mat.specularmap .bitmap.filename else writestring ostream "" if classof mat.bumpmap == Bitmaptexture and mat.bumpmapenable then writestring ostream mat.bumpmap .bitmap.filename else writestring ostream "" if classof mat.SelfIllumMap == Bitmaptexture and mat.SelfIllumMapenable then writestring ostream mat.SelfIllumMap.bitmap.filename else writestring ostream "" if classof mat.reflectionmap == Bitmaptexture and mat.reflectionmapenable then writestring ostream mat.reflectionmap.bitmap.filename else writestring ostream "" writefloat ostream mat.ambient.r writefloat ostream mat.ambient.g writefloat ostream mat.ambient.b writefloat ostream mat.ambient.a writefloat ostream mat.diffuse.r writefloat ostream mat.diffuse.g writefloat ostream mat.diffuse.b writefloat ostream mat.diffuse.a writefloat ostream mat.specular.r writefloat ostream mat.specular.g writefloat ostream mat.specular.b writefloat ostream mat.specular.a if (mat.selfIllumAmount > 0) then ( writefloat ostream mat.selfIllumAmount writefloat ostream mat.selfIllumAmount writefloat ostream mat.selfIllumAmount writefloat ostream mat.selfIllumAmount ) else ( writefloat ostream mat.selfIllumColor.r writefloat ostream mat.selfIllumColor.g writefloat ostream mat.selfIllumColor.b writefloat ostream mat.selfIllumColor.a ) writefloat ostream mat.specularlevel writefloat ostream mat.glossiness ) ------------------------------------------------------------------------------------- -- This function exports a geometry object to the BLU file. function ExportMesh node meshObj name = ( local isSkin = (node.modifiers["skin"] != undefined) if isSkin then ( max modify mode modPanel.setCurrentObject node.modifiers["skin"] ) -- mesh header writelong ostream 0 -- 0: mesh writestring ostream name if meshObj.material != undefined then ( writestring ostream meshObj.material.name AddMaterialToList meshObj.material ) else ( writestring ostream "" ) -- vertex count writelong ostream meshObj.numVerts #unsigned -- vertex elements -- 0: Position2D -- 1: Position3D -- 2: Position4D -- 3: Normal -- 4: ColorRGB -- 5: ColorARGB -- 6: ColorRGBf -- 7: ColorARGBf -- 8: Texture2D -- 9: Texture3D -- 10: Texture4D -- 11: Tangent3D -- 12: Tangent4D -- 13: BlendIndices -- 14: BlendWeights local numVElements = 3 if meshObj.material != undefined and meshObj.material.bumpmapenable then numVElements += 1 if isSkin then numVElements += 2 -- blendindicies and weights writelong ostream numVElements -- number of vertexelements writelong ostream 1 writelong ostream 3 writelong ostream 8 PerFaceTangents = #() if meshObj.material != undefined and meshObj.material.bumpmapenable then ( writelong ostream 11 -- tangent PerFaceTangents = computeTangent meshObj ) if isSkin then ( writelong ostream 13 -- BlendIndices writelong ostream 14 -- BlendWeights ) local hasTooMuchBonesPerVertex = false for i = 1 to meshObj.numVerts do ( vert = ((GetVert meshObj i)-meshObj.pos) writefloat ostream vert.x writefloat ostream vert.y writefloat ostream vert.z normal = GetNormal meshObj i writefloat ostream normal.x writefloat ostream normal.y writefloat ostream normal.z uvw = GetTVert meshObj i writefloat ostream uvw.x writefloat ostream uvw.y -- tangent if meshObj.material != undefined and meshObj.material.bumpmapenable then ( local vertfaces = meshop.getFacesUsingVert meshObj #{i} local tangent = 0; for face in vertfaces do ( tangent += PerFaceTangents[face] ) tangent = (normalize tangent) writefloat ostream tangent.x writefloat ostream tangent.y writefloat ostream tangent.z ) if isSkin then ( local bone_idA = 0 local weightA = 0.0 local bone_idB = 0 local weightB = 0.0 local bone_idC = 0 local weightC = 0.0 local bone_idD = 0 local weightD = 0.0 if (skinops.getVertexWeightCount node.skin i > 0) then ( local bone_idA = skinops.getVertexWeightBoneID node.skin i 1 local weightA = skinops.getVertexWeight node.skin i 1 ) if (skinops.getVertexWeightCount node.skin i > 1) then ( local bone_idB = skinops.getVertexWeightBoneID node.skin i 2 local weightB = skinops.getVertexWeight node.skin i 2 ) if (skinops.getVertexWeightCount node.skin i > 2) then ( local bone_idC = skinops.getVertexWeightBoneID node.skin i 3 local weightC = skinops.getVertexWeight node.skin i 3 ) if (skinops.getVertexWeightCount node.skin i > 3) then ( local bone_idD = skinops.getVertexWeightBoneID node.skin i 4 local weightD = skinops.getVertexWeight node.skin i 4 ) if (skinops.getVertexWeightCount node.skin i > 4) then hasTooMuchBonesPerVertex = true writefloat ostream (bone_idA - 1) writefloat ostream (bone_idB - 1) writefloat ostream (bone_idC - 1) writefloat ostream (bone_idD - 1) writefloat ostream weightA writefloat ostream weightB writefloat ostream weightC writefloat ostream weightD ) ) writelong ostream (meshObj.numFaces * 3) #unsigned for i = 1 to meshObj.numFaces do ( poly = GetFace meshObj i writelong ostream (poly.x as integer -1) #unsigned writelong ostream (poly.y as integer -1) #unsigned writelong ostream (poly.z as integer -1) #unsigned ) if hasTooMuchBonesPerVertex then MessageBox (name + " has too much bones per vertex.") ) ------------------------------------------------------------------------------------- -- This function is called once per node in the scene. -- A node in Max may be all sorts of things. We are only interested in geometry. function ExportNode node = ( -- Create node and export class specific data if SuperClassOf node == GeometryClass and ClassOf node == BoneGeometry then ( ExportBone node ) else if SuperClassOf node == GeometryClass and ClassOf node == Editable_mesh then ( ExportMesh node node node.name ) else if SuperClassOf node == GeometryClass then ( -- Build a mesh out of this object and save it local temp = copy node convertToMesh temp if SuperClassOf temp == GeometryClass and ClassOf temp == Editable_mesh then ( ExportMesh node temp node.name ) delete temp ) else -- Not geometry.. could be a camera, light, etc. return false if (node.position.controller.keys.count > 0 or node.scale.controller.keys.count > 0 or node.rotation.controller.keys.count > 0) then ( ExportNodeAnimation node ) return true ) ------------------------------------------------------------------------------------- -- This function recurses down the node hierarchy calling ExportNode for each node. function RecursiveExportNode node = ( if (ExportNode node) == false then return false -- Recurse children before writing this node for child in node.children do RecursiveExportNode child ) ------------------------------------------------------------------------------------- -- The highest level function called to export an BLU file. function ExportBLU = ( -- Write the BLU header writestring ostream "BLUF" writelong ostream version if cb_exportSelOnly.checked then ( for node in selection do ExportNode node ) else ( for node in rootnode.children do RecursiveExportNode node ) for mat in usedMaterials do ( ExportMaterial mat ) ) ------------------------------------------------------------------------------------- -- Open an prepare a file handle for writing. function GetSaveFileStream = ( fname = GetSaveFileName types:"BLU (*.blu)|*.blu|All Files(*.*)|*.*|" if fname == undefined then return undefined ostream = fopen fname "wbS" -- CreateFile fname if ostream == undefined then ( MessageBox "Couldn't open file for writing!" return undefined ) return ostream ) ------------------------------------------------------------------------------------- -- This is the function called when the user activates the utility by pressing on -- the export button. It opens the file and calls the export routine. on btn_export pressed do ( ostream = GetSaveFileStream() if ostream != undefined then ( ExportBLU() fclose ostream ) ) ) -- End BLUExport