public TgcFrustum() { FrustumPlanes = new TGCPlane[6]; Color = Color.Green; AlphaBlendingValue = 0.7f; }
override protected void UpdateValues() { base.UpdateValues(); directionPlane = TGCPlane.FromPointNormal(GetPosition(), -GetVectorCostadoIzquierda()); planoCostado = TGCPlane.FromPointNormal(GetPosition(), GetVectorAdelante()); radarSphere.setValues(this.GetPosition(), radarSphere.Radius); }
/// <summary> /// Separa los objetos en dos listas, segun el testo contra el plano de corte /// </summary> private void splitByPlane(TGCPlane cutPlane, List <StaticObject> objetos, List <StaticObject> possitiveList, List <StaticObject> negativeList) { TgcCollisionUtils.PlaneBoxResult c; foreach (var objeto in objetos) { c = TgcCollisionUtils.classifyPlaneAABB(cutPlane, objeto.Meshes[0].BoundingBox); // TODO CALCULAR BOUNDING BOX DEL GAMEOBJECT //possitive side if (c == TgcCollisionUtils.PlaneBoxResult.IN_FRONT_OF) { possitiveList.Add(objeto); } //negative side else if (c == TgcCollisionUtils.PlaneBoxResult.BEHIND) { negativeList.Add(objeto); } //both sides else { possitiveList.Add(objeto); negativeList.Add(objeto); } } }
/// <summary> /// Obtiene el mejor plano de corte recto, en el volumen dado, en la direccion dada /// </summary> private TGCPlane getCutPlane(List <TgcMesh> modelos, TGCVector3 n, float pMin, float pMax, ref float cutValue) { var vueltas = (int)((pMax - pMin) / D_DESPLAZAMIENTO); var bestBalance = int.MaxValue; var bestPlane = TGCPlane.Zero; cutValue = 0; for (var i = 0; i < vueltas; i++) { //crear plano de corte var currentCutValue = pMin + D_DESPLAZAMIENTO * i; var p = new TGCPlane(n.X, n.Y, n.Z, -currentCutValue); //clasificar todos los modelos contra ese plano var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); splitByPlane(p, modelos, possitiveList, negativeList); //calcular balance var balance = Math.Abs(possitiveList.Count - negativeList.Count); //guardar mejor if (balance < bestBalance) { bestBalance = balance; bestPlane = p; cutValue = currentCutValue; } } return(bestPlane); }
/// <summary> /// Separa los modelos en dos listas, segun el testo contra el plano de corte /// </summary> private void splitByPlane(TGCPlane cutPlane, List <TgcMesh> modelos, List <TgcMesh> possitiveList, List <TgcMesh> negativeList) { TgcCollisionUtils.PlaneBoxResult c; foreach (var modelo in modelos) { c = TgcCollisionUtils.classifyPlaneAABB(cutPlane, modelo.BoundingBox); //possitive side if (c == TgcCollisionUtils.PlaneBoxResult.IN_FRONT_OF) { possitiveList.Add(modelo); } //negative side else if (c == TgcCollisionUtils.PlaneBoxResult.BEHIND) { negativeList.Add(modelo); } //both sides else { possitiveList.Add(modelo); negativeList.Add(modelo); } } }
/// <summary> /// Actualiza la visibilidad de todos los modelos de las celdas. /// Las modelos visibles se cargan como Enable = true, mientras que el /// resto se deshabilita. /// </summary> /// <param name="cameraPos">Posición de la cámara</param> public void updateVisibility(TGCVector3 cameraPos, TgcFrustum frustum) { //Armar Frustum para uso internor, en base al Frustum actual var currentFrustumPlanes = new TGCPlane[6]; currentFrustumPlanes = new TGCPlane[6]; currentFrustumPlanes[0] = frustum.NearPlane; currentFrustumPlanes[1] = frustum.FarPlane; currentFrustumPlanes[2] = frustum.LeftPlane; currentFrustumPlanes[3] = frustum.RightPlane; currentFrustumPlanes[4] = frustum.BottomPlane; currentFrustumPlanes[5] = frustum.TopPlane; //Deshabilitar todas las celdas foreach (var cell in Cells) { cell.Visited = false; foreach (var connection in cell.Connections) { connection.Portal.Visited = false; } } //Buscar la celda actual en la que se encuentra la cámara var currentCell = findCellFromPoint(cameraPos); if (currentCell == null) { return; } //Recorrer grafo de celdas desde la celda actual currentCell.Visited = true; traverseCellGraph(cameraPos, currentFrustumPlanes, currentCell); }
/// <summary> /// Crear un nuevo Frustum acotado usando como base el portal recorado. /// La cantidad de planos del nuevo Frustum no tiene por qué ser 6. /// Depende de la forma que haya quedado en el portal recortado. /// </summary> private TGCPlane[] createFrustumPlanes(TGCVector3 cameraPos, TGCPlane[] currentFrustumPlanes, TGCVector3[] portalVerts, TGCPlane portalPlane) { //Hay un plano por cada vértice del polígono + 2 por el near y far plane var frustumPlanes = new TGCPlane[2 + portalVerts.Length]; //Cargar near y far plane originales //TODO: habria que usar el portalPlane para acercar el NearPlane hasta el portal frustumPlanes[0] = currentFrustumPlanes[0]; frustumPlanes[1] = currentFrustumPlanes[1]; //Generar los planos laterales en base al polígono remanente del portal //Vamos tomando de a dos puntos del polígono + la posición de la cámara y creamos un plano var lastP = portalVerts[portalVerts.Length - 1]; for (var i = 0; i < portalVerts.Length; i++) { var nextP = portalVerts[i]; //Armar el plano para que la normal apunte hacia adentro del Frustum var a = lastP - cameraPos; var b = nextP - cameraPos; var plane = TGCPlane.FromPointNormal(cameraPos, TGCVector3.Cross(b, a)); //Guardar después del near y far plane frustumPlanes[i + 2] = plane; lastP = nextP; } return(frustumPlanes); }
/// <summary> /// Crear triangulo. /// Calcula su plano y BoundingSphere /// </summary> public Triangle(TGCVector3 a, TGCVector3 b, TGCVector3 c) { A = a; B = b; C = c; Plane = TGCPlane.FromPoints(a, b, c); BoundingSphere = TgcBoundingSphere.computeFromPoints(new[] { a, b, c }).toClass(); }
public TgcPortalRenderingConnection(TgcPortalRenderingPortal portal, TgcPortalRenderingCell nextCell, TgcConvexPolygon polygon, TGCPlane plane) { Portal = portal; NextCell = nextCell; Polygon = polygon; Plane = plane; }
/// <summary> /// Crear triangulo. /// Calcula su plano /// </summary> public Triangle(TGCVector3 a, TGCVector3 b, TGCVector3 c, TgcBoundingSphere sphere) { A = a; B = b; C = c; Plane = TGCPlane.FromPoints(a, b, c); BoundingSphere = sphere; }
public bool IntersectsWithObject(TGCPlane objectPlane, float distance) { pickingRay.updateRay(); bool intersected = TgcCollisionUtils.intersectRayPlane(pickingRay.Ray, objectPlane, out _, out TGCVector3 collisionPoint); bool inSight = Math.Sqrt(TGCVector3.LengthSq(pickingRay.Ray.Origin, collisionPoint)) < distance; return(intersected && inSight); }
/// <summary> /// Corte de plano Z /// </summary> private void doSectorOctreeZ(OctreeNode parent, TGCVector3 center, TGCVector3 size, int step, List <TgcMesh> meshes, int childIndex) { var z = center.Z; //Crear listas para realizar corte var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); //Z-cut var zCutPlane = new TGCPlane(0, 0, 1, -z); splitByPlane(zCutPlane, meshes, possitiveList, negativeList); //obtener lista de children del parent, con iniciacion lazy if (parent.children == null) { parent.children = new OctreeNode[8]; } //crear nodo positivo en parent, segun childIndex var posNode = new OctreeNode(); parent.children[childIndex] = posNode; //cargar nodo negativo en parent, segun childIndex var negNode = new OctreeNode(); parent.children[childIndex + 1] = negNode; //condicion de corte if (step >= MAX_SECTOR_OCTREE_RECURSION || meshes.Count <= MIN_MESH_PER_LEAVE_THRESHOLD) { //cargar hijos de nodo positivo posNode.models = possitiveList.ToArray(); //cargar hijos de nodo negativo negNode.models = negativeList.ToArray(); //seguir recursividad } else { step++; //recursividad de positivos con plano X, usando resultados positivos doSectorOctreeX(posNode, new TGCVector3(center.X, center.Y, z + size.Z / 2), new TGCVector3(size.X, size.Y, size.Z / 2), step, possitiveList); //recursividad de negativos con plano Y, usando resultados negativos doSectorOctreeX(negNode, new TGCVector3(center.X, center.Y, z - size.Z / 2), new TGCVector3(size.X, size.Y, size.Z / 2), step, negativeList); } }
public Plane(TGCVector3 minPoint, TGCVector3 maxPoint, TGCVector3 orientation, string fileName, float UCoordinate, float VCoordinate) { orientation.Normalize(); this.plane = new TgcPlane(new TGCVector3(0, 0, 0), new TGCVector3(0, 0, 0), this.GetPlaneOrientation(orientation), TgcTexture.createTexture(GlobalConcepts.GetInstance().GetMediaDir() + "MeshCreator\\Meshes\\" + fileName), UCoordinate, VCoordinate); this.plane.setExtremes(minPoint, maxPoint); this.plane.updateValues(); this.mesh = this.plane.toMesh("plane"); mesh.AutoTransform = false; //negrada atomica InvertNormals(orientation); this.realPlane = TGCPlane.FromPointNormal(minPoint, orientation); mesh.Effect = TgcShaders.Instance.TgcMeshPointLightShader; mesh.Technique = "DIFFUSE_MAP"; }
protected bool IsReallyTheFloor(Collidable element) { TGCVector3 directionOfCollision = new TGCVector3(0, -1f, 0); TgcRay ray = new TgcRay(); ray.Origin = this.auto.GetLastPosition(); ray.Direction = directionOfCollision; TGCPlane plane = element.GetPlaneOfCollision(ray, this.auto); TGCVector3 normal = new TGCVector3(0, 1, 0); if (normal != new TGCVector3(0, 1, 0)) { return(false); } return(true); }
/// <summary> /// Actualiza los planos que conforman el volumen del Frustum. /// Los planos se calculan con las normales apuntando hacia adentro /// </summary> /// <param name="viewMatrix">View matrix</param> /// <param name="projectionMatrix">Projection matrix</param> public void updateVolume(TGCMatrix viewMatrix, TGCMatrix projectionMatrix) { var viewProjection = viewMatrix * projectionMatrix; //Left plane FrustumPlanes[0].A = viewProjection.M14 + viewProjection.M11; FrustumPlanes[0].B = viewProjection.M24 + viewProjection.M21; FrustumPlanes[0].C = viewProjection.M34 + viewProjection.M31; FrustumPlanes[0].D = viewProjection.M44 + viewProjection.M41; //Right plane FrustumPlanes[1].A = viewProjection.M14 - viewProjection.M11; FrustumPlanes[1].B = viewProjection.M24 - viewProjection.M21; FrustumPlanes[1].C = viewProjection.M34 - viewProjection.M31; FrustumPlanes[1].D = viewProjection.M44 - viewProjection.M41; //Top plane FrustumPlanes[2].A = viewProjection.M14 - viewProjection.M12; FrustumPlanes[2].B = viewProjection.M24 - viewProjection.M22; FrustumPlanes[2].C = viewProjection.M34 - viewProjection.M32; FrustumPlanes[2].D = viewProjection.M44 - viewProjection.M42; //Bottom plane FrustumPlanes[3].A = viewProjection.M14 + viewProjection.M12; FrustumPlanes[3].B = viewProjection.M24 + viewProjection.M22; FrustumPlanes[3].C = viewProjection.M34 + viewProjection.M32; FrustumPlanes[3].D = viewProjection.M44 + viewProjection.M42; //Near plane FrustumPlanes[4].A = viewProjection.M13; FrustumPlanes[4].B = viewProjection.M23; FrustumPlanes[4].C = viewProjection.M33; FrustumPlanes[4].D = viewProjection.M43; //Far plane FrustumPlanes[5].A = viewProjection.M14 - viewProjection.M13; FrustumPlanes[5].B = viewProjection.M24 - viewProjection.M23; FrustumPlanes[5].C = viewProjection.M34 - viewProjection.M33; FrustumPlanes[5].D = viewProjection.M44 - viewProjection.M43; //Normalize planes for (var i = 0; i < 6; i++) { FrustumPlanes[i] = TGCPlane.Normalize(FrustumPlanes[i]); } }
/// <summary> /// Corte con plano X /// </summary> private void doSectorOctreeX(OctreeNode parent, TGCVector3 center, TGCVector3 size, int step, List <TgcMesh> meshes) { var x = center.X; //Crear listas para realizar corte var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); //X-cut var xCutPlane = new TGCPlane(1, 0, 0, -x); splitByPlane(xCutPlane, meshes, possitiveList, negativeList); //recursividad de positivos con plano Y, usando resultados positivos y childIndex 0 doSectorOctreeY(parent, new TGCVector3(x + size.X / 2, center.Y, center.Z), new TGCVector3(size.X / 2, size.Y, size.Z), step, possitiveList, 0); //recursividad de negativos con plano Y, usando resultados negativos y childIndex 4 doSectorOctreeY(parent, new TGCVector3(x - size.X / 2, center.Y, center.Z), new TGCVector3(size.X / 2, size.Y, size.Z), step, negativeList, 4); }
/// <summary> /// Corte con plano Y /// </summary> private void doSectorOctreeY(OctreeNode parent, TGCVector3 center, TGCVector3 size, int step, List <TgcMesh> meshes, int childIndex) { var y = center.Y; //Crear listas para realizar corte var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); //Y-cut var yCutPlane = new TGCPlane(0, 1, 0, -y); splitByPlane(yCutPlane, meshes, possitiveList, negativeList); //recursividad de positivos con plano Z, usando resultados positivos y childIndex 0 doSectorOctreeZ(parent, new TGCVector3(center.X, y + size.Y / 2, center.Z), new TGCVector3(size.X, size.Y / 2, size.Z), step, possitiveList, childIndex + 0); //recursividad de negativos con plano Z, usando plano X negativo y childIndex 2 doSectorOctreeZ(parent, new TGCVector3(center.X, y - size.Y / 2, center.Z), new TGCVector3(size.X, size.Y / 2, size.Z), step, negativeList, childIndex + 2); }
private void Collide(TgcMesh elemento, Vehicle car) { //direccion a la que estoy yendo antes de chocar TGCVector3 directionOfCollision = car.GetDirectionOfCollision(); TgcRay ray = new TgcRay(); ray.Origin = car.GetLastPosition(); ray.Direction = directionOfCollision; //interseco el rayo con el aabb, para conocer un punto del plano con el que colisione TgcBoundingAxisAlignBox.Face[] faces; faces = elemento.BoundingBox.computeFaces(); TGCPlane plane = this.CreatePlane(ray, faces, car.GetLastPosition()); TGCVector3 normal = GlobalConcepts.GetInstance().GetNormalPlane(plane); TGCVector3 output = normal + directionOfCollision * 2; float angle = car.SetDirection(output, normal); car.Crash(angle); while (TgcCollisionUtils.testObbAABB(car.GetTGCBoundingOrientedBox(), elemento.BoundingBox)) { car.Translate(TGCMatrix.Translation(normal)); car.Transform(); } }
public override void Init() { //Cargar un mesh var loader = new TgcSceneLoader(); var scene = loader.loadSceneFromFile(MediaDir + "MeshCreator\\Meshes\\Cimientos\\PilarEgipcio\\PilarEgipcio-TgcScene.xml"); mesh = scene.Meshes[0]; //Obtener los vértices del mesh (esta operacion es lenta, copia de la GPU a la CPU, no hacer a cada rato) var vertices = mesh.getVertexPositions(); //Iterar sobre todos los vertices y construir triangulos, normales y planos var triCount = vertices.Length / 3; triangles = new List <TgcTriangle>(triCount); normals = new List <TgcArrow>(); planes = new List <TGCQuad>(); for (var i = 0; i < triCount; i++) { //Obtenemos los 3 vertices del triangulo, es importante saber como esta estructurado nuestro mesh. var a = vertices[i * 3]; var b = vertices[i * 3 + 1]; var c = vertices[i * 3 + 2]; //Obtener normal del triangulo. El orden influye en si obtenemos el vector normal hacia adentro o hacia afuera del mesh var normal = TGCVector3.Cross(c - a, b - a); normal.Normalize(); //Crear plano que contiene el triangulo a partir un vertice y la normal var plane = TGCPlane.FromPointNormal(a, normal); //Calcular el centro del triangulo. Hay muchos tipos de centros para un triangulo (http://www.mathopenref.com/trianglecenters.html) //Aca calculamos el mas simple var center = TGCVector3.Scale(a + b + c, 1 / 3f); ///////////// Creacion de elementos para poder dibujar a pantalla (propios de este ejemplo) /////////////// //Crear un quad (pequeno plano) con la clase TgcQuad para poder dibujar el plano que contiene al triangulo var quad = new TGCQuad(); quad.Center = center; quad.Normal = normal; quad.Color = adaptColorRandom(Color.DarkGreen); quad.Size = new TGCVector2(10, 10); quad.updateValues(); planes.Add(quad); //Creamos una flecha con la clase TgcArrow para poder dibujar la normal (la normal la estiramos un poco para que se pueda ver) normals.Add(TgcArrow.fromDirection(center, TGCVector3.Scale(normal, 10f))); //Creamos la clase TgcTriangle que es un helper para dibujar triangulos sueltos var t = new TgcTriangle(); t.A = a; t.B = b; t.C = c; t.Color = adaptColorRandom(Color.Red); t.updateValues(); triangles.Add(t); } //Modifiers meshModifier = AddBoolean("mesh", "mesh", true); trianglesModifier = AddBoolean("triangles", "triangles", true); normalsModifier = AddBoolean("normals", "normals", true); planesModifier = AddBoolean("planes", "planes", false); //Camara Camera = new TgcRotationalCamera(mesh.BoundingBox.calculateBoxCenter(), mesh.BoundingBox.calculateBoxRadius() * 2, Input); }
/// <summary> /// Tomar un mesh cargar todas las estructuras internas necesarias para poder editarlo /// </summary> private void loadMesh(TgcMesh origMesh) { //Obtener vertices del mesh mesh = origMesh; mesh.AutoTransformEnable = true; var origVertices = getMeshOriginalVertexData(origMesh); var origTriCount = origVertices.Count / 3; //Iterar sobre los triangulos y generar data auxiliar unificada Vertices = new List <EditPolyVertex>(); Edges = new List <EditPolyEdge>(); Polygons = new List <EditPolyPolygon>(); IndexBuffer = new short[origTriCount * 3]; var attributeBuffer = origMesh.D3dMesh.LockAttributeBufferArray(LockFlags.ReadOnly); origMesh.D3dMesh.UnlockAttributeBuffer(attributeBuffer); for (var i = 0; i < origTriCount; i++) { var v1 = origVertices[i * 3]; var v2 = origVertices[i * 3 + 1]; var v3 = origVertices[i * 3 + 2]; //Agregar vertices a la lista, si es que son nuevos var v1Idx = EditablePolyUtils.addVertexToListIfUnique(Vertices, v1); var v2Idx = EditablePolyUtils.addVertexToListIfUnique(Vertices, v2); var v3Idx = EditablePolyUtils.addVertexToListIfUnique(Vertices, v3); v1 = Vertices[v1Idx]; v2 = Vertices[v2Idx]; v3 = Vertices[v3Idx]; //Crear edges var e1 = new EditPolyEdge(); e1.a = v1; e1.b = v2; var e2 = new EditPolyEdge(); e2.a = v2; e2.b = v3; var e3 = new EditPolyEdge(); e3.a = v3; e3.b = v1; //Crear poligono para este triangulo var p = new EditPolyPolygon(); p.vertices = new List <EditPolyVertex>(); p.vertices.Add(v1); p.vertices.Add(v2); p.vertices.Add(v3); p.edges = new List <EditPolyEdge>(); p.edges.Add(e1); p.edges.Add(e2); p.edges.Add(e3); p.vbTriangles = new List <int>(); p.vbTriangles.Add(i * 3); p.TGCPlane = TGCPlane.FromPoints(v1.position, v2.position, v3.position); p.TGCPlane.Normalize(); p.matId = attributeBuffer[i]; //Agregar triangulo al index buffer IndexBuffer[i * 3] = (short)v1Idx; IndexBuffer[i * 3 + 1] = (short)v2Idx; IndexBuffer[i * 3 + 2] = (short)v3Idx; //Agregar a lista de poligonos Polygons.Add(p); /* * //Buscar si hay un poligono ya existente al cual sumarnos (coplanar y que compartan una arista) * EditPolyPolygon coplanarP = null; * for (int j = 0; j < polygons.Count; j++) * { * //Coplanares y con igual material ID * EditPolyPolygon p0 = polygons[j]; * if (p0.matId == p.matId && EditablePolyUtils.sameTGCPlane(p0.TGCPlane, p.TGCPlane)) * { * //Buscar si tienen una arista igual * int p0SharedEdgeIdx; * int pSharedEdgeIdx; * if (EditablePolyUtils.findShareEdgeBetweenPolygons(p0, p, out p0SharedEdgeIdx, out pSharedEdgeIdx)) * { * //Obtener el tercer vertice del triangulo que no es parte de la arista compartida * EditPolyEdge sharedEdge = p0.edges[p0SharedEdgeIdx]; * EditPolyVertex thirdVert; * if (p.vertices[0] != sharedEdge.a && p.vertices[0] != sharedEdge.b) * thirdVert = p.vertices[0]; * else if (p.vertices[1] != sharedEdge.a && p.vertices[1] != sharedEdge.b) * thirdVert = p.vertices[1]; * else * thirdVert = p.vertices[2]; * * //Agregar el tercer vertice al poligno existente * EditablePolyUtils.addVertexToPolygon(p0, sharedEdge, thirdVert); * * //Quitar arista compartida * p0.edges.Remove(sharedEdge); * * //Agregar al poligono dos nuevas aristas que conectar los extremos de la arista compartida hacia el tercer vertice * EditPolyEdge newPolEdge1 = new EditPolyEdge(); * newPolEdge1.a = sharedEdge.a; * newPolEdge1.b = thirdVert; * p0.edges.Add(newPolEdge1); * * EditPolyEdge newPolEdge2 = new EditPolyEdge(); * newPolEdge2.a = thirdVert; * newPolEdge2.b = sharedEdge.b; * p0.edges.Add(newPolEdge2); * * //Agregar indice de triangulo del vertexBuffer que se sumo al poligono * p0.vbTriangles.Add(p.vbTriangles[0]); * * coplanarP = p0; * } * } * } * //Es un nuevo poligono, agregarlo * if (coplanarP == null) * { * polygons.Add(p); * } */ } //Unificar aristas de los poligonos foreach (var p in Polygons) { for (var i = 0; i < p.edges.Count; i++) { bool newEdgeAdded; var eIdx = EditablePolyUtils.addEdgeToListIfUnique(Edges, p.edges[i], out newEdgeAdded); var e = Edges[eIdx]; //Nueva arista incorporada a la lista if (newEdgeAdded) { e.faces = new List <EditPolyPolygon>(); //Agregar referencia a vertices que usan la arista e.a.edges.Add(e); e.b.edges.Add(e); } //Se usa arista existente de la lista else { //Reemplazar en poligono por la nueva p.edges[i] = e; } //Indicar a la arista que pertenece al poligono actual e.faces.Add(p); } } setDirtyValues(false); }
/// <summary> /// Compara si dos planos son iguales /// </summary> public static bool sameTGCPlane(TGCPlane p1, TGCPlane p2) { //TODO: comparar en ambos sentidos por las dudas return(equalsTGCVector3(new TGCVector3(p1.A, p1.B, p1.C), new TGCVector3(p2.A, p2.B, p2.C)) && equalsFloat(p1.D, p2.D)); }
/// <summary> /// Detección de colisiones recursiva /// </summary> public void doCollideWithWorld(TgcBoundingSphere characterSphere, TGCVector3 movementVector, List <Collider> colliders, int recursionDepth, TgcBoundingSphere movementSphere, bool sliding, float slidingMinY) { //Limitar recursividad if (recursionDepth > 5) { return; } //Posicion deseada var originalSphereCenter = characterSphere.Center; var nextSphereCenter = originalSphereCenter + movementVector; //Buscar el punto de colision mas cercano de todos los objetos candidatos Collision = false; TGCVector3 q; float t; TGCVector3 n; var minT = float.MaxValue; foreach (var collider in colliders) { //Colisionar Sphere en movimiento contra Collider (cada Collider resuelve la colision) if (collider.intersectMovingSphere(characterSphere, movementVector, movementSphere, out t, out q, out n)) { //Quedarse con el menor instante de colision if (t < minT) { minT = t; Collision = true; LastCollisionPoint = q; LastCollisionNormal = n; lastCollider = collider; } } } //Si nunca hubo colisión, avanzar todo lo requerido if (!Collision) { //Avanzar todo lo pedido //lastCollisionDistance = movementVector.Length(); characterSphere.moveCenter(movementVector); return; } //Solo movernos si ya no estamos muy cerca if (minT >= EPSILON) { //Restar un poco al instante de colision, para movernos hasta casi esa distancia minT -= EPSILON; var realMovementVector = movementVector * minT; //Mover el BoundingSphere characterSphere.moveCenter(realMovementVector); //Quitarle al punto de colision el EPSILON restado al movimiento, para no afectar al plano de sliding var v = TGCVector3.Normalize(realMovementVector); LastCollisionPoint -= v * EPSILON; } if (sliding) { //Calcular plano de Sliding, como un plano tangete al punto de colision con la esfera, apuntando hacia el centro de la esfera var slidePlaneOrigin = LastCollisionPoint; var slidePlaneNormal = characterSphere.Center - LastCollisionPoint; slidePlaneNormal.Normalize(); var slidePlane = TGCPlane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal); //Calcular vector de movimiento para sliding, proyectando el punto de destino original sobre el plano de sliding var distance = TgcCollisionUtils.distPointPlane(nextSphereCenter, slidePlane); var newDestinationPoint = nextSphereCenter - distance * slidePlaneNormal; var slideMovementVector = newDestinationPoint - LastCollisionPoint; //No hacer recursividad si es muy pequeño slideMovementVector.Scale(SlideFactor); if (slideMovementVector.Length() < EPSILON) { return; } if (LastCollisionNormal.Y <= slidingMinY) { //Recursividad para aplicar sliding doCollideWithWorld(characterSphere, slideMovementVector, colliders, recursionDepth + 1, movementSphere, sliding, slidingMinY); } } }
/// <summary> /// Cargar información de PortalRendering /// </summary> public TgcPortalRenderingManager loadFromData(TgcScene scene, TgcPortalRenderingData portalRenderingData) { var manager = new TgcPortalRenderingManager(scene); //Crear dictionary de nombres de los meshes var meshDictionary = new Dictionary<string, TgcMesh>(); foreach (var mesh in scene.Meshes) { meshDictionary.Add(mesh.Name, mesh); } //Cargar celdas foreach (var cellData in portalRenderingData.cells) { //Crear cuerpo Convexo var convexPoly = new TgcConvexPolyhedron(); convexPoly.Planes = new TGCPlane[cellData.facePlanes.Length / 4]; for (var i = 0; i < convexPoly.Planes.Length; i++) { convexPoly.Planes[i] = new TGCPlane( cellData.facePlanes[i * 4], cellData.facePlanes[i * 4 + 1], cellData.facePlanes[i * 4 + 2], cellData.facePlanes[i * 4 + 3] ); } convexPoly.BoundingVertices = new TGCVector3[cellData.boundingVertices.Length / 3]; for (var i = 0; i < convexPoly.BoundingVertices.Length; i++) { convexPoly.BoundingVertices[i] = new TGCVector3( cellData.boundingVertices[i * 3], cellData.boundingVertices[i * 3 + 1], cellData.boundingVertices[i * 3 + 2] ); } //Crear celda var cell = new TgcPortalRenderingCell(cellData.name, convexPoly); manager.Cells.Add(cell); //Cargar meshes en celda for (var i = 0; i < cellData.meshes.Length; i++) { var mesh = meshDictionary[cellData.meshes[i]]; cell.Meshes.Add(mesh); } } //Cargar portales foreach (var portalData in portalRenderingData.portals) { //BoundingBox del portal var boundingBox = new TgcBoundingAxisAlignBox( new TGCVector3(portalData.pMin[0], portalData.pMin[1], portalData.pMin[2]), new TGCVector3(portalData.pMax[0], portalData.pMax[1], portalData.pMax[2]) ); //Crear portal var portal = new TgcPortalRenderingPortal(portalData.name, boundingBox); manager.Portals.Add(portal); //Cargar conexiones para celdas A y B var cellA = manager.Cells[portalData.cellA]; var cellB = manager.Cells[portalData.cellB]; //Poligono del portal para la celda A var polygonA = new TgcConvexPolygon(); polygonA.BoundingVertices = new TGCVector3[portalData.boundingVerticesA.Length / 3]; for (var i = 0; i < polygonA.BoundingVertices.Length; i++) { polygonA.BoundingVertices[i] = new TGCVector3( portalData.boundingVerticesA[i * 3], portalData.boundingVerticesA[i * 3 + 1], portalData.boundingVerticesA[i * 3 + 2] ); } //Plano del portal para la celda A var planeA = TGCPlane.Float4ArrayToPlane(portalData.planeA); //Crear conexion A var connectionA = new TgcPortalRenderingConnection(portal, cellB, polygonA, planeA); cellA.Connections.Add(connectionA); //Poligono del portal para la celda B var polygonB = new TgcConvexPolygon(); polygonB.BoundingVertices = new TGCVector3[portalData.boundingVerticesB.Length / 3]; for (var i = 0; i < polygonB.BoundingVertices.Length; i++) { polygonB.BoundingVertices[i] = new TGCVector3( portalData.boundingVerticesB[i * 3], portalData.boundingVerticesB[i * 3 + 1], portalData.boundingVerticesB[i * 3 + 2] ); } //Plano del portal para la celda B var planeB = TGCPlane.Float4ArrayToPlane(portalData.planeB); //Crear conexion B var connectionB = new TgcPortalRenderingConnection(portal, cellA, polygonB, planeB); cellB.Connections.Add(connectionB); } return manager; }
public bool IsInFrontOf(TGCVector3 testpoint, TGCPlane plane) { return(plane.A * testpoint.X + plane.B * testpoint.Y + plane.C * testpoint.Z + plane.D >= 0); }
public TGCVector3 GetNormalPlane(TGCPlane plane) { return(new TGCVector3(plane.A, plane.B, plane.C)); }
/// <summary> /// Detección de colisiones recursiva /// </summary> /// <param name="eSphere">Sphere de radio 1 pasada a Elipsoid space</param> /// <param name="eMovementVector">Movimiento pasado a Elipsoid space</param> /// <param name="eRadius">Radio de la elipsoide</param> /// <param name="colliders">Objetos contra los cuales colisionar</param> /// <param name="recursionDepth">Nivel de recursividad</param> /// <param name="movementSphere">Esfera real que representa el movimiento abarcado</param> /// <param name="slidingMinY">Minimo valor de normal Y de colision para hacer sliding</param> /// <returns>Resultado de colision</returns> public CollisionResult doCollideWithWorld(TgcBoundingSphere eSphere, TGCVector3 eMovementVector, TGCVector3 eRadius, List <Collider> colliders, int recursionDepth, TgcBoundingSphere movementSphere, float slidingMinY) { var result = new CollisionResult(); result.collisionFound = false; //Limitar recursividad if (recursionDepth > 5) { return(result); } //Posicion deseada var nextSphereCenter = eSphere.Center + eMovementVector; //Buscar el punto de colision mas cercano de todos los objetos candidatos TGCVector3 q; float t; TGCVector3 n; var minT = float.MaxValue; foreach (var collider in colliders) { //Colisionar Sphere en movimiento contra Collider (cada Collider resuelve la colision) if (collider.intersectMovingElipsoid(eSphere, eMovementVector, eRadius, movementSphere, out t, out q, out n)) { //Quedarse con el menor instante de colision if (t < minT) { minT = t; result.collisionFound = true; result.collisionPoint = q; result.collisionNormal = n; result.collider = collider; } } } //Si nunca hubo colisión, avanzar todo lo requerido if (!result.collisionFound) { //Avanzar todo lo pedido eSphere.moveCenter(eMovementVector); result.realMovmentVector = eMovementVector; result.collisionNormal = TGCVector3.Empty; result.collisionPoint = TGCVector3.Empty; result.collider = null; return(result); } //Solo movernos si ya no estamos muy cerca if (minT >= EPSILON) { //Restar un poco al instante de colision, para movernos hasta casi esa distancia minT -= EPSILON; result.realMovmentVector = eMovementVector * minT; eSphere.moveCenter(result.realMovmentVector); //Quitarle al punto de colision el EPSILON restado al movimiento, para no afectar al plano de sliding var v = TGCVector3.Normalize(result.realMovmentVector); result.collisionPoint -= v * EPSILON; } //Calcular plano de Sliding, como un plano tangete al punto de colision con la esfera, apuntando hacia el centro de la esfera var slidePlaneOrigin = result.collisionPoint; var slidePlaneNormal = eSphere.Center - result.collisionPoint; slidePlaneNormal.Normalize(); var slidePlane = TGCPlane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal); //Calcular vector de movimiento para sliding, proyectando el punto de destino original sobre el plano de sliding var distance = TgcCollisionUtils.distPointPlane(nextSphereCenter, slidePlane); var newDestinationPoint = nextSphereCenter - distance * slidePlaneNormal; var slideMovementVector = newDestinationPoint - result.collisionPoint; //No hacer recursividad si es muy pequeño slideMovementVector.Scale(SlideFactor); if (slideMovementVector.Length() < EPSILON) { return(result); } //Ver si posee la suficiente pendiente en Y para hacer sliding if (result.collisionNormal.Y <= slidingMinY) { //Recursividad para aplicar sliding doCollideWithWorld(eSphere, slideMovementVector, eRadius, colliders, recursionDepth + 1, movementSphere, slidingMinY); } return(result); }
public TGCPlane GetPlaneOfCollision(TgcRay ray, Vehicle car) { return(TGCPlane.FromPointNormal(this.GetPosition(), TGCVector3.Up)); }
/// <summary> /// Detección de colisiones recursiva /// </summary> public void doCollideWithWorld(TgcBoundingSphere characterSphere, TGCVector3 movementVector, List <TgcBoundingAxisAlignBox> obstaculos, int recursionDepth) { //Limitar recursividad if (recursionDepth > 5) { return; } //Ver si la distancia a recorrer es para tener en cuenta var distanceToTravelSq = movementVector.LengthSq(); if (distanceToTravelSq < EPSILON) { return; } //Posicion deseada var originalSphereCenter = characterSphere.Center; var nextSphereCenter = originalSphereCenter + movementVector; //Buscar el punto de colision mas cercano de todos los objetos candidatos var minCollisionDistSq = float.MaxValue; var realMovementVector = movementVector; TgcBoundingAxisAlignBox.Face collisionFace = null; TgcBoundingAxisAlignBox collisionObstacle = null; var nearestPolygonIntersectionPoint = TGCVector3.Empty; foreach (var obstaculoBB in obstaculos) { //Obtener los polígonos que conforman las 6 caras del BoundingBox var bbFaces = obstaculoBB.computeFaces(); foreach (var bbFace in bbFaces) { var pNormal = TgcCollisionUtils.getPlaneNormal(bbFace.Plane); var movementRay = new TgcRay(originalSphereCenter, movementVector); float brutePlaneDist; TGCVector3 brutePlaneIntersectionPoint; if ( !TgcCollisionUtils.intersectRayPlane(movementRay, bbFace.Plane, out brutePlaneDist, out brutePlaneIntersectionPoint)) { continue; } var movementRadiusLengthSq = TGCVector3.Multiply(movementVector, characterSphere.Radius).LengthSq(); if (brutePlaneDist * brutePlaneDist > movementRadiusLengthSq) { continue; } //Obtener punto de colisión en el plano, según la normal del plano float pDist; TGCVector3 planeIntersectionPoint; TGCVector3 sphereIntersectionPoint; var planeNormalRay = new TgcRay(originalSphereCenter, -pNormal); var embebbed = false; var collisionFound = false; if (TgcCollisionUtils.intersectRayPlane(planeNormalRay, bbFace.Plane, out pDist, out planeIntersectionPoint)) { //Ver si el plano está embebido en la esfera if (pDist <= characterSphere.Radius) { embebbed = true; //TODO: REVISAR ESTO, caso embebido a analizar con más detalle sphereIntersectionPoint = originalSphereCenter - pNormal * characterSphere.Radius; } //Esta fuera de la esfera else { //Obtener punto de colisión del contorno de la esfera según la normal del plano sphereIntersectionPoint = originalSphereCenter - TGCVector3.Multiply(pNormal, characterSphere.Radius); //Disparar un rayo desde el contorno de la esfera hacia el plano, con el vector de movimiento var sphereMovementRay = new TgcRay(sphereIntersectionPoint, movementVector); if ( !TgcCollisionUtils.intersectRayPlane(sphereMovementRay, bbFace.Plane, out pDist, out planeIntersectionPoint)) { //no hay colisión continue; } } //Ver si planeIntersectionPoint pertenece al polígono TGCVector3 newMovementVector; float newMoveDistSq; TGCVector3 polygonIntersectionPoint; if (pointInBounbingBoxFace(planeIntersectionPoint, bbFace)) { if (embebbed) { //TODO: REVISAR ESTO, nunca debería pasar //throw new Exception("El polígono está dentro de la esfera"); } polygonIntersectionPoint = planeIntersectionPoint; collisionFound = true; } else { //Buscar el punto mas cercano planeIntersectionPoint que tiene el polígono real de esta cara polygonIntersectionPoint = TgcCollisionUtils.closestPointRectangle3d(planeIntersectionPoint, bbFace.Extremes[0], bbFace.Extremes[1], bbFace.Extremes[2]); //Revertir el vector de velocidad desde el nuevo polygonIntersectionPoint para ver donde colisiona la esfera, si es que llega var reversePointSeg = polygonIntersectionPoint - movementVector; if (TgcCollisionUtils.intersectSegmentSphere(polygonIntersectionPoint, reversePointSeg, characterSphere, out pDist, out sphereIntersectionPoint)) { collisionFound = true; } } if (collisionFound) { //Nuevo vector de movimiento acotado newMovementVector = polygonIntersectionPoint - sphereIntersectionPoint; newMoveDistSq = newMovementVector.LengthSq(); if (newMoveDistSq <= distanceToTravelSq && newMoveDistSq < minCollisionDistSq) { minCollisionDistSq = newMoveDistSq; realMovementVector = newMovementVector; nearestPolygonIntersectionPoint = polygonIntersectionPoint; collisionFace = bbFace; collisionObstacle = obstaculoBB; } } } } } //Si nunca hubo colisión, avanzar todo lo requerido if (collisionFace == null) { //Avanzar hasta muy cerca var movementLength = movementVector.Length(); movementVector.Multiply((movementLength - EPSILON) / movementLength); characterSphere.moveCenter(movementVector); return; } //Solo movernos si ya no estamos muy cerca if (minCollisionDistSq >= EPSILON) { //Mover el BoundingSphere hasta casi la nueva posición real var movementLength = realMovementVector.Length(); realMovementVector.Multiply((movementLength - EPSILON) / movementLength); characterSphere.moveCenter(realMovementVector); } //Calcular plano de Sliding var slidePlaneOrigin = nearestPolygonIntersectionPoint; var slidePlaneNormal = characterSphere.Center - nearestPolygonIntersectionPoint; slidePlaneNormal.Normalize(); var slidePlane = TGCPlane.FromPointNormal(slidePlaneOrigin, slidePlaneNormal); //Proyectamos el punto original de destino en el plano de sliding var slideRay = new TgcRay(nearestPolygonIntersectionPoint + TGCVector3.Multiply(movementVector, SlideFactor), slidePlaneNormal); float slideT; TGCVector3 slideDestinationPoint; if (TgcCollisionUtils.intersectRayPlane(slideRay, slidePlane, out slideT, out slideDestinationPoint)) { //Nuevo vector de movimiento var slideMovementVector = slideDestinationPoint - nearestPolygonIntersectionPoint; if (slideMovementVector.LengthSq() < EPSILON) { return; } //Recursividad para aplicar sliding doCollideWithWorld(characterSphere, slideMovementVector, obstaculos, recursionDepth + 1); } }