void loadVerts(Model m, Bob.ModelChunk bmc) { VertexBufferObject vbo = new VertexBufferObject(BufferUsageHint.StaticDraw); if (bmc.animated == false) { List <V3N3T2> verts = new List <V3N3T2>(); for (int i = 0; i < bmc.vertexCount; i++) { V3N3T2 v = new V3N3T2(); v.Position = bmc.verts[i]; v.Normal = bmc.normals[i]; v.TexCoord = bmc.uvs[i]; verts.Add(v); } m.myBindings = V3N3T2.bindings(); vbo.setData(verts); } else { List <V3N3T2B4W4> verts = new List <V3N3T2B4W4>(); for (int i = 0; i < bmc.vertexCount; i++) { V3N3T2B4W4 v = new V3N3T2B4W4(); v.Position = bmc.verts[i]; v.Normal = bmc.normals[i]; v.TexCoord = bmc.uvs[i]; v.BoneId = bmc.boneIdx[i]; v.BoneWeight = bmc.boneWeights[i]; verts.Add(v); } m.myBindings = V3N3T2B4W4.bindings(); vbo.setData(verts); } m.myVbos.Add(vbo); }
public SkinnedModel loadFromFile(string filename) { Info.print("Loading Assimp model {0}", filename); if (File.Exists(filename) == false) { Warn.print("Cannot find file {0}", filename); return(null); } myRootPath = Path.GetDirectoryName(filename); try { myScene = theAssimpContext.ImportFile(filename, PostProcessSteps.Triangulate | PostProcessSteps.GenerateNormals); Node rootNode = myScene.RootNode; //load static meshes createMeshes(rootNode); loadBoneData(); loadAnimationData(); } catch { Warn.print("Failed to load model {0}", filename); return(null); } myModel.myBindings = V3N3T2B4W4.bindings(); VertexBufferObject vbo = new VertexBufferObject(BufferUsageHint.StaticDraw); vbo.setData(myVerts); myModel.myVbos.Add(vbo); myModel.myIbo.setData(index); //should probably build a bounding box myModel.size = (findMax() - findMin()).Length / 2.0f; return(myModel); }
public SkinnedModel loadFromFile(string filename) { Info.print("Loading IQM model {0}", filename); if (File.Exists(filename) == false) { Warn.print("Cannot find file {0}", filename); return(null); } V3N3T2B4W4[] vertexData; ushort[] triangleIndexes; IQMHeader myHeader; byte[] myTexts; List <String> myComments = new List <String>(); iqmvertexarray[] myVertArrays; iqmjoint[] myJoints; iqmpose[] myPoses; iqmanim[] myAnimataions; iqmbounds[] myBounds; iqmmesh[] meshData; ushort[] myFrameData; SkinnedModel sm = new SkinnedModel(); System.IO.FileStream stream = null; System.IO.BinaryReader reader = null; try { // Open the specified file as a stream and create a reader stream = new System.IO.FileStream(filename, FileMode.Open, FileAccess.Read); reader = new System.IO.BinaryReader(stream); myHeader.magic = reader.ReadChars(16); myHeader.version = reader.ReadUInt32(); if (myHeader.version != 2) { return(null); } myHeader.filesize = reader.ReadUInt32(); myHeader.flags = reader.ReadUInt32(); myHeader.num_text = reader.ReadUInt32(); myHeader.ofs_text = reader.ReadUInt32(); myHeader.num_meshes = reader.ReadUInt32(); myHeader.ofs_meshes = reader.ReadUInt32(); myHeader.num_vertexarrays = reader.ReadUInt32(); myHeader.num_vertexes = reader.ReadUInt32(); myHeader.ofs_vertexarrays = reader.ReadUInt32(); myHeader.num_triangles = reader.ReadUInt32(); myHeader.ofs_triangles = reader.ReadUInt32(); myHeader.ofs_adjacency = reader.ReadUInt32(); myHeader.num_joints = reader.ReadUInt32(); myHeader.ofs_joints = reader.ReadUInt32(); myHeader.num_poses = reader.ReadUInt32(); myHeader.ofs_poses = reader.ReadUInt32(); myHeader.num_anims = reader.ReadUInt32(); myHeader.ofs_anims = reader.ReadUInt32(); myHeader.num_frames = reader.ReadUInt32(); myHeader.num_framechannels = reader.ReadUInt32(); myHeader.ofs_frames = reader.ReadUInt32(); myHeader.ofs_bounds = reader.ReadUInt32(); myHeader.num_comment = reader.ReadUInt32(); myHeader.ofs_comment = reader.ReadUInt32(); myHeader.num_extensions = reader.ReadUInt32(); myHeader.ofs_extensions = reader.ReadUInt32(); boneCount = (int)myHeader.num_joints; //read text myTexts = new byte[myHeader.num_text]; stream.Seek(myHeader.ofs_text, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_text; i++) { myTexts[i] = reader.ReadByte(); } #region read geometry //create geometry fields for (int m = 0; m < myHeader.num_meshes; m++) { sm.myMeshes.Add(new Mesh()); } //read the mesh data meshData = new iqmmesh[myHeader.num_meshes]; stream.Seek(myHeader.ofs_meshes, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_meshes; i++) { iqmmesh temp = new iqmmesh(); UInt32 n = reader.ReadUInt32(); temp.name = readNullTerminated(myTexts, n); n = reader.ReadUInt32(); temp.material = readNullTerminated(myTexts, n); String fn = System.IO.Path.GetFileName(temp.material); fn = System.IO.Path.ChangeExtension(fn, ".png"); String dir = System.IO.Path.GetDirectoryName(filename); fn = System.IO.Path.Combine(dir, fn); TextureDescriptor td = new TextureDescriptor(fn); Texture tex = myResourceManager.getResource(td) as Texture; Material m = new Material(temp.material); m.addAttribute(new TextureAttribute("diffuseMap", tex)); m.myFeatures |= Material.Feature.Lighting; m.myFeatures |= Material.Feature.DiffuseMap; sm.myMeshes[i].material = m; temp.first_vertex = reader.ReadUInt32(); temp.num_vertexes = reader.ReadUInt32(); temp.first_triangle = reader.ReadUInt32(); temp.num_triangles = reader.ReadUInt32(); meshData[i] = temp; } //read vertex arrays myVertArrays = new iqmvertexarray[myHeader.num_vertexarrays]; stream.Seek(myHeader.ofs_vertexarrays, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_vertexarrays; i++) { iqmvertexarray temp = new iqmvertexarray(); temp.type = (VertexArrayType)reader.ReadUInt32(); temp.flags = reader.ReadUInt32(); temp.format = (VertexArrayFormat)reader.ReadUInt32(); temp.size = reader.ReadUInt32(); temp.offset = reader.ReadUInt32(); myVertArrays[i] = temp; } //read the vertex data vertexData = new V3N3T2B4W4[myHeader.num_vertexes]; for (int i = 0; i < myHeader.num_vertexarrays; i++) { iqmvertexarray va = myVertArrays[i]; switch (va.type) { case VertexArrayType.IQM_POSITION: { stream.Seek(va.offset, SeekOrigin.Begin); for (int j = 0; j < myHeader.num_vertexes; j++) { Vector3 temp = new Vector3(); temp.X = reader.ReadSingle(); temp.Y = reader.ReadSingle(); temp.Z = reader.ReadSingle(); vertexData[j].Position = temp; } break; } case VertexArrayType.IQM_TEXCOORD: { stream.Seek(va.offset, SeekOrigin.Begin); for (int j = 0; j < myHeader.num_vertexes; j++) { Vector2 temp = new Vector2(); temp.X = reader.ReadSingle(); temp.Y = reader.ReadSingle(); vertexData[j].TexCoord = temp; } break; } case VertexArrayType.IQM_NORMAL: { stream.Seek(va.offset, SeekOrigin.Begin); for (int j = 0; j < myHeader.num_vertexes; j++) { Vector3 temp = new Vector3(); temp.X = reader.ReadSingle(); temp.Y = reader.ReadSingle(); temp.Z = reader.ReadSingle(); vertexData[j].Normal = temp; } break; } case VertexArrayType.IQM_BLENDINDEXES: { stream.Seek(va.offset, SeekOrigin.Begin); for (int j = 0; j < myHeader.num_vertexes; j++) { Vector4 temp = new Vector4(); temp.X = (float)reader.ReadByte(); temp.Y = (float)reader.ReadByte(); temp.Z = (float)reader.ReadByte(); temp.W = (float)reader.ReadByte(); vertexData[j].BoneId = temp; } break; } case VertexArrayType.IQM_BLENDWEIGHTS: { stream.Seek(va.offset, SeekOrigin.Begin); for (int j = 0; j < myHeader.num_vertexes; j++) { Vector4 temp = new Vector4(); temp.X = ((float)reader.ReadByte()) / 255.0f; temp.Y = ((float)reader.ReadByte()) / 255.0f; temp.Z = ((float)reader.ReadByte()) / 255.0f; temp.W = ((float)reader.ReadByte()) / 255.0f; vertexData[j].BoneWeight = temp; } break; } } } //read triangles indexes triangleIndexes = new ushort[myHeader.num_triangles * 3]; stream.Seek(myHeader.ofs_triangles, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_triangles * 3; i++) { triangleIndexes[i] = (ushort)reader.ReadUInt32(); } #endregion #region read animation data //read joints myJoints = new iqmjoint[myHeader.num_joints]; stream.Seek(myHeader.ofs_joints, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_joints; i++) { iqmjoint temp = new iqmjoint(); UInt32 n = reader.ReadUInt32(); temp.name = readNullTerminated(myTexts, n); temp.parent = reader.ReadInt32(); temp.translate = new Vector3(); temp.translate.X = reader.ReadSingle(); temp.translate.Y = reader.ReadSingle(); temp.translate.Z = reader.ReadSingle(); temp.rotate = new Quaternion(); temp.rotate.X = reader.ReadSingle(); temp.rotate.Y = reader.ReadSingle(); temp.rotate.Z = reader.ReadSingle(); temp.rotate.W = reader.ReadSingle(); temp.rotate.Normalize(); temp.scale = new Vector3(); temp.scale.X = reader.ReadSingle(); temp.scale.Y = reader.ReadSingle(); temp.scale.Z = reader.ReadSingle(); myJoints[i] = temp; } //read poses myPoses = new iqmpose[myHeader.num_poses]; stream.Seek(myHeader.ofs_poses, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_poses; i++) { iqmpose temp = new iqmpose(); temp.parent = reader.ReadInt32(); temp.channelmask = reader.ReadUInt32(); temp.channeloffset = new float[10]; for (int j = 0; j < 10; j++) { temp.channeloffset[j] = reader.ReadSingle(); } temp.channelscale = new float[10]; for (int j = 0; j < 10; j++) { temp.channelscale[j] = reader.ReadSingle(); } myPoses[i] = temp; } //read animations myAnimataions = new iqmanim[myHeader.num_anims]; stream.Seek(myHeader.ofs_anims, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_anims; i++) { iqmanim temp = new iqmanim(); UInt32 n = reader.ReadUInt32(); temp.name = readNullTerminated(myTexts, n); temp.first_frame = reader.ReadUInt32(); temp.num_frames = reader.ReadUInt32(); temp.framerate = reader.ReadSingle(); temp.flags = reader.ReadUInt32(); myAnimataions[i] = temp; Animation a = new Animation(); a.name = temp.name; a.fps = temp.framerate; a.loop = true; sm.animations.Add(a.name, a); } //read frame data myFrameData = new ushort[myHeader.num_frames * myHeader.num_framechannels]; stream.Seek(myHeader.ofs_frames, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_frames * myHeader.num_framechannels; i++) { myFrameData[i] = reader.ReadUInt16(); } #endregion //read bounds myBounds = new iqmbounds[myHeader.num_frames]; stream.Seek(myHeader.ofs_bounds, SeekOrigin.Begin); for (int i = 0; i < myHeader.num_frames; i++) { iqmbounds temp = new iqmbounds(); temp.bbmins = new float[3]; temp.bbmaxs = new float[3]; temp.bbmins[0] = reader.ReadSingle(); temp.bbmins[1] = reader.ReadSingle(); temp.bbmins[2] = reader.ReadSingle(); temp.bbmaxs[0] = reader.ReadSingle(); temp.bbmaxs[1] = reader.ReadSingle(); temp.bbmaxs[2] = reader.ReadSingle(); temp.xyradius = reader.ReadSingle(); temp.radius = reader.ReadSingle(); if (i == 0) { sm.size = temp.radius; } } //read comments stream.Seek(myHeader.ofs_comment, SeekOrigin.Begin); int charRead = 0; while (charRead < myHeader.num_comment) { char c = reader.ReadChar(); charRead++; string s = ""; while (c != '\0') { s += c; c = reader.ReadChar(); charRead++; } myComments.Add(s); } //read extensions //TODO //setup the bone data Matrix4[] baseframe = new Matrix4[myHeader.num_joints]; Matrix4[] inversebaseframe = new Matrix4[myHeader.num_joints]; for (int i = 0; i < (int)myHeader.num_joints; i++) { iqmjoint joint = myJoints[i]; Matrix4 r, t, s; r = Matrix4.CreateFromQuaternion(joint.rotate); t = Matrix4.CreateTranslation(joint.translate); s = Matrix4.CreateScale(joint.scale); baseframe[i] = s * r * t; inversebaseframe[i] = baseframe[i].Inverted(); if (joint.parent >= 0) { baseframe[i] = baseframe[i] * baseframe[joint.parent]; inversebaseframe[i] = inversebaseframe[joint.parent] * inversebaseframe[i]; } Bone b = new Bone(); b.myName = myJoints[i].name; b.myParent = myJoints[i].parent; b.myWorldBindMatrix = baseframe[i]; sm.skeleton.myBones.Add(b); } Matrix4[] absMatrix = new Matrix4[myHeader.num_frames * myHeader.num_poses]; int count = 0; for (int i = 0; i < myHeader.num_frames; i++) { for (int j = 0; j < myHeader.num_poses; j++) { iqmpose p = myPoses[j]; Quaternion rotate = new Quaternion(); Vector3 translate = new Vector3(); Vector3 scale = new Vector3(); translate.X = p.channeloffset[0]; if ((p.channelmask & 0x01) != 0) { translate.X += myFrameData[count++] * p.channelscale[0]; } translate.Y = p.channeloffset[1]; if ((p.channelmask & 0x02) != 0) { translate.Y += myFrameData[count++] * p.channelscale[1]; } translate.Z = p.channeloffset[2]; if ((p.channelmask & 0x04) != 0) { translate.Z += myFrameData[count++] * p.channelscale[2]; } rotate.X = p.channeloffset[3]; if ((p.channelmask & 0x08) != 0) { rotate.X += myFrameData[count++] * p.channelscale[3]; } rotate.Y = p.channeloffset[4]; if ((p.channelmask & 0x10) != 0) { rotate.Y += myFrameData[count++] * p.channelscale[4]; } rotate.Z = p.channeloffset[5]; if ((p.channelmask & 0x20) != 0) { rotate.Z += myFrameData[count++] * p.channelscale[5]; } rotate.W = p.channeloffset[6]; if ((p.channelmask & 0x40) != 0) { rotate.W += myFrameData[count++] * p.channelscale[6]; } scale.X = p.channeloffset[7]; if ((p.channelmask & 0x80) != 0) { scale.X += myFrameData[count++] * p.channelscale[7]; } scale.Y = p.channeloffset[8]; if ((p.channelmask & 0x100) != 0) { scale.Y += myFrameData[count++] * p.channelscale[8]; } scale.Z = p.channeloffset[9]; if ((p.channelmask & 0x200) != 0) { scale.Z += myFrameData[count++] * p.channelscale[9]; } // Concatenate each pose with the inverse base pose to avoid doing this at animation time. // If the joint has a parent, then it needs to be pre-concatenated with its parent's base pose. // Thus it all negates at animation time like so: // (parentPose * parentInverseBasePose) * (parentBasePose * childPose * childInverseBasePose) => // parentPose * (parentInverseBasePose * parentBasePose) * childPose * childInverseBasePose => // parentPose * childPose * childInverseBasePose rotate.Normalize(); Matrix4 r, t, s; r = Matrix4.CreateFromQuaternion(rotate); t = Matrix4.CreateTranslation(translate); s = Matrix4.CreateScale(scale); Matrix4 pose = s * r * t; if (p.parent >= 0) { Matrix4 parent = baseframe[p.parent]; Matrix4 inv = inversebaseframe[j]; Matrix4 parentPose = absMatrix[i * myHeader.num_poses + p.parent]; absMatrix[i * myHeader.num_poses + j] = inv * pose * parent * parentPose; } else { Matrix4 inv = inversebaseframe[j]; absMatrix[i * myHeader.num_poses + j] = inv * pose; } } } Vector4[] boneData = new Vector4[myHeader.num_frames * myHeader.num_poses * 4]; int next = 0; for (int i = 0; i < myHeader.num_frames * myHeader.num_poses; i++) { boneData[next++] = absMatrix[i].Row0; boneData[next++] = absMatrix[i].Row1; boneData[next++] = absMatrix[i].Row2; boneData[next++] = absMatrix[i].Row3; } //setup the buffers sm.myBindings = V3N3T2B4W4.bindings(); VertexBufferObject vbo = new VertexBufferObject(BufferUsageHint.StaticDraw); vbo.setData(vertexData); sm.myVbos.Add(vbo); List <ushort> indexes = new List <ushort>(); int indexCount = 0; for (int m = 0; m < sm.myMeshes.Count; m++) { Mesh mesh = sm.myMeshes[m]; mesh.primativeType = PrimitiveType.Triangles; mesh.indexBase = indexCount; for (int t = 0; t < meshData[m].num_triangles; t++) { //swap the order the indicies since we want counter clockwise triangles instead of clockwise indexes.Add(triangleIndexes[meshData[m].first_triangle * 3 + (t * 3) + 0]); indexes.Add(triangleIndexes[meshData[m].first_triangle * 3 + (t * 3) + 2]); indexes.Add(triangleIndexes[meshData[m].first_triangle * 3 + (t * 3) + 1]); indexCount += 3; } mesh.indexCount = indexCount - mesh.indexBase; } sm.myIbo.setData(indexes); //upload the frame data sm.myFrames.setData(boneData); return(sm); } catch (Exception ex) { throw new Exception("Error while loading IQM model from definition file ( " + filename + " ).", ex); } finally { if (reader != null) { reader.Close(); } if (stream != null) { stream.Close(); stream.Dispose(); } } }
//public override void preparePerViewBegin(View v) { } //public override void preparePerView(Renderable r, View v) { } //public override void preparePerViewFinalize(View v) { } //public override void preparePerPassBegin(Pass p) { } //public override void preparePerPass(Renderable r, Pass p) { } public override void preparePerPass(Renderable r, Pass p) { SkinnedModelRenderable smr = r as SkinnedModelRenderable; SkinnedModelUniformData modelData = new SkinnedModelUniformData(); modelData.modelMatrix = smr.model.myInitialTransform * smr.modelMatrix; modelData.normalMatrix = (smr.model.myInitialTransform * smr.modelMatrix).ClearTranslation(); //modelData.inverseNormalMatrix = modelData.normalMatrix.Inverted(); modelData.activeLights = new Vector4(0, 1, 2, 3); modelData.boneCount = smr.model.skeleton.boneCount; myModelData.Add(modelData); //save the index for this model int modelDataIndex = myModelData.Count - 1; foreach (Mesh mesh in smr.model.myMeshes) { MaterialEffect effect = getEffect(p.technique, (UInt32)mesh.material.myFeatures); PipelineState pipeline = effect.createPipeline(mesh.material); RenderQueue <SkinnedModelInfo> rq = p.findRenderQueue(pipeline.id) as RenderQueue <SkinnedModelInfo>; if (rq == null) { rq = Renderer.device.createRenderQueue <SkinnedModelInfo>(effect.createPipeline(mesh.material)); rq.name = rq.myPipeline.shaderState.shaderProgram.name + "-" + (mesh.material.hasTransparency == true ? "transparent" : "opaque"); rq.myPipeline.vaoState.vao = new VertexArrayObject(); rq.myPipeline.vaoState.vao.bindVertexFormat(rq.myPipeline.shaderState.shaderProgram, V3N3T2B4W4.bindings()); rq.visualizer = this; p.registerQueue(rq); } SkinnedModelInfo info = rq.nextInfo(); effect.updateRenderState(mesh.material, info.renderState); float dist = (p.view.camera.position - r.position).Length; info.distToCamera = dist; info.indexCount = mesh.indexCount; info.indexOffset = mesh.indexBase; info.renderState.setUniform(new UniformData(0, Uniform.UniformType.Int, modelDataIndex)); info.renderState.setStorageBuffer(myModelBuffer.id, 0); info.renderState.setUniformBuffer(smr.mySkinningBuffer.id, 3); info.renderState.setVertexBuffer(smr.model.myVbos[0].id, 0, 0, V3N3T2B4W4.stride); info.renderState.setIndexBuffer(smr.model.myIbo.id); info.sortId = getSortId(info); } }
SkinnedModel bufferData() { SkinnedModel sm = new SkinnedModel(); //just the first group at this time int vertCount = 0; for (int g = 0; g < msGroups.Length; g++) { vertCount += msGroups[g].numTriangles * 3; } V3N3T2B4W4[] verts = new V3N3T2B4W4[vertCount]; ushort[] indexes = new ushort[vertCount]; Vector4[] boneData = new Vector4[msJointCount * msTotalFrames * 4]; //store an absolute matrix for each bone for each frame int next = 0; for (int g = 0; g < msGroups.Length; g++) { Mesh m = new Mesh(); m.indexBase = next; for (int t = 0; t < msGroups[g].numTriangles; t++) { MilkshapeTriangle tri = msTris[msGroups[g].triangleIndexs[t]]; for (int v = 0; v < 3; v++) { verts[next].Position = msVerts[tri.vertexIndex[v]].vertex; verts[next].Normal = tri.vertexNormals[v]; verts[next].TexCoord = tri.uvs[v]; //this format only uses 1 bone per vertex, but my vertex format allows for up to 4 verts[next].BoneId.X = msVerts[tri.vertexIndex[v]].boneId; verts[next].BoneId.Y = -1; verts[next].BoneId.Z = -1; verts[next].BoneId.W = -1; verts[next].BoneWeight.X = 1.0f; //only 1 bone influences the mesh verts[next].BoneWeight.Y = 0.0f; verts[next].BoneWeight.Z = 0.0f; verts[next].BoneWeight.W = 0.0f; indexes[next] = (ushort)next; next++; } } m.indexCount = msGroups[g].numTriangles * 3; m.material = myMaterials[msGroups[g].materialIndex]; sm.myMeshes.Add(m); } //prep the bone data next = 0; for (int f = 0; f < msTotalFrames; f++) { //get the joints as an absolute matrix Matrix4[] absMat = getJoints(f); for (int j = 0; j < msJointCount; j++) { boneData[next++] = absMat[j].Row0; boneData[next++] = absMat[j].Row1; boneData[next++] = absMat[j].Row2; boneData[next++] = absMat[j].Row3; } } sm.myBindings = V3N3T2B4W4.bindings(); VertexBufferObject vbo = new VertexBufferObject(BufferUsageHint.StaticDraw); vbo.setData(verts); sm.myVbos.Add(vbo); sm.myIbo.setData(indexes); sm.myFrames.setData(boneData); return(sm); }