/// <summary> /// Dibujar el esqueleto de la malla /// </summary> protected void renderSkeletonMesh() { Device device = GuiController.Instance.D3dDevice; Vector3 ceroVec = new Vector3(0, 0, 0); //Dibujar huesos y joints for (int i = 0; i < bones.Length; i++) { TgcSkeletalBone bone = bones[i]; //Renderizar Joint TgcBox jointBox = skeletonRenderJoints[i]; jointBox.Transform = bone.MatFinal * this.transform; jointBox.render(); //Modificar línea del bone if (bone.ParentBone != null) { TgcLine boneLine = skeletonRenderBones[i]; boneLine.PStart = TgcVectorUtils.transform(ceroVec, bone.MatFinal * this.transform); boneLine.PEnd = TgcVectorUtils.transform(ceroVec, bone.ParentBone.MatFinal * this.transform); boneLine.updateValues(); } } //Dibujar bones foreach (TgcLine boneLine in skeletonRenderBones) { if (boneLine != null) { boneLine.render(); } } }
public static Vector3[] computeCorners(Auto auto) { TgcObb obbAuto = auto.obb; Vector3[] corners = new Vector3[8]; Vector3 extents; Vector3[] orientation = obbAuto.Orientation; Vector3 center = obbAuto.Center; extents = auto.mesh.BoundingBox.calculateAxisRadius(); extents = TgcVectorUtils.abs(extents); Vector3 eX = extents.X * orientation[0]; Vector3 eY = extents.Y * orientation[1]; Vector3 eZ = extents.Z * orientation[2]; corners[0] = center - eX - eY - eZ; corners[1] = center - eX - eY + eZ; corners[2] = center - eX + eY - eZ; corners[3] = center - eX + eY + eZ; corners[4] = center + eX - eY - eZ; corners[5] = center + eX - eY + eZ; corners[6] = center + eX + eY - eZ; corners[7] = center + eX + eY + eZ; return(corners); }
/// <summary> /// Dibujar el esqueleto de la malla /// </summary> protected void renderSkeletonMesh() { var ceroVec = new Vector3(0, 0, 0); //Dibujar huesos y joints for (var i = 0; i < bones.Length; i++) { var bone = bones[i]; //Renderizar Joint var jointBox = skeletonRenderJoints[i]; jointBox.Transform = bone.MatFinal * transform; jointBox.render(); //Modificar línea del bone if (bone.ParentBone != null) { var boneLine = skeletonRenderBones[i]; boneLine.PStart = TgcVectorUtils.transform(ceroVec, bone.MatFinal * transform); boneLine.PEnd = TgcVectorUtils.transform(ceroVec, bone.ParentBone.MatFinal * transform); boneLine.updateValues(); } } //Dibujar bones foreach (var boneLine in skeletonRenderBones) { if (boneLine != null) { boneLine.render(); } } }
/// <summary> /// Transformar fisicamente los vertices del mesh segun su transformacion actual /// </summary> private void applyMeshTransformToVertices(TgcMesh m) { //Transformacion actual Matrix transform = Matrix.Scaling(m.Scale) * Matrix.RotationYawPitchRoll(m.Rotation.Y, m.Rotation.X, m.Rotation.Z) * Matrix.Translation(m.Position); switch (m.RenderType) { case TgcMesh.MeshRenderType.VERTEX_COLOR: TgcSceneLoader.VertexColorVertex[] verts1 = (TgcSceneLoader.VertexColorVertex[])m.D3dMesh.LockVertexBuffer( typeof(TgcSceneLoader.VertexColorVertex), LockFlags.ReadOnly, m.D3dMesh.NumberVertices); for (int i = 0; i < verts1.Length; i++) { verts1[i].Position = TgcVectorUtils.transform(verts1[i].Position, transform); } m.D3dMesh.SetVertexBufferData(verts1, LockFlags.None); m.D3dMesh.UnlockVertexBuffer(); break; case TgcMesh.MeshRenderType.DIFFUSE_MAP: TgcSceneLoader.DiffuseMapVertex[] verts2 = (TgcSceneLoader.DiffuseMapVertex[])m.D3dMesh.LockVertexBuffer( typeof(TgcSceneLoader.DiffuseMapVertex), LockFlags.ReadOnly, m.D3dMesh.NumberVertices); for (int i = 0; i < verts2.Length; i++) { verts2[i].Position = TgcVectorUtils.transform(verts2[i].Position, transform); } m.D3dMesh.SetVertexBufferData(verts2, LockFlags.None); m.D3dMesh.UnlockVertexBuffer(); break; case TgcMesh.MeshRenderType.DIFFUSE_MAP_AND_LIGHTMAP: TgcSceneLoader.DiffuseMapAndLightmapVertex[] verts3 = (TgcSceneLoader.DiffuseMapAndLightmapVertex[])m.D3dMesh.LockVertexBuffer( typeof(TgcSceneLoader.DiffuseMapAndLightmapVertex), LockFlags.ReadOnly, m.D3dMesh.NumberVertices); for (int i = 0; i < verts3.Length; i++) { verts3[i].Position = TgcVectorUtils.transform(verts3[i].Position, transform); } m.D3dMesh.SetVertexBufferData(verts3, LockFlags.None); m.D3dMesh.UnlockVertexBuffer(); break; } //Quitar movimientos del mesh m.Position = new Vector3(0, 0, 0); m.Scale = new Vector3(1, 1, 1); m.Rotation = new Vector3(0, 0, 0); m.Transform = Matrix.Identity; m.AutoTransformEnable = true; //Calcular nuevo bounding box m.createBoundingBox(); }
/// <summary> /// Colisiona un Elipsoide en movimiento contra un conjunto de triangulos. /// Si hay colision devuelve el instante t de colision, el punto q de colision y el vector normal n de la superficie /// contra la que /// se colisiona. /// Todo se devuelve en Elipsoid space. /// Pasa cada triangulo a Elipsoid space para hacer el testeo. /// </summary> /// <param name="eSphere">BoundingSphere de radio 1 en Elipsoid space</param> /// <param name="eMovementVector">movimiento en Elipsoid space</param> /// <param name="eRadius">radio del Elipsoide</param> /// <param name="movementSphere"> /// BoundingSphere que abarca el sphere en su punto de origen mas el sphere en su punto final /// deseado /// </param> /// <param name="minT">Menor instante de colision, en Elipsoid space</param> /// <param name="minQ">Punto mas cercano de colision, en Elipsoid space</param> /// <param name="n">Vector normal de la superficie contra la que se colisiona</param> /// <returns>True si hay colision</returns> public override bool intersectMovingElipsoid(TgcBoundingSphere eSphere, Vector3 eMovementVector, Vector3 eRadius, TgcBoundingSphere movementSphere, out float minT, out Vector3 minQ, out Vector3 n) { minQ = Vector3.Empty; minT = float.MaxValue; n = Vector3.Empty; var collisionPlane = Plane.Empty; //Colision contra cada triangulo del collider, quedarse con el menor Vector3 q; float t; for (var i = 0; i < Triangles.Length; i++) { var triangle = Triangles[i]; //Primero hacer un Sphere-Sphere test if (TgcCollisionUtils.testSphereSphere(movementSphere, triangle.BoundingSphere)) { //Pasar triangle a Elipsoid Space var eTriangle = new Triangle( TgcVectorUtils.div(triangle.A, eRadius), TgcVectorUtils.div(triangle.B, eRadius), TgcVectorUtils.div(triangle.C, eRadius), null ); //Interseccion Moving Sphere-Triangle if (intersectMovingSphereTriangle(eSphere, eMovementVector, eTriangle, out t, out q)) { if (t < minT) { minT = t; minQ = q; collisionPlane = triangle.Plane; } } } } if (minT != float.MaxValue) { n = TgcCollisionUtils.getPlaneNormal(collisionPlane); return(true); } return(false); }
/// <summary> /// Mover Elipsoide con detección de colisiones, sliding y gravedad. /// Se actualiza la posición del centro del Elipsoide /// </summary> /// <param name="characterElipsoid">Elipsoide del cuerpo a mover</param> /// <param name="movementVector">Movimiento a realizar</param> /// <param name="colliders">Obstáculos contra los cuales se puede colisionar</param> /// <returns>Desplazamiento relativo final efecutado al Elipsoide</returns> public Vector3 moveCharacter(TgcBoundingElipsoid characterElipsoid, Vector3 movementVector, List <Collider> colliders) { //Guardar posicion original del Elipsoide var originalElipsoidCenter = characterElipsoid.Center; //Pasar elipsoid space var eCenter = TgcVectorUtils.div(characterElipsoid.Center, characterElipsoid.Radius); var eMovementVector = TgcVectorUtils.div(movementVector, characterElipsoid.Radius); eSphere.setValues(eCenter, 1); var eOrigCenter = eSphere.Center; //Ver si la distancia a recorrer es para tener en cuenta var distanceToTravelSq = movementVector.LengthSq(); if (distanceToTravelSq >= EPSILON) { //Mover la distancia pedida selectPotentialColliders(characterElipsoid, movementVector, colliders); result = doCollideWithWorld(eSphere, eMovementVector, characterElipsoid.Radius, objetosCandidatos, 0, movementSphere, 1); } //Aplicar gravedad if (GravityEnabled) { //Mover con gravedad var eGravity = TgcVectorUtils.div(GravityForce, characterElipsoid.Radius); selectPotentialColliders(characterElipsoid, eGravity, colliders); result = doCollideWithWorld(eSphere, eGravity, characterElipsoid.Radius, objetosCandidatos, 0, movementSphere, OnGroundMinDotValue); } //Mover Elipsoid pasando valores de colision a R3 var movement = TgcVectorUtils.mul(eSphere.Center - eOrigCenter, characterElipsoid.Radius); characterElipsoid.moveCenter(movement); //Ajustar resultados result.realMovmentVector = TgcVectorUtils.mul(result.realMovmentVector, characterElipsoid.Radius); result.collisionPoint = TgcVectorUtils.mul(result.collisionPoint, characterElipsoid.Radius); return(movement); }
/// <summary> /// Transforma el BondingBox en base a una matriz de transformación. /// Esto implica escalar, rotar y trasladar. /// El procedimiento es mas costoso que solo hacer scaleTranslate(). /// Se construye un nuevo BoundingBox en base a los puntos extremos del original /// más la transformación pedida. /// Si el BoundingBox se transformó y luego se llama a scaleTranslate(), se respeta /// la traslación y la escala, pero la rotación se va a perder. /// </summary> /// <param name="transform"></param> public void transform(Matrix transform) { //Transformar vertices extremos originales var corners = computeCorners(pMinOriginal, pMaxOriginal); var newCorners = new Vector3[corners.Length]; for (var i = 0; i < corners.Length; i++) { newCorners[i] = TgcVectorUtils.transform(corners[i], transform); } //Calcular nuevo BoundingBox en base a extremos transformados var newBB = computeFromPoints(newCorners); //actualizar solo pMin y pMax, pMinOriginal y pMaxOriginal quedan sin ser transformados pMin = newBB.pMin; pMax = newBB.pMax; dirtyValues = true; }
/// <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; } }
public void MoverHaciaObjetivo(float tiempoRenderizado, Vector3 posicionObjetivo) { if (this.Modelo.Enabled) { //Resto los dos vectores para hallar el vector distancia Vector3 Distancia = Vector3.Subtract(posicionObjetivo, this.Modelo.Position); //Otro vector, con valores absolutos para hallar la componente maxima Vector3 DistanciaAbs = TgcVectorUtils.abs(Distancia); //Calculo matriz de rotacion Vector3 DireccionObjetivo = Vector3.Normalize(posicionObjetivo - this.Modelo.Position); float angulo = FastMath.Acos(Vector3.Dot(RotacionOriginal, DireccionObjetivo)); Vector3 axisRotation = Vector3.Cross(this.Modelo.Rotation, DireccionObjetivo); MatrizRotacion = Matrix.RotationAxis(axisRotation, angulo); float cantidadDeMovimiento = this.VelocidadMovimiento * tiempoRenderizado; float giro = this.Modelo.Rotation.Y - angulo; if (giro < -0.1) { this.Modelo.rotateY(Geometry.DegreeToRadian(-giro * 100 * tiempoRenderizado)); return; } else if (giro > 0.1) { this.Modelo.rotateY(Geometry.DegreeToRadian(-giro * 100 * tiempoRenderizado)); return; } if (DistanciaAbs.X + DistanciaAbs.Y + DistanciaAbs.Z > 700f) { //Hallo la componente de mayor valor y me muevo en esa direccion. VER SENTIDO. if (DistanciaAbs.X >= DistanciaAbs.Y) { if (DistanciaAbs.X >= DistanciaAbs.Z) { // MOVER EN X if (Distancia.X > cantidadDeMovimiento) { this.Modelo.move(cantidadDeMovimiento, 0, 0); } else { this.Modelo.move(cantidadDeMovimiento * -1, 0, 0); } } else { // MOVER EN Z if (Distancia.Z > 0) { this.Modelo.move(0, 0, cantidadDeMovimiento); } else { this.Modelo.move(0, 0, cantidadDeMovimiento * -1); } } } else { if (DistanciaAbs.Y >= DistanciaAbs.Z) { // MOVER EN Y if (Distancia.Y > 0) { this.Modelo.move(0, cantidadDeMovimiento, 0); } else { this.Modelo.move(0, cantidadDeMovimiento * -1, 0); } } else { // MOVER EN Z if (Distancia.Z > 0) { this.Modelo.move(0, 0, cantidadDeMovimiento); } else { this.Modelo.move(0, 0, cantidadDeMovimiento * -1); } } } } else { //Disparar. Tambien deberia rotar para que el disparo vaya bien if (TiempoParado == 0 || TiempoParado >= TiempoRecarga) { Disparo disparo = new Disparo(this.Modelo, MatrizRotacion); Disparos.Add(disparo); TiempoParado = 0f; } TiempoParado = TiempoParado + tiempoRenderizado * 4; } } }
/// <summary> /// Colisiona un Elipsoide en movimiento contra el BoundingBox. /// Si hay colision devuelve el instante t de colision, el punto q de colision y el vector normal n de la superficie /// contra la que /// se colisiona. /// Todo se devuelve en Elipsoid space. /// El BoundingBox se pasa a Elipsoid space para comparar. /// </summary> /// <param name="eSphere">BoundingSphere de radio 1 en Elipsoid space</param> /// <param name="eMovementVector">movimiento en Elipsoid space</param> /// <param name="eRadius">radio del Elipsoide</param> /// <param name="movementSphere"> /// BoundingSphere que abarca el sphere en su punto de origen mas el sphere en su punto final /// deseado /// </param> /// <param name="t">Menor instante de colision, en Elipsoid space</param> /// <param name="q">Punto mas cercano de colision, en Elipsoid space</param> /// <param name="n">Vector normal de la superficie contra la que se colisiona</param> /// <returns>True si hay colision</returns> public override bool intersectMovingElipsoid(TgcBoundingSphere eSphere, Vector3 eMovementVector, Vector3 eRadius, TgcBoundingSphere movementSphere, out float t, out Vector3 q, out Vector3 n) { //Pasar AABB a Elipsoid Space eAABB.setExtremes( TgcVectorUtils.div(Aabb.PMin, eRadius), TgcVectorUtils.div(Aabb.PMax, eRadius) ); t = -1f; q = Vector3.Empty; n = Vector3.Empty; // Compute the AABB resulting from expanding b by sphere radius r var e = eAABB.toStruct(); e.min.X -= eSphere.Radius; e.min.Y -= eSphere.Radius; e.min.Z -= eSphere.Radius; e.max.X += eSphere.Radius; e.max.Y += eSphere.Radius; e.max.Z += eSphere.Radius; // Intersect ray against expanded AABB e. Exit with no intersection if ray // misses e, else get intersection point p and time t as result Vector3 p; var ray = new TgcRay.RayStruct(); ray.origin = eSphere.Center; ray.direction = eMovementVector; if (!intersectRayAABB(ray, e, out t, out p) || t > 1.0f) { return(false); } // Compute which min and max faces of b the intersection point p lies // outside of. Note, u and v cannot have the same bits set and // they must have at least one bit set among them var i = 0; var sign = new int[3]; if (p.X < eAABB.PMin.X) { sign[0] = -1; i++; } if (p.X > eAABB.PMax.X) { sign[0] = 1; i++; } if (p.Y < eAABB.PMin.Y) { sign[1] = -1; i++; } if (p.Y > eAABB.PMax.Y) { sign[1] = 1; i++; } if (p.Z < eAABB.PMin.Z) { sign[2] = -1; i++; } if (p.Z > eAABB.PMax.Z) { sign[2] = 1; i++; } //Face if (i == 1) { n = new Vector3(sign[0], sign[1], sign[2]); q = eSphere.Center + t * eMovementVector - eSphere.Radius * n; return(true); } // Define line segment [c, c+d] specified by the sphere movement var seg = new Segment(eSphere.Center, eSphere.Center + eMovementVector); //Box extent and center var extent = eAABB.calculateAxisRadius(); var center = eAABB.PMin + extent; //Edge if (i == 2) { //Generar los dos puntos extremos del Edge float[] extentDir = { sign[0], sign[1], sign[2] }; var zeroIndex = sign[0] == 0 ? 0 : (sign[1] == 0 ? 1 : 2); extentDir[zeroIndex] = 1; var capsuleA = center + new Vector3(extent.X * extentDir[0], extent.Y * extentDir[1], extent.Z * extentDir[2]); extentDir[zeroIndex] = -1; var capsuleB = center + new Vector3(extent.X * extentDir[0], extent.Y * extentDir[1], extent.Z * extentDir[2]); //Colision contra el Edge hecho Capsula if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, eSphere.Radius), out t)) { n = new Vector3(sign[0], sign[1], sign[2]); n.Normalize(); q = eSphere.Center + t * eMovementVector - eSphere.Radius * n; return(true); } } //Vertex if (i == 3) { var tmin = float.MaxValue; var capsuleA = center + new Vector3(extent.X * sign[0], extent.Y * sign[1], extent.Z * sign[2]); Vector3 capsuleB; capsuleB = center + new Vector3(extent.X * -sign[0], extent.Y * sign[1], extent.Z * sign[2]); if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, eSphere.Radius), out t)) { tmin = TgcCollisionUtils.min(t, tmin); } capsuleB = center + new Vector3(extent.X * sign[0], extent.Y * -sign[1], extent.Z * sign[2]); if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, eSphere.Radius), out t)) { tmin = TgcCollisionUtils.min(t, tmin); } capsuleB = center + new Vector3(extent.X * sign[0], extent.Y * sign[1], extent.Z * -sign[2]); if (intersectSegmentCapsule(seg, new Capsule(capsuleA, capsuleB, eSphere.Radius), out t)) { tmin = TgcCollisionUtils.min(t, tmin); } if (tmin == float.MaxValue) { return(false); // No intersection } t = tmin; n = new Vector3(sign[0], sign[1], sign[2]); n.Normalize(); q = eSphere.Center + t * eMovementVector - eSphere.Radius * n; return(true); // Intersection at time t == tmin } return(false); }
/// <summary> /// Calcular OBB a partir de un conjunto de puntos. /// Prueba todas las orientaciones entre initValues y endValues, saltando de angulo en cada intervalo segun step /// Continua recursivamente hasta llegar a un step menor a 0.01f /// </summary> /// <returns></returns> private static OBBStruct computeFromPointsRecursive(Vector3[] points, Vector3 initValues, Vector3 endValues, float step) { var minObb = new OBBStruct(); var minVolume = float.MaxValue; var minInitValues = Vector3.Empty; var minEndValues = Vector3.Empty; var transformedPoints = new Vector3[points.Length]; float x, y, z; x = initValues.X; while (x <= endValues.X) { y = initValues.Y; var rotX = FastMath.ToRad(x); while (y <= endValues.Y) { z = initValues.Z; var rotY = FastMath.ToRad(y); while (z <= endValues.Z) { //Matriz de rotacion var rotZ = FastMath.ToRad(z); var rotM = Matrix.RotationYawPitchRoll(rotY, rotX, rotZ); Vector3[] orientation = { new Vector3(rotM.M11, rotM.M12, rotM.M13), new Vector3(rotM.M21, rotM.M22, rotM.M23), new Vector3(rotM.M31, rotM.M32, rotM.M33) }; //Transformar todos los puntos a OBB-space for (var i = 0; i < transformedPoints.Length; i++) { transformedPoints[i].X = Vector3.Dot(points[i], orientation[0]); transformedPoints[i].Y = Vector3.Dot(points[i], orientation[1]); transformedPoints[i].Z = Vector3.Dot(points[i], orientation[2]); } //Obtener el AABB de todos los puntos transformados var aabb = TgcBoundingBox.computeFromPoints(transformedPoints); //Calcular volumen del AABB var extents = aabb.calculateAxisRadius(); extents = TgcVectorUtils.abs(extents); var volume = extents.X * 2 * extents.Y * 2 * extents.Z * 2; //Buscar menor volumen if (volume < minVolume) { minVolume = volume; minInitValues = new Vector3(x, y, z); minEndValues = new Vector3(x + step, y + step, z + step); //Volver centro del AABB a World-space var center = aabb.calculateBoxCenter(); center = center.X * orientation[0] + center.Y * orientation[1] + center.Z * orientation[2]; //Crear OBB minObb.center = center; minObb.extents = extents; minObb.orientation = orientation; } z += step; } y += step; } x += step; } //Recursividad en mejor intervalo encontrado if (step > 0.01f) { minObb = computeFromPointsRecursive(points, minInitValues, minEndValues, step / 10f); } return(minObb); }