/* * ****************************************************************************** * * Copyright 2015 See AUTHORS file. * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * ***************************************************************************** */ package com.uwsoft.editor.view.stage; import com.badlogic.ashley.core.Engine; import com.badlogic.ashley.core.Entity; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.viewport.Viewport; import com.commons.MsgAPI; import com.overlap2d.extensions.spine.SpineItemType; import com.uwsoft.editor.Overlap2DFacade; import com.uwsoft.editor.proxy.ProjectManager; import com.uwsoft.editor.proxy.ResourceManager; import com.uwsoft.editor.proxy.SceneDataManager; import com.uwsoft.editor.renderer.SceneLoader; import com.uwsoft.editor.renderer.components.ViewPortComponent; import com.uwsoft.editor.renderer.components.additional.ButtonComponent; import com.uwsoft.editor.renderer.data.CompositeItemVO; import com.uwsoft.editor.renderer.data.CompositeVO; import com.uwsoft.editor.renderer.data.LayerItemVO; import com.uwsoft.editor.renderer.data.SceneVO; import com.uwsoft.editor.renderer.systems.PhysicsSystem; import com.uwsoft.editor.renderer.systems.render.Overlap2dRenderer; import com.uwsoft.editor.renderer.utils.ComponentRetriever; import com.uwsoft.editor.system.PhysicsAdjustSystem; import com.uwsoft.editor.view.ItemControlMediator; import com.uwsoft.editor.view.SceneControlMediator; import com.uwsoft.editor.view.stage.input.InputListener; import com.uwsoft.editor.view.ui.widget.actors.basic.PixelRect; import com.vo.ProjectVO; import com.vo.SceneConfigVO; import java.util.HashMap; /** * Sandbox is a complex hierarchy of managing classes that is supposed to be a main hub for the "commands" the part of editor where * user drops all panels, moves them around, and composes the scene. commands is responsible for using runtime to render the visual scene, * it is responsible to listen for all the events, item resizing, selecting, aligning, removing and things like that. * * @author azakhary */ public class Sandbox { private static Sandbox instance = null; public SceneControlMediator sceneControl; public ItemControlMediator itemControl; private Object clipboard; private HashMap<String, Object> localClipboard = new HashMap<>(); private Entity currentViewingEntity; /** * this part contains legacy params that need to be removed one by one */ public int currTransformType = -1; public Entity currTransformHost; public boolean isResizing = false; public boolean dirty = false; public Vector3 copedItemCameraOffset; public String currentLoadedSceneFileName; private int gridSize = 1; // pixels private float zoomPercent = 100; private UIStage uiStage; private ItemSelector selector; private Overlap2DFacade facade; private ProjectManager projectManager; private ResourceManager resourceManager; public PixelRect selectionRec; //public Group mainBox = new Group(); //public SandboxUI ui; //public Group frontUI; private SceneLoader sceneLoader; private Array<InputListener> listeners = new Array<InputListener>(1); /** * end of shitty part */ private Sandbox() { } public synchronized static Sandbox getInstance() { /* * The instance gets created only when it is called for first time. * Lazy-loading */ if (instance == null) { instance = new Sandbox(); instance.init(); } return instance; } private void init() { facade = Overlap2DFacade.getInstance(); projectManager = facade.retrieveProxy(ProjectManager.NAME); resourceManager = facade.retrieveProxy(ResourceManager.NAME); UIStageMediator uiStageMediator = facade.retrieveMediator(UIStageMediator.NAME); uiStage = uiStageMediator.getViewComponent(); sceneLoader = new SceneLoader(resourceManager); // adding spine as external component sceneLoader.injectExternalItemType(new SpineItemType()); //Remove Physics System and add Adjusting System for box2d objects to follow items and stop world tick sceneLoader.engine.removeSystem(sceneLoader.engine.getSystem(PhysicsSystem.class)); sceneLoader.engine.addSystem(new PhysicsAdjustSystem(sceneLoader.world)); sceneLoader.engine.getSystem(Overlap2dRenderer.class).setPhysicsOn(false); sceneControl = new SceneControlMediator(sceneLoader); itemControl = new ItemControlMediator(sceneControl); selector = new ItemSelector(this); } public void initView() { //TODO fix and uncomment // getCamera().position.set(0, 0, 0); selectionRec = new PixelRect(0, 0); selectionRec.setFillColor(new Color(1, 1, 1, 0.1f)); selectionRec.setOpacity(0.0f); selectionRec.setTouchable(Touchable.disabled); uiStage.midUI.addActor(selectionRec); } public void setCursor(int cursor) { //UIController.instance.sendNotification(NameConstants.SET_CURSOR, cursor); } public void setKeyboardFocus() { uiStage.setKeyboardFocus(uiStage.midUI); } /** * Getters * */ public UIStage getUIStage() { return uiStage; } public SceneControlMediator getSceneControl() { return sceneControl; } public Engine getEngine() { return sceneLoader.getEngine(); } /** * TODO: loading fonts this way is a bit outdated and needs to change * * @param sceneName */ public void initData(String sceneName) { sceneControl.initScene(sceneName); } public void loadCurrentProject(String name) { //TODO fix and uncomment //sceneControl.getEssentials().rm = resourceManager; loadScene(name); } public void loadCurrentProject() { ProjectVO projectVO = projectManager.getCurrentProjectVO(); loadCurrentProject(projectVO.lastOpenScene.isEmpty() ? "MainScene" : projectVO.lastOpenScene); } public void loadScene(String sceneName) { currentLoadedSceneFileName = sceneName; initData(sceneName); initView(); initSceneView(sceneControl.getRootSceneVO()); ProjectVO projectVO = projectManager.getCurrentProjectVO(); projectVO.lastOpenScene = sceneName; projectManager.saveCurrentProject(); //TODO fix and uncomment //sandboxStage.getCamera().position.set(0, 0, 0); facade.sendNotification(MsgAPI.LIBRARY_LIST_UPDATED); currentViewingEntity = getRootEntity(); //TODO: move this into SceneDataManager! SceneDataManager sceneDataManager = facade.retrieveProxy(SceneDataManager.NAME); sceneDataManager.sendNotification(MsgAPI.SCENE_LOADED); ProjectManager projectManager = Overlap2DFacade.getInstance().retrieveProxy(ProjectManager.NAME); SceneConfigVO sceneConfigVO = projectManager.getCurrentSceneConfigVO(); getCamera().position.set(sceneConfigVO.cameraPosition[0], sceneConfigVO.cameraPosition[1], 0); // add additional components // TODO: maybe moe this somewhere else sceneControl.sceneLoader.addComponentsByTagName("button", ButtonComponent.class); } public void initSceneView(CompositeItemVO compositeItemVO) { //TODO fix and uncomment //initSceneView(sceneControl.initSceneView(compositeItemVO)); } public void initSceneView(Entity composite) { //TODO fix and uncomment // selector.clearSelections(); // sandboxStage.mainBox.clear(); // sceneControl.initSceneView(composite, true/*uiStage.getCompositePanel().isRootScene()*/); //// if (uiStage.getCompositePanel().isRootScene()) { //// uiStage.getCompositePanel().updateRootScene(sceneControl.getRootSceneVO()); //// } // // sandboxStage.mainBox.addActor(sceneControl.getCurrentScene()); // sceneControl.getCurrentScene().setX(0); // sceneControl.getCurrentScene().setY(0); // // //uiStage.getLayerPanel().initContent(); // forceContinuousParticles(composite); } /** * Some particle panels might not be continuous, so they will stop after first iteration, which is ok * This method will make sure they look continuous while in editor, so user will find and see them easily. * * @param composite composite on screen with particles to be forced to be continuous */ //TODO fix and uncomment // private void forceContinuousParticles(CompositeItem composite) { // ArrayList<IBaseItem> asd = composite.getItems(); // for (int i = 0; i < asd.size(); i++) { // IBaseItem item = asd.get(i); // if (item instanceof ParticleItem) { // ((ParticleItem) item).forceContinuous(); // continue; // } // if (item instanceof CompositeItem) { // forceContinuousParticles((CompositeItem) item); // } // // } // } /** * Well... that's a bummer, I cannot remember why this was for. but the name speaks for itself sort of. * TODO: figure this out * * @return SceneVO */ public SceneVO sceneVoFromItems() { sceneControl.getCurrentSceneVO().composite = new CompositeVO(); sceneControl.getCurrentSceneVO().composite.loadFromEntity(getRootEntity()); return sceneControl.getCurrentSceneVO(); } /** * Initializes current scene on screen from a tools object. * * @param vo CompositeItemVO tools */ public void reconstructFromSceneVo(CompositeItemVO vo) { initSceneView(vo); } /** * TODO: what does this do? seems to be saving as checkpoint of Flow? it so it should be renamed */ public void saveSceneCurrentSceneData() { //TODO fix and uncomment //sceneControl.getCurrentScene().updateDataVO(); } public void setSceneAmbientColor(Color color, boolean showChange) { sceneControl.getCurrentSceneVO().ambientColor[0] = color.r; sceneControl.getCurrentSceneVO().ambientColor[1] = color.g; sceneControl.getCurrentSceneVO().ambientColor[2] = color.b; sceneControl.getCurrentSceneVO().ambientColor[3] = color.a; //TODO fix and uncomment //if (showChange) sceneControl.getEssentials().rayHandler.setAmbientLight(color); } public ItemSelector getSelector() { return selector; } /** * @deprecated * @return */ public boolean isComponentSkinAvailable() { return true; } public LayerItemVO getSelectedLayer() { return uiStage.getCurrentSelectedLayer(); } public void setCurrentlyTransforming(Entity item, int transformType) { if (item == null || item.getClass().getSimpleName().equals("LabelItem")) return; currTransformType = transformType; currTransformHost = item; } public Entity getCurrentScene() { return sceneControl.getCurrentScene(); } public void enablePan() { //cameraPanOn = true; //selector.clearSelections(); //isItemTouched = false; } public void prepareSelectionRectangle(float x, float y, boolean setOpacity) { // space is panning, so if we are not, then prepare the selection rectangle if (setOpacity) { selectionRec.setOpacity(0.6f); } selectionRec.setWidth(0); selectionRec.setHeight(0); selectionRec.setX(x); selectionRec.setY(y); } public int getZoomPercent() { return (int)zoomPercent; } public void setZoomPercent(float percent) { zoomPercent = percent; getCamera().zoom = 1f / (zoomPercent / 100f); } public void zoomBy(float amount) { zoomPercent += -amount * 15f; if (zoomPercent < 20) zoomPercent = 20; if (zoomPercent > 1000) zoomPercent = 1000; setZoomPercent(zoomPercent); facade.sendNotification(MsgAPI.ZOOM_CHANGED); } public void zoomDevideBy(float amount) { zoomPercent /= amount; if (zoomPercent < 20) zoomPercent = 20; if (zoomPercent > 1000) zoomPercent = 1000; setZoomPercent(zoomPercent); facade.sendNotification(MsgAPI.ZOOM_CHANGED); } public float getWorldGridSize(){ return (float)gridSize/sceneControl.sceneLoader.getRm().getProjectVO().pixelToWorld; } public int getGridSize() { return gridSize; } public void setGridSize(int gridSize) { this.gridSize = gridSize; facade.sendNotification(MsgAPI.GRID_SIZE_CHANGED, gridSize); } public Entity getRootEntity(){ return sceneControl.getRootEntity(); } //Global Listeners part public void addListener(InputListener listener){ if (!listeners.contains(listener, true)) { listeners.add(listener); } } public void removeListener(InputListener listener){ listeners.removeValue(listener, true); } public void removeAllListener(){ listeners.clear(); } public Array<InputListener> getAllListeners(){ listeners.shrink(); return listeners; } public OrthographicCamera getCamera() { return (OrthographicCamera) getViewport().getCamera(); } public Entity getCurrentViewingEntity() { return currentViewingEntity; } public void setCurrentViewingEntity(Entity entity) { currentViewingEntity = entity; } public ViewPortComponent getViewportComponent() { if(getCurrentViewingEntity() == null) return null; ViewPortComponent viewPortComponent = ComponentRetriever.get(getCurrentViewingEntity(), ViewPortComponent.class); return viewPortComponent; } public Viewport getViewport() { ViewPortComponent viewPortComponent = getViewportComponent(); if(viewPortComponent == null) return null; return viewPortComponent.viewPort; } /** Transformations **/ public Rectangle screenToWorld(Rectangle rect) { Vector2 pos = screenToWorld(new Vector2(rect.x, rect.y)); Vector2 pos2 = screenToWorld(new Vector2(rect.x + rect.width, rect.y + rect.height)); rect.x = pos.x; rect.y = pos.y; rect.width = pos2.x - rect.x; rect.height = pos2.y - rect.y; return rect; } public Vector2 screenToWorld(Vector2 vector) { // TODO: now unproject doesnot do well too. I am completely lost here. how hard is it to do screen to world, madafakas. //getViewport().unproject(vector); int pixelPerWU = sceneControl.sceneLoader.getRm().getProjectVO().pixelToWorld; OrthographicCamera camera = Sandbox.getInstance().getCamera(); Viewport viewport = Sandbox.getInstance().getViewport(); vector.x = (vector.x - (viewport.getScreenWidth()/2 - camera.position.x*pixelPerWU/camera.zoom))*camera.zoom; vector.y = (vector.y - (viewport.getScreenHeight()/2 - camera.position.y*pixelPerWU/camera.zoom))*camera.zoom; vector.scl(1f/pixelPerWU); return vector; } public Vector2 worldToScreen(Vector2 vector) { // TODO: WTF, project had to work instead I am back to this barbarian methods of unholy land! //vector = getViewport().project(vector); int pixelPerWU = sceneControl.sceneLoader.getRm().getProjectVO().pixelToWorld; OrthographicCamera camera = Sandbox.getInstance().getCamera(); Viewport viewport = Sandbox.getInstance().getViewport(); vector.x = vector.x/camera.zoom + (viewport.getWorldWidth()/2 - (camera.position.x)/camera.zoom); vector.y = vector.y/camera.zoom + (viewport.getWorldHeight()/2 - (camera.position.y)/camera.zoom); vector.scl(pixelPerWU); return vector; } public Vector2 screenToWorld(float x, float y) { return screenToWorld(new Vector2(x, y)); } public Vector2 worldToScreen(float x, float y) { return worldToScreen(new Vector2(x, y)); } public void copyToClipboard(Object data) { //TODO: make this an actual clipboard call (dunno how though, need to make all serializable?) this.clipboard = data; } public Object retrieveFromClipboard() { //TODO: make this an actual clipboard call (dunno how though, need to make all serializable?) return clipboard; } public void copyToLocalClipboard(String key, Object data) { this.localClipboard.put(key, data); } public Object retrieveFromLocalClipboard(String key) { return localClipboard.get(key); } public int getPixelPerWU() { return sceneLoader.getRm().getProjectVO().pixelToWorld; } }