2016-10-18 6 views
0

중복으로 표시하기 전에. 나는 네가 비슷하게 말한 제목에 몇 가지 질문이 있다는 것을 알고 싶다. 그러나 나는 그것들을 읽었으며 그것들은 크게 다르다.3D Collision Mesh (보다 효율적인 충돌 계산)

저는 최근에 가장 복잡한 3D 메시에서부터 가장 복잡한 3D 메시에 이르기까지 어디에서나 충돌을 감지하는 완벽한 시스템을 완성했습니다. 문제는 내 엔진에서 게임 플레이 경험이 매우 비효율적이고 비용이 많이 든다는 것입니다. 부수적으로, 나는이 코드를 완벽하게 만들었다. 아무런 참고 문헌도없이이 충돌을 독자적으로 처리 할 수 ​​있는지 알아보기 위해. 엉망으로해서 미안해. 그래서 더 이상 신경 쓰지 않고 여기에 중요한 코드가 있습니다.

package nope; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

import org.lwjgl.util.vector.Vector3f; 

import net.aionstudios.nightfall.entities.Entity; 
import net.aionstudios.nightfall.renderEngine.model.TexturedModel; 

public class ColliderEntity extends Entity { 

private List<CollisionMesh> entityBounds = new ArrayList<CollisionMesh>(); 
private boolean alertCollisions = false; 

public ColliderEntity(TexturedModel model, Vector3f position, float rotX, float rotY, float rotZ, float scale, BoundingBox entityBounds) { 
    super(model, position, rotX, rotY, rotZ, scale); 
    this.entityBounds.add(entityBounds); 
} 

public List<ColliderEntity> detectImpact(List<ColliderEntity> colliders){ 
    List<ColliderEntity> colE = new ArrayList<ColliderEntity>(); 
    colE.clear(); 
    for (ColliderEntity ce : colliders) { 
     if(ce != this) { 
      Vector3f boundsOffsets = new Vector3f(difference(this.getPosition().x, ce.getPosition().x), difference(this.getPosition().y, ce.getPosition().y), difference(this.getPosition().z, ce.getPosition().z)); 
      boolean xCollide = false; 
      boolean yCollide = false; 
      boolean zCollide = false; 
      for (CollisionMesh b1 : this.getEntityBounds()){ 
       for(MeshPoint mp : b1.getPoints()){ 
        List<Vector3f> points = mp.getConnectionsAndPoint(); 
        for (CollisionMesh b2 : ce.getEntityBounds()) { 
         for(MeshPoint mp2 : b2.getPoints()){ 
          List<Vector3f> points2 = mp2.getConnectionsAndPoint(); 
          for (Vector3f pt : points2){ 
           pt = new Vector3f(pt.x-boundsOffsets.x, pt.y-boundsOffsets.y, pt.z-boundsOffsets.z); 
           for (int i = 1; i < points.size(); i++){ 
            if(!xCollide || !yCollide || !zCollide){ 
             if(points.get(i-1).x > pt.x && pt.x > points.get(i).x) { 
              xCollide = true; 
             } 
             if(points.get(i-1).y > pt.y && pt.y > points.get(i).y) { 
              yCollide = true; 
             } 
             if(points.get(i-1).z > pt.z && pt.z > points.get(i).z) { 
              zCollide = true; 
             } 
            } 
           } 
          } 
          if(!!xCollide || !yCollide || !zCollide){ 
           for (Vector3f pts : points){ 
            pts = new Vector3f(pts.x-boundsOffsets.x, pts.y-boundsOffsets.y, pts.z-boundsOffsets.z); 
            for (int i = 1; i < points2.size(); i++){ 
             if(!xCollide || !yCollide || !zCollide){ 
              if(points2.get(i-1).x > pts.x && pts.x > points2.get(i).x) { 
               xCollide = true; 
              } 
              if(points2.get(i-1).y > pts.y && pts.y > points2.get(i).y) { 
               yCollide = true; 
              } 
              if(points2.get(i-1).z > pts.z && pts.z > points2.get(i).z) { 
               zCollide = true; 
              } 
             } 
            } 
           } 
          } 
          if(xCollide && yCollide && zCollide){ 
           colE.add(ce); 
           if(alertCollisions) { 
            System.out.println("Collision on Entity "+this.toString()+" at: "+this.getPosition().x+" "+this.getPosition().y+" "+this.getPosition().z+" with Entity "+ce.toString()+" at: "+ce.getPosition().x+" "+ce.getPosition().y+" "+ce.getPosition().z); 
           } 
          } 
         } 
        } 
       } 
      } 
     } 
    } 
    return colE; 
} 

private float difference(float x, float x1){ 
    float dx = x - x1; 

    return (float) Math.sqrt(dx * dx); 
} 

public boolean isAlertCollisions() { 
    return alertCollisions; 
} 

public void setAlertCollisions(boolean alertCollisions) { 
    this.alertCollisions = alertCollisions; 
} 

public List<CollisionMesh> getEntityBounds() { 
    return entityBounds; 
} 

public void addEntityBounds(BoundingBox b){ 
    this.entityBounds.add(b); 
} 

public void removeEntityBounds(BoundingBox b){ 
    this.entityBounds.remove(entityBounds); 
} 

} 

이 클래스는 충돌 메시가있는 엔티티입니다. 그리고 충격 감지. 여기에 무슨 일이 일어나고 있는지 이해하려면 좀 더 통찰력이 필요합니다.

package nope; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

import org.lwjgl.util.vector.Vector3f; 

public class CollisionMesh { 

private List<MeshPoint> points = new ArrayList<MeshPoint>(); 

public CollisionMesh(MeshPoint[] points){ 
    for(MeshPoint p : points){ 
     this.points.add(p); 
    } 
} 

public List<MeshPoint> getPoints() { 
    return points; 
} 

public void addMeshPoint(MeshPoint point){ 
    for (MeshPoint p : points){ 
     if(point == p){ 
      return; 
     } 
    } 
    points.add(point); 
} 

public void removeMeshPoint(MeshPoint point){ 
    for(MeshPoint p : points){ 
     if(p == point){ 
      points.remove(point); 
      return; 
     } 
    } 
    cleanupMeshPoints(); 
} 

public void cleanupMeshPoints(){ 
    for(MeshPoint p : points){ 
     for(Vector3f pi : p.getConnections()){ 
      boolean connected = false; 
      for(MeshPoint p2 : points){ 
       if(p2.getPoint() == pi){ 
        connected = true; 
       } 
      } 
      if(!connected){ 
       p.getConnections().remove(pi); 
      } 
     } 
    } 
} 

} 

이것은 충돌 가능한 엔티티에 제공된 충돌 메쉬이며, 연결을 저장하는 개별 메쉬 포인트로 구성됩니다. 그 클래스는 다음과 같습니다.

package nope; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

import org.lwjgl.util.vector.Vector3f; 

public class MeshPoint { 

private Vector3f point; 
private List<Vector3f> connections = new ArrayList<Vector3f>(); 

public MeshPoint(Vector3f point, Vector3f[] connections){ 
    this.point = point; 
    for(Vector3f connection : connections){ 
     this.connections.add(connection); 
    } 
} 

public Vector3f getPoint() { 
    return point; 
} 

public void setPoint(Vector3f point) { 
    this.point = point; 
} 

public List<Vector3f> getConnections() { 
    return connections; 
} 

public List<Vector3f> getConnectionsAndPoint() { 
    List<Vector3f> cp = connections; 
    cp.add(this.point); 
    return cp; 
} 

public void addConnection(Vector3f connection){ 
    for (Vector3f c : connections){ 
     if(c.x == connection.x && c.y == connection.y && c.z == connection.z){ 
      return; 
     } 
    } 
    connections.add(connection); 
} 

public void removeConnection(Vector3f connection){ 
    for (Vector3f c : connections){ 
     if(c.x == connection.x && c.y == connection.y && c.z == connection.z){ 
      connections.remove(connection); 
      return; 
     } 
    } 
} 

} 

메쉬 연결은 게임의 프레임 속도를 실제로 죽이고 있다고 생각합니다. 2 개의 상자처럼 단순한 객체가 활성화 된 경우 120의 프레임 뚜껑에서 일반적으로 약 3까지 떨어집니다. 몇 가지 문제를 식별 할 수 있지만이 코드를 현재보다 덜 복잡하게 만들 수는 없습니다. 어떤 도움이라도 대단히 감사합니다.

나는 이와 같은 질문이 일반적으로 잘 받아 들여지지 않을 것이고 여기에 오는 많은 사람들은 최소한의 완전한 예제를 찾고있을 것입니다.하지만이 사실을 확인하기 위해 할 일이 전혀 없었습니다. 그렇습니다.

+1

코드를 프로파일 링했는지 궁금하십니까? 비효율적 인 코드의 특정 영역을 강조 표시하면 대답하는 것이 더 쉬울 수도 있습니다. 메쉬 연결이라고 생각한다고 말씀하셨습니다. 그러나 코드 검사를 통해 성능 병목 현상을 추측하는 것은 매우 어렵습니다. 프로파일 링은 튜닝 노력에 집중하기 위해 내가해야 할 첫 번째 일이 될 것입니다. – sprinter

+2

물어 보는 질문에 대해 더 명확히 할 수 있습니까? 문제를 설명합니다 ("내 엔진에서 게임 플레이 경험에 비싸고 비효율적이며 비용이 많이 듭니다.").하지만 우리가 원하는 도움이나 어떤 부분을 조사해야하는지 명시 적으로 나타내지는 않습니다. –

+0

필자는 Bullet 라이브러리 (예, Java라고 알고 있음)와 Broad Phase, Narrow Phase의 개념을 항상 이렇게 지적합니다. 광역 단계는 교차 할 수있는 요소 (구형 구체, 상자 상자 등)를 매우 빠르게 결정합니다. 좁은 위상은 엄격하고 느린 삼각형 교차 테스트입니다.이 테스트는 모델에있는 메타 데이터에 따라 광범위하게/좁게 나누어 질 수 있습니다. – Robinson

답변

1

제안 :

  1. detectImpact만큼이나 6 개 중첩주기. 성능이 떨어지는 것은 놀라운 일이 아닙니다. 중첩 수를 줄일 수 있습니까? 그렇지 않은 경우, 해당주기에서 고려한 데이터를 최소한 전제 조건으로 할 수 있습니까? (예 : 모든 꼭지점을 고려하지 않고 경계 상자가 겹치는 부분 만 고려하십시오. 경계 상자 교차가 이 아님이 대부분의 꼭지점을 포함하기를 바랍니다. 충돌 감지가 적절한 작업을 수행하지 않은 경우 이전 단계에서, 물체가 "얽히게"되기 전에).

  2. detectImpact 가장 안쪽 사이클은 for (int i = 1; i < points.size(); i++)의 형태이다. 사이클 중에 size()이 변경됩니까? 그렇지 않다면 일반적인 인터페이스의 (가상) 메소드 호출의 요점은 무엇입니까? 제안 : size을 저장할 로컬 변수를 만들고 사용하십시오. 더 나은 여전히 ​​foreach/for-in 양식을 사용해보십시오. better performance than the "iterating by index"입니다 (예, 가장 안쪽 사이클이 1에서 시작한다는 것을 알았습니다.주기 내 첫 번째 단계는 건너 뜁니다). 이것이 가장 안쪽 루프이기 때문에 모든 비트가 중요합니다.

  3. 메쉬의 꼭지점/가장자리 /면이 생성 된 후에는 거의 수정하지 않으므로 목록 대신 배열을 사용하는 것이 좋습니다. 그렇습니다, 자기 조절 컨테이너의 유연성을 갖는 것이 좋지만 ... 무료 점심과 같은 그런 것은 없으며 성과는 당신이 지불하는 지갑입니다.
    메시 (mesh)에 정점/모서리를 추가 할 때 - 자체 조정 모음을 편리하게 사용할 수 있습니다.) 및 "고정/사후 빌드 - 단계"를 사용하여 메쉬 개체의 라이프 사이클을 두 단계로 구분할 수 있습니다. 컨테이너가 아닌 배열을 사용할 때).유연성과 성능을 모두 얻을 수 있으며 "코드 복잡성"계정에서 비용을 지불하게됩니다.

+0

1. 미리 정의 할 수있는 몇 가지 작은 변수가 있지만, 실제로 영역 2의 모든 엔티티에서 테스트 된 점을 좁히는 방법이 없습니다. 실제로 수행 할 수있는 것은 없습니다. 테스트는 점진적으로 수행됩니다 3. 변경 가능하고 완전한 단계는 엄청난 성능 향상이 될 것입니다 ... 그리고 각 항목에 모든 것을 적용하여 배열로 옮길 수 있습니다. 정말 고맙습니다. –