/*******************************************************************************
* Copyright 2011 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 sink.scene3d;
import sink.core.Sink;
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.scenes.scene2d.EventListener;
import com.badlogic.gdx.scenes.scene2d.Group;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;
public class Stage3d extends InputAdapter implements Disposable{
private float viewportX, viewportY, viewportWidth, viewportHeight;
private float width, height;
private float gutterWidth, gutterHeight;
private final ModelBatch modelBatch;
private final Environment environment;
private PerspectiveCamera camera;
private final Group3d root;
private Actor3d scrollFocus;
private Actor3d keyboardFocus;
/** 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);
}
/** Creates a stage with the specified {@link #setViewport(float, float, boolean) viewport}. The stage will use its own
* {@link SpriteBatch}, which will be disposed when the stage is disposed. */
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 PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(10f, 10f, 10f);
camera.lookAt(0,0,0);
camera.near = 0.1f;
camera.far = 300f;
camera.update();
environment = new Environment();
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
setViewport(width, height, keepAspectRatio);
}
/** Sets up the stage size using a viewport that fills the entire screen without keeping the aspect ratio.
* @see #setViewport(float, float, boolean, float, float, float, float) */
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) {
this.viewportX = viewportX;
this.viewportY = viewportY;
this.viewportWidth = viewportWidth;
this.viewportHeight = 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;
gutterWidth = lengthen / 2;
gutterHeight = 0;
} 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;
gutterWidth = 0;
gutterHeight = lengthen / 2;
}
} else {
this.width = stageWidth;
this.height = stageHeight;
gutterWidth = 0;
gutterHeight = 0;
}
camera.viewportWidth = this.width;
camera.viewportHeight = this.height;
}
public void draw(){
camera.update();
if (!root.isVisible()) return;
modelBatch.begin(camera);
root.drawChildren(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.addActor(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 (EventListener listener) {
return root.addListener(listener);
}
/** Removes a listener from the root.
* @see Actor#removeListener(EventListener) */
public boolean removeListener (EventListener listener) {
return root.removeListener(listener);
}
/** Removes the root's children, actions, and listeners. */
public void clear () {
unfocusAll();
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 Sink#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 Environment getEnvironment(){
return environment;
}
@Override
public void dispose() {
clear();
modelBatch.dispose();
}
}