Исправление проблем с кубами в libgdx

Я пытаюсь разработать игру, в которой я отображаю до 300 кубов на экране. Производительность modelBatch при создании новой модели экземпляра для каждого куба ужасна. Насколько я знаю, не существует 3d-пакета, который объединяет все кубы в один колл-колл. Так что я отчаянно пытаюсь как-то их заморозить.

Этот вопрос напрямую связан с этим: LibGDX 3D увеличить производительность

Опубликованный ответ успешно объединяет все кубы, но когда добавляется среда, чтобы получить некоторое освещение, кажется, что у кубов есть недостающие стороны или что-то еще не так с ними.

Вот картинка:

Вот мой класс куба (в значительной степени скопированный из ответа выше)

public class Cube {

  int index;
  int vertexFloatSize;
  int posOffset;
  int norOffset;
  boolean hasColor;
  int colOffset;
  private Vector3 position = new Vector3();
  private Matrix4 rotationTransform = new Matrix4().idt();

  private Color color = new Color();
  public float halfWidth, halfHeight, halfDepth;
  private boolean transformDirty = false;
  private boolean colorDirty = false;

  static final Vector3 CORNER000 = new Vector3();
  static final Vector3 CORNER010 = new Vector3();
  static final Vector3 CORNER100 = new Vector3();
  static final Vector3 CORNER110 = new Vector3();
  static final Vector3 CORNER001 = new Vector3();
  static final Vector3 CORNER011 = new Vector3();
  static final Vector3 CORNER101 = new Vector3();
  static final Vector3 CORNER111 = new Vector3();

  static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
  static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
  static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
  static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
  static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
  static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
  static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};

  static final Vector3 NORMAL0 = new Vector3();
  static final Vector3 NORMAL1 = new Vector3();
  static final Vector3 NORMAL2 = new Vector3();
  static final Vector3 NORMAL3 = new Vector3();
  static final Vector3 NORMAL4 = new Vector3();
  static final Vector3 NORMAL5 = new Vector3();
  static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};

  float[] meshVertices;

  public Cube(float x, float y, float z, float width, float height, float depth, int index, 
        VertexAttributes vertexAttributes, float[] meshVertices){

      position.set(x,y,z);
      this.halfWidth = width/2;
      this.halfHeight = height/2;
      this.halfDepth = depth/2;
      this.index = index;
      this.meshVertices = meshVertices;

      NORMAL0.set(0,0,-1);
      NORMAL1.set(0,0,1);
      NORMAL2.set(-1,0,0);
      NORMAL3.set(1,0,0);
      NORMAL4.set(0,-1,0);
      NORMAL5.set(0,1,0);

      vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
      posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
      norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;

      VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
      hasColor = colorAttribute!=null;
      if (hasColor){
          colOffset = colorAttribute.offset/4;
          this.setColor(Color.WHITE);
      }
      transformDirty = true;

  }

  public void setDimensions(float x, float y , float z){

      this.halfWidth = x/2;
      this.halfHeight = y/2;
      this.halfDepth = z/2;

  }

  public void setIndex(int index){

      this.index = index;
      transformDirty = true;
      colorDirty = true;

  }

  /**
   * Call this after moving and/or rotating.
   */
  public void update(){

      if (colorDirty && hasColor){
          for (int faceIndex= 0; faceIndex<6; faceIndex++){
              int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
              for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
                  int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
                  meshVertices[vertexIndex] = color.r;
                  meshVertices[++vertexIndex] = color.g;
                  meshVertices[++vertexIndex] = color.b;
                  meshVertices[++vertexIndex] = color.a;
              }
          }
          colorDirty = false;
      }


      if (!transformDirty){
          return;
      }

      transformDirty = false;

      CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
      CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
      CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
      CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
    CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);

      NORMAL0.set(0,0,-1).rot(rotationTransform);
      NORMAL1.set(0,0,1).rot(rotationTransform);
      NORMAL2.set(-1,0,0).rot(rotationTransform);
      NORMAL3.set(1,0,0).rot(rotationTransform);
      NORMAL4.set(0,-1,0).rot(rotationTransform);
      NORMAL5.set(0,1,0).rot(rotationTransform);

      for (int faceIndex= 0; faceIndex<6; faceIndex++){
          int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
          for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
              int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
              meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
              meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
              meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;

              vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
              meshVertices[vertexIndex] = NORMALS[faceIndex].x;
              meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
              meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
          }
      }
  }

  public Cube setColor(Color color){

      if (hasColor){
          this.color.set(color);
          colorDirty = true;
      }
      return this;
  }

  public void setAlpha(float alpha) {

       if (hasColor){

           this.color.set(this.color.r, this.color.g, this.color.b, alpha);
           colorDirty = true;

       }
  }

  public Cube translate(float x, float y, float z){
      position.add(x,y,z);
      transformDirty = true;
      return this;
  }

  public Cube setPosition(float x, float y, float z){
      position.set(x,y,z);
      transformDirty = true;
      return this;
  }

public Cube setPosition(Vector3 position1) {

       position.set(position1);
       transformDirty = true;
       return this;

  }

  public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
      int len = attributes.size();
      for (int i = 0; i < len; i++)
          if (attributes.get(i).usage == usage) return attributes.get(i);

      return null;
  }



  public Vector3 getPosition() {

      return this.position;
  }

}

И вот тестовый пример, который я создал, чтобы проверить кубы.

public class TestCase {

    ModelInstance mBatchedCubesModelInstance;
    Mesh mBatchedCubesMesh;
    float[] mBatchedCubesVertices;
    Array<Cube> mBatchedCubes;

    TestCase(){

            int width = 5;
            int height = 5;
            int length = 5;
            int numCubes = width*height*length;

            ModelBuilder mb = new ModelBuilder();
            mb.begin();
            MeshPartBuilder mpb = mb.part("cubes", GL20.GL_TRIANGLES, (Usage.Position | Usage.Normal | Usage.Color), new Material());
            for (int i=0; i<numCubes; i++){
                mpb.box(1, 1, 1);
            }
            Model model = mb.end();
            mBatchedCubesModelInstance = new ModelInstance(model);

            mBatchedCubesMesh = model.meshes.get(0);
            VertexAttributes vertexAttributes = mBatchedCubesMesh.getVertexAttributes();
            int vertexFloatSize = vertexAttributes .vertexSize / 4; //4 bytes per float
            mBatchedCubesVertices = new float[numCubes * 24 * vertexFloatSize]; //24 unique vertices per cube
            mBatchedCubesMesh.getVertices(mBatchedCubesVertices);

            mBatchedCubes = new Array<Cube>(numCubes);
            int cubeNum = 0;
            for (int x = 0; x < width; x++) {
                    for (int y = 0; y < height; y++) {
                            for (int z = 0; z < length; z++) {
                                    mBatchedCubes.add(new Cube((x-(width/2f))*1.5f, -((y-(height/2f)) * 1.5f), -(z-(length/2f))*1.5f, 1,1,1, cubeNum++, vertexAttributes, mBatchedCubesVertices ).setColor(Colors.getMixedColor()));
                            }
                    }
            }

    }

    public void render(ModelBatch batch, Environment environment){

            for (Cube cube : mBatchedCubes){ //must update any changed cubes.
                cube.update();
            }
            mBatchedCubesMesh.setVertices(mBatchedCubesVertices); //apply changes to mesh

            if(environment!=null) batch.render(mBatchedCubesModelInstance,environment);
            else batch.render(this.mBatchedCubesModelInstance);

    }

}

Кубы даже не двигаются в моей игре, поэтому мне даже не нужно преобразование. Мне просто нужно установить цвета (включая альфа-канал) каждого кадра. Пакетная сетка также должна работать с теневым отображением libgdx (я надеюсь, что он работает автоматически, когда у вас есть правильно пакетный экземпляр модели).

Может ли кто-нибудь сказать мне, что не так с моим кодом и почему некоторые лица не отображаются. Я понимаю, что могу много просить, поэтому я вознагражу за этот вопрос (50 баллов) через два дня.

РЕДАКТИРОВАТЬ: После ответа от Tenfour04 все стало намного лучше. Альфа-канал, кажется, работает, странная проблема с лицом, кажется, исчезла. Однако, когда я применил изменения к своей реальной игре, я заметил, что иногда мука рисует поверх игрового ландшафта. Я обновил тест, чтобы проиллюстрировать проблему, добавив большую плоскость в середине кубов. Сделал видео: https://www.youtube.com/watch?v=LQhSMJfuyZY.

Я также хотел бы уточнить, что я не использую никаких пользовательских шейдеров. Использование только методов ModelBatch.begin() и.end() без дополнительных вызовов openGl.

1 ответ

Решение

Я вижу пару проблем:

  1. Вы смешиваетесь и / или ваш шейдер не устанавливает альфа-значение

  2. Вы не отбрасываете лица.

Поэтому, когда вы создаете новый материал, а не просто new Material(), используйте:

new Material(
    IntAttribute.createCullFace(GL20.GL_FRONT),//For some reason, libgdx ModelBuilder makes boxes with faces wound in reverse, so cull FRONT
    new BlendingAttribute(1f), //opaque since multiplied by vertex color
    new DepthTestAttribute(false), //don't want depth mask or rear cubes might not show through
    ColorAttribute.createDiffuse(Color.WHITE) //white since multiplied by vertex color
    );

Вам также нужно будет отсортировать кубы по расстоянию от камеры, чтобы их альфа правильно наслоились. Вот обновленный класс Cube, который поддерживает сортировку. Он должен иметь возможность отслеживать цвет независимо от массива вершин в случае изменения его индекса:

public class Cube implements Comparable<Cube>{

    private int index;
    int vertexFloatSize;
    int posOffset;
    int norOffset;
    boolean hasColor;
    int colOffset;
    private Vector3 position = new Vector3();
    private Matrix4 rotationTransform = new Matrix4().idt();
    public float halfWidth, halfHeight, halfDepth;
    private boolean transformDirty = false;
    private boolean colorDirty = false;
    private Color color = new Color();
    float camDistSquared;

    static final Vector3 CORNER000 = new Vector3();
    static final Vector3 CORNER010 = new Vector3();
    static final Vector3 CORNER100 = new Vector3();
    static final Vector3 CORNER110 = new Vector3();
    static final Vector3 CORNER001 = new Vector3();
    static final Vector3 CORNER011 = new Vector3();
    static final Vector3 CORNER101 = new Vector3();
    static final Vector3 CORNER111 = new Vector3();

    static final Vector3[] FACE0 = {CORNER000, CORNER100, CORNER110, CORNER010};
    static final Vector3[] FACE1 = {CORNER101, CORNER001, CORNER011, CORNER111};
    static final Vector3[] FACE2 = {CORNER000, CORNER010, CORNER011, CORNER001};
    static final Vector3[] FACE3 = {CORNER101, CORNER111, CORNER110, CORNER100};
    static final Vector3[] FACE4 = {CORNER101, CORNER100, CORNER000, CORNER001};
    static final Vector3[] FACE5 = {CORNER110, CORNER111, CORNER011, CORNER010};
    static final Vector3[][] FACES = {FACE0, FACE1, FACE2, FACE3, FACE4, FACE5};

    static final Vector3 NORMAL0 = new Vector3();
    static final Vector3 NORMAL1 = new Vector3();
    static final Vector3 NORMAL2 = new Vector3();
    static final Vector3 NORMAL3 = new Vector3();
    static final Vector3 NORMAL4 = new Vector3();
    static final Vector3 NORMAL5 = new Vector3();
    static final Vector3[] NORMALS = {NORMAL0, NORMAL1, NORMAL2, NORMAL3, NORMAL4, NORMAL5};

    public Cube(float x, float y, float z, float width, float height, float depth, int index, 
        VertexAttributes vertexAttributes, float[] meshVertices){
    position.set(x,y,z);
    this.halfWidth = width/2;
    this.halfHeight = height/2;
    this.halfDepth = depth/2;
    this.index = index;


    vertexFloatSize = vertexAttributes.vertexSize/4; //4 bytes per float
    posOffset = getVertexAttribute(Usage.Position, vertexAttributes).offset/4;
    norOffset = getVertexAttribute(Usage.Normal, vertexAttributes).offset/4;

    VertexAttribute colorAttribute = getVertexAttribute(Usage.Color, vertexAttributes);
    hasColor = colorAttribute!=null;
    if (hasColor){
        colOffset = colorAttribute.offset/4;
        this.setColor(Color.WHITE, meshVertices);
    }
    transformDirty = true;
    }

    public void updateCameraDistance(Camera cam){
    camDistSquared = cam.position.dst2(position);
    }

    /**
     * Call this after moving and/or rotating.
     */
    public void update(float[] meshVertices){

    if (transformDirty){
        transformDirty = false;

        CORNER000.set(-halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
        CORNER010.set(-halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
        CORNER100.set(halfWidth,-halfHeight,-halfDepth).rot(rotationTransform).add(position);
        CORNER110.set(halfWidth,halfHeight,-halfDepth).rot(rotationTransform).add(position);
        CORNER001.set(-halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
        CORNER011.set(-halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);
        CORNER101.set(halfWidth,-halfHeight,halfDepth).rot(rotationTransform).add(position);
        CORNER111.set(halfWidth,halfHeight,halfDepth).rot(rotationTransform).add(position);

        NORMAL0.set(0,0,-1).rot(rotationTransform);
        NORMAL1.set(0,0,1).rot(rotationTransform);
        NORMAL2.set(-1,0,0).rot(rotationTransform);
        NORMAL3.set(1,0,0).rot(rotationTransform);
        NORMAL4.set(0,-1,0).rot(rotationTransform);
        NORMAL5.set(0,1,0).rot(rotationTransform);

        for (int faceIndex= 0; faceIndex<6; faceIndex++){
        int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
        for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
            int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + posOffset;
            meshVertices[vertexIndex] = FACES[faceIndex][cornerIndex].x;
            meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].y;
            meshVertices[++vertexIndex] = FACES[faceIndex][cornerIndex].z;

            vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + norOffset;
            meshVertices[vertexIndex] = NORMALS[faceIndex].x;
            meshVertices[++vertexIndex] = NORMALS[faceIndex].y;
            meshVertices[++vertexIndex] = NORMALS[faceIndex].z;
        }
        }
    }

    if (colorDirty){
        colorDirty = false;

        for (int faceIndex= 0; faceIndex<6; faceIndex++){
        int baseVertexIndex = (index*24 + faceIndex*4)*vertexFloatSize;//24 unique vertices per cube, 4 unique vertices per face
        for (int cornerIndex=0; cornerIndex<4; cornerIndex++){
            int vertexIndex = baseVertexIndex + cornerIndex*vertexFloatSize + colOffset;
            meshVertices[vertexIndex] = color.r;
            meshVertices[++vertexIndex] = color.g;
            meshVertices[++vertexIndex] = color.b;
            meshVertices[++vertexIndex] = color.a;
        }
        }
    }
    }

    public Cube setColor(Color color, float[] meshVertices){
    if (hasColor){
        this.color.set(color);
        colorDirty = true;

    }
    return this;
    }

    public void setIndex(int index){
    if (this.index != index){
        transformDirty = true;
        colorDirty = true;
        this.index = index;
    }
    }

    public Cube translate(float x, float y, float z){
    position.add(x,y,z);
    transformDirty = true;
    return this;
    }

    public Cube translateTo(float x, float y, float z){
    position.set(x,y,z);
    transformDirty = true;
    return this;
    }

    public Cube rotate(float axisX, float axisY, float axisZ, float degrees){
    rotationTransform.rotate(axisX, axisY, axisZ, degrees);
    transformDirty = true;
    return this;
    }

    public Cube rotateTo(float axisX, float axisY, float axisZ, float degrees){
    rotationTransform.idt();
    rotationTransform.rotate(axisX, axisY, axisZ, degrees);
    transformDirty = true;
    return this;
    }

    public VertexAttribute getVertexAttribute (int usage, VertexAttributes attributes) {
    int len = attributes.size();
    for (int i = 0; i < len; i++)
        if (attributes.get(i).usage == usage) return attributes.get(i);

    return null;
    }

    @Override
    public int compareTo(Cube other) {
    //the cube has a lower index than a cube that is closer to the camera
    if (camDistSquared>other.camDistSquared)
        return -1;
    return camDistSquared<other.camDistSquared ? 1 : 0;
    }
}

И вы бы отсортировали это так:

for (Cube cube : mBatchedCubes){
    cube.updateCameraDistance(camera);
}
mBatchedCubes.sort();
int index = 0;
for (Cube cube : mBatchedCubes){
    cube.setIndex(index++);
}
Другие вопросы по тегам