package scene3d; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.graphics.Camera; import com.badlogic.gdx.graphics.PerspectiveCamera; import com.badlogic.gdx.graphics.g3d.Environment; import com.badlogic.gdx.graphics.g3d.ModelBatch; import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.collision.Ray; import com.badlogic.gdx.scenes.scene2d.Actor; import com.badlogic.gdx.scenes.scene2d.EventListener; import com.badlogic.gdx.scenes.scene2d.Group; import com.badlogic.gdx.scenes.scene2d.Touchable; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.SnapshotArray; public class Stage3d extends InputAdapter implements Disposable { private float width, height; private final ModelBatch modelBatch; private Environment environment; private PerspectiveCamera camera; private final Group3d root; private Actor3d scrollFocus; private Actor3d keyboardFocus; public Touchable touchable = Touchable.disabled; private int selecting = -1; private boolean canHit = false; /** Creates a stage with a {@link #setViewport(float, float, boolean) viewport} equal to the device screen resolution. The stage * will use its own {@link SpriteBatch}. */ public Stage3d () { this(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false); } /** Creates a stage with the specified {@link #setViewport(float, float, boolean) viewport} that doesn't keep the aspect ratio. * The stage will use its own {@link SpriteBatch}, which will be disposed when the stage is disposed. */ public Stage3d (float width, float height) { this(width, height, false); } public Stage3d (float width, float height, boolean keepAspectRatio) { this.width = width; this.height = height; root = new Group3d(); root.setStage3d(this); modelBatch = new ModelBatch(); camera = new Camera3d(); environment = new Environment(); environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.9f, 0.9f, 0.9f, 1f)); environment.add(new DirectionalLight().set(0.8f, 0f, 0f, -1f, -0.8f, -0.2f)); setViewport(width, height, keepAspectRatio); } public Stage3d (float width, float height, PerspectiveCamera camera) { this.width = width; this.height = height; root = new Group3d(); root.setStage3d(this); modelBatch = new ModelBatch(); this.camera = camera; } public Stage3d (float width, float height, PerspectiveCamera camera, Environment environment) { this.width = width; this.height = height; root = new Group3d(); root.setStage3d(this); modelBatch = new ModelBatch(); this.camera = camera; this.environment = environment; } public void setViewport (float width, float height) { setViewport(width, height, false, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } /** Sets up the stage size using a viewport that fills the entire screen. * @see #setViewport(float, float, boolean, float, float, float, float) */ public void setViewport (float width, float height, boolean keepAspectRatio) { setViewport(width, height, keepAspectRatio, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); } /** Sets up the stage size and viewport. The viewport is the glViewport position and size, which is the portion of the screen * used by the stage. The stage size determines the units used within the stage, depending on keepAspectRatio: * <p> * If keepAspectRatio is false, the stage is stretched to fill the viewport, which may distort the aspect ratio. * <p> * If keepAspectRatio is true, the stage is first scaled to fit the viewport in the longest dimension. Next the shorter * dimension is lengthened to fill the viewport, which keeps the aspect ratio from changing. The {@link #getGutterWidth()} and * {@link #getGutterHeight()} provide access to the amount that was lengthened. * @param viewportX The top left corner of the viewport in glViewport coordinates (the origin is bottom left). * @param viewportY The top left corner of the viewport in glViewport coordinates (the origin is bottom left). * @param viewportWidth The width of the viewport in pixels. * @param viewportHeight The height of the viewport in pixels. */ public void setViewport (float stageWidth, float stageHeight, boolean keepAspectRatio, float viewportX, float viewportY, float viewportWidth, float viewportHeight) { if (keepAspectRatio) { if (viewportHeight / viewportWidth < stageHeight / stageWidth) { float toViewportSpace = viewportHeight / stageHeight; float toStageSpace = stageHeight / viewportHeight; float deviceWidth = stageWidth * toViewportSpace; float lengthen = (viewportWidth - deviceWidth) * toStageSpace; this.width = stageWidth + lengthen; this.height = stageHeight; } else { float toViewportSpace = viewportWidth / stageWidth; float toStageSpace = stageWidth / viewportWidth; float deviceHeight = stageHeight * toViewportSpace; float lengthen = (viewportHeight - deviceHeight) * toStageSpace; this.height = stageHeight + lengthen; this.width = stageWidth; } } else { this.width = stageWidth; this.height = stageHeight; } camera.viewportWidth = this.width; camera.viewportHeight = this.height; } public void draw(){ camera.update(); if (!root.isVisible()) return; modelBatch.begin(camera); root.draw(modelBatch, environment); modelBatch.end(); } /** Calls {@link #act(float)} with {@link Graphics#getDeltaTime()}. */ public void act () { act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); } /** Calls the {@link Actor#act(float)} method on each actor in the stage. Typically called each frame. This method also fires * enter and exit events. * @param delta Time in seconds since the last frame. */ public void act(float delta) { root.act(delta); } /** Adds an actor to the root of the stage. * @see Group#addActor(Actor) * @see Actor#remove() */ public void addActor3d(Actor3d actor) { root.addActor3d(actor); } /** Adds an action to the root of the stage. * @see Group#addAction3d(Action) */ public void addAction3d(Action3d action) { root.addAction3d(action); } /** Returns the root's child actors. * @see Group#getChildren() */ public Array<Actor3d> getActors3d() { return root.getChildren(); } /** Adds a listener to the root. * @see Actor#addListener(EventListener) */ public boolean addListener (Event3dListener listener) { return root.addListener(listener); } /** Removes a listener from the root. * @see Actor#removeListener(EventListener) */ public boolean removeListener (Event3dListener listener) { return root.removeListener(listener); } /** Removes the root's children, actions, and listeners. */ public void clear () { unfocusAll(); root.dispose(); root.clear(); } /** Removes the touch, keyboard, and scroll focused actors. */ public void unfocusAll () { scrollFocus = null; keyboardFocus = null; //cancelTouchFocus(); } /** Removes the touch, keyboard, and scroll focus for the specified actor and any descendants. */ public void unfocus(Actor3d actor) { if (scrollFocus != null && scrollFocus.isDescendantOf(actor)) scrollFocus = null; if (keyboardFocus != null && keyboardFocus.isDescendantOf(actor)) keyboardFocus = null; } /** Sets the actor that will receive key events. * @param actor May be null. */ public void setKeyboardFocus (Actor3d actor) { if (keyboardFocus == actor) return; } /** Gets the actor that will receive key events. * @return May be null. */ public Actor3d getKeyboardFocus () { return keyboardFocus; } /** Sets the actor that will receive scroll events. * @param actor May be null. */ public void setScrollFocus(Actor3d actor) { if (scrollFocus == actor) return; } /** Gets the actor that will receive scroll events. * @return May be null. */ public Actor3d getScrollFocus () { return scrollFocus; } public ModelBatch getModelBatch () { return modelBatch; } public PerspectiveCamera getCamera () { return camera; } /** Sets the stage's camera. The camera must be configured properly or {@link #setViewport(float, float, boolean)} can be called * after the camera is set. {@link Stage#draw()} will call {@link Camera#update()} and use the {@link Camera#combined} matrix * for the SpriteBatch {@link SpriteBatch#setProjectionMatrix(com.badlogic.gdx.math.Matrix4) projection matrix}. */ public void setCamera (PerspectiveCamera camera) { this.camera = camera; } /** Returns the root group which holds all actors in the stage. */ public Group3d getRoot () { return root; } public void setEnvironment(Environment environment){ this.environment = environment; } public Environment getEnvironment(){ return environment; } public void enableHit(){ canHit = true; } public void disableHit(){ canHit = false; } @Override public boolean touchDown(int screenX, int screenY, int pointer, int button) { if(canHit){ Actor3d actor3d = getObject(screenX, screenY); selecting = actor3d != null?1:-1; if(actor3d != null && actor3d.getName() != null) Gdx.app.log("", ""+actor3d.getName()); } return selecting > 0; //return false; } @Override public boolean touchUp(int screenX, int screenY, int pointer, int button) { if (selecting >= 0) { //setSelected(getObject(screenX, screenY)); selecting = -1; return true; } return false; //if(touchable == Touchable.enabled) // hit(screenX, screenY); //return false; } @Override public boolean touchDragged (int screenX, int screenY, int pointer) { return selecting >= 0; } Vector3 position = new Vector3(); int result = -1; float distance = -1; public Actor3d getObject(int screenX, int screenY) { Actor3d temp = null; SnapshotArray<Actor3d> children = root.getChildren(); Actor3d[] actors = children.begin(); for(int i = 0, n = children.size; i < n; i++){ temp = hit3d(screenX, screenY, actors[i]); if(actors[i] instanceof Group3d) temp = hit3d(screenX, screenY, (Group3d)actors[i]); } children.end(); return temp; } public Actor3d hit3d(int screenX, int screenY, Actor3d actor3d) { Ray ray = camera.getPickRay(screenX, screenY); float distance = -1; final float dist2 = actor3d.intersects(ray); if (dist2 >= 0f && (distance < 0f || dist2 <= distance)) { distance = dist2; return actor3d; } return null; } public Actor3d hit3d(int screenX, int screenY, Group3d group3d) { Actor3d temp = null; SnapshotArray<Actor3d> children = group3d.getChildren(); Actor3d[] actors = children.begin(); for(int i = 0, n = children.size; i < n; i++){ temp = hit3d(screenX, screenY, actors[i]); if(actors[i] instanceof Group3d) temp = hit3d(screenX, screenY, (Group3d)actors[i]); } children.end(); return temp; } @Override public void dispose() { modelBatch.dispose(); clear(); } }