package com.uwsoft.editor.renderer;
import box2dLight.RayHandler;
import com.badlogic.ashley.core.Component;
import com.badlogic.ashley.core.Engine;
import com.badlogic.ashley.core.Entity;
import com.badlogic.ashley.core.EntityListener;
import com.badlogic.ashley.utils.ImmutableArray;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Scaling;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.ReflectionException;
import com.badlogic.gdx.utils.viewport.ScalingViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import com.uwsoft.editor.renderer.commons.IExternalItemType;
import com.uwsoft.editor.renderer.components.*;
import com.uwsoft.editor.renderer.components.light.LightObjectComponent;
import com.uwsoft.editor.renderer.components.physics.PhysicsBodyComponent;
import com.uwsoft.editor.renderer.data.*;
import com.uwsoft.editor.renderer.factory.EntityFactory;
import com.uwsoft.editor.renderer.physics.PhysicsBodyLoader;
import com.uwsoft.editor.renderer.resources.IResourceRetriever;
import com.uwsoft.editor.renderer.resources.ResourceManager;
import com.uwsoft.editor.renderer.scripts.IScript;
import com.uwsoft.editor.renderer.systems.*;
import com.uwsoft.editor.renderer.systems.action.ActionSystem;
import com.uwsoft.editor.renderer.systems.render.Overlap2dRenderer;
import com.uwsoft.editor.renderer.utils.ComponentRetriever;
/**
* SceneLoader is important part of runtime that utilizes provided
* IResourceRetriever (or creates default one shipped with runtime) in order to
* load entire scene data into viewable actors provides the functionality to get
* root actor of scene and load scenes.
*/
public class SceneLoader {
private String curResolution = "orig";
private SceneVO sceneVO;
private IResourceRetriever rm = null;
public Engine engine = null;
public RayHandler rayHandler;
public World world;
public Entity rootEntity;
public EntityFactory entityFactory;
private float pixelsPerWU = 1;
private Overlap2dRenderer renderer;
private Entity root;
public SceneLoader(World world, RayHandler rayHandler) {
this.world = world;
this.rayHandler = rayHandler;
ResourceManager rm = new ResourceManager();
rm.initAllResources();
this.rm = rm;
this.engine = new Engine();
initSceneLoader();
}
public SceneLoader(IResourceRetriever rm, World world, RayHandler rayHandler) {
this.world = world;
this.rayHandler = rayHandler;
this.engine = new Engine();
this.rm = rm;
initSceneLoader();
}
public SceneLoader() {
this(null, null);
}
public SceneLoader(IResourceRetriever rm) {
this(rm, null, null);
}
/**
* this method is called when rm has loaded all data
*/
private void initSceneLoader() {
if (world == null) {
world = new World(new Vector2(0,-10), true);
} else {
PhysicsBodyLoader.getInstance().mul = 1;
}
if (rayHandler == null) {
RayHandler.setGammaCorrection(true);
RayHandler.useDiffuseLight(true);
rayHandler = new RayHandler(world);
rayHandler.setAmbientLight(1f, 1f, 1f, 1f);
rayHandler.setCulling(true);
rayHandler.setBlur(true);
rayHandler.setBlurNum(3);
rayHandler.setShadows(true);
}
addSystems();
entityFactory = new EntityFactory(rayHandler, world, rm);
}
public void setResolution(String resolutionName) {
ResolutionEntryVO resolution = getRm().getProjectVO().getResolution(resolutionName);
if(resolution != null) {
curResolution = resolutionName;
}
}
public SceneVO getSceneVO() {
return sceneVO;
}
public SceneVO loadScene(String sceneName, Viewport viewport, boolean customLight) {
// this has to be done differently.
engine.removeAllEntities();
entityFactory.clean();
pixelsPerWU = rm.getProjectVO().pixelToWorld;
sceneVO = rm.getSceneVO(sceneName);
world.setGravity(new Vector2(sceneVO.physicsPropertiesVO.gravityX, sceneVO.physicsPropertiesVO.gravityY));
if(sceneVO.composite == null) {
sceneVO.composite = new CompositeVO();
}
rootEntity = entityFactory.createRootEntity(sceneVO.composite, viewport);
engine.addEntity(rootEntity);
if(sceneVO.composite != null) {
entityFactory.initAllChildren(engine, rootEntity, sceneVO.composite);
}
if (!customLight) {
setAmbienceInfo(sceneVO);
}
rayHandler.useCustomViewport(viewport.getScreenX(), viewport.getScreenY(), viewport.getScreenWidth(), viewport.getScreenHeight());
return sceneVO;
}
public SceneVO loadScene(String sceneName, Viewport viewport) {
return loadScene(sceneName, viewport, false);
}
public SceneVO loadScene(String sceneName) {
return loadScene(sceneName, false);
}
public SceneVO loadScene(String sceneName, boolean customLight) {
ProjectInfoVO projectVO = rm.getProjectVO();
Viewport viewport = new ScalingViewport(Scaling.stretch, (float)projectVO.originalResolution.width/ pixelsPerWU, (float)projectVO.originalResolution.height/ pixelsPerWU, new OrthographicCamera());
return loadScene(sceneName, viewport, customLight);
}
public void injectExternalItemType(IExternalItemType itemType) {
itemType.injectDependencies(rayHandler, world, rm);
itemType.injectMappers();
entityFactory.addExternalFactory(itemType);
engine.addSystem(itemType.getSystem());
renderer.addDrawableType(itemType);
}
private void addSystems() {
PhysicsBodyLoader.getInstance().setScaleFromPPWU(pixelsPerWU);
ParticleSystem particleSystem = new ParticleSystem();
LightSystem lightSystem = new LightSystem();
SpriteAnimationSystem animationSystem = new SpriteAnimationSystem();
LayerSystem layerSystem = new LayerSystem();
PhysicsSystem physicsSystem = new PhysicsSystem(world);
CompositeSystem compositeSystem = new CompositeSystem();
LabelSystem labelSystem = new LabelSystem();
ScriptSystem scriptSystem = new ScriptSystem();
ActionSystem actionSystem = new ActionSystem();
renderer = new Overlap2dRenderer(new PolygonSpriteBatch(2000, createDefaultShader()));
renderer.setRayHandler(rayHandler);
// renderer.setBox2dWorld(world);
engine.addSystem(animationSystem);
engine.addSystem(particleSystem);
engine.addSystem(lightSystem);
engine.addSystem(layerSystem);
engine.addSystem(physicsSystem);
engine.addSystem(compositeSystem);
engine.addSystem(labelSystem);
engine.addSystem(scriptSystem);
engine.addSystem(actionSystem);
engine.addSystem(renderer);
// additional
engine.addSystem(new ButtonSystem());
addEntityRemoveListener();
}
private void addEntityRemoveListener() {
engine.addEntityListener(new EntityListener() {
@Override
public void entityAdded(Entity entity) {
// TODO: Gev knows what to do. (do this for all entities)
// mae sure we assign correct z-index here
/*
ZindexComponent zindexComponent = ComponentRetriever.get(entity, ZindexComponent.class);
ParentNodeComponent parentNodeComponent = ComponentRetriever.get(entity, ParentNodeComponent.class);
if (parentNodeComponent != null) {
NodeComponent nodeComponent = parentNodeComponent.parentEntity.getComponent(NodeComponent.class);
zindexComponent.setZIndex(nodeComponent.children.size);
zindexComponent.needReOrder = false;
}*/
// call init for a system
ScriptComponent scriptComponent = entity.getComponent(ScriptComponent.class);
if (scriptComponent != null) {
for (IScript script : scriptComponent.scripts) {
script.init(entity);
}
}
}
@Override
public void entityRemoved(Entity entity) {
ParentNodeComponent parentComponent = ComponentRetriever.get(entity, ParentNodeComponent.class);
if (parentComponent == null) {
return;
}
Entity parentEntity = parentComponent.parentEntity;
NodeComponent parentNodeComponent = ComponentRetriever.get(parentEntity, NodeComponent.class);
parentNodeComponent.removeChild(entity);
// check if composite and remove all children
NodeComponent nodeComponent = ComponentRetriever.get(entity, NodeComponent.class);
if (nodeComponent != null) {
// it is composite
for (Entity node : nodeComponent.children) {
engine.removeEntity(node);
}
}
//check for physics
PhysicsBodyComponent physicsBodyComponent = ComponentRetriever.get(entity, PhysicsBodyComponent.class);
if (physicsBodyComponent != null && physicsBodyComponent.body != null) {
world.destroyBody(physicsBodyComponent.body);
}
// check if it is light
LightObjectComponent lightObjectComponent = ComponentRetriever.get(entity, LightObjectComponent.class);
if(lightObjectComponent != null) {
lightObjectComponent.lightObject.remove(true);
}
}
});
}
public Entity loadFromLibrary(String libraryName) {
ProjectInfoVO projectInfoVO = getRm().getProjectVO();
CompositeItemVO compositeItemVO = projectInfoVO.libraryItems.get(libraryName);
if(compositeItemVO != null) {
Entity entity = entityFactory.createEntity(null, compositeItemVO);
return entity;
}
return null;
}
public CompositeItemVO loadVoFromLibrary(String libraryName) {
ProjectInfoVO projectInfoVO = getRm().getProjectVO();
CompositeItemVO compositeItemVO = projectInfoVO.libraryItems.get(libraryName);
return compositeItemVO;
}
public void addComponentsByTagName(String tagName, Class componentClass) {
ImmutableArray<Entity> entities = engine.getEntities();
for(Entity entity: entities) {
MainItemComponent mainItemComponent = ComponentRetriever.get(entity, MainItemComponent.class);
if(mainItemComponent.tags.contains(tagName)) {
try {
entity.add(ClassReflection.<Component>newInstance(componentClass));
} catch (ReflectionException e) {
e.printStackTrace();
}
}
}
}
/**
* Sets ambient light to the one specified in scene from editor
*
* @param vo
* - Scene data file to invalidate
*/
public void setAmbienceInfo(SceneVO vo) {
if(!vo.lightSystemEnabled) {
rayHandler.setAmbientLight(1f, 1f, 1f, 1f);
return;
}
if (vo.ambientColor != null) {
Color clr = new Color(vo.ambientColor[0], vo.ambientColor[1],
vo.ambientColor[2], vo.ambientColor[3]);
rayHandler.setAmbientLight(clr);
}
}
public EntityFactory getEntityFactory() {
return entityFactory;
}
public IResourceRetriever getRm() {
return rm;
}
public Engine getEngine() {
return engine;
}
public Entity getRoot() {
return rootEntity;
}
/** Returns a new instance of the default shader used by SpriteBatch for GL2 when no shader is specified. */
static public ShaderProgram createDefaultShader () {
String vertexShader = "attribute vec4 " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "attribute vec4 " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ "attribute vec2 " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ "uniform mat4 u_projTrans;\n" //
+ "varying vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "\n" //
+ "void main()\n" //
+ "{\n" //
+ " v_color = " + ShaderProgram.COLOR_ATTRIBUTE + ";\n" //
+ " v_color.a = v_color.a * (255.0/254.0);\n" //
+ " v_texCoords = " + ShaderProgram.TEXCOORD_ATTRIBUTE + "0;\n" //
+ " gl_Position = u_projTrans * " + ShaderProgram.POSITION_ATTRIBUTE + ";\n" //
+ "}\n";
String fragmentShader = "#ifdef GL_ES\n" //
+ "#define LOWP lowp\n" //
+ "precision mediump float;\n" //
+ "#else\n" //
+ "#define LOWP \n" //
+ "#endif\n" //
+ "varying LOWP vec4 v_color;\n" //
+ "varying vec2 v_texCoords;\n" //
+ "uniform sampler2D u_texture;\n" //
+ "uniform vec2 atlasCoord;\n" //
+ "uniform vec2 atlasSize;\n" //
+ "uniform int isRepeat;\n" //
+ "void main()\n"//
+ "{\n" //
+ "vec4 textureSample = vec4(0.0,0.0,0.0,0.0);\n"//
+ "if(isRepeat == 1)\n"//
+ "{\n"//
+ "textureSample = v_color * texture2D(u_texture, atlasCoord+mod(v_texCoords, atlasSize));\n"//
+ "}\n"//
+ "else\n"//
+ "{\n"//
+ "textureSample = v_color * texture2D(u_texture, v_texCoords);\n"//
+ "}\n"//
+ " gl_FragColor = textureSample;\n" //
+ "}";
ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);
if (!shader.isCompiled()) throw new IllegalArgumentException("Error compiling shader: " + shader.getLog());
return shader;
}
public Batch getBatch() {
return renderer.getBatch();
}
}