package com.siondream.core.entity.systems;
import java.util.Comparator;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.BitmapFont.TextBounds;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.renderers.OrthogonalTiledMapRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.esotericsoftware.spine.SkeletonRenderer;
import com.siondream.core.Env;
import com.siondream.core.entity.components.ColorComponent;
import com.siondream.core.entity.components.MapComponent;
import com.siondream.core.entity.components.ParticleComponent;
import com.siondream.core.entity.components.SpineComponent;
import com.siondream.core.entity.components.TextureComponent;
import com.siondream.core.entity.components.TransformComponent;
import ashley.core.Engine;
import ashley.core.Entity;
import ashley.core.EntitySystem;
import ashley.core.Family;
import ashley.utils.IntMap;
import ashley.utils.IntMap.Values;
public class RenderingSystem extends EntitySystem implements Disposable {
protected SpriteBatch batch;
protected OrthographicCamera camera;
protected Viewport viewport;
protected OrthographicCamera uiCamera;
protected Viewport uiViewport;
protected ShapeRenderer shapeRenderer;
protected IntMap<Entity> mapEntities;
private IntMap<Entity> worldEntities;
private IntMap<Entity> particleEntities;
private IntMap<Entity> spineAnimatedEntities;
private Array<Entity> sortedEntities;
private DepthSorter sorter;
private Box2DDebugRenderer box2DRenderer;
private TiledMap map;
private OrthogonalTiledMapRenderer mapRenderer;
private FrameBuffer particleFrameBuffer;
private TextureRegion particleRegion;
private SkeletonRenderer skeletonRenderer;
private BitmapFont debugFont;
private int previousWidth;
private int previousHeight;
public RenderingSystem() {
super();
this.sortedEntities = new Array<Entity>(100);
this.batch = new SpriteBatch();
this.camera = Env.game.getCamera();
this.viewport = Env.game.getViewport();
this.uiCamera = Env.game.getUICamera();
this.uiViewport = Env.game.getUIViewport();
this.sorter = new DepthSorter();
this.shapeRenderer = new ShapeRenderer();
this.box2DRenderer = new Box2DDebugRenderer(Env.drawBodies,
Env.drawJoints,
Env.drawABBs,
Env.drawInactiveBodies,
Env.drawVelocities,
Env.drawContacts);
this.particleFrameBuffer = new FrameBuffer(Pixmap.Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
this.particleRegion = new TextureRegion(particleFrameBuffer.getColorBufferTexture());
this.particleRegion.flip(false, true);
this.skeletonRenderer = new SkeletonRenderer();
if (Env.debug) {
debugFont = Env.game.getAssets().get("data/ui/default.fnt", BitmapFont.class);
}
}
@Override
public void addedToEngine(Engine engine) {
super.addedToEngine(engine);
worldEntities = engine.getEntitiesFor(Family.getFamilyFor(TextureComponent.class, TransformComponent.class));
particleEntities = engine.getEntitiesFor(Family.getFamilyFor(ParticleComponent.class));
mapEntities = engine.getEntitiesFor(Family.getFamilyFor(MapComponent.class));
spineAnimatedEntities = engine.getEntitiesFor(Family.getFamilyFor(SpineComponent.class));
}
@Override
public void update(float deltaTime) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
viewport.update(width, height);
renderMap();
batch.begin();
renderWorldEntities();
batch.end();
renderParticles();
uiViewport.update(width, height);
uiCamera.position.set(uiViewport.getWorldWidth() * 0.5f, uiViewport.getWorldHeight() * 0.5f, 0.0f);
Env.game.getStage().draw();
viewport.update(width, height);
debugDrawWorld();
uiViewport.update(width, height);
debugDrawUI();
if (previousWidth != width || previousHeight != height) {
Env.game.resize(width, height);
previousWidth = width;
previousHeight = height;
}
}
@Override
public void dispose() {
if (mapRenderer != null) {
mapRenderer.dispose();
mapRenderer = null;
}
batch.dispose();
shapeRenderer.dispose();
box2DRenderer.dispose();
// particleFrameBuffer.dispose();
}
protected void renderWorldEntities() {
Values<Entity> values = worldEntities.values();
while (values.hasNext()) {
Entity entity = values.next();
TextureComponent texture = entity.getComponent(TextureComponent.class);
TransformComponent transform = entity.getComponent(TransformComponent.class);
if (isInFustrum(texture, transform)) {
sortedEntities.add(entity);
}
}
values = spineAnimatedEntities.values();
while (values.hasNext()) {
sortedEntities.add(values.next());
}
sortedEntities.sort(sorter);
batch.setProjectionMatrix(camera.combined);
for (Entity entity : sortedEntities) {
if (entity.hasComponent(TextureComponent.class)) {
TextureComponent texture = entity.getComponent(TextureComponent.class);
TransformComponent transform = entity.getComponent(TransformComponent.class);
float scale = transform.scale * Env.pixelsToMetres;
float width = texture.region.getRegionWidth();
float height = texture.region.getRegionHeight();
float originX = width * 0.5f;
float originY = height * 0.5f;
batch.draw(texture.region,
transform.position.x - originX,
transform.position.y - originY,
originX,
originY,
width,
height,
scale,
scale,
MathUtils.radiansToDegrees * transform.angle);
}
else {
SpineComponent animation = entity.getComponent(SpineComponent.class);
skeletonRenderer.draw(batch, animation.skeleton);
}
}
sortedEntities.clear();
}
protected void renderMap() {
// If there are no map entities, dispose the renderer
if (mapEntities.size == 0) {
if (mapRenderer != null) {
mapRenderer.dispose();
mapRenderer = null;
}
}
else {
Entity mapEntity = mapEntities.values().next();
MapComponent mapComponent = mapEntity.getComponent(MapComponent.class);
if (map != mapComponent.map) {
if (mapRenderer != null) {
mapRenderer.dispose();
}
map = mapComponent.map;
mapRenderer = new OrthogonalTiledMapRenderer(map, Env.pixelsToMetres);
}
// Render
mapRenderer.setView(camera);
mapRenderer.render();
}
}
protected void renderUI() {
Env.game.getStage().draw();
}
protected void renderParticles() {
particleFrameBuffer.begin();
batch.begin();
Color initialColor = batch.getColor();
Values<Entity> values = particleEntities.values();
while (values.hasNext()) {
Entity entity = values.next();
ParticleComponent particle = entity.getComponent(ParticleComponent.class);
ColorComponent color = entity.getComponent(ColorComponent.class);
if (color != null) {
batch.setColor(color.color);
}
particle.effect.draw(batch);
batch.setColor(initialColor);
}
batch.end();
particleFrameBuffer.end();
batch.begin();
batch.draw(particleRegion, 0.0f, 0.0f);
batch.end();
}
protected void debugDrawWorld() {
if (Env.debug) {
shapeRenderer.setProjectionMatrix(camera.combined);
if (Env.drawGrid) {
// Debug shapes
shapeRenderer.setColor(1.0f, 0.0f, 0.0f, 1.0f);
shapeRenderer.begin(ShapeType.Line);
shapeRenderer.line(-Env.virtualWidth * 0.5f, 0.0f, Env.virtualWidth * 0.5f, 0.0f);
shapeRenderer.line(0.0f, -Env.virtualHeight * 0.5f, 0.0f, Env.virtualHeight * 0.5f);
shapeRenderer.setColor(0.0f, 1.0f, 0.0f, 1.0f);
for (int i = -100; i <= 100; ++i) {
if (i == 0)
continue;
shapeRenderer.line(-Env.virtualWidth * 0.5f, i, Env.virtualWidth * 0.5f, i);
}
for (int i = -100; i <= 100; ++i) {
if (i == 0)
continue;
shapeRenderer.line(i, -Env.virtualHeight * 0.5f, i, Env.virtualHeight * 0.5f);
}
shapeRenderer.end();
}
box2DRenderer.setDrawAABBs(Env.drawABBs);
box2DRenderer.setDrawBodies(Env.drawBodies);
box2DRenderer.setDrawContacts(Env.drawContacts);
box2DRenderer.setDrawInactiveBodies(Env.drawInactiveBodies);
box2DRenderer.setDrawJoints(Env.drawJoints);
box2DRenderer.setDrawVelocities(Env.drawVelocities);
box2DRenderer.render(Env.game.getWorld(), camera.combined);
}
}
protected void debugDrawUI() {
if (Env.debug) {
if (Env.drawFPS) {
String fpsText = String.format("%d FPS", Gdx.graphics.getFramesPerSecond());
TextBounds bounds = debugFont.getBounds(fpsText);
batch.setProjectionMatrix(uiCamera.combined);
batch.begin();
debugFont.setColor(1.0f, 1.0f, 1.0f, 1.0f);
debugFont.draw(batch, fpsText, uiViewport.getWorldWidth() - bounds.width - 20.0f, 20.0f);
batch.end();
}
Table.drawDebug(Env.game.getStage());
}
}
private boolean isInFustrum(TextureComponent texture, TransformComponent transform) {
if (camera == null) {
return false;
}
Vector3 cameraPos = camera.position;
Vector3 position = transform.position;
float width = texture.region.getRegionWidth();
float height = texture.region.getRegionHeight();
float originX = width * 0.5f;
float originY = height * 0.5f;
float scale = transform.scale;
float halfWidth = camera.viewportWidth * 0.5f;
float halfHeight = camera.viewportHeight * 0.5f;
if (position.x + width * scale - originX < cameraPos.x - halfWidth || position.x - originX > cameraPos.x + halfWidth) return false;
if (position.y + height * scale - originY < cameraPos.y - halfHeight || position.y - originY > cameraPos.y + halfHeight) return false;
return true;
}
private class DepthSorter implements Comparator<Entity> {
@Override
public int compare(Entity e1, Entity e2) {
TransformComponent t1 = e1.getComponent(TransformComponent.class);
TransformComponent t2 = e2.getComponent(TransformComponent.class);
if (t1 == null) return -1;
if (t2 == null) return 1;
return (int)(t2.position.z - t1.position.z);
}
}
}