
// Sprite3D.java
/* From: 
     Java Game Development with LibGDX, 2nd ed.
     Lee Stemkoski, Apress, 2018
     Chapter 16
*/
/* Modified version of his BaseActor3D by Andrew Davison, 
   ad@coe.psu.ac.th, Feb. 2023

   Removed Stage3D field and remove(), getList(), count()
*/


package com.mygdx.game;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.g3d.*;
import com.badlogic.gdx.graphics.g3d.attributes.*;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.math.*;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.math.Intersector.MinimumTranslationVector;

import java.util.ArrayList;


public class Sprite3D
{
  private Model model;
  private ModelInstance modelInst;

  private final Vector3 position;
  private final Quaternion rotation;
  private final Vector3 scale;

  private Polygon boundingPolygon;



  public Sprite3D(Model m, ModelInstance mi,
                      float x, float y, float z)
  { model = m;
    modelInst = mi;

    position  = new Vector3(x,y,z);
    rotation  = new Quaternion();
    scale     = new Vector3(1,1,1);

    boundingPolygon = null;
  } // end of Sprite3D()


  public Matrix4 calculateTransform()
  {  return new Matrix4(position, rotation, scale);  }


  public void act(float dt)
  {  modelInst.transform.set( calculateTransform() );  }


  public void draw(ModelBatch batch, Environment env)
  {  batch.render(modelInst, env);  }


  public void dispose()
  {  model.dispose();  }



  public void setColor(Color c)
  {
    for (Material m : modelInst.materials)
      m.set( ColorAttribute.createDiffuse(c) );
  }


  public void loadTexture(String fileName)
  {
    Texture tex = new Texture(Gdx.files.internal(fileName), true);
    tex.setFilter( TextureFilter.Linear, TextureFilter.Linear );

    for (Material m : modelInst.materials)
      m.set( TextureAttribute.createDiffuse(tex) );
  } /* loadTexture */


  // ------------------- movement ----------------------

  public Vector3 getPosition()
  {  return position;  }

  public void setPosition(Vector3 v)
  {  position.set(v);  }

  public void setPosition(float x, float y, float z)
  {  position.set(x,y,z);  }


  public void moveBy(Vector3 v)
  {  position.add(v);  }

  public void moveBy(float x, float y, float z)
  {  moveBy( new Vector3(x,y,z) );  }


  public void moveForward(float dist)
  {  moveBy( rotation.transform( new Vector3(0,0,-1) ).scl( dist ) );  }


  public void moveUp(float dist)
  {  moveBy( rotation.transform( new Vector3(0,1,0) ).scl( dist ) );  }


  public void moveRight(float dist)
  {  moveBy( rotation.transform( new Vector3(1,0,0) ).scl( dist ) );  }


  public float getTurnAngle()
  {   return rotation.getAngleAround(0,-1,0);  }

  public void setTurnAngle(float degrees)
  {  rotation.set( new Quaternion(Vector3.Y, degrees) );  }


  public void turnBy(float degrees)
  {  rotation.mul( new Quaternion(Vector3.Y, -degrees) );  }


  public void setScale(float x, float y, float z)
  {  scale.set(x,y,z);  }


  // ------------ 2D collision detection ------------------

  public void setBaseRectangle()
  {
    BoundingBox modelBounds =
                 modelInst.calculateBoundingBox( new BoundingBox());
    Vector3 max = modelBounds.max;
    Vector3 min = modelBounds.min;

    float[] vertices =
        { max.x, max.z,  min.x, max.z, 
          min.x, min.z,  max.x, min.z  };
    boundingPolygon = new Polygon(vertices);
    boundingPolygon.setOrigin(0,0);
  } /* setBaseRectangle */


  public void setBasePolygon()
  {
    BoundingBox modelBounds =
                 modelInst.calculateBoundingBox( new BoundingBox());
    Vector3 max = modelBounds.max;
    Vector3 min = modelBounds.min;

    float a = 0.75f;     // offset amount.
    float[] vertices =
        { max.x, 0,   a*max.x, a*max.z, 
          0, max.z,   a*min.x, a*max.z,
          min.x, 0,   a*min.x, a*min.z, 
          0, min.z,   a*max.x, a*min.z   };
    boundingPolygon = new Polygon(vertices);
    boundingPolygon.setOrigin(0,0);
  } /* setBasePolygon */


  public Polygon getBoundaryPolygon()
  {
    boundingPolygon.setPosition(position.x, position.z);
    boundingPolygon.setRotation(getTurnAngle());
    boundingPolygon.setScale(scale.x, scale.z);
    return boundingPolygon;
  } /* getBoundaryPolygon */


  public boolean overlaps(Sprite3D other)
  {
    Polygon poly1 = this.getBoundaryPolygon();
    Polygon poly2 = other.getBoundaryPolygon();
    if (!poly1.getBoundingRectangle().
                overlaps(poly2.getBoundingRectangle()))
      return false;

    MinimumTranslationVector mtv = new MinimumTranslationVector();
    return Intersector.overlapConvexPolygons(poly1, poly2, mtv);
  } /* overlaps */


  public void preventOverlap(Sprite3D other)
  {
    Polygon poly1 = this.getBoundaryPolygon();
    Polygon poly2 = other.getBoundaryPolygon();
    // initial test to improve performance
    if (!poly1.getBoundingRectangle().
                 overlaps(poly2.getBoundingRectangle()))
      return;

    MinimumTranslationVector mtv = new MinimumTranslationVector();
    boolean polygonOverlap =
            Intersector.overlapConvexPolygons(poly1, poly2, mtv);
    if (polygonOverlap)
      this.moveBy(mtv.normal.x * mtv.depth, 0, mtv.normal.y * mtv.depth);
  } /* preventOverlap */


}  // end of Sprite3D class
