/// <summary> /// Load the bone information for each vertex /// </summary> /// <param name="assimpMesh"> mesh that contains the information to be processed </param> /// <param name="mesh"> customized mesh that stores information for later use </param> protected void LoadBoneWeights(Mesh assimpMesh, ClientMesh mesh) { // create a new data structures to store the bones mesh.VertexBoneDatas = new List <VertexBoneData>(mesh.CountVertices); for (int i = 0; i < mesh.CountVertices; i++) { mesh.VertexBoneDatas.Add(new VertexBoneData()); } // copy bone weights from the meshes for each vertex for (int boneIndex = 0; boneIndex < assimpMesh.BoneCount; boneIndex++) { Bone currBone = assimpMesh.Bones[boneIndex]; for (int weighti = 0; weighti < currBone.VertexWeightCount; weighti++) { VertexWeight vertexWeight = currBone.VertexWeights[weighti]; mesh.VertexBoneDatas[vertexWeight.VertexID].AddBoneData(_allBoneMappings[currBone.Name], vertexWeight.Weight); } } // create new datastream so that we can stream them to the VBOs mesh.boneIDSize = sizeof(int) * VertexBoneData.MAX_BONES_PER_VERTEX * mesh.CountVertices; mesh.boneWeightSize = sizeof(float) * VertexBoneData.MAX_BONES_PER_VERTEX * mesh.CountVertices; mesh.DSBoneIDs = new DataStream(mesh.boneIDSize, true, true); mesh.DSBoneWeights = new DataStream(mesh.boneWeightSize, true, true); // for each vertex, write the vertex buffer datastreams for (int i = 0; i < mesh.CountVertices; i++) { // normalize bone weights //mesh.VertexBoneDatas[i].NormalizeBoneData(); // write the data into datastreams for (int bonei = 0; bonei < VertexBoneData.MAX_BONES_PER_VERTEX; bonei++) { mesh.DSBoneIDs.Write(mesh.VertexBoneDatas[i].BoneIndices[bonei]); mesh.DSBoneWeights.Write(mesh.VertexBoneDatas[i].BoneWeights[bonei]); } } mesh.DSBoneWeights.Position = 0; mesh.DSBoneIDs.Position = 0; // create the datastreams mesh.VBOBoneIDs = new Buffer(GraphicsRenderer.Device, mesh.DSBoneIDs, mesh.boneIDSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.VBOBoneWeights = new Buffer(GraphicsRenderer.Device, mesh.DSBoneWeights, mesh.boneWeightSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); }
/// <summary> /// Draw a model by using the modelmatrix it is assigned to /// </summary> /// <param name="modelMatrix"> describes how the object is viewed in the world space </param> /// <param name="shader"> the shader that is used to draw the geometry </param> public void Draw(Matrix modelMatrix, Shader shader) { shader.ShaderEffect.GetVariableByName("gWorld").AsMatrix().SetMatrix(modelMatrix); shader.ShaderEffect.GetVariableByName("gView").AsMatrix() .SetMatrix(GraphicsManager.ActiveCamera.m_ViewMatrix); shader.ShaderEffect.GetVariableByName("gProj").AsMatrix().SetMatrix(GraphicsRenderer.ProjectionMatrix); GraphicsManager.ActiveLightSystem.UpdateShader(shader, modelMatrix); shader.ShaderEffect.GetVariableByName("CamPosObj").AsVector().Set( Vector4.Transform(new Vector4(GraphicsManager.ActiveCamera.CameraPosition, 1.0f), Matrix.Invert(modelMatrix))); if (CurrentAnimationIndex != -1) { //shader.ShaderEffect.GetVariableByName("boneTransforms") // .SetRawValue(_boneTransformStream, MAX_BONES_PER_GEO * sizeof(float) * 16); shader.ShaderEffect.GetVariableByName("boneTransforms").AsMatrix().SetMatrixArray(_boneTransformList.ToArray()); } for (int i = 0; i < scene.MeshCount; i++) { ClientMesh mesh = allMeshes[i]; // pass vertices, normals, and indices into the shader GraphicsRenderer.Device.ImmediateContext.InputAssembler.SetVertexBuffers(VertexLoc, new VertexBufferBinding(mesh.VBOPositions, Vector3.SizeInBytes, 0)); GraphicsRenderer.Device.ImmediateContext.InputAssembler.SetVertexBuffers(NormalLoc, new VertexBufferBinding(mesh.VBONormals, Vector3.SizeInBytes, 0)); GraphicsRenderer.Device.ImmediateContext.InputAssembler.SetIndexBuffer(mesh.EBO, Format.R32_UInt, 0); // pass texture coordinates into the shader if applicable if (mesh.Materials.texCount > 0) { // note that the raw parsed tex coords are in vec3, we just need the first 2 elements of the vector GraphicsRenderer.Device.ImmediateContext.InputAssembler.SetVertexBuffers(TexLoc, new VertexBufferBinding(mesh.VBOTexCoords, Vector3.SizeInBytes, 0)); } // pass bone IDs and weights if applicable shader.ShaderEffect.GetVariableByName("animationIndex").AsScalar().Set(CurrentAnimationIndex); if (CurrentAnimationIndex != -1) { shader.ShaderEffect.GetVariableByName("meshTransform").AsMatrix().SetMatrix(_allBones[0].GlobalAnimatedTransform); GraphicsRenderer.Device.ImmediateContext.InputAssembler.SetVertexBuffers(BoneIdLoc, new VertexBufferBinding(mesh.VBOBoneIDs, sizeof(int) * VertexBoneData.MAX_BONES_PER_VERTEX, 0)); GraphicsRenderer.Device.ImmediateContext.InputAssembler.SetVertexBuffers(BoneWeightLoc, new VertexBufferBinding(mesh.VBOBoneWeights, sizeof(float) * VertexBoneData.MAX_BONES_PER_VERTEX, 0)); } // pass texture resource into the shader if applicable if (mesh.Materials.texSRV != null) { shader.ShaderEffect.GetVariableByName("tex_diffuse").AsResource().SetResource(mesh.Materials.texSRV); } // pass material properties into the shader if (!useAltColor || mesh.Materials.texCount > 0) { shader.ShaderEffect.GetVariableByName("Diffuse").AsVector().Set(mesh.Materials.diffuse); } else { shader.ShaderEffect.GetVariableByName("Diffuse").AsVector().Set(mesh.Materials.diffuse.ScalarMultiply(altDiffuseColor)); } shader.ShaderEffect.GetVariableByName("Specular").AsVector().Set(mesh.Materials.specular); shader.ShaderEffect.GetVariableByName("Ambient").AsVector().Set(mesh.Materials.ambient); shader.ShaderEffect.GetVariableByName("Emissive").AsVector().Set(mesh.Materials.emissive); shader.ShaderEffect.GetVariableByName("Shininess").AsScalar().Set(mesh.Materials.shininess); shader.ShaderEffect.GetVariableByName("Opacity").AsScalar().Set(mesh.Materials.opacity); shader.ShaderEffect.GetVariableByName("texCount").AsScalar().Set(mesh.Materials.texCount); // Draw the object using the indices shader.ShaderPass.Apply(GraphicsRenderer.Device.ImmediateContext); GraphicsRenderer.Device.ImmediateContext.DrawIndexed(mesh.faceSize / sizeof(int), 0, 0); } }
/// <summary> /// Create a new geometry given filename /// </summary> /// <param name="fileName"> filepath to the 3D model file </param> public Geometry(string fileName, bool enableRigging = false) { RiggingEnabled = enableRigging; sourceFileName = fileName; //Create new importer. importer = new AssimpContext(); //import the file scene = importer.ImportFile(fileName, PostProcessSteps.CalculateTangentSpace | PostProcessSteps.Triangulate | PostProcessSteps.JoinIdenticalVertices | PostProcessSteps.SortByPrimitiveType | PostProcessSteps.GenerateUVCoords | PostProcessSteps.FlipUVs | PostProcessSteps.LimitBoneWeights | PostProcessSteps.ValidateDataStructure); //make sure scene not null if (scene == null) { throw new FileNotFoundException(); } //loop through sizes and count them. allMeshes = new List <ClientMesh>(scene.MeshCount); //loop through and store sizes for (int idx = 0; idx < scene.MeshCount; idx++) { ClientMesh mesh = new ClientMesh(); allMeshes.Add(mesh); mesh.CountVertices = scene.Meshes[idx].VertexCount; mesh.vertSize = scene.Meshes[idx].VertexCount * Vector3.SizeInBytes; mesh.normSize = scene.Meshes[idx].Normals.Count * Vector3.SizeInBytes; mesh.faceSize = scene.Meshes[idx].FaceCount * scene.Meshes[idx].Faces[0].IndexCount * sizeof(int); if (scene.Meshes[idx].HasTextureCoords(0)) { mesh.texSize = scene.Meshes[idx].TextureCoordinateChannels[0].Count * Vector3.SizeInBytes; } } diffuseTextureSRV = new Dictionary <string, ShaderResourceView>(); // do all the processing that rigging is required to have if (enableRigging) { _allBones = new List <ClientBone>(); _allBoneMappings = new Dictionary <string, int>(); _allBoneLookup = new Dictionary <string, ClientBone>(); // set the animation related lookup tables AnimationIndices = new Dictionary <string, int>(); _animationNodes = new List <Dictionary <string, ClientAnimationNode> >(scene.AnimationCount); for (int i = 0; i < scene.AnimationCount; i++) { AnimationIndices[scene.Animations[i].Name] = i; _animationNodes.Add(new Dictionary <string, ClientAnimationNode>()); for (int j = 0; j < scene.Animations[i].NodeAnimationChannelCount; j++) { NodeAnimationChannel ch = scene.Animations[i].NodeAnimationChannels[j]; ClientAnimationNode myNode = new ClientAnimationNode(ch.NodeName); _animationNodes[i][ch.NodeName] = myNode; myNode.Translations = new List <Vector3>(); myNode.TranslationTime = new List <double>(); myNode.Rotations = new List <Quaternion>(); myNode.RotationTime = new List <double>(); myNode.Scalings = new List <Vector3>(); myNode.ScalingTime = new List <double>(); // copy over all the necessary information in the animation channels for (int k = 0; k < ch.PositionKeyCount; k++) { myNode.Translations.Add(ch.PositionKeys[k].Value.ToVector3()); myNode.TranslationTime.Add(ch.PositionKeys[k].Time); } for (int k = 0; k < ch.RotationKeyCount; k++) { myNode.Rotations.Add(ch.RotationKeys[k].Value.ToQuaternion()); myNode.RotationTime.Add(ch.RotationKeys[k].Time); } for (int k = 0; k < ch.ScalingKeyCount; k++) { myNode.Scalings.Add(ch.ScalingKeys[k].Value.ToVector3()); myNode.ScalingTime.Add(ch.ScalingKeys[k].Time); } } } // create and store the big scene tree _rootBone = CreateBoneTree(scene.RootNode, null); // set each bone offset foreach (var sceneMesh in scene.Meshes) { foreach (var rawBone in sceneMesh.Bones) { ClientBone found; if (!_allBoneLookup.TryGetValue(rawBone.Name, out found)) { Console.WriteLine("Cannot find bone: " + rawBone.Name); continue; } found.BoneOffset = rawBone.OffsetMatrix.ToMatrix(); _allBones.Add(found); _allBoneMappings[found.BoneName] = _allBones.IndexOf(found); } } // for bones not inside the meshes...? jasdkl;fja;lskdjkfl foreach (var boneName in _allBoneLookup.Keys.Where(b => _allBones.All(b1 => b1.BoneName != b) && b.StartsWith("Bone"))) { _allBoneLookup[boneName].BoneOffset = _allBoneLookup[boneName].Parent.BoneOffset.Clone(); _allBones.Add(_allBoneLookup[boneName]); _allBoneMappings[boneName] = _allBones.IndexOf(_allBoneLookup[boneName]); } // load the bone weights for (int idx = 0; idx < scene.MeshCount; idx++) { LoadBoneWeights(scene.Meshes[idx], allMeshes[idx]); } //_boneTransformStream = new DataStream(MAX_BONES_PER_GEO * sizeof(float) * 16, true, true); _boneTransformList = new List <Matrix>(MAX_BONES_PER_GEO); for (int i = 0; i < MAX_BONES_PER_GEO; i++) { _boneTransformList.Add(Matrix.Identity); } } // main loading loop; copy cover the scene content into the datastreams and then to the buffers for (int idx = 0; idx < scene.MeshCount; idx++) { ClientMesh mesh = allMeshes[idx]; //create new datastreams. mesh.Vertices = new DataStream(mesh.vertSize, true, true); mesh.Normals = new DataStream(mesh.normSize, true, true); mesh.Faces = new DataStream(mesh.faceSize, true, true); // create a new material mesh.Materials = new ClientMaterial(); //min and max bounds var min = new Vector3(float.MaxValue); var max = new Vector3(float.MinValue); // copy the buffers scene.Meshes[idx].Vertices.ForEach(vertex => { mesh.Vertices.Write(vertex.ToVector3()); //keep track of min and max for obj boundaries. min = Vector3.Minimize(min, vertex.ToVector3()); max = Vector3.Maximize(max, vertex.ToVector3()); }); BoundingBoxes.Add(new BoundingBox(min, max)); scene.Meshes[idx].Normals.ForEach(normal => { mesh.Normals.Write(normal.ToVector3()); }); scene.Meshes[idx].Faces.ForEach(face => { mesh.Faces.WriteRange(face.Indices.ToArray()); }); // check if the mesh has texture coordinates if (scene.Meshes[idx].HasTextureCoords(0)) { mesh.TexCoords = new DataStream(mesh.texSize, true, true); scene.Meshes[idx].TextureCoordinateChannels[0].ForEach(texture => { mesh.TexCoords.Write(texture); }); mesh.TexCoords.Position = 0; } // Parse material properties ApplyMaterial(scene.Materials[scene.Meshes[idx].MaterialIndex], mesh.Materials); // reset datastream positions mesh.Vertices.Position = 0; mesh.Normals.Position = 0; mesh.Faces.Position = 0; //create vertex vbo and faces ebo. mesh.VBOPositions = new Buffer(GraphicsRenderer.Device, mesh.Vertices, mesh.vertSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.VBONormals = new Buffer(GraphicsRenderer.Device, mesh.Normals, mesh.normSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); if (scene.Meshes[idx].HasTextureCoords(0)) { mesh.VBOTexCoords = new Buffer(GraphicsRenderer.Device, mesh.TexCoords, mesh.texSize, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); } // buffer creation flags var ibd = new BufferDescription( mesh.faceSize, ResourceUsage.Immutable, BindFlags.IndexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0); mesh.EBO = new Buffer(GraphicsRenderer.Device, mesh.Faces, ibd); } _inverseGlobalTransform = Matrix.Invert(scene.RootNode.Transform.ToMatrix()); }