package com.bitwaffle.spaceout.entities.passive; import java.util.ArrayList; import java.util.Random; import java.util.Stack; 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.Entities; import com.bitwaffle.spaceguts.entities.Entity; import com.bitwaffle.spaceguts.graphics.render.Render3D; import com.bitwaffle.spaceguts.util.QuaternionHelper; import com.bitwaffle.spaceout.entities.dynamic.Asteroid; import com.bitwaffle.spaceout.resources.Models; import com.bitwaffle.spaceout.resources.Textures; import com.bulletphysics.linearmath.Transform; /** * Creates asteroids on a timer and keeps em all within a given box * @author TranquilMarmot */ public class AsteroidField extends Entity{ /** Random number generator */ private Random randy; /** List of all asteroids */ private ArrayList<Asteroid> asteroids; /** Asteroids to add to field on next update (to avoid ConcurrentModificationException) */ private Stack<Asteroid> toAdd; /** Number of maximum asteroids */ private int numAsteroids; /** How fast the field creates asteroids */ private float releaseInterval, lastRelease = 0.0f; /** Min/max size of created asteroids */ private float minSize, maxSize; /** How far/fast the asteroids go when created */ private Vector3f range, asteroidSpeed; /** * Create an asteroid field * @param location Location of field * @param range How far the field reaches (if asteroids get this far away from the field's location, they loop around) * @param asteroidSpeed How fast the created asteroids move * @param numAsteroids Maximum number of asteroids * @param initialAsteroids How many asteroids to create when this field is added * @param releaseInterval How fast to release new asteroids * @param minSize Minimum asteroid size * @param maxSize Maximum asteroid size */ public AsteroidField(Vector3f location, Vector3f range, Vector3f asteroidSpeed, int numAsteroids, int initialAsteroids, float releaseInterval, float minSize, float maxSize){ this.numAsteroids = numAsteroids; asteroids = new ArrayList<Asteroid>(numAsteroids); toAdd = new Stack<Asteroid>(); this.releaseInterval = releaseInterval; this.minSize = minSize; this.maxSize = maxSize; this.location = location; this.rotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); this.range = range; this.asteroidSpeed = asteroidSpeed; randy = new Random(); randy.setSeed(System.currentTimeMillis()); for(int i = 0; i < initialAsteroids; i++) releaseAsteroid(); } /** * Tells the field to create a new asteroid */ public void releaseAsteroid(){ float asteroidX = randy.nextBoolean() ? randy.nextFloat() * range.x : randy.nextFloat() * -range.x; float asteroidY = randy.nextBoolean() ? randy.nextFloat() * range.y : randy.nextFloat() * -range.y; float asteroidZ = randy.nextBoolean() ? randy.nextFloat() * range.z : randy.nextFloat() * -range.z; float asteroidSize = minSize + (randy.nextFloat() * ((maxSize - minSize) + 1)); // just to make things interesting if(randy.nextInt(100) == 42) asteroidSize *= 7.5f; Quaternion asteroidRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); QuaternionHelper.rotate(asteroidRotation, new Vector3f(randy.nextInt(90), randy.nextInt(90), randy.nextInt(90))); Asteroid a = new Asteroid(new Vector3f(asteroidX, asteroidY, asteroidZ), asteroidRotation, asteroidSize, this); float impulseX = randy.nextBoolean() ? randy.nextFloat() * asteroidSpeed.x: randy.nextFloat() * asteroidSpeed.x; float impulseY = randy.nextBoolean() ? randy.nextFloat() * asteroidSpeed.y: randy.nextFloat() * -asteroidSpeed.y; float impulseZ = randy.nextBoolean() ? randy.nextFloat() * asteroidSpeed.z: randy.nextFloat() * -asteroidSpeed.z; a.rigidBody.applyCentralImpulse(new javax.vecmath.Vector3f(impulseX, impulseY, impulseZ)); Entities.addDynamicEntity(a); //asteroids.add(a); } @Override /** * This goes through and makes sure all the asteroids in the field are within the field's range */ public void update(float timeStep) { lastRelease += timeStep; // let an asteroid go if it's time if(asteroids.size() <= numAsteroids && lastRelease >= releaseInterval){ releaseAsteroid(); lastRelease = 0.0f; } // to keep track of asteroids to remove from the list, to avoid ConcurrentModificationException Stack<Asteroid> toRemove = new Stack<Asteroid>(); for(Asteroid a : asteroids){ // check if it's time to remove the asteroid from the field if(a.removeFlag){ toRemove.push(a); } else{ // loop asteroids around if they're out of bounds Transform trans = new Transform(); a.rigidBody.getWorldTransform(trans); if(a.location.x > this.location.x + (range.x * 2.0f) + a.getSize()) trans.origin.x = this.location.x - (range.x * 2.0f) - a.getSize(); else if(a.location.x < this.location.x - (range.x * 2.0f) - a.getSize()) trans.origin.x = this.location.x + (range.x * 2.0f) + a.getSize(); if(a.location.y > this.location.y + (range.y * 2.0f) + a.getSize()) trans.origin.y = this.location.y - (range.y * 2.0f) - a.getSize(); else if(a.location.y < this.location.y - (range.y * 2.0f) - a.getSize()) trans.origin.y = this.location.y + (range.y * 2.0f) + a.getSize(); if(a.location.z > this.location.z + (range.z * 2.0f) + a.getSize()) trans.origin.z = this.location.z - (range.z * 2.0f) - a.getSize(); else if(a.location.z < this.location.z - (range.z * 2.0f) - a.getSize()) trans.origin.z = this.location.z + (range.z * 2.0f) + a.getSize(); a.rigidBody.setWorldTransform(trans); } } while(!toRemove.isEmpty()) asteroids.remove(toRemove.pop()); while(!toAdd.isEmpty()) asteroids.add(toAdd.pop()); } /** * Add an asteroid to the field * @param a Asteroid to add */ public void addAsteroidToField(Asteroid a){ toAdd.push(a); } @Override public void draw() { Render3D.program.setUniform("Light.LightEnabled", false); GL11.glEnable(GL11.GL_BLEND); Matrix4f oldModelview = new Matrix4f(); oldModelview.load(Render3D.modelview); Render3D.modelview.scale(new Vector3f(range.x * 2, range.y * 2, range.z * 2)); Render3D.program.setUniform("ModelViewMatrix", Render3D.modelview); Textures.TRANSPARENT_CHECKERS.texture().bind(); Models.BOX.getModel().render(); Render3D.modelview.load(oldModelview); GL11.glDisable(GL11.GL_BLEND); Render3D.program.setUniform("Light.LightEnabled", true); } @Override public void cleanup() {} }