package com.bitwaffle.spaceout.entities.passive.particles;
import java.util.Random;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Quaternion;
import org.lwjgl.util.vector.Vector3f;
import com.bitwaffle.spaceguts.entities.Entities;
import com.bitwaffle.spaceguts.entities.Entity;
import com.bitwaffle.spaceguts.graphics.render.Render3D;
import com.bitwaffle.spaceguts.graphics.shapes.Circle2D;
import com.bitwaffle.spaceguts.util.Noise;
import com.bitwaffle.spaceguts.util.QuaternionHelper;
import com.bitwaffle.spaceout.resources.Textures;
/**
* Fancy-looking space debris effect using perlin noise.
*
* @author TranquilMarmot
* @see Entity
*
*/
public class Debris extends Entity {
/** array to hold all the stars */
public Particle[] particles;
/** random number generator */
Random randy;
/** the entity that the star field generates stars around */
public Entity following;
/** number of stars to have at a time */
public int numParticles;
/** how far to draw the stars */
public float distance;
private static Circle2D circle = new Circle2D(0.85f, 1);
/**
* The random floats between 1.0f and 0.0f are multiplied by this when added
* to a random stars location. Making it a lot bigger makes the stars
* generate farther apart
*/
private final float JUMP_AMOUNT = 1500000.0f;
/**
* Create some cool space debris so you know which way you're going
*
* @param following
* The entity that the debris is being generated around
* @param numParticles
* How many particles to generate
* @param distance
* How far out to generate the particles
* @param seed
* Seed for the random number generator
*/
public Debris(Entity following, int numParticles, float distance, long seed) {
// initialize variables
super();
this.type = "debris";
this.following = following;
this.numParticles = numParticles;
this.distance = distance;
rotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
// initialize random number generator
randy = new Random(seed);
// set the location of the debris field to the entity that it's
// following
this.location.x = following.location.x;
this.location.y = following.location.y;
this.location.z = following.location.z;
// initialize the array of particles
particles = new Particle[numParticles];
// each star is generated using the location of the previous star
float prevX = location.x + randy.nextFloat() * 10.0f;
float prevY = location.y + randy.nextFloat() * 10.0f;
float prevZ = location.z - randy.nextFloat() * 100.0f;
// fill the array
for (int i = 0; i < numParticles; i++) {
// generate a random particle and put it into the array
Particle p = getRandomParticle(prevX, prevY, prevZ);
particles[i] = p;
// update the variables that keep track of previous particle
// location
prevX = p.location.x;
prevY = p.location.y;
prevZ = p.location.z;
}
}
/**
* Goes through all the particles owned by this Debris field and checks to
* see if any have gone out of range (i.e. the player/camera moved) If a
* particle has gone out of range, it is replaced with a new, randomly
* generated one
*/
public void update(float timeStep) {
// update the location of the debris field
this.location.x = following.location.x;
this.location.y = following.location.y;
this.location.z = following.location.z;
// loop through all the particles
for (int i = 0; i < numParticles; i++) {
Particle s = particles[i];
// grab the particle's location
float tempX = s.location.x;
float tempY = s.location.y;
float tempZ = s.location.z;
// noise value generated by perlin noise used for changing star
// location
float noise = (float) Noise.noise((double) s.location.x,
(double) s.location.y, (double) s.location.z);
/*
* This checks along all three axes to see if the particle has gone
* too far (it's location is > or < the location of the debris field
* + the distance the field was initialized with) If the particle
* has gone too far, it sets the location that the new star is
* generated at at the opposite end that it just left. So, if a
* particle goes too far on, say, the X axis in the positive
* direction (particle x > debris field x + distance) then it is
* replaces with a particle generated at the opposite end (debris
* field x - distance)
*/
if (s.location.x > this.location.x + distance)
tempX = this.location.x - distance
+ (noise * (JUMP_AMOUNT / 10));
else if (s.location.x < this.location.x - distance)
tempX = this.location.x + distance
- (noise * (JUMP_AMOUNT / 10));
if (s.location.z > this.location.z + distance)
tempZ = this.location.z - distance
+ (noise * (JUMP_AMOUNT / 10));
else if (s.location.z < this.location.z - distance)
tempZ = this.location.z + distance
- (noise * (JUMP_AMOUNT / 10));
if (s.location.y > this.location.y + distance)
tempY = this.location.y - distance
+ (noise * (JUMP_AMOUNT / 10));
else if (s.location.y < this.location.y - distance)
tempY = this.location.y + distance
- (noise * (JUMP_AMOUNT / 10));
// if the temporary location variables have changed at all, it means
// the particle we're checking has gone out of the debris field
// distance and that a new one needs to replace it
if (tempX != s.location.x || tempY != s.location.y
|| tempZ != s.location.z) {
particles[i].location.set(tempX, tempY, tempZ);
}
}
}
/**
* Generates a random star based on previous star coordinates
*
* @param prevX
* X coordinate of previous star
* @param prevY
* Y coordinate of previous star
* @param prevZ
* Z coordinate of previous star
* @return A new random star
*/
public Particle getRandomParticle(float prevX, float prevY, float prevZ) {
float x = prevX;
float y = prevY;
float z = prevZ;
// noise value generated by perlin noise used for changing star location
float noise = (float) (Noise.noise((double) x, (double) y, (double) z));
// For each axis, this decides whether it wants to go up or down
if (randy.nextBoolean())
x += noise * JUMP_AMOUNT;
else
x -= noise * JUMP_AMOUNT;
if (randy.nextBoolean())
y += noise * JUMP_AMOUNT;
else
y -= noise * JUMP_AMOUNT;
if (randy.nextBoolean()) {
if (randy.nextBoolean())
z += noise * (JUMP_AMOUNT / 2);
else
z -= noise * (JUMP_AMOUNT / 2);
}
float size = noise * 200.0f;
Particle ret = new Particle(x, y, z, size);
return ret;
}
@Override
public void draw() {
// we don't want lighting for our particles
//Render3D.program.setUniform("Light.LightEnabled", false);
Quaternion revQuat = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f);
Entities.camera.rotation.negate(revQuat);
// bind a white texture
Textures.WHITE.texture().bind();
GL30.glBindVertexArray(circle.getVaoHandle());
// loop through all the particles to draw them
for (Particle s : particles) {
s.update();
// translate to the star
float transx = this.location.x - s.location.x;
float transy = this.location.y - s.location.y;
float transz = this.location.z - s.location.z;
Matrix4f oldModelview = new Matrix4f(Render3D.modelview);{
// translate and scale the modelview to match the star
Render3D.modelview.translate(new Vector3f(transx, transy, transz));
Matrix4f.mul(Render3D.modelview, QuaternionHelper.toMatrix(Entities.camera.rotation), Render3D.modelview);
Render3D.modelview.scale(new Vector3f(s.size, s.size, s.size));
Render3D.program.setUniform("ModelViewMatrix", Render3D.modelview);
// draw the star
GL11.glDrawArrays(GL11.GL_TRIANGLE_FAN, 0, circle.getNumIndices());
}Render3D.modelview = oldModelview;
}
// don't forget to re-enable lighting!
//Render3D.program.setUniform("Light.LightEnabled", true);
}
/**
* A debris particle.
*
*/
private class Particle {
/** the size of the particle */
float size;
/** the location of the particle */
Vector3f location;
public Particle(float x, float y, float z, float size) {
location = new Vector3f(x, y, z);
this.size = size;
}
public void update(){
/*
float scale = 10.0f;
if(randy.nextBoolean()){
if(randy.nextBoolean())
this.location.x += randy.nextFloat() * scale;
else
this.location.x -= randy.nextFloat() * scale;
}
if(randy.nextBoolean()){
if(randy.nextBoolean())
this.location.y += randy.nextFloat() * scale;
else
this.location.y -= randy.nextFloat() * scale;
}
if(randy.nextBoolean()){
if(randy.nextBoolean())
this.location.z += randy.nextFloat() * scale;
else
this.location.z -= randy.nextFloat() * scale;
}
*/
}
}
@Override
public void cleanup() {
}
}