package org.terasology.componentSystem;
import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glCallList;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glEndList;
import static org.lwjgl.opengl.GL11.glGenLists;
import static org.lwjgl.opengl.GL11.glNewList;
import static org.lwjgl.opengl.GL11.glPopMatrix;
import static org.lwjgl.opengl.GL11.glPushMatrix;
import static org.lwjgl.opengl.GL11.glScalef;
import static org.lwjgl.opengl.GL11.glTranslated;
import static org.lwjgl.opengl.GL11.glTranslatef;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TObjectIntHashMap;
import java.nio.FloatBuffer;
import java.util.Iterator;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;
import javax.vecmath.Vector4f;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.spout.api.material.BlockMaterial;
import org.spout.api.material.MaterialRegistry;
import org.terasology.components.BlockParticleEffectComponent;
import org.terasology.components.BlockParticleEffectComponent.Particle;
import org.terasology.components.world.LocationComponent;
import org.terasology.entitySystem.EntityManager;
import org.terasology.entitySystem.EntityRef;
import org.terasology.entitySystem.RegisterComponentSystem;
import org.terasology.game.CoreRegistry;
import org.terasology.logic.manager.ShaderManager;
import org.terasology.logic.world.WorldProvider;
import org.terasology.math.Side;
import org.terasology.rendering.shader.ShaderProgram;
import org.terasology.rendering.world.WorldRenderer;
import org.terasology.teraspout.TeraBlock;
import org.terasology.teraspout.TeraSpout;
import org.terasology.utilities.FastRandom;
/**
* @author Immortius <immortius@gmail.com>
*/
// TODO: Generalise for non-block particles
@RegisterComponentSystem(headedOnly = true)
public class BlockParticleEmitterSystem implements RenderSystem {
private static final int PARTICLES_PER_UPDATE = 32;
private static final float TEX_SIZE = TeraBlock.TEXTURE_OFFSET / 4f;
private EntityManager entityManager;
private WorldProvider worldProvider;
// TODO: lose dependency on worldRenderer?
private WorldRenderer worldRenderer;
private FastRandom random = new FastRandom();
private TObjectIntMap displayLists;
public void initialise() {
entityManager = CoreRegistry.get(EntityManager.class);
worldProvider = CoreRegistry.get(WorldProvider.class);
worldRenderer = CoreRegistry.get(WorldRenderer.class);
displayLists = new TObjectIntHashMap();
}
@Override
public void shutdown() {
}
public void update(float delta) {
for (EntityRef entity : entityManager.iteratorEntities(BlockParticleEffectComponent.class, LocationComponent.class)) {
BlockParticleEffectComponent particleEffect = entity.getComponent(BlockParticleEffectComponent.class);
Iterator<Particle> iterator = particleEffect.particles.iterator();
while (iterator.hasNext()) {
BlockParticleEffectComponent.Particle p = iterator.next();
p.lifeRemaining -= delta;
if (p.lifeRemaining <= 0) {
iterator.remove();
} else {
updateVelocity(entity, particleEffect, p, delta);
updatePosition(p, delta);
}
}
for (int i = 0; particleEffect.spawnCount > 0 && i < PARTICLES_PER_UPDATE; ++i) {
spawnParticle(particleEffect);
}
if (particleEffect.particles.size() == 0 && particleEffect.destroyEntityOnCompletion) {
entity.destroy();
} else {
entity.saveComponent(particleEffect);
}
}
}
private void spawnParticle(BlockParticleEffectComponent particleEffect) {
Particle p = new Particle();
p.lifeRemaining = random.randomPosFloat() * (particleEffect.maxLifespan - particleEffect.maxLifespan) + particleEffect.minLifespan;
p.velocity.set(particleEffect.initialVelocityRange.x * random.randomFloat(), particleEffect.initialVelocityRange.y * random.randomFloat(), particleEffect.initialVelocityRange.z * random.randomFloat());
p.size = random.randomPosFloat() * (particleEffect.maxSize - particleEffect.minSize) + particleEffect.minSize;
p.position.set(particleEffect.spawnRange.x * random.randomFloat(), particleEffect.spawnRange.y * random.randomFloat(), particleEffect.spawnRange.z * random.randomFloat());
p.texOffset.set(random.randomPosFloat() * (TeraBlock.TEXTURE_OFFSET - TEX_SIZE), random.randomPosFloat() * (TeraBlock.TEXTURE_OFFSET - TEX_SIZE));
//p.texSize.set(TEX_SIZE,TEX_SIZE);
particleEffect.particles.add(p);
particleEffect.spawnCount--;
}
protected void updateVelocity(EntityRef entity, BlockParticleEffectComponent particleEffect, Particle particle, float delta) {
Vector3f diff = new Vector3f(particleEffect.targetVelocity);
diff.sub(particle.velocity);
diff.x *= particleEffect.acceleration.x * delta;
diff.y *= particleEffect.acceleration.y * delta;
diff.z *= particleEffect.acceleration.z * delta;
particle.velocity.add(diff);
if (particleEffect.collideWithBlocks == true) {
LocationComponent location = entity.getComponent(LocationComponent.class);
Vector3f pos = location.getWorldPosition();
pos.add(particle.position);
if (worldProvider.getBlock(new Vector3f(pos.x, pos.y + 2 * Math.signum(particle.velocity.y) * particle.size, pos.z)).getId() != 0x0)
particle.velocity.y = 0;
}
}
protected void updatePosition(Particle particle, float delta) {
particle.position.x += particle.velocity.x * delta;
particle.position.y += particle.velocity.y * delta;
particle.position.z += particle.velocity.z * delta;
}
public void renderTransparent() {
ShaderManager.getInstance().enableShader("particle");
glDisable(GL11.GL_CULL_FACE);
Vector3d cameraPosition = worldRenderer.getActiveCamera().getPosition();
for (EntityRef entity : entityManager.iteratorEntities(BlockParticleEffectComponent.class, LocationComponent.class)) {
LocationComponent location = entity.getComponent(LocationComponent.class);
Vector3f worldPos = location.getWorldPosition();
if (!worldProvider.isBlockActive(worldPos)) {
continue;
}
double temperature = worldProvider.getBiomeProvider().getTemperatureAt((int) worldPos.x, (int) worldPos.z);
double humidity = worldProvider.getBiomeProvider().getHumidityAt((int) worldPos.x, (int) worldPos.z);
glPushMatrix();
glTranslated(worldPos.x - cameraPosition.x, worldPos.y - cameraPosition.y, worldPos.z - cameraPosition.z);
BlockParticleEffectComponent particleEffect = entity.getComponent(BlockParticleEffectComponent.class);
if (particleEffect.blockType == null) {
return;
}
for (Particle particle : particleEffect.particles) {
glPushMatrix();
glTranslatef(particle.position.x, particle.position.y, particle.position.z);
applyOrientation();
glScalef(particle.size, particle.size, particle.size);
float light = worldRenderer.getRenderingLightValueAt(new Vector3f(worldPos.x + particle.position.x, worldPos.y + particle.position.y, worldPos.z + particle.position.z));
renderParticle(particle, particleEffect.blockType.getArchetypeBlock().getId(), temperature, humidity, light);
glPopMatrix();
}
glPopMatrix();
}
glEnable(GL11.GL_CULL_FACE);
}
private void applyOrientation() {
// Fetch the current modelview matrix
final FloatBuffer model = BufferUtils.createFloatBuffer(16);
GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, model);
// And undo all rotations and scaling
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j)
model.put(i * 4 + j, 1.0f);
else
model.put(i * 4 + j, 0.0f);
}
}
GL11.glLoadMatrix(model);
}
protected void renderParticle(Particle particle, short blockType, double temperature, double humidity, float light) {
int displayList = displayLists.get(TeraSpout.getInstance().getBlock((BlockMaterial) MaterialRegistry.get(blockType)).getBlockFamily());
if (displayList == 0) {
displayList = glGenLists(1);
glNewList(displayList, GL11.GL_COMPILE);
drawParticle(blockType);
glEndList();
displayLists.put(TeraSpout.getInstance().getBlock((BlockMaterial) MaterialRegistry.get(blockType)).getBlockFamily(), displayList);
}
ShaderProgram shader = ShaderManager.getInstance().getShaderProgram("particle");
Vector4f color = TeraSpout.getInstance().getBlock((BlockMaterial) MaterialRegistry.get(blockType)).calcColorOffsetFor(Side.FRONT);
shader.setFloat3("colorOffset", color.x, color.y, color.z);
shader.setFloat("texOffsetX", particle.texOffset.x);
shader.setFloat("texOffsetY", particle.texOffset.y);
shader.setFloat("light", light);
glCallList(displayList);
}
private void drawParticle(short blockType) {
TeraBlock b = TeraSpout.getInstance().getBlock((BlockMaterial) MaterialRegistry.get(blockType));
glBegin(GL_QUADS);
GL11.glTexCoord2f(b.calcTextureOffsetFor(Side.FRONT).x, b.calcTextureOffsetFor(Side.FRONT).y);
GL11.glVertex3f(-0.5f, -0.5f, 0.0f);
GL11.glTexCoord2f(b.calcTextureOffsetFor(Side.FRONT).x + TEX_SIZE, b.calcTextureOffsetFor(Side.FRONT).y);
GL11.glVertex3f(0.5f, -0.5f, 0.0f);
GL11.glTexCoord2f(b.calcTextureOffsetFor(Side.FRONT).x + TEX_SIZE, b.calcTextureOffsetFor(Side.FRONT).y + TEX_SIZE);
GL11.glVertex3f(0.5f, 0.5f, 0.0f);
GL11.glTexCoord2f(b.calcTextureOffsetFor(Side.FRONT).x, b.calcTextureOffsetFor(Side.FRONT).y + TEX_SIZE);
GL11.glVertex3f(-0.5f, 0.5f, 0.0f);
glEnd();
}
public void renderOpaque() {
}
public void renderOverlay() {
}
public void renderFirstPerson() {
}
}