/// <summary> /// Crear un modelo adjunto a un hueso /// </summary> /// <param name="model">Modelo a adjuntar</param> /// <param name="bone">Hueso al cual adjuntarse</param> /// <param name="offset">Offset desde el cual el modelo sigue al hueso</param> public TgcSkeletalBoneAttach(TgcMesh mesh, TgcSkeletalBone bone, Matrix offset) { this.bone = bone; this.mesh = mesh; this.offset = offset; updateValues(); }
/// <summary> /// Actualizar los vertices de la malla segun las posiciones del los huesos del esqueleto /// </summary> protected void updateMeshVertices() { //Precalcular la multiplicación para llevar a un vertice a Bone-Space y luego transformarlo segun el hueso //Estas matrices se envian luego al Vertex Shader para hacer skinning en GPU for (int i = 0; i < bones.Length; i++) { TgcSkeletalBone bone = bones[i]; boneSpaceFinalTransforms[i] = bone.MatInversePose * bone.MatFinal; } }
/// <summary> /// Prepara una nueva animacion para ser ejecutada /// </summary> protected void initAnimationSettings(string animationName, bool playLoop, float userFrameRate) { isAnimating = true; currentAnimation = animations[animationName]; this.playLoop = playLoop; currentTime = 0; currentFrame = 0; //Cambiar BoundingBox boundingBox = currentAnimation.BoundingBox; updateBoundingBox(); //Si el usuario no especifico un FrameRate, tomar el default de la animacion if (userFrameRate == -1f) { frameRate = (float)currentAnimation.FrameRate; } else { frameRate = userFrameRate; } //La duracion de la animacion. animationTimeLenght = ((float)currentAnimation.FramesCount - 1) / frameRate; //Configurar postura inicial de los huesos for (int i = 0; i < bones.Length; i++) { TgcSkeletalBone bone = bones[i]; if (!currentAnimation.hasFrames(i)) { throw new Exception("El hueso " + bone.Name + " no posee KeyFrames"); } //Determinar matriz local inicial TgcSkeletalAnimationFrame firstFrame = currentAnimation.BoneFrames[i][0]; bone.MatLocal = Matrix.RotationQuaternion(firstFrame.Rotation) * Matrix.Translation(firstFrame.Position); //Multiplicar por matriz del padre, si tiene if (bone.ParentBone != null) { bone.MatFinal = bone.MatLocal * bone.ParentBone.MatFinal; } else { bone.MatFinal = bone.MatLocal; } } //Ajustar vertices a posicion inicial del esqueleto updateMeshVertices(); }
/// <summary> /// Actualiza la posicion de cada hueso del esqueleto segun sus KeyFrames de la animacion /// </summary> protected void updateSkeleton() { for (int i = 0; i < bones.Length; i++) { TgcSkeletalBone bone = bones[i]; //Tomar el frame actual para este hueso List <TgcSkeletalAnimationFrame> boneFrames = currentAnimation.BoneFrames[i]; //Solo hay un frame, no hacer nada, ya se hizo en el init de la animacion if (boneFrames.Count == 1) { continue; } //Obtener cuadro actual segun el tiempo transcurrido float currentFrameF = currentTime * frameRate; //Ve a que KeyFrame le corresponde int keyFrameIdx = getCurrentFrameBone(boneFrames, currentFrameF); this.currentFrame = keyFrameIdx; //Armar un intervalo entre el proximo KeyFrame y el anterior TgcSkeletalAnimationFrame frame1 = boneFrames[keyFrameIdx - 1]; TgcSkeletalAnimationFrame frame2 = boneFrames[keyFrameIdx]; //Calcular la cantidad que hay interpolar en base al la diferencia entre cuadros float framesDiff = frame2.Frame - frame1.Frame; float interpolationValue = (currentFrameF - frame1.Frame) / framesDiff; //Interpolar traslacion Vector3 frameTranslation = (frame2.Position - frame1.Position) * interpolationValue + frame1.Position; //Interpolar rotacion con SLERP Quaternion quatFrameRotation = Quaternion.Slerp(frame1.Rotation, frame2.Rotation, interpolationValue); //Unir ambas transformaciones de este frame Matrix frameMatrix = Matrix.RotationQuaternion(quatFrameRotation) * Matrix.Translation(frameTranslation); //Multiplicar por la matriz del padre, si tiene if (bone.ParentBone != null) { bone.MatFinal = frameMatrix * bone.ParentBone.MatFinal; } else { bone.MatFinal = frameMatrix; } } }
/// <summary> /// Cargar Weights de vertices /// </summary> /// <param name="meshData"></param> /// <returns></returns> private TgcSkeletalVertexWeight[] loadVerticesWeights(TgcSkeletalMeshData meshData, TgcSkeletalBone[] bones) { //Crear un array de Weights para cada uno de los vertices de la malla TgcSkeletalVertexWeight[] weights = new TgcSkeletalVertexWeight[meshData.verticesCoordinates.Length / 3]; float[] vertexWeightTotals = new float[weights.Length]; for (int i = 0; i < weights.Length; i++) { weights[i] = new TgcSkeletalVertexWeight(); vertexWeightTotals[i] = 0; } //Cargar los weights de cada vertice int weightsCount = meshData.verticesWeights.Length / 3; for (int i = 0; i < weightsCount; i++) { int vertexIdx = (int)meshData.verticesWeights[i * 3]; int boneIdx = (int)meshData.verticesWeights[i * 3 + 1]; float weightVal = meshData.verticesWeights[i * 3 + 2]; TgcSkeletalBone bone = bones[boneIdx]; TgcSkeletalVertexWeight.BoneWeight weight = new TgcSkeletalVertexWeight.BoneWeight(bone, weightVal); weights[vertexIdx].Weights.Add(weight); //acumular total de weight para ese vertice, para luego poder normalizar vertexWeightTotals[vertexIdx] += weightVal; } //Normalizar weights de cada vertice for (int i = 0; i < weights.Length; i++) { TgcSkeletalVertexWeight vertexWeight = weights[i]; float vTotal = vertexWeightTotals[i]; //Normalizar cada valor segun el total acumulado en el vertice foreach (TgcSkeletalVertexWeight.BoneWeight w in vertexWeight.Weights) { w.Weight = w.Weight / vTotal; } } return(weights); }
/// <summary> /// Configuracion inicial del esquleto /// </summary> protected void setupSkeleton() { //Actualizar jerarquia for (int i = 0; i < bones.Length; i++) { TgcSkeletalBone bone = bones[i]; //Es hijo o padre if (bone.ParentBone == null) { bone.MatFinal = bone.MatLocal; } else { //Multiplicar por la matriz del padre bone.MatFinal = bone.MatLocal * bone.ParentBone.MatFinal; } //Almacenar la inversa de la posicion original del hueso, para la referencia inicial de los vertices bone.MatInversePose = Matrix.Invert(bone.MatFinal); } }
/// <summary> /// Crea mallas a modo Debug para visualizar la configuración del esqueleto /// </summary> public void buildSkletonMesh() { //Crear array para dibujar los huesos y joints Color jointsColor = Color.Violet; Color bonesColor = Color.Yellow; Vector3 jointsSize = new Vector3(2, 2, 2); Vector3 ceroVec = new Vector3(0, 0, 0); skeletonRenderJoints = new TgcBox[bones.Length]; skeletonRenderBones = new TgcLine[bones.Length]; int boneColor = Color.Yellow.ToArgb(); //Actualizar jerarquia for (int i = 0; i < bones.Length; i++) { TgcSkeletalBone bone = bones[i]; //Es hijo o padre if (bone.ParentBone == null) { skeletonRenderBones[i] = null; } else { //Crear linea de hueso para renderziar esqueleto TgcLine boneLine = new TgcLine(); boneLine.PStart = TgcVectorUtils.transform(ceroVec, bone.MatFinal); boneLine.PEnd = TgcVectorUtils.transform(ceroVec, bone.ParentBone.MatFinal); boneLine.Color = bonesColor; skeletonRenderBones[i] = boneLine; } //Crear malla de Joint para renderizar el esqueleto TgcBox jointBox = TgcBox.fromSize(jointsSize, jointsColor); jointBox.AutoTransformEnable = false; skeletonRenderJoints[i] = jointBox; } }
/// <summary> /// Crea una nueva malla. /// </summary> /// <param name="mesh">Mesh de DirectX</param> /// <param name="renderType">Formato de renderizado de la malla</param> /// <param name="bones">Datos de los huesos</param> public TgcSkeletalMesh(Mesh mesh, string name, MeshRenderType renderType, TgcSkeletalBone[] bones) { this.initData(mesh, name, renderType, bones); }
public BoneWeight(TgcSkeletalBone bone, float weight) { this.bone = bone; this.weight = weight; }
public TgcSkeletalMesh createNewMesh(Mesh d3dMesh, string meshName, TgcSkeletalMesh.MeshRenderType renderType, TgcSkeletalBone[] bones) { return new TgcSkeletalMesh(d3dMesh, meshName, renderType, bones); }
/// <summary> /// Cargar Weights de vertices /// </summary> /// <param name="meshData"></param> /// <returns></returns> private TgcSkeletalVertexWeight[] loadVerticesWeights(TgcSkeletalMeshData meshData, TgcSkeletalBone[] bones) { int maxWeights = 4; TgcSkeletalVertexWeight.BoneWeight.GreaterComparer weightComparer = new TgcSkeletalVertexWeight.BoneWeight.GreaterComparer(); //Crear un array de Weights para cada uno de los vertices de la malla TgcSkeletalVertexWeight[] weights = new TgcSkeletalVertexWeight[meshData.verticesCoordinates.Length / 3]; for (int i = 0; i < weights.Length; i++) { weights[i] = new TgcSkeletalVertexWeight(); } //Cargar los weights de cada vertice int weightsCount = meshData.verticesWeights.Length / 3; for (int i = 0; i < weightsCount; i++) { int vertexIdx = (int)meshData.verticesWeights[i * 3]; int boneIdx = (int)meshData.verticesWeights[i * 3 + 1]; float weightVal = meshData.verticesWeights[i * 3 + 2]; TgcSkeletalBone bone = bones[boneIdx]; TgcSkeletalVertexWeight.BoneWeight weight = new TgcSkeletalVertexWeight.BoneWeight(bone, weightVal); weights[vertexIdx].Weights.Add(weight); } //Normalizar weights de cada vertice for (int i = 0; i < weights.Length; i++) { TgcSkeletalVertexWeight vertexWeight = weights[i]; //Se soportan hasta 4 weights por vertice. Si hay mas se quitan y se reparten las influencias entre el resto de los huesos if (vertexWeight.Weights.Count > maxWeights) { //Ordenar por weight de menor a mayor y luego revertir, para que quede de mayor a menor peso vertexWeight.Weights.Sort(weightComparer); vertexWeight.Weights.Reverse(); //Quitar los ultimos los weight que superan 4 while (vertexWeight.Weights.Count > maxWeights) { vertexWeight.Weights.RemoveAt(vertexWeight.Weights.Count - 1); } } //Sumar el total de todos los weights de este vertice float weightTotal = 0; foreach (TgcSkeletalVertexWeight.BoneWeight w in vertexWeight.Weights) { weightTotal += w.Weight; } //Normalizar cada valor segun el total acumulado en el vertice foreach (TgcSkeletalVertexWeight.BoneWeight w in vertexWeight.Weights) { w.Weight = w.Weight / weightTotal; } } return weights; }
/// <summary> /// Cargar estructura de esqueleto /// </summary> private TgcSkeletalBone[] loadSkeleton(TgcSkeletalMeshData meshData) { //Crear huesos TgcSkeletalBone[] bones = new TgcSkeletalBone[meshData.bones.Length]; for (int i = 0; i < bones.Length; i++) { TgcSkeletalBoneData boneData = meshData.bones[i]; TgcSkeletalBone bone = new TgcSkeletalBone(i, boneData.name, new Vector3(boneData.startPosition[0], boneData.startPosition[1], boneData.startPosition[2]), new Quaternion(boneData.startRotation[0], boneData.startRotation[1], boneData.startRotation[2], boneData.startRotation[3]) ); bones[i] = bone; } //Cargar padres en huesos for (int i = 0; i < bones.Length; i++) { TgcSkeletalBoneData boneData = meshData.bones[i]; if (boneData.parentId == -1) { bones[i].ParentBone = null; } else { bones[i].ParentBone = bones[boneData.parentId]; } } return bones; }
/// <summary> /// Cargar Weights de vertices /// </summary> /// <param name="meshData"></param> /// <returns></returns> private TgcSkeletalVertexWeight[] loadVerticesWeights(TgcSkeletalMeshData meshData, TgcSkeletalBone[] bones) { //Crear un array de Weights para cada uno de los vertices de la malla TgcSkeletalVertexWeight[] weights = new TgcSkeletalVertexWeight[meshData.verticesCoordinates.Length / 3]; float[] vertexWeightTotals = new float[weights.Length]; for (int i = 0; i < weights.Length; i++) { weights[i] = new TgcSkeletalVertexWeight(); vertexWeightTotals[i] = 0; } //Cargar los weights de cada vertice int weightsCount = meshData.verticesWeights.Length / 3; for (int i = 0; i < weightsCount; i++) { int vertexIdx = (int)meshData.verticesWeights[i * 3]; int boneIdx = (int)meshData.verticesWeights[i * 3 + 1]; float weightVal = meshData.verticesWeights[i * 3 + 2]; TgcSkeletalBone bone = bones[boneIdx]; TgcSkeletalVertexWeight.BoneWeight weight = new TgcSkeletalVertexWeight.BoneWeight(bone, weightVal); weights[vertexIdx].Weights.Add(weight); //acumular total de weight para ese vertice, para luego poder normalizar vertexWeightTotals[vertexIdx] += weightVal; } //Normalizar weights de cada vertice for (int i = 0; i < weights.Length; i++) { TgcSkeletalVertexWeight vertexWeight = weights[i]; float vTotal = vertexWeightTotals[i]; //Normalizar cada valor segun el total acumulado en el vertice foreach (TgcSkeletalVertexWeight.BoneWeight w in vertexWeight.Weights) { w.Weight = w.Weight / vTotal; } } return weights; }
/// <summary> /// Cargar datos iniciales /// </summary> protected void initData(Mesh mesh, string name, MeshRenderType renderType, TgcSkeletalBone[] bones) { this.d3dMesh = mesh; this.name = name; this.renderType = renderType; this.enabled = false; this.autoUpdateBoundingBox = true; this.bones = bones; this.attachments = new List<TgcSkeletalBoneAttach>(); this.meshInstances = new List<TgcSkeletalMesh>(); this.renderSkeleton = false; this.alphaBlendEnable = false; //variables de movimiento this.autoTransformEnable = true; this.translation = new Vector3(0f, 0f, 0f); this.rotation = new Vector3(0f, 0f, 0f); this.scale = new Vector3(1f, 1f, 1f); this.transform = Matrix.Identity; //variables de animacion this.isAnimating = false; this.currentAnimation = null; this.playLoop = false; this.frameRate = 0f; this.currentTime = 0f; this.animationTimeLenght = 0f; this.currentFrame = 0; this.animations = new Dictionary<string, TgcSkeletalAnimation>(); //Matrices de huesos this.boneSpaceFinalTransforms = new Matrix[MAX_BONE_COUNT]; //Shader vertexDeclaration = new VertexDeclaration(mesh.Device, mesh.Declaration); this.effect = GuiController.Instance.Shaders.TgcSkeletalMeshShader; this.technique = GuiController.Instance.Shaders.getTgcSkeletalMeshTechnique(this.renderType); //acomodar huesos setupSkeleton(); }
/// <summary> /// Crea una nueva malla. /// </summary> /// <param name="mesh">Mesh de DirectX</param> /// <param name="renderType">Formato de renderizado de la malla</param> /// <param name="origData">Datos originales de la malla que hay almacenar</param> /// <param name="bones">Datos de los huesos</param> /// <param name="verticesWeights">Datos de los Weights para cada vertice</param> public TgcSkeletalMesh(Mesh mesh, string name, MeshRenderType renderType, OriginalData origData, TgcSkeletalBone[] bones, TgcSkeletalVertexWeight[] verticesWeights) { this.initData(mesh, name, renderType, origData, bones, verticesWeights); }
/// <summary> /// Cargar datos iniciales /// </summary> protected void initData(Mesh mesh, string name, MeshRenderType renderType, OriginalData originalData, TgcSkeletalBone[] bones, TgcSkeletalVertexWeight[] verticesWeights) { this.d3dMesh = mesh; this.name = name; this.renderType = renderType; this.originalData = originalData; this.enabled = false; this.autoUpdateBoundingBox = true; this.bones = bones; this.verticesWeights = verticesWeights; this.attachments = new List<TgcSkeletalBoneAttach>(); this.meshInstances = new List<TgcSkeletalMesh>(); this.renderSkeleton = false; this.alphaBlendEnable = false; //Crear vertexDeclaration vertexDeclaration = new VertexDeclaration(mesh.Device, mesh.Declaration); //variables de movimiento this.autoTransformEnable = true; this.translation = new Vector3(0f, 0f, 0f); this.rotation = new Vector3(0f, 0f, 0f); this.scale = new Vector3(1f, 1f, 1f); this.transform = Matrix.Identity; //variables de animacion this.isAnimating = false; this.currentAnimation = null; this.playLoop = false; this.frameRate = 0f; this.currentTime = 0f; this.animationTimeLenght = 0f; this.currentFrame = 0; this.animations = new Dictionary<string, TgcSkeletalAnimation>(); //acomodar huesos setupSkeleton(); }
public BoneWeight(TgcSkeletalBone bone, float weight) { this.bone = bone; this.weight = weight; }
public TgcSkeletalMesh createNewMesh(Mesh d3dMesh, string meshName, TgcSkeletalMesh.MeshRenderType renderType, TgcSkeletalMesh.OriginalData origData, TgcSkeletalBone[] bones, TgcSkeletalVertexWeight[] verticesWeights) { return new TgcSkeletalMesh(d3dMesh, meshName, renderType, origData, bones, verticesWeights); }
/// <summary> /// Primer constructor de TgcSkeletalMesh. /// No se hace nada, solo se llama al constructor del padre. /// </summary> public MyCustomMesh(Mesh mesh, string name, MeshRenderType renderType, TgcSkeletalBone[] bones) : base(mesh, name, renderType, bones) { }
/// <summary> /// Cargar Weights de vertices /// </summary> /// <param name="meshData"></param> /// <returns></returns> private TgcSkeletalVertexWeight[] loadVerticesWeights(TgcSkeletalMeshData meshData, TgcSkeletalBone[] bones) { int maxWeights = 4; TgcSkeletalVertexWeight.BoneWeight.GreaterComparer weightComparer = new TgcSkeletalVertexWeight.BoneWeight.GreaterComparer(); //Crear un array de Weights para cada uno de los vertices de la malla TgcSkeletalVertexWeight[] weights = new TgcSkeletalVertexWeight[meshData.verticesCoordinates.Length / 3]; for (int i = 0; i < weights.Length; i++) { weights[i] = new TgcSkeletalVertexWeight(); } //Cargar los weights de cada vertice int weightsCount = meshData.verticesWeights.Length / 3; for (int i = 0; i < weightsCount; i++) { int vertexIdx = (int)meshData.verticesWeights[i * 3]; int boneIdx = (int)meshData.verticesWeights[i * 3 + 1]; float weightVal = meshData.verticesWeights[i * 3 + 2]; TgcSkeletalBone bone = bones[boneIdx]; TgcSkeletalVertexWeight.BoneWeight weight = new TgcSkeletalVertexWeight.BoneWeight(bone, weightVal); weights[vertexIdx].Weights.Add(weight); } //Normalizar weights de cada vertice for (int i = 0; i < weights.Length; i++) { TgcSkeletalVertexWeight vertexWeight = weights[i]; //Se soportan hasta 4 weights por vertice. Si hay mas se quitan y se reparten las influencias entre el resto de los huesos if (vertexWeight.Weights.Count > maxWeights) { //Ordenar por weight de menor a mayor y luego revertir, para que quede de mayor a menor peso vertexWeight.Weights.Sort(weightComparer); vertexWeight.Weights.Reverse(); //Quitar los ultimos los weight que superan 4 while (vertexWeight.Weights.Count > maxWeights) { vertexWeight.Weights.RemoveAt(vertexWeight.Weights.Count - 1); } } //Sumar el total de todos los weights de este vertice float weightTotal = 0; foreach (TgcSkeletalVertexWeight.BoneWeight w in vertexWeight.Weights) { weightTotal += w.Weight; } //Normalizar cada valor segun el total acumulado en el vertice foreach (TgcSkeletalVertexWeight.BoneWeight w in vertexWeight.Weights) { w.Weight = w.Weight / weightTotal; } } return(weights); }