package com.bitwaffle.spaceguts.entities.particles.trail;
import java.util.LinkedList;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Quaternion;
import org.lwjgl.util.vector.Vector3f;
import com.bitwaffle.spaceguts.entities.DynamicEntity;
import com.bitwaffle.spaceguts.graphics.render.Render3D;
import com.bitwaffle.spaceguts.util.QuaternionHelper;
import com.bitwaffle.spaceout.resources.Textures;
/**
* A trail that follows a {@link DynamicEntity}
* Whenever the entity owning the Trail calls its update() and draw() methods,
* the Trail's update() and draw() methods should be called as well.
* @author TranquilMarmot
*
*/
public class Trail {
/** All the actual links */
protected LinkedList<TrailLink> chain;
/** Handles the vertex array object for this trail */
private TrailRenderer renderer;
/** Texture to use for each link */
protected Textures linkTex;
/** Used for preserving the modelview matrix */
private static Matrix4f oldModelView = new Matrix4f();
/** Entity that the trail is coming from */
private DynamicEntity following;
/** Offset from the center of the entity the trail is following */
private Vector3f offset;
/** How long this trail is allowed to get */
public int length;
/** How wide each link is */
private float width;
/** How often the trail sends out a new link */
private float updateSpeed = 0.05f;
/** Counter to know when to update */
private float timeSinceUpdate = 0.0f;
/**
* Create a trail
* @param following Entity the trail is coming from
* @param length How long the trail is
* @param width How wide each link in the trail is
* @param linkTexture The texture to use for rendering the trail links
* @param offset Offset from following's center
*/
public Trail(DynamicEntity following, int length, float width, Textures linkTexture, Vector3f offset){
this.linkTex = linkTexture;
this.following = following;
this.length = length;
this.offset = offset;
this.width = width;
chain = new LinkedList<TrailLink>();
while(chain.size() < length)
addLink();
renderer = new TrailRenderer(this, linkTex);
}
/**
* Update the trail
* @param timeStep How long has passed since the last update
*/
public void update(float timeStep){
// update timer
timeSinceUpdate += timeStep;
// only add a link if the timer is up
if(timeSinceUpdate >= updateSpeed){
// reset timer
timeSinceUpdate = 0.0f;
// if we haven't reached out length, add a new link
if(chain.size() < length){
addLink();
} else{
// remove the last link (addLink() adds to the front of the list)
chain.removeLast();
addLink();
}
// let the renderer know that it needs to update the vertex buffers
renderer.updateVBO();
}
}
/**
* Adds a new link to the front of the list
*/
private void addLink(){
// rotate the offset by the current rotation
Vector3f offsetRot = QuaternionHelper.rotateVectorByQuaternion(offset, following.rotation);
Vector3f start = new Vector3f();
// add location to offset
Vector3f.add(following.location, offsetRot, start);
// top point for link
Vector3f top = QuaternionHelper.rotateVectorByQuaternion(new Vector3f(width / 2, 0.0f, 0.0f), this.following.rotation);
// bottom point for link
Vector3f bottom = QuaternionHelper.rotateVectorByQuaternion(new Vector3f(-width / 2, 0.0f, 0.0f), this.following.rotation);
// add to the starting vector
Vector3f.add(start, top, top);
Vector3f.add(start, bottom, bottom);
// add new link to chain
chain.addFirst(new TrailLink(top, bottom));
}
/**
* Draws the trail
*/
public void draw(){
if(chain.size() > 0){
// disable lighting and enable blending
Render3D.program.setUniform("Light.LightEnabled", false);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
linkTex.texture().bind();
oldModelView.load(Render3D.modelview);{
/*
* Basically what we have to do here is undo all the translations
* done when rendering whatever we're following is translated to inside
* of Render3D. This is because all the points created for the trail
* are positioned directly behind what we're following, so the translations
* throw everything off.
*/
// to undo rotation
Quaternion revQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
Quaternion.negate(this.following.rotation, revQuat);
Matrix4f.mul(Render3D.modelview, QuaternionHelper.toMatrix(revQuat), Render3D.modelview);
// to undo translation
Vector3f undo = new Vector3f();
this.following.location.negate(undo);
Render3D.modelview.translate(undo);
// we want to translate to the last link
TrailLink last = chain.getLast();
// get the middle of the top and bottom of the last link, so it renders in the middle
float midX = (last.top.x + last.bottom.x) / 2;
float midY = (last.top.y + last.bottom.y) / 2;
float midZ = (last.top.z + last.bottom.z) / 2;
// offset by given amount, rotated (so that it's from the center of the entity)
Vector3f offsetRot = QuaternionHelper.rotateVectorByQuaternion(this.offset, this.following.rotation);
// to translate to the last link
float transX = following.location.x - midX + offsetRot.x;
float transY = following.location.y - midY + offsetRot.y;
float transZ = following.location.z - midZ + offsetRot.z;
Render3D.modelview.translate(new Vector3f(transX, transY, transZ));
Render3D.program.setUniform("ModelViewMatrix", Render3D.modelview);
renderer.draw();
}Render3D.modelview.load(oldModelView);
// Don't forget to re-disable blending and re-enable lighting!
GL11.glDisable(GL11.GL_BLEND);
Render3D.program.setUniform("Light.LightEnabled", true);
}
}
}