diff --git a/README.md b/README.md index 1d5ec59..a591f42 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,27 @@ -NxtAR-core -========== +NxtAR: A generic software architecture for Augmented Reality based mobile robot control. +======================================================================================== -Modulo 2 de mi trabajo especial de grado. +Core module +----------- + +### Abstract ### + +NxtAR is a generic software architecture for the development of Augmented Reality games +and applications centered around mobile robot control. This is a reference implementation +with support for [LEGO Mindstorms NXT][1] mobile robots. + +### Module description ### + +The core module comprises all the operating system independent classes that implemente the +base architecture and the different scenarios for the application. This implementation is +designed and built around the [LibGDX][2] and the [Artemis Entity-System Framework][3] libraries. + +Currently there is one scenario titled *Bomb Game*. + +### Module installation and usage. ### + +The core module cannot be used directly. It is intended to be compiled with a LibGDX backend module. + + [1]: http://www.lego.com/en-us/mindstorms/?domainredir=mindstorms.lego.com + [2]: http://libgdx.badlogicgames.com/ + [3]: http://gamadu.com/artemis/ diff --git a/src/ve/ucv/ciens/ccg/networkdata/MotorEvent.java b/src/ve/ucv/ciens/ccg/networkdata/MotorEvent.java index 50f1c60..40761b6 100644 --- a/src/ve/ucv/ciens/ccg/networkdata/MotorEvent.java +++ b/src/ve/ucv/ciens/ccg/networkdata/MotorEvent.java @@ -5,7 +5,7 @@ import java.io.Serializable; public class MotorEvent implements Serializable{ private static final long serialVersionUID = 9989L; - public enum motor_t {NONE, MOTOR_A, MOTOR_B, MOTOR_C, MOTOR_AC, RECENTER}; + public enum motor_t {NONE, MOTOR_A, MOTOR_B, MOTOR_C, MOTOR_AC, RECENTER, ROTATE_90}; private motor_t motor; private byte power; diff --git a/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java index 315e89d..526ed65 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java +++ b/src/ve/ucv/ciens/ccg/nxtar/NxtARCore.java @@ -15,21 +15,26 @@ */ package ve.ucv.ciens.ccg.nxtar; -import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor; -import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener; import ve.ucv.ciens.ccg.nxtar.interfaces.ActionResolver; +import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener; +import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor; import ve.ucv.ciens.ccg.nxtar.network.RobotControlThread; import ve.ucv.ciens.ccg.nxtar.network.SensorReportThread; import ve.ucv.ciens.ccg.nxtar.network.ServiceDiscoveryThread; import ve.ucv.ciens.ccg.nxtar.network.VideoStreamingThread; +import ve.ucv.ciens.ccg.nxtar.scenarios.ScenarioGlobals; +import ve.ucv.ciens.ccg.nxtar.states.AutomaticActionState; +import ve.ucv.ciens.ccg.nxtar.states.AutomaticActionSummaryState; import ve.ucv.ciens.ccg.nxtar.states.BaseState; import ve.ucv.ciens.ccg.nxtar.states.CameraCalibrationState; import ve.ucv.ciens.ccg.nxtar.states.InGameState; +import ve.ucv.ciens.ccg.nxtar.states.InstructionsState; import ve.ucv.ciens.ccg.nxtar.states.MainMenuStateBase; import ve.ucv.ciens.ccg.nxtar.states.OuyaMainMenuState; -import ve.ucv.ciens.ccg.nxtar.states.PauseState; +import ve.ucv.ciens.ccg.nxtar.states.ScenarioEndSummaryState; import ve.ucv.ciens.ccg.nxtar.states.TabletMainMenuState; import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; import aurelienribon.tweenengine.Tween; import aurelienribon.tweenengine.TweenEquations; import aurelienribon.tweenengine.primitives.MutableFloat; @@ -51,7 +56,7 @@ import com.badlogic.gdx.graphics.glutils.ShaderProgram; /** *
Core of the application.
* - *This class has three basic resposibilities:
+ *This class has three basic responsibilities:
*Wrapper around the Operating System methods.
*/ - private ActionResolver osFunction; + private ActionResolver actionResolver; // Networking related fields. /** @@ -206,10 +211,10 @@ public class NxtARCore extends Game implements ApplicationEventsListener{ // Check if the concrete application implements all required interfaces. try{ - this.osFunction = (ActionResolver)concreteApp; + this.actionResolver = (ActionResolver)concreteApp; }catch(ClassCastException cc){ Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement the Toaster interface. Toasting disabled."); - this.osFunction = null; + this.actionResolver = null; } try{ @@ -229,46 +234,85 @@ public class NxtARCore extends Game implements ApplicationEventsListener{ * sets the application states. */ public void create(){ - // Create the state objects. - states = new BaseState[game_states_t.getNumStates()]; - if(Ouya.runningOnOuya) - states[game_states_t.MAIN_MENU.getValue()] = new OuyaMainMenuState(this); - else - states[game_states_t.MAIN_MENU.getValue()] = new TabletMainMenuState(this); - states[game_states_t.IN_GAME.getValue()] = new InGameState(this); - states[game_states_t.PAUSED.getValue()] = new PauseState(this); - states[game_states_t.CALIBRATION.getValue()] = new CameraCalibrationState(this); - - // Register controller listeners. - for(BaseState state : states){ - Controllers.addListener(state); + try { + ScenarioGlobals.init(this); + } catch (IllegalArgumentException e) { + Gdx.app.error(TAG, CLASS_NAME + ".create(): Illegal argument initializing globals: ", e); + System.exit(1); + return; + } catch (InstantiationException e) { + Gdx.app.error(TAG, CLASS_NAME + ".create(): Instantiation exception initializing globals: ", e); + System.exit(1); + return; + } catch (IllegalAccessException e) { + Gdx.app.error(TAG, CLASS_NAME + ".create(): Illegal access exception initializing globals: ", e); + System.exit(1); + return; } // Set up rendering fields and settings. batch = new SpriteBatch(); batch.enableBlending(); batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); - pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - ShaderProgram.pedantic = false; - // Set up the overlay font. - if(ProjectConstants.DEBUG){ - overlayX = -((Gdx.graphics.getWidth() * ProjectConstants.OVERSCAN) / 2) + 10; - overlayY = ((Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN) / 2) - 10; + // Create the state objects. + states = new BaseState[game_states_t.getNumStates()]; - font = new BitmapFont(); - font.setColor(1.0f, 1.0f, 0.0f, 1.0f); - if(!Ouya.runningOnOuya){ - font.setScale(1.0f); - }else{ - font.setScale(2.5f); + try{ + if(Ouya.runningOnOuya) + states[game_states_t.MAIN_MENU.getValue()] = new OuyaMainMenuState(this); + else + states[game_states_t.MAIN_MENU.getValue()] = new TabletMainMenuState(this); + + try{ + states[game_states_t.IN_GAME.getValue()] = new InGameState(this); + }catch(IllegalStateException e){ + Gdx.app.error(TAG, CLASS_NAME + ".create(): Illegal state in IN_GAME_STATE: ", e); + System.exit(1); + return; } + + states[game_states_t.CALIBRATION.getValue()] = new CameraCalibrationState(this); + + try{ + states[game_states_t.AUTOMATIC_ACTION.getValue()] = new AutomaticActionState(this); + }catch(IllegalStateException e){ + Gdx.app.error(TAG, CLASS_NAME + ".create(): Illegal state in AUTOMATIC_ACTION_STATE: ", e); + System.exit(1); + return; + } + + states[game_states_t.AUTOMATIC_ACTION_SUMMARY.getValue()] = new AutomaticActionSummaryState(this); + states[game_states_t.SCENARIO_END_SUMMARY.getValue()] = new ScenarioEndSummaryState(this); + states[game_states_t.HINTS.getValue()] = new InstructionsState(this); + + }catch(IllegalArgumentException e){ + Gdx.app.error(TAG, CLASS_NAME + ".create(): Illegal argument caught creating states: ", e); + System.exit(1); + return; + } + + // Register controller listeners. + for(BaseState state : states){ + Controllers.addListener(state); + } + + // Set up the overlay font. + overlayX = -(Utils.getScreenWidthWithOverscan() / 2) + 10; + overlayY = (Utils.getScreenHeightWithOverscan() / 2) - 10; + + font = new BitmapFont(); + font.setColor(1.0f, 1.0f, 0.0f, 1.0f); + if(!Ouya.runningOnOuya){ + font.setScale(1.0f); + }else{ + font.setScale(2.5f); } // Start networking. - osFunction.enableMulticast(); + actionResolver.enableMulticast(); Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads"); serviceDiscoveryThread = ServiceDiscoveryThread.getInstance(); @@ -302,11 +346,10 @@ public class NxtARCore extends Game implements ApplicationEventsListener{ fadeTexture = new Texture(pixmap); pixmap.dispose(); - alpha = new MutableFloat(0.0f); + alpha = new MutableFloat(0.0f); fadeOut = Tween.to(alpha, 0, 0.5f).target(1.0f).ease(TweenEquations.easeInQuint); - fadeIn = Tween.to(alpha, 0, 0.5f).target(0.0f).ease(TweenEquations.easeInQuint); - - fading = false; + fadeIn = Tween.to(alpha, 0, 0.5f).target(0.0f).ease(TweenEquations.easeInQuint); + fading = false; // Set initial input handlers. Gdx.input.setInputProcessor(states[currState.getValue()]); @@ -327,6 +370,10 @@ public class NxtARCore extends Game implements ApplicationEventsListener{ public void render(){ super.render(); + // Load the assets. + if(!ScenarioGlobals.getEntityCreator().areEntitiesCreated()) + ScenarioGlobals.getEntityCreator().updateAssetManager(); + // If the current state set a value for nextState then switch to that state. if(nextState != null){ states[currState.getValue()].onStateUnset(); @@ -385,98 +432,111 @@ public class NxtARCore extends Game implements ApplicationEventsListener{ batch.setProjectionMatrix(pixelPerfectCamera.combined); batch.begin();{ // Draw the FPS overlay. - font.draw(batch, String.format("Render FPS: %d", Gdx.graphics.getFramesPerSecond()), overlayX, overlayY); - font.draw(batch, String.format("Total stream FPS: %d", videoThread.getFps()), overlayX, overlayY - font.getCapHeight() - 5); - font.draw(batch, String.format("Lost stream FPS: %d", videoThread.getLostFrames()), overlayX, overlayY - (2 * font.getCapHeight()) - 10); + font.draw(batch, String.format("Render FPS: %d", Gdx.graphics.getFramesPerSecond()), overlayX, overlayY); + font.draw(batch, String.format("Total stream FPS: %d", videoThread.getFps()), overlayX, overlayY - font.getCapHeight() - 5); + font.draw(batch, String.format("Lost stream FPS: %d", videoThread.getLostFrames()), overlayX, overlayY - (2 * font.getCapHeight()) - 10); font.draw(batch, String.format("Light sensor data: %d", sensorThread.getLightSensorReading()), overlayX, overlayY - (3 * font.getCapHeight()) - 15); + font.draw(batch, String.format("Device roll: %f", Gdx.input.getRoll()), overlayX, overlayY - (4 * font.getCapHeight()) - 20); + font.draw(batch, String.format("Device pitch: %f", Gdx.input.getPitch()), overlayX, overlayY - (5 * font.getCapHeight()) - 25); + font.draw(batch, String.format("Device azimuth: %f", Gdx.input.getAzimuth()), overlayX, overlayY - (6 * font.getCapHeight()) - 30); }batch.end(); } } /** - *Pause a currently running thread. Pausing an already paused thread is a - * no op.
+ *Pauses the video streaming and the current state.
*/ public void pause(){ if(videoThread != null) videoThread.pause(); - // TODO: Ignore pausing paused threads. - // TODO: Pause the other threads. + + states[currState.getValue()].pause(); } /** - *Resume a currently paused thread. Resuming an already resumed thread is a - * no op.
+ *Resumes the video streaming and the current state.
*/ public void resume(){ if(videoThread != null) videoThread.play(); - // TODO: Ignore resuming resumed threads. - // TODO: Resume the other threads. + + states[currState.getValue()].resume(); } /** *Clear graphic resources
*/ public void dispose(){ - // Finish network threads. - videoThread.finish(); - robotThread.finish(); - - // Dispose graphic objects. - fadeTexture.dispose(); - batch.dispose(); - if(ProjectConstants.DEBUG){ - font.dispose(); - } - // Dispose screens. for(int i = 0; i < states.length; i++){ states[i].dispose(); } + + // Finish network threads. + serviceDiscoveryThread.finish(); + videoThread.finish(); + robotThread.finish(); + sensorThread.finish(); + serviceDiscoveryThread = null; + videoThread = null; + robotThread = null; + sensorThread = null; + ServiceDiscoveryThread.freeInstance(); + VideoStreamingThread.freeInstance(); + RobotControlThread.freeInstance(); + SensorReportThread.freeInstance(); + + // Dispose graphic objects. + fadeTexture.dispose(); + batch.dispose(); + font.dispose(); + + ScenarioGlobals.dispose(); } /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; APPLICATION EVENTS LISTENER INTERFACE METHODS ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ - // TODO: Disable start game button until camera has been sucessfully calibrated. - // TODO: Add calibration listener callback. - - /** - *Callback used by the networking threads to notify sucessfull connections - * to the application
- * - * @param streamName The name of the thread notifying a connection. - */ @Override - public synchronized void networkStreamConnected(String streamName){ + public synchronized void onNetworkStreamConnected(String streamName){ Gdx.app.log(TAG, CLASS_NAME + ".networkStreamConnected() :: Stream " + streamName + " connected."); connections += 1; if(connections >= 3){ Gdx.app.debug(TAG, CLASS_NAME + ".networkStreamConnected() :: Stopping service broadcast."); serviceDiscoveryThread.finish(); - osFunction.disableMulticast(); - osFunction.showShortToast("Client connected"); + if(actionResolver != null) actionResolver.disableMulticast(); + if(actionResolver != null) actionResolver.showShortToast("Client connected"); ((MainMenuStateBase)states[game_states_t.MAIN_MENU.getValue()]).onClientConnected(); } } + @Override + public void onAssetsLoaded(){ + if(actionResolver != null) actionResolver.showShortToast("All assets sucessfully loaded."); + ((MainMenuStateBase)states[game_states_t.MAIN_MENU.getValue()]).onAssetsLoaded(); + } + + @Override + public void onCameraCalibrated(){ + if(actionResolver != null) actionResolver.showShortToast("Camera successfully calibrated."); + ((MainMenuStateBase)states[game_states_t.MAIN_MENU.getValue()]).onCameraCalibrated(); + } + /*;;;;;;;;;;;;;;;;;; ; HELPER METHODS ; ;;;;;;;;;;;;;;;;;;*/ /** - *Show a toast message on screen using the O.S. functionality - * provider.
+ *Show a toast message on screen using the {@link ActionResolver}.
* @param msg The message to show. * @param longToast True for a lasting toast. False for a short toast. */ public void toast(String msg, boolean longToast){ - if(osFunction != null){ - if(longToast) osFunction.showLongToast(msg); - else osFunction.showShortToast(msg); + if(actionResolver != null){ + if(longToast) actionResolver.showLongToast(msg); + else actionResolver.showShortToast(msg); } } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/components/AnimationComponent.java b/src/ve/ucv/ciens/ccg/nxtar/components/AnimationComponent.java index 36e6ea9..8c646e5 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/components/AnimationComponent.java +++ b/src/ve/ucv/ciens/ccg/nxtar/components/AnimationComponent.java @@ -24,11 +24,16 @@ import com.badlogic.gdx.graphics.g3d.utils.AnimationController; public class AnimationComponent extends Component { public AnimationController controller; + public AnimationController collisionController; public ListCreates a fade to/from white depending on the parameter.
+ * + * @param fadeIn True to create a fade FROM white, false for a fade TO white. + */ + public FadeEffectComponent(boolean fadeIn){ + if(fadeIn){ + this.alpha = new MutableFloat(1.0f); + this.fadeIn = Tween.to(alpha, 0, 2.0f).target(0.0f).ease(TweenEquations.easeInQuint); + this.fadeOut = null; + }else{ + this.alpha = new MutableFloat(0.0f); + this.fadeOut = Tween.to(alpha, 0, 2.5f).target(1.0f).ease(TweenEquations.easeInQuint); + this.fadeIn = null; + } + color = new Color(Color.WHITE); + } + + /** + *Creates a fade effect with the desired parameters.
+ * + * @param fadeIn True to create a fade FROM color, false for a fade TO color. + * @param color The color of the effect. + */ + public FadeEffectComponent(boolean fadeIn, Color color){ + this(fadeIn); + this.color.set(color); + } + + /** + *Creates a fade out effect of the desired color.
+ * + * @param color The color of the effect. + */ + public FadeEffectComponent(Color color){ + this(false, color); + } + + /** + *Creates a white fade out effect.
+ */ + public FadeEffectComponent(){ + this(false); + } + + /** + *The current transparency of the effect.
+ * + * @return The transparency. + */ + public float getFloatValue(){ + return alpha.floatValue(); + } + + /** + *Interpolates the transparency of the effect by the given delta time in seconds.
+ * + * @param delta + */ + public void update(float delta){ + if(fadeIn != null) + fadeIn.update(delta); + + if(fadeOut != null) + fadeOut.update(delta); + } + + /** + *Initializes the effect.
+ */ + public void startEffect(){ + if(fadeIn != null) + fadeIn.start(); + + if(fadeOut != null) + fadeOut.start(); + } + + /** + * @return True if the effect has been initialized. False otherwise. + */ + public boolean isEffectStarted(){ + return fadeIn != null ? fadeIn.isStarted() : fadeOut.isStarted(); + } + + /** + * @return True if this effect is a fade in. False if it is a fade out. + */ + public boolean isEffectFadeIn(){ + return fadeIn != null; + } + + /** + * @return True if the effect's interpolation is over. False otherwise. + */ + public boolean isEffectFinished(){ + return fadeIn != null ? fadeIn.isFinished() : fadeOut.isFinished(); + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/components/MarkerCodeComponent.java b/src/ve/ucv/ciens/ccg/nxtar/components/MarkerCodeComponent.java index 400f503..9dfdc0e 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/components/MarkerCodeComponent.java +++ b/src/ve/ucv/ciens/ccg/nxtar/components/MarkerCodeComponent.java @@ -18,11 +18,18 @@ package ve.ucv.ciens.ccg.nxtar.components; import com.artemis.Component; public class MarkerCodeComponent extends Component { - public int code; + public int code; + public boolean enabled; public MarkerCodeComponent(int code) throws IllegalArgumentException{ if(code < 0 || code > 1024) throw new IllegalArgumentException("Marker code must be between [0, 1024]."); this.code = code; + this.enabled = true; + } + + public MarkerCodeComponent(int code, boolean enabled){ + this(code); + this.enabled = enabled; } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/components/PlayerComponentBase.java b/src/ve/ucv/ciens/ccg/nxtar/components/PlayerComponentBase.java new file mode 100644 index 0000000..008f6f0 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/components/PlayerComponentBase.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.components; + +import com.artemis.Component; + +/** + * Tag class. + */ +public abstract class PlayerComponentBase extends Component { + public static final String PLAYER_GROUP = "PLAYER"; + + public boolean gameFinished; + public boolean victory; + + public PlayerComponentBase(){ + this.gameFinished = false; + this.victory = false; + } + + public void reset(){ + this.gameFinished = false; + this.victory = false; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/components/ModelComponent.java b/src/ve/ucv/ciens/ccg/nxtar/components/RenderModelComponent.java similarity index 74% rename from src/ve/ucv/ciens/ccg/nxtar/components/ModelComponent.java rename to src/ve/ucv/ciens/ccg/nxtar/components/RenderModelComponent.java index e630fca..e070059 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/components/ModelComponent.java +++ b/src/ve/ucv/ciens/ccg/nxtar/components/RenderModelComponent.java @@ -19,13 +19,20 @@ import com.artemis.Component; import com.badlogic.gdx.graphics.g3d.Model; import com.badlogic.gdx.graphics.g3d.ModelInstance; -public class ModelComponent extends Component { +public class RenderModelComponent extends Component { public ModelInstance instance; - public ModelComponent(Model model) throws IllegalArgumentException{ + public RenderModelComponent(Model model) throws IllegalArgumentException{ if(model == null) throw new IllegalArgumentException("Model is null."); this.instance = new ModelInstance(model); } + + public RenderModelComponent(ModelInstance instance) throws IllegalArgumentException{ + if(instance == null) + throw new IllegalArgumentException("Instance is null."); + + this.instance = instance; + } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/components/CustomShaderComponent.java b/src/ve/ucv/ciens/ccg/nxtar/components/VisibilityComponent.java similarity index 66% rename from src/ve/ucv/ciens/ccg/nxtar/components/CustomShaderComponent.java rename to src/ve/ucv/ciens/ccg/nxtar/components/VisibilityComponent.java index e66e879..2499f36 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/components/CustomShaderComponent.java +++ b/src/ve/ucv/ciens/ccg/nxtar/components/VisibilityComponent.java @@ -15,17 +15,16 @@ */ package ve.ucv.ciens.ccg.nxtar.components; -import ve.ucv.ciens.ccg.nxtar.graphics.shaders.CustomShaderBase; - import com.artemis.Component; -public class CustomShaderComponent extends Component { - public CustomShaderBase shader; +public class VisibilityComponent extends Component { + public boolean visible; - public CustomShaderComponent(CustomShaderBase shader) throws IllegalArgumentException{ - if(shader == null) - throw new IllegalArgumentException("Shader cannot be null."); + public VisibilityComponent(){ + this.visible = true; + } - this.shader = shader; + public VisibilityComponent(boolean visibility){ + this.visible = visibility; } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/entities/EntityCreatorBase.java b/src/ve/ucv/ciens/ccg/nxtar/entities/EntityCreatorBase.java index a5a8fa0..0d32630 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/entities/EntityCreatorBase.java +++ b/src/ve/ucv/ciens/ccg/nxtar/entities/EntityCreatorBase.java @@ -15,19 +15,72 @@ */ package ve.ucv.ciens.ccg.nxtar.entities; +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener; + import com.artemis.World; +import com.badlogic.gdx.assets.AssetManager; +import com.badlogic.gdx.utils.Disposable; -public abstract class EntityCreatorBase { - protected World world; +/** + * + */ +public abstract class EntityCreatorBase implements Disposable{ + protected World world = null; + protected ApplicationEventsListener core = null; + protected boolean entitiesCreated = false; + protected AssetManager manager = null; - public void setWorld(World world) throws IllegalArgumentException{ + /** + *Sets the Artemis {@link World} to use to create entities.
+ * + * @param world The Artemis {@link World}. + * @throws IllegalArgumentException if world is null. + */ + public final void setWorld(World world) throws IllegalArgumentException{ if(world == null) throw new IllegalArgumentException("World cannot be null."); this.world = world; } - public abstract void createAllEntities(); + /** + *Sets the application core to listen for asset loading events.
+ * + * @param core The application core to be used as listener. + * @throws IllegalArgumentException if core is null. + */ + public final void setCore(NxtARCore core) throws IllegalArgumentException{ + if(core == null) throw new IllegalArgumentException("Core is null."); + this.core = core; + } + /** + *Updates the state of the {@link AssetManager}.
+ * + * @return true if the {@link AssetManager} has finished loading. + */ + public abstract boolean updateAssetManager(); + + /** + *Unloads all assets loaded for the scenario.
+ */ public abstract void dispose(); + + /** + * @return true if the createAllEntities method has been called. + */ + public boolean areEntitiesCreated(){ + return entitiesCreated; + } + + /** + *Creates all entities for a game scenario.
+ */ + protected abstract void createAllEntities(); + + /** + *Recreates all entities in the game.
+ */ + public abstract void resetAllEntities(); } diff --git a/src/ve/ucv/ciens/ccg/nxtar/entities/MarkerTestEntityCreator.java b/src/ve/ucv/ciens/ccg/nxtar/entities/MarkerTestEntityCreator.java deleted file mode 100644 index d9ee15f..0000000 --- a/src/ve/ucv/ciens/ccg/nxtar/entities/MarkerTestEntityCreator.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.entities; - -import ve.ucv.ciens.ccg.nxtar.components.AnimationComponent; -import ve.ucv.ciens.ccg.nxtar.components.EnvironmentComponent; -import ve.ucv.ciens.ccg.nxtar.components.GeometryComponent; -import ve.ucv.ciens.ccg.nxtar.components.MarkerCodeComponent; -import ve.ucv.ciens.ccg.nxtar.components.ModelComponent; -import ve.ucv.ciens.ccg.nxtar.components.ShaderComponent; -import ve.ucv.ciens.ccg.nxtar.graphics.shaders.SingleLightPerPixelShader; - -import com.artemis.Entity; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.VertexAttribute; -import com.badlogic.gdx.graphics.VertexAttributes; -import com.badlogic.gdx.graphics.VertexAttributes.Usage; -import com.badlogic.gdx.graphics.g3d.Environment; -import com.badlogic.gdx.graphics.g3d.Material; -import com.badlogic.gdx.graphics.g3d.Model; -import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; -import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute; -import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; -import com.badlogic.gdx.graphics.g3d.loader.G3dModelLoader; -import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder; -import com.badlogic.gdx.math.Matrix3; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.JsonReader; - -public class MarkerTestEntityCreator extends EntityCreatorBase { - private static final String TAG = "MARKER_TEST_ENTITY_CREATOR"; - private static final String CLASS_NAME = MarkerTestEntityCreator.class.getSimpleName(); - - private Model bombModel; - private Model animatedModel; - private Model boxModel; - private SingleLightPerPixelShader ppShader; - - @Override - public void createAllEntities() { - ModelBuilder builder; - Entity bomb, box, anim; - G3dModelLoader loader; - Environment environment; - Material material; - - // Create mesh. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Creating the meshes."); - - loader = new G3dModelLoader(new JsonReader()); - - bombModel = loader.loadModel(Gdx.files.internal("models/Bomb_test_2.g3dj")); - animatedModel = loader.loadModel(Gdx.files.internal("models/cube.g3dj")); - - material = new Material(new FloatAttribute(FloatAttribute.Shininess, 50.0f), new ColorAttribute(ColorAttribute.Diffuse, 1.0f, 1.0f, 1.0f, 1.0f), new ColorAttribute(ColorAttribute.Specular, 1.0f, 1.0f, 1.0f, 1.0f)); - - builder = new ModelBuilder(); - boxModel = builder.createBox(0.5f, 0.5f, 6.0f, material, new VertexAttributes(new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.Color, 4, "a_color")).getMask()); - - // Load the shader. - ppShader = new SingleLightPerPixelShader(); - ppShader.init(); - - environment = new Environment(); - environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.3f, 0.3f, 0.3f, 1.0f)); - environment.add(new DirectionalLight().set(new Color(1, 1, 1, 1), new Vector3(1, 0, 0.5f))); - - // Create the entities. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Creating the enitites."); - bomb = world.createEntity(); - bomb.addComponent(new GeometryComponent(new Vector3(0.0f, 0.0f, 0.0f), new Matrix3().idt(), new Vector3(1.0f, 1.0f, 1.0f))); - bomb.addComponent(new ModelComponent(bombModel)); - bomb.addComponent(new EnvironmentComponent(environment)); - bomb.addComponent(new ShaderComponent(ppShader)); - bomb.addComponent(new MarkerCodeComponent(1023)); - - anim = world.createEntity(); - anim.addComponent(new GeometryComponent(new Vector3(0.0f, 0.0f, 0.0f), new Matrix3().idt(), new Vector3(0.25f, 0.25f, -0.25f))); - anim.addComponent(new ModelComponent(animatedModel)); - anim.addComponent(new AnimationComponent(anim.getComponent(ModelComponent.class).instance, 0, true)); - anim.addComponent(new EnvironmentComponent(environment)); - anim.addComponent(new MarkerCodeComponent(89)); - anim.addComponent(new ShaderComponent(ppShader)); - - box = world.createEntity(); - box.addComponent(new GeometryComponent(new Vector3(-1.0f, 0.0f, 0.0f), new Matrix3().idt(), new Vector3(1.0f, 1.0f, 1.0f))); - box.addComponent(new ModelComponent(boxModel)); - box.addComponent(new ShaderComponent(ppShader)); - box.addComponent(new EnvironmentComponent(environment)); - - // Add the entities to the world. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Adding entities to the world."); - //sphere.addToWorld(); - bomb.addToWorld(); - anim.addToWorld(); - box.addToWorld(); - } - - @Override - public void dispose() { - if(boxModel != null) - boxModel.dispose(); - - if(animatedModel != null) - animatedModel.dispose(); - - if(bombModel != null) - bombModel.dispose(); - - if(ppShader != null) - ppShader.dispose(); - } -} diff --git a/src/ve/ucv/ciens/ccg/nxtar/entities/TestGameEntityCreator.java b/src/ve/ucv/ciens/ccg/nxtar/entities/TestGameEntityCreator.java deleted file mode 100644 index b3842fc..0000000 --- a/src/ve/ucv/ciens/ccg/nxtar/entities/TestGameEntityCreator.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.entities; - -import ve.ucv.ciens.ccg.nxtar.components.GeometryComponent; -import ve.ucv.ciens.ccg.nxtar.components.MeshComponent; -import ve.ucv.ciens.ccg.nxtar.components.CustomShaderComponent; -import ve.ucv.ciens.ccg.nxtar.exceptions.ShaderFailedToLoadException; -import ve.ucv.ciens.ccg.nxtar.graphics.shaders.CustomShaderBase; -import ve.ucv.ciens.ccg.nxtar.graphics.shaders.SingleLightPhongShader; - -import com.artemis.Entity; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.Mesh; -import com.badlogic.gdx.graphics.VertexAttribute; -import com.badlogic.gdx.graphics.VertexAttributes; -import com.badlogic.gdx.graphics.VertexAttributes.Usage; -import com.badlogic.gdx.graphics.g3d.utils.MeshBuilder; -import com.badlogic.gdx.math.Matrix3; -import com.badlogic.gdx.math.Vector3; - -public class TestGameEntityCreator extends EntityCreatorBase { - private static final String TAG = "TEST_ENTITY_CREATOR"; - private static final String CLASS_NAME = TestGameEntityCreator.class.getSimpleName(); - - private MeshBuilder builder; - private Mesh sphereMesh; - private Mesh cubeMesh; - private Mesh capsuleMesh; - private CustomShaderBase singleLightPhongShader; - - @Override - public void createAllEntities() { - Matrix3 identity = new Matrix3(); - Entity sphere; - Entity cube; - Entity capsule1; - Entity capsule2; - - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Started."); - - identity.idt(); - - // Create the sphere. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Creating the meshes."); - builder = new MeshBuilder(); - builder.begin(new VertexAttributes(new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.Color, 4, "a_color")), GL20.GL_TRIANGLES);{ - builder.setColor(1.0f, 1.0f, 1.0f, 1.0f); - builder.sphere(1.0f, 1.0f, 1.0f, 10, 10); - }sphereMesh = builder.end(); - - // Create the cube. - builder.begin(new VertexAttributes(new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.Color, 4, "a_color")), GL20.GL_TRIANGLES);{ - builder.setColor(0.2f, 0.5f, 1.0f, 1.0f); - builder.box(0.5f, 0.5f, 0.5f); - }cubeMesh = builder.end(); - - // Create the capsule. - builder.begin(new VertexAttributes(new VertexAttribute(Usage.Position, 3, "a_position"), new VertexAttribute(Usage.Normal, 3, "a_normal"), new VertexAttribute(Usage.Color, 4, "a_color")), GL20.GL_TRIANGLES);{ - builder.setColor(1.0f, 1.0f, 1.0f, 1.0f); - builder.capsule(0.25f, 0.5f, 10); - }capsuleMesh = builder.end(); - - // Load the phong shader. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Loading the phong shader."); - try{ - singleLightPhongShader = new SingleLightPhongShader().loadShader(); - }catch(ShaderFailedToLoadException se){ - Gdx.app.error(TAG, CLASS_NAME + ".InGameState(): " + se.getMessage()); - Gdx.app.exit(); - } - - // Create the entities. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Creating the enitites."); - sphere = world.createEntity(); - sphere.addComponent(new GeometryComponent(new Vector3(0.5f, 0.5f, 0.0f), identity, new Vector3(1.0f, 1.0f, 1.0f))); - sphere.addComponent(new MeshComponent(sphereMesh)); - sphere.addComponent(new CustomShaderComponent(singleLightPhongShader)); - - cube = world.createEntity(); - cube.addComponent(new GeometryComponent(new Vector3(-0.5f, -0.5f, 0.0f), identity, new Vector3(1.0f, 1.0f, 1.0f))); - cube.addComponent(new MeshComponent(cubeMesh)); - cube.addComponent(new CustomShaderComponent(singleLightPhongShader)); - - capsule1 = world.createEntity(); - capsule1.addComponent(new GeometryComponent(new Vector3(-0.5f, 0.5f, 0.0f), identity, new Vector3(1.5f, 1.0f, 1.0f))); - capsule1.addComponent(new MeshComponent(capsuleMesh)); - capsule1.addComponent(new CustomShaderComponent(singleLightPhongShader)); - - capsule2 = world.createEntity(); - capsule2.addComponent(new GeometryComponent(new Vector3(0.5f, -0.5f, 0.0f), identity, new Vector3(1.0f, 1.5f, 1.0f))); - capsule2.addComponent(new MeshComponent(capsuleMesh)); - capsule2.addComponent(new CustomShaderComponent(singleLightPhongShader)); - - // Add the entities to the world. - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Adding entities to the world."); - sphere.addToWorld(); - cube.addToWorld(); - capsule1.addToWorld(); - capsule2.addToWorld(); - - Gdx.app.log(TAG, CLASS_NAME + ".createAllEntities(): Finished."); - } - - @Override - public void dispose() { - if(singleLightPhongShader != null && singleLightPhongShader.getShaderProgram() != null) - singleLightPhongShader.getShaderProgram().dispose(); - - if(sphereMesh != null) - sphereMesh.dispose(); - - if(cubeMesh != null) - cubeMesh.dispose(); - - if(capsuleMesh != null) - capsuleMesh.dispose(); - } - -} diff --git a/src/ve/ucv/ciens/ccg/nxtar/graphics/CustomPerspectiveCamera.java b/src/ve/ucv/ciens/ccg/nxtar/graphics/CustomPerspectiveCamera.java index bd0f89d..730e8f2 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/graphics/CustomPerspectiveCamera.java +++ b/src/ve/ucv/ciens/ccg/nxtar/graphics/CustomPerspectiveCamera.java @@ -27,22 +27,47 @@ public class CustomPerspectiveCamera extends PerspectiveCamera{ private final Vector3 tmp = new Vector3(); public CustomPerspectiveCamera(float fieldOfView, float viewportWidth, float viewportHeight){ - this.fieldOfView = fieldOfView; - this.viewportWidth = viewportWidth; - this.viewportHeight = viewportHeight; + super(fieldOfView, viewportWidth, viewportHeight); update(); } + public void update(Matrix4 customProjection){ + this.update(customProjection, true); + } + public void update(Matrix4 customProjection, boolean updateFrustum){ projection.set(customProjection); view.setToLookAt(position, tmp.set(position).add(direction), up); - combined.set(projection); - Matrix4.mul(combined.val, view.val); + combined.set(projection).mul(view); if(updateFrustum){ - invProjectionView.set(combined); - Matrix4.inv(invProjectionView.val); + invProjectionView.set(combined).inv(); frustum.update(invProjectionView); } } + + public void setCustomARProjectionMatrix(final float focalPointX, final float focalPointY, final float cameraCenterX, final float cameraCenterY, final float near, final float far, final float w, final float h){ + final float FAR_PLUS_NEAR = far + near; + final float FAR_LESS_NEAR = far - near; + + projection.val[Matrix4.M00] = -2.0f * focalPointX / w; + projection.val[Matrix4.M10] = 0.0f; + projection.val[Matrix4.M20] = 0.0f; + projection.val[Matrix4.M30] = 0.0f; + + projection.val[Matrix4.M01] = 0.0f; + projection.val[Matrix4.M11] = 2.0f * focalPointY / h; + projection.val[Matrix4.M21] = 0.0f; + projection.val[Matrix4.M31] = 0.0f; + + projection.val[Matrix4.M02] = 2.0f * cameraCenterX / w - 1.0f; + projection.val[Matrix4.M12] = 2.0f * cameraCenterY / h - 1.0f; + projection.val[Matrix4.M22] = -FAR_PLUS_NEAR / FAR_LESS_NEAR; + projection.val[Matrix4.M32] = -1.0f; + + projection.val[Matrix4.M03] = 0.0f; + projection.val[Matrix4.M13] = 0.0f; + projection.val[Matrix4.M23] = -2.0f * far * near / FAR_LESS_NEAR; + projection.val[Matrix4.M33] = 0.0f; + } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/graphics/LightSource.java b/src/ve/ucv/ciens/ccg/nxtar/graphics/LightSource.java deleted file mode 100644 index d7dab46..0000000 --- a/src/ve/ucv/ciens/ccg/nxtar/graphics/LightSource.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.graphics; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.Vector3; - -/** - *A 3D light source.
- */ -public class LightSource{ - private Vector3 position; - private Color ambientColor; - private Color diffuseColor; - private Color specularColor; - private float shinyness; - - /** - *Creates a default white light source positioned at (0,0,0).
- */ - public LightSource(){ - position = new Vector3(0.0f, 0.0f, 0.0f); - ambientColor = new Color(0.15f, 0.15f, 0.15f, 1.0f); - diffuseColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); - specularColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); - ambientColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); - shinyness = 10.0f; - } - - /** - *Creates a white light source at the specified position.
- * - * @param position The location of the light source. - */ - public LightSource(Vector3 position){ - this.position = new Vector3(); - - this.position.set(position); - ambientColor = new Color(0.15f, 0.15f, 0.15f, 1.0f); - diffuseColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); - specularColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); - ambientColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); - shinyness = 10.0f; - } - - /** - *Creates a custom light source.
- * - * @param position The location of the light source. - * @param ambientColor - * @param diffuseColor - * @param specularColor - * @param shinyness The shinyness component. Must be between (0.0, 128.0]. - * @throws IllegalArgumentException When shinyness is outside the valid range. - */ - public LightSource(Vector3 position, Color ambientColor, Color diffuseColor, Color specularColor, float shinyness) throws IllegalArgumentException { - if(shinyness <= 0.0 || shinyness > 128.0) - throw new IllegalArgumentException("Shinyness must be between (0.0, 128.0]."); - - this.position = new Vector3(); - this.ambientColor = new Color(); - this.diffuseColor = new Color(); - this.ambientColor = new Color(); - this.specularColor = new Color(); - - this.position.set(position); - this.ambientColor.set(ambientColor); - this.diffuseColor.set(diffuseColor); - this.specularColor.set(specularColor); - this.shinyness = shinyness; - } - - public LightSource(LightSource light){ - this.position = new Vector3(); - this.ambientColor = new Color(); - this.diffuseColor = new Color(); - this.ambientColor = new Color(); - this.specularColor = new Color(); - - set(light); - } - - public void set(LightSource light){ - this.position.set(light.getPosition()); - this.ambientColor.set(light.getAmbientColor()); - this.diffuseColor.set(light.getDiffuseColor()); - this.specularColor.set(light.getSpecularColor()); - this.shinyness = light.shinyness; - } - - public void setPosition(float x, float y, float z){ - position.set(x, y, z); - } - - public void setPosition(Vector3 position){ - this.position.set(position); - } - - public void setAmbientColor(float r, float g, float b, float a){ - ambientColor.set(r, g, b, a); - } - - public void setAmbientColor(Color ambientColor){ - this.ambientColor.set(ambientColor); - } - - public void setDiffuseColor(float r, float g, float b, float a){ - diffuseColor.set(r, g, b, a); - } - - public void setdiffuseColor(Color diffuseColor){ - this.diffuseColor.set(diffuseColor); - } - - public void setSpecularColor(float r, float g, float b, float a){ - specularColor.set(r, g, b, a); - } - - public void setSpecularColor(Color specularColor){ - this.specularColor.set(specularColor); - } - - public void setShinyness(float shinyness) throws IllegalArgumentException { - if(shinyness <= 0.0 || shinyness > 128.0) - throw new IllegalArgumentException("Shinyness must be between (0.0, 128.0]."); - - this.shinyness = shinyness; - } - - public Vector3 getPosition(){ - return position; - } - - public Color getAmbientColor(){ - return ambientColor; - } - - public Color getDiffuseColor(){ - return diffuseColor; - } - - public Color getSpecularColor(){ - return specularColor; - } - - public float getShinyness(){ - return shinyness; - } -} diff --git a/src/ve/ucv/ciens/ccg/nxtar/graphics/RenderParameters.java b/src/ve/ucv/ciens/ccg/nxtar/graphics/RenderParameters.java deleted file mode 100644 index 12fd477..0000000 --- a/src/ve/ucv/ciens/ccg/nxtar/graphics/RenderParameters.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.graphics; - -import com.badlogic.gdx.math.Matrix4; -import com.badlogic.gdx.math.Vector3; - -public abstract class RenderParameters { - private static Matrix4 modelViewProjection; - private static Matrix4 geometricTransformation; - private static Vector3 eyePosition; - private static LightSource lightSource1; - private static LightSource lightSource2; - - static{ - modelViewProjection = new Matrix4(); - geometricTransformation = new Matrix4(); - eyePosition = new Vector3(0.0f, 0.0f, 1.4142f); - lightSource1 = new LightSource(); - lightSource2 = new LightSource(); - } - - public static synchronized void setModelViewProjectionMatrix(Matrix4 modelViewMatrix){ - modelViewProjection.set(modelViewMatrix); - } - - public static synchronized void setTransformationMatrix(Matrix4 transformationMatrix){ - geometricTransformation.set(transformationMatrix); - } - - public static synchronized void setEyePosition(Vector3 newEyePostition){ - eyePosition.set(newEyePostition); - } - - public static synchronized void setLightSource1(LightSource newLightSource1){ - lightSource1.set(newLightSource1); - } - - public static synchronized void setLightSource2(LightSource newLightSource2){ - lightSource2.set(newLightSource2); - } - - public static synchronized Matrix4 getModelViewProjectionMatrix(){ - return modelViewProjection; - } - - public static synchronized Matrix4 getTransformationMatrix(){ - return geometricTransformation; - } - - public static synchronized Vector3 getEyePosition(){ - return eyePosition; - } - - public static synchronized LightSource getLightSource1(){ - return lightSource1; - } - - public static synchronized LightSource getLightSource2(){ - return lightSource2; - } -} diff --git a/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/SingleLightPerPixelShader.java b/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/DirectionalLightPerPixelShader.java similarity index 79% rename from src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/SingleLightPerPixelShader.java rename to src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/DirectionalLightPerPixelShader.java index b123837..e3d523f 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/SingleLightPerPixelShader.java +++ b/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/DirectionalLightPerPixelShader.java @@ -30,12 +30,14 @@ import com.badlogic.gdx.math.Matrix4; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.GdxRuntimeException; -public class SingleLightPerPixelShader implements Shader{ +public class DirectionalLightPerPixelShader implements Shader{ private static final int MAX_NUM_BONES = 4; private static final Matrix4 IDENTITY = new Matrix4(); private static final String VERTEX_SHADER_PATH = "shaders/directionalPerPixelSingleLight/directionalPerPixel_vert.glsl"; private static final String FRAGMENT_SHADER_PATH = "shaders/directionalPerPixelSingleLight/directionalPerPixel_frag.glsl"; private static final String INCLUDE_SKINNING = "#define SKINNING\n"; + private static final float DEFAULT_SHININESS = 50.0f; + private static final Vector3 DEFAULT_LIGHT = new Vector3(1, 1, 1); private ShaderProgram skinningProgram; private ShaderProgram baseProgram; @@ -56,7 +58,7 @@ public class SingleLightPerPixelShader implements Shader{ private int[] u_normalMatrix; private int[] u_bones; - public SingleLightPerPixelShader(){ + public DirectionalLightPerPixelShader(){ skinningProgram = null; baseProgram = null; camera = null; @@ -79,7 +81,7 @@ public class SingleLightPerPixelShader implements Shader{ throw new GdxRuntimeException(skinningProgram.getLog()); if(!baseProgram.isCompiled()) - throw new GdxRuntimeException(skinningProgram.getLog()); + throw new GdxRuntimeException(baseProgram.getLog()); // Create uniform locations. u_projTrans = new int[2]; @@ -134,22 +136,8 @@ public class SingleLightPerPixelShader implements Shader{ @Override public boolean canRender(Renderable renderable){ - // Check for all needed lighting and material attributes. - if(renderable.environment.directionalLights.size < 1) - return false; - - if(!renderable.environment.has(ColorAttribute.AmbientLight)) - return false; - - if(!renderable.material.has(ColorAttribute.Diffuse)) - return false; - - if(!renderable.material.has(ColorAttribute.Specular)) - return false; - - if(!renderable.material.has(FloatAttribute.Shininess)) - return false; - + // Easier to always return true. Missing material properties are replaced by + // default values during render. return true; } @@ -169,16 +157,43 @@ public class SingleLightPerPixelShader implements Shader{ @Override public void render(Renderable renderable){ ShaderProgram program; - int index; - boolean bonesEnabled; + int index; + boolean bonesEnabled; + Vector3 lightPosition; + Color diffuseLightColor; + Color diffuseColor; + Color specularColor; + Color ambientColor; + float shininess; // Get material colors. - Vector3 lightPosition = renderable.environment.directionalLights.get(0).direction; - Color diffuseLightColor = renderable.environment.directionalLights.get(0).color; - Color diffuseColor = ((ColorAttribute)renderable.material.get(ColorAttribute.Diffuse)).color; - Color specularColor = ((ColorAttribute)renderable.material.get(ColorAttribute.Specular)).color; - Color ambientColor = ((ColorAttribute)renderable.environment.get(ColorAttribute.AmbientLight)).color; - float shininess = ((FloatAttribute)renderable.material.get(FloatAttribute.Shininess)).value; + if(renderable.environment != null && renderable.environment.directionalLights != null && renderable.environment.directionalLights.size >= 1){ + lightPosition = renderable.environment.directionalLights.get(0).direction; + diffuseLightColor = renderable.environment.directionalLights.get(0).color; + }else{ + lightPosition = DEFAULT_LIGHT; + diffuseLightColor = Color.WHITE; + } + + if(renderable.material.has(ColorAttribute.Diffuse)) + diffuseColor = ((ColorAttribute)renderable.material.get(ColorAttribute.Diffuse)).color; + else + diffuseColor = Color.WHITE; + + if(renderable.material.has(ColorAttribute.Specular)) + specularColor = ((ColorAttribute)renderable.material.get(ColorAttribute.Specular)).color; + else + specularColor = Color.BLACK; + + if(renderable.environment != null && renderable.environment.has(ColorAttribute.AmbientLight)) + ambientColor = ((ColorAttribute)renderable.environment.get(ColorAttribute.AmbientLight)).color; + else + ambientColor = Color.BLACK; + + if(renderable.material.has(FloatAttribute.Shininess)) + shininess = ((FloatAttribute)renderable.material.get(FloatAttribute.Shininess)).value; + else + shininess = DEFAULT_SHININESS; if(renderable.mesh.getVertexAttribute(VertexAttributes.Usage.BoneWeight) != null){ program = skinningProgram; @@ -198,7 +213,7 @@ public class SingleLightPerPixelShader implements Shader{ // Set model dependant uniforms. program.setUniformMatrix(u_geomTrans[index], renderable.worldTransform); - program.setUniformMatrix(u_normalMatrix[index], normalMatrix.idt().mul(renderable.worldTransform).inv().tra()); + program.setUniformMatrix(u_normalMatrix[index], normalMatrix.set(renderable.worldTransform).toNormalMatrix()); program.setUniformf(u_lightPos[index], lightPosition); program.setUniformf(u_lightDiffuse[index], diffuseLightColor); program.setUniformf(u_materialDiffuse[index], diffuseColor); diff --git a/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/SingleLightPhongShader.java b/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/SingleLightPhongShader.java deleted file mode 100644 index 66dcd54..0000000 --- a/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/SingleLightPhongShader.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.graphics.shaders; - -import ve.ucv.ciens.ccg.nxtar.exceptions.ShaderFailedToLoadException; -import ve.ucv.ciens.ccg.nxtar.graphics.LightSource; -import ve.ucv.ciens.ccg.nxtar.graphics.RenderParameters; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.glutils.ShaderProgram; - -public class SingleLightPhongShader extends CustomShaderBase{ - private static String VERTEX_SHADER_PATH = "shaders/singleDiffuseLight/singleDiffuseLight_vert.glsl"; - private static String FRAGMENT_SHADER_PATH = "shaders/singleDiffuseLight/singleDiffuseLight_frag.glsl"; - - @Override - public SingleLightPhongShader loadShader() throws ShaderFailedToLoadException{ - shaderProgram = new ShaderProgram(Gdx.files.internal(VERTEX_SHADER_PATH), Gdx.files.internal(FRAGMENT_SHADER_PATH)); - - if(!shaderProgram.isCompiled()){ - throw new ShaderFailedToLoadException("SingleLightPerPixelPhongShader failed to load.\n" + shaderProgram.getLog()); - } - - return this; - } - - @Override - public void setUniforms(){ - LightSource light = RenderParameters.getLightSource1(); - float[] diffuseColor = {light.getDiffuseColor().r, light.getDiffuseColor().g, light.getDiffuseColor().b, light.getDiffuseColor().a}; - float[] ambientColor = {light.getAmbientColor().r, light.getAmbientColor().g, light.getAmbientColor().b, light.getAmbientColor().a}; - float[] specularColor = {light.getSpecularColor().r, light.getSpecularColor().g, light.getSpecularColor().b, light.getSpecularColor().a}; - float[] position = {light.getPosition().x, light.getPosition().y, light.getPosition().z, 0.0f}; - float[] shinyness = {light.getShinyness()}; - - shaderProgram.setUniformMatrix("u_projTrans", RenderParameters.getModelViewProjectionMatrix()); - shaderProgram.setUniformMatrix("u_geomTrans", RenderParameters.getTransformationMatrix()); - shaderProgram.setUniform4fv("u_lightPos", position, 0, 4); - shaderProgram.setUniform4fv("u_lightDiffuse", diffuseColor, 0, 4); - shaderProgram.setUniform4fv("u_specular", specularColor, 0, 4); - shaderProgram.setUniform4fv("u_ambient", ambientColor, 0, 4); - shaderProgram.setUniform1fv("u_shiny", shinyness, 0, 1); - shaderProgram.setUniformf("u_cameraPos", RenderParameters.getEyePosition()); - } -} diff --git a/src/ve/ucv/ciens/ccg/nxtar/input/GamepadUserInput.java b/src/ve/ucv/ciens/ccg/nxtar/input/GamepadUserInput.java new file mode 100644 index 0000000..8e16006 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/input/GamepadUserInput.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.input; + +public class GamepadUserInput extends UserInput { + public float axisLeftX; + public float axisLeftY; + public float axisRightX; + public float axisRightY; + public boolean oButton; + + public GamepadUserInput(){ + this.axisLeftX = 0.0f; + this.axisLeftY = 0.0f; + this.axisRightX = 0.0f; + this.axisRightY = 0.0f; + this.oButton = false; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/input/KeyboardUserInput.java b/src/ve/ucv/ciens/ccg/nxtar/input/KeyboardUserInput.java new file mode 100644 index 0000000..3c2c6d7 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/input/KeyboardUserInput.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.input; + +public class KeyboardUserInput extends UserInput { + public boolean keyLeft; + public boolean keyRight; + public boolean keyUp; + public boolean keyDown; + public boolean keySpace; + + public KeyboardUserInput(){ + this.keyLeft = false; + this.keyRight = false; + this.keyUp = false; + this.keyDown = false; + this.keySpace = false; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/CustomShaderBase.java b/src/ve/ucv/ciens/ccg/nxtar/input/TouchUserInput.java similarity index 58% rename from src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/CustomShaderBase.java rename to src/ve/ucv/ciens/ccg/nxtar/input/TouchUserInput.java index 648fb22..221da6c 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/graphics/shaders/CustomShaderBase.java +++ b/src/ve/ucv/ciens/ccg/nxtar/input/TouchUserInput.java @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ve.ucv.ciens.ccg.nxtar.graphics.shaders; +package ve.ucv.ciens.ccg.nxtar.input; -import ve.ucv.ciens.ccg.nxtar.exceptions.ShaderFailedToLoadException; +import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.graphics.glutils.ShaderProgram; +public class TouchUserInput extends UserInput { + public Vector3 userTouchEndPoint; -public abstract class CustomShaderBase{ - protected ShaderProgram shaderProgram; + public TouchUserInput(){ + this.userTouchEndPoint = new Vector3(); + } - public abstract CustomShaderBase loadShader() throws ShaderFailedToLoadException; + public TouchUserInput(Vector3 userTouchEndPoint){ + this.userTouchEndPoint = new Vector3(userTouchEndPoint); + } - public abstract void setUniforms(); - - public ShaderProgram getShaderProgram(){ - return this.shaderProgram; + public TouchUserInput(float x, float y, float z){ + this.userTouchEndPoint = new Vector3(x, y, z); } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/exceptions/ShaderFailedToLoadException.java b/src/ve/ucv/ciens/ccg/nxtar/input/UserInput.java similarity index 73% rename from src/ve/ucv/ciens/ccg/nxtar/exceptions/ShaderFailedToLoadException.java rename to src/ve/ucv/ciens/ccg/nxtar/input/UserInput.java index c8ca5f3..b1ac203 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/exceptions/ShaderFailedToLoadException.java +++ b/src/ve/ucv/ciens/ccg/nxtar/input/UserInput.java @@ -13,12 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package ve.ucv.ciens.ccg.nxtar.exceptions; +package ve.ucv.ciens.ccg.nxtar.input; -public class ShaderFailedToLoadException extends Exception { - private static final long serialVersionUID = 9989L; - - public ShaderFailedToLoadException(String msg){ - super(msg); - } -} +/** + * Tag class for different user interaction wrapper classes. + */ +public abstract class UserInput{ } diff --git a/src/ve/ucv/ciens/ccg/nxtar/interfaces/ApplicationEventsListener.java b/src/ve/ucv/ciens/ccg/nxtar/interfaces/ApplicationEventsListener.java index f759925..77f9014 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/interfaces/ApplicationEventsListener.java +++ b/src/ve/ucv/ciens/ccg/nxtar/interfaces/ApplicationEventsListener.java @@ -16,5 +16,23 @@ package ve.ucv.ciens.ccg.nxtar.interfaces; public interface ApplicationEventsListener { - public void networkStreamConnected(String streamName); + /** + *Callback used by the networking threads to notify sucessfull connections + * to the application
+ * + * @param streamName The name of the thread notifying a connection. + */ + public void onNetworkStreamConnected(String streamName); + + /** + *Callback used by the assets loader to notify that all + * required game assets are ready to be used.
+ */ + public void onAssetsLoaded(); + + /** + *Callback used by the camera calibration state to notify that the + * camera has been succesfully calibrated.
+ */ + public void onCameraCalibrated(); } diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java index 52bfa8e..2852cad 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/RobotControlThread.java @@ -34,6 +34,7 @@ public class RobotControlThread extends Thread { public static final String THREAD_NAME = "RobotControlThread"; private static final String TAG = "NXTAR_CORE_ROBOTTHREAD"; private static final String CLASS_NAME = RobotControlThread.class.getSimpleName(); + private static int refCount = 0; private ApplicationEventsListener netListener; private ServerSocket server; @@ -62,13 +63,21 @@ public class RobotControlThread extends Thread { } private static class SingletonHolder{ - public static final RobotControlThread INSTANCE = new RobotControlThread(); + public static RobotControlThread INSTANCE; } public static RobotControlThread getInstance(){ + if(refCount == 0) + SingletonHolder.INSTANCE = new RobotControlThread(); + refCount++; return SingletonHolder.INSTANCE; } + public static void freeInstance(){ + refCount--; + if(refCount == 0) SingletonHolder.INSTANCE = null; + } + public void addNetworkConnectionListener(ApplicationEventsListener listener){ netListener = listener; } @@ -87,6 +96,11 @@ public class RobotControlThread extends Thread { public void finish(){ done = true; + try{ + server.close(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client: " + io.getMessage(), io); + } } @Override @@ -97,7 +111,7 @@ public class RobotControlThread extends Thread { try{ client = server.accept(); client.setTcpNoDelay(true); - if(netListener != null) netListener.networkStreamConnected(THREAD_NAME); + if(netListener != null) netListener.onNetworkStreamConnected(THREAD_NAME); os = new ObjectOutputStream(client.getOutputStream()); is = new ObjectInputStream(client.getInputStream()); @@ -168,7 +182,6 @@ public class RobotControlThread extends Thread { } } - try{ client.close(); }catch(IOException io){ diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/SensorReportThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/SensorReportThread.java index f980e63..84409ad 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/SensorReportThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/SensorReportThread.java @@ -29,6 +29,7 @@ public class SensorReportThread extends Thread { public static final String THREAD_NAME = "SensorReportThread"; private static final String TAG = "NXTAR_CORE_ROBOTTHREAD"; private static final String CLASS_NAME = SensorReportThread.class.getSimpleName(); + private static int refCount = 0; private ApplicationEventsListener netListener; private ServerSocket server; @@ -56,13 +57,21 @@ public class SensorReportThread extends Thread { } private static class SingletonHolder{ - public final static SensorReportThread INSTANCE = new SensorReportThread(); + public static SensorReportThread INSTANCE; } public static SensorReportThread getInstance(){ + if(refCount == 0) + SingletonHolder.INSTANCE = new SensorReportThread(); + refCount++; return SingletonHolder.INSTANCE; } + public static void freeInstance(){ + refCount--; + if(refCount == 0) SingletonHolder.INSTANCE = null; + } + public void addNetworkConnectionListener(ApplicationEventsListener listener){ netListener = listener; } @@ -81,6 +90,11 @@ public class SensorReportThread extends Thread { public void finish(){ done = true; + try{ + server.close(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException closing sockets: " + io.getMessage(), io); + } } public byte getLightSensorReading(){ @@ -100,7 +114,7 @@ public class SensorReportThread extends Thread { try{ client = server.accept(); client.setTcpNoDelay(true); - if(netListener != null) netListener.networkStreamConnected(THREAD_NAME); + if(netListener != null) netListener.onNetworkStreamConnected(THREAD_NAME); reader = client.getInputStream(); }catch(IOException io){ @@ -122,5 +136,13 @@ public class SensorReportThread extends Thread { lightReading = reading[0]; } } + + try{ + reader.close(); + client.close(); + server.close(); + }catch(IOException io){ + Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException closing sockets: " + io.getMessage(), io); + } } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java index e740513..8a54bcf 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/ServiceDiscoveryThread.java @@ -38,35 +38,28 @@ import com.badlogic.gdx.Gdx; * @author miky */ public class ServiceDiscoveryThread extends Thread { - /** - * The name used to identify this thread. - */ - public static final String THREAD_NAME = "ServiceDiscoveryThread"; - /** - * Tag used for logging. - */ + public static final String THREAD_NAME = "ServiceDiscoveryThread"; private static final String TAG = "NXTAR_CORE_UDPTHREAD"; - /** - * Class name used for logging. - */ private static final String CLASS_NAME = ServiceDiscoveryThread.class.getSimpleName(); - /** - * Maximum number of transmission attempts before ending the thread abruptly. - */ - private static final int MAX_RETRIES = 5; + private static final int MAX_RETRIES = 5; + + private static int refCount = 0; /** * A semaphore object used to synchronize acces to this thread finish flag. */ private Object semaphore; + /** * The finish flag. */ private boolean done; + /** * The UDP server socket used for the ad hoc service discovery protocol. */ private DatagramSocket udpServer; + /** * Holder for the multicast address used in the protocol. */ @@ -101,7 +94,7 @@ public class ServiceDiscoveryThread extends Thread { * Singleton holder for this class. */ private static class SingletonHolder{ - public static final ServiceDiscoveryThread INSTANCE = new ServiceDiscoveryThread(); + public static ServiceDiscoveryThread INSTANCE; } /** @@ -110,9 +103,17 @@ public class ServiceDiscoveryThread extends Thread { * @return The singleton instance. */ public static ServiceDiscoveryThread getInstance(){ + if(refCount == 0) + SingletonHolder.INSTANCE = new ServiceDiscoveryThread(); + refCount++; return SingletonHolder.INSTANCE; } + public static void freeInstance(){ + refCount--; + if(refCount == 0) SingletonHolder.INSTANCE = null; + } + /** * This thread's run method. * @@ -138,6 +139,7 @@ public class ServiceDiscoveryThread extends Thread { // Verify if the thread should end. If that is the case, close the server. synchronized(semaphore){ if(done){ + Gdx.app.log(TAG, CLASS_NAME + ".run(): Closing."); udpServer.close(); break; } diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java index ac2eb60..65354e6 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/VideoStreamingThread.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; -import java.net.Socket; import ve.ucv.ciens.ccg.networkdata.VideoFrameDataMessage; import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener; @@ -33,6 +32,7 @@ public class VideoStreamingThread extends Thread{ public static final String THREAD_NAME = "VideoStreamingThread"; private static final String TAG = "NXTAR_CORE_VIDEOTHREAD"; private static final String CLASS_NAME = VideoStreamingThread.class.getSimpleName(); + private static int refCount = 0; private ApplicationEventsListener netListener; private DatagramSocket socket; @@ -41,7 +41,6 @@ public class VideoStreamingThread extends Thread{ private boolean pause; private boolean coreNotified; private Object protocolPauseMonitor; - private Socket client; private VideoFrameMonitor frameMonitor; private long then; private long now; @@ -72,13 +71,21 @@ public class VideoStreamingThread extends Thread{ } private static class SingletonHolder{ - public static final VideoStreamingThread INSTANCE = new VideoStreamingThread(); + public static VideoStreamingThread INSTANCE; } public static VideoStreamingThread getInstance(){ + if(refCount == 0) + SingletonHolder.INSTANCE = new VideoStreamingThread(); + refCount++; return SingletonHolder.INSTANCE; } + public static void freeInstance(){ + refCount--; + if(refCount == 0) SingletonHolder.INSTANCE = null; + } + public void addNetworkConnectionListener(ApplicationEventsListener listener){ netListener = listener; } @@ -141,6 +148,7 @@ public class VideoStreamingThread extends Thread{ //Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message size from socket."); try{ packet = new DatagramPacket(size, size.length); + socket.setSoTimeout(1000); socket.receive(packet); }catch(IOException io){ Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving size " + io.getMessage()); @@ -200,7 +208,7 @@ public class VideoStreamingThread extends Thread{ public int getFps(){ return fps; } - + public int getLostFrames(){ return lostFrames; } @@ -220,7 +228,7 @@ public class VideoStreamingThread extends Thread{ //Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Receiving."); if(netListener != null && !coreNotified && frameMonitor.getCurrentFrame() != null){ coreNotified = true; - netListener.networkStreamConnected(THREAD_NAME); + netListener.onNetworkStreamConnected(THREAD_NAME); } receiveUdp(); frames++; @@ -236,12 +244,6 @@ public class VideoStreamingThread extends Thread{ } } - try{ - client.close(); - }catch(IOException io){ - Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client socket.", io); - } - Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished."); } @@ -250,7 +252,7 @@ public class VideoStreamingThread extends Thread{ pause = true; } } - + public void play(){ synchronized (pauseMonitor){ pause = false; diff --git a/src/ve/ucv/ciens/ccg/nxtar/network/monitors/VideoFrameMonitor.java b/src/ve/ucv/ciens/ccg/nxtar/network/monitors/VideoFrameMonitor.java index a0902ec..4d598ff 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/network/monitors/VideoFrameMonitor.java +++ b/src/ve/ucv/ciens/ccg/nxtar/network/monitors/VideoFrameMonitor.java @@ -61,14 +61,11 @@ public class VideoFrameMonitor{ public void setNewFrame(byte[] frame){ byte[] temp; - Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Loading new frame in frameA."); frameA = frame; temp = frameA; synchronized(frameMonitor){ - Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping frameA and frameB."); frameA = frameB; frameB = temp; - Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping done."); } } @@ -76,7 +73,6 @@ public class VideoFrameMonitor{ byte[] frame; synchronized(frameMonitor){ - //Gdx.app.debug(TAG, CLASS_NAME + ".getCurrentFrame() :: Fetching frameB."); frame = frameB; } return frame; diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/AutomaticActionPerformerBase.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/AutomaticActionPerformerBase.java new file mode 100644 index 0000000..95b279d --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/AutomaticActionPerformerBase.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios; + +import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor.MarkerData; + +public abstract class AutomaticActionPerformerBase { + public enum automatic_action_t{ + NO_ACTION, + GO_FORWARD, + GO_BACKWARDS, + STOP, + TURN_LEFT, + TURN_RIGHT, + BACKWARDS_LEFT, + BACKWARDS_RIGHT, + ROTATE_90, + RECENTER, + LOOK_RIGHT, + LOOK_LEFT, + STOP_LOOKING; + } + + public abstract boolean performAutomaticAction(int lightSensorReading, MarkerData markers); + public abstract automatic_action_t getNextAction(); + public abstract SummaryBase getSummary(); + public abstract void reset(); +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/HintsOverlayBase.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/HintsOverlayBase.java new file mode 100644 index 0000000..2a7d685 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/HintsOverlayBase.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios; + +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.utils.Disposable; + +/** + *Base class for hint screens.
+ */ +public abstract class HintsOverlayBase implements Disposable{ + /** + *Renders the overlay.
+ * + * @param batch The {@link SpriteBatch} to use for rendering. + */ + public abstract void render(SpriteBatch batch); +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/ScenarioGlobals.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/ScenarioGlobals.java new file mode 100644 index 0000000..864df04 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/ScenarioGlobals.java @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios; + +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.entities.EntityCreatorBase; +import ve.ucv.ciens.ccg.nxtar.systems.AnimationSystem; +import ve.ucv.ciens.ccg.nxtar.systems.CollisionDetectionSystem; +import ve.ucv.ciens.ccg.nxtar.systems.FadeEffectRenderingSystem; +import ve.ucv.ciens.ccg.nxtar.systems.GameLogicSystemBase; +import ve.ucv.ciens.ccg.nxtar.systems.GeometrySystem; +import ve.ucv.ciens.ccg.nxtar.systems.MarkerPositioningSystem; +import ve.ucv.ciens.ccg.nxtar.systems.MarkerRenderingSystem; +import ve.ucv.ciens.ccg.nxtar.systems.ObjectRenderingSystem; +import ve.ucv.ciens.ccg.nxtar.systems.PlayerSystemBase; +import ve.ucv.ciens.ccg.nxtar.systems.RobotArmPositioningSystem; + +import com.artemis.EntitySystem; +import com.artemis.World; +import com.artemis.managers.GroupManager; +import com.artemis.utils.ImmutableBag; +import com.badlogic.gdx.graphics.g3d.ModelBatch; +import com.badlogic.gdx.utils.Disposable; + +public abstract class ScenarioGlobals{ + private static EntityCreatorBase entityCreator = null; + private static GameLogicSystemBase gameLogicSystem = null; + private static World gameWorld = null; + private static ModelBatch modelBatch = null; + private static AutomaticActionPerformerBase automaticActionPerformer = null; + private static SummaryOverlayBase automaticActionSummaryOverlay = null; + private static PlayerSystemBase playerSystem = null; + private static SummaryOverlayBase scenarioSummaryOverlay = null; + private static HintsOverlayBase hintsOverlay = null; + + public static void init(NxtARCore core) throws IllegalArgumentException, InstantiationException, IllegalAccessException{ + if(core == null) + throw new IllegalArgumentException("Core is null."); + + if(modelBatch == null) + modelBatch = new ModelBatch(); + + if(gameWorld == null){ + gameWorld = new World(); + gameWorld.setManager(new GroupManager()); + } + + if(entityCreator == null){ + try { + entityCreator = (EntityCreatorBase) ScenarioImplementation.entityCreatorClass.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating entity creator."); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing entity creator."); + throw e; + } + entityCreator.setWorld(gameWorld); + entityCreator.setCore(core); + } + + if(gameLogicSystem == null){ + try { + gameLogicSystem = (GameLogicSystemBase) ScenarioImplementation.gameLogicSystemClass.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating game logic system."); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing game logic system."); + throw e; + } + } + + if(automaticActionPerformer == null){ + try { + automaticActionPerformer = (AutomaticActionPerformerBase) ScenarioImplementation.automaticActionPerformerClass.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating automatic action performer."); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing automatic action performer."); + throw e; + } + } + + if(automaticActionSummaryOverlay == null){ + try { + automaticActionSummaryOverlay = (SummaryOverlayBase) ScenarioImplementation.automaticActionSummaryOverlay.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating automatic action summary overlay"); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing automatic action summary overlay."); + throw e; + } + } + + if(playerSystem == null){ + try { + playerSystem = (PlayerSystemBase) ScenarioImplementation.playerSystemClass.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating player system."); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing player system."); + throw e; + } + } + + if(scenarioSummaryOverlay == null){ + try { + scenarioSummaryOverlay = (SummaryOverlayBase) ScenarioImplementation.scenarioSummaryOverlayClass.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating scenario summary overlay."); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing scenario summary overlay."); + throw e; + } + } + + if(hintsOverlay == null){ + try { + hintsOverlay = (HintsOverlayBase) ScenarioImplementation.hintsOverlayClass.newInstance(); + } catch (InstantiationException e) { + System.out.println("Error instantiating hints overlay."); + throw e; + } catch (IllegalAccessException e) { + System.out.println("Error accessing hints overlay."); + throw e; + } + } + + playerSystem.setCore(core); + + gameWorld.setSystem(new MarkerPositioningSystem()); + gameWorld.setSystem(new RobotArmPositioningSystem()); + gameWorld.setSystem(new GeometrySystem()); + gameWorld.setSystem(new AnimationSystem()); + gameWorld.setSystem(new CollisionDetectionSystem()); + gameWorld.setSystem(gameLogicSystem); + gameWorld.setSystem(playerSystem, true); + gameWorld.setSystem(new MarkerRenderingSystem(modelBatch), true); + gameWorld.setSystem(new ObjectRenderingSystem(modelBatch), true); + gameWorld.setSystem(new FadeEffectRenderingSystem(), true); + + gameWorld.initialize(); + } + + public static void dispose() throws IllegalStateException{ + ImmutableBagBase class for summary screens. Just renders a summary overlay.
+ */ +public abstract class SummaryOverlayBase implements Disposable{ + /** + *Renders the overlay.
+ * + * @param batch The {@link SpriteBatch} to use for rendering. + */ + public abstract void render(SpriteBatch batch, SummaryBase summary); +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombComponent.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombComponent.java new file mode 100644 index 0000000..5f75e50 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombComponent.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios.bombgame; + +import com.artemis.Component; + +public class BombComponent extends Component { + public enum bomb_type_t{ + COMBINATION(0), INCLINATION(1), WIRES(2); + + private int value; + + private bomb_type_t(int value){ + this.value = value; + } + + public int getValue(){ + return this.value; + } + }; + + public int id; + public bomb_type_t bombType; + public boolean enabled; + + public BombComponent(int id, bomb_type_t bomb_type){ + this.id = id; + this.bombType = bomb_type; + this.enabled = true; + } + + public BombComponent(BombComponent bomb){ + this.id = bomb.id; + this.bombType = bomb.bombType; + this.enabled = bomb.enabled; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGameAutomaticActionPerformer.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGameAutomaticActionPerformer.java new file mode 100644 index 0000000..e9d98ba --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGameAutomaticActionPerformer.java @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios.bombgame; + +import java.util.LinkedList; +import java.util.List; + +import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor.MarkerData; +import ve.ucv.ciens.ccg.nxtar.scenarios.AutomaticActionPerformerBase; +import ve.ucv.ciens.ccg.nxtar.scenarios.ScenarioGlobals; +import ve.ucv.ciens.ccg.nxtar.scenarios.SummaryBase; +import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; + +import com.artemis.Entity; +import com.artemis.World; +import com.artemis.managers.GroupManager; +import com.artemis.utils.ImmutableBag; +import com.badlogic.gdx.Gdx; + +public class BombGameAutomaticActionPerformer extends AutomaticActionPerformerBase { + private static final String TAG = "BOMB_GAME_AUTO_PERFORMER"; + private static final String CLASS_NAME = BombGameAutomaticActionPerformer.class.getSimpleName(); + private static final int GOAL_FLOOR_MIN_LUMINANCE = 75; + private static final int MARKER_NEARBY_FLOOR_MIN_LUMINANCE = 45; + + private enum action_state_t{ + START, WALK_FORWARD, DETECT_MARKER, FINISHING, END; + } + + public class BombGameAutomaticActionSummary extends SummaryBase{ + private int numCombinationBombs; + private int numInclinationBombs; + private int numWireBombs; + + public BombGameAutomaticActionSummary(){ + reset(); + } + + public int getNumCombinationBombs() { + return numCombinationBombs; + } + + public int getNumInclinationBombs() { + return numInclinationBombs; + } + + public int getNumWireBombs() { + return numWireBombs; + } + + public void addCombinationBomb(){ + numCombinationBombs++; + } + + public void addInclinationBomb(){ + numInclinationBombs++; + } + + public void addWireBomb(){ + numWireBombs++; + } + + @Override + public void reset() { + this.numCombinationBombs = 0; + this.numInclinationBombs = 0; + this.numWireBombs = 0; + } + } + + private automatic_action_t nextAction; + private action_state_t state; + private ListChecks if the current player interaction disables a wire based bomb.
+ * + * @param b An Artemis {@link Entity} that possibly represents any of a Wire Bomb's wires. + */ + private void processWireBomb(Entity b){ + CollisionDetectionComponent collision; + MarkerCodeComponent marker; + BombGameEntityTypeComponent wireType; + + // Get this wire's parameters. + collision = collisionMapper.getSafe(b); + marker = markerMapper.getSafe(b); + wireType = typeMapper.getSafe(b); + + // if any of the parameters is missing then skip. + if(marker == null || collision == null || wireType == null){ + Gdx.app.log(TAG, CLASS_NAME + ".processInclinationBomb(): Wire bomb is missing some components."); + return; + } + + // If this bomb is still enabled and it's door is already open then process it. + try{ + if(marker.enabled && isDoorOpen(marker.code, manager) && collision.colliding){ + manager.remove(b, CollisionDetectionSystem.COLLIDABLE_OBJECTS_GROUP); + manager.remove(b, Integer.toString(marker.code)); + b.deleteFromWorld(); + + if(wireType.type != BombGameEntityTypeComponent.BOMB_WIRE_1){ + Gdx.app.log(TAG, CLASS_NAME + ".processWireBomb(): Wire bomb exploded."); + createFadeOutEffect(); + reducePlayerLivesByOne(); + } + + disableBomb(marker.code); + Gdx.app.log(TAG, CLASS_NAME + ".processWireBomb(): Wire bomb disabled."); + } + }catch(IllegalArgumentException e){ + Gdx.app.error(TAG, CLASS_NAME + ".processWireBomb(): IllegalArgumentException caught: " + e.getMessage()); + } + } + + /** + *Checks if the current player interaction disables a combination bomb.
+ * + * @param b An Artemis {@link Entity} that possibly represents any of a Combination Bomb's buttons. + */ + private void processCombinationBomb(Entity b){ + combination_button_state_t state; + CollisionDetectionComponent collision; + MarkerCodeComponent marker; + BombGameEntityTypeComponent buttonType; + + // Get this wire's parameters. + collision = collisionMapper.getSafe(b); + marker = markerMapper.getSafe(b); + buttonType = typeMapper.getSafe(b); + + // if any of the parameters is missing then skip. + if(marker == null || collision == null || buttonType == null){ + Gdx.app.log(TAG, CLASS_NAME + ".processInclinationBomb(): Wire bomb is missing some components."); + return; + } + + // If this bomb is still enabled and it's door is already open then process it. + try{ + if(marker.enabled && isDoorOpen(marker.code, manager) && collision.colliding){ + manager.remove(b, CollisionDetectionSystem.COLLIDABLE_OBJECTS_GROUP); + manager.remove(b, Integer.toString(marker.code)); + b.deleteFromWorld(); + + // Check the state of the other buttons associated with this bomb. + + state = checkCombinationBombButtons(buttonType.type, marker.code); + + if(state.getValue() == combination_button_state_t.INCORRECT.getValue()){ + Gdx.app.log(TAG, CLASS_NAME + ".processCombinationBomb(): Combination bomb exploded."); + createFadeOutEffect(); + disableBomb(marker.code); + reducePlayerLivesByOne(); + }else if(state.getValue() == combination_button_state_t.DISABLED.getValue()){ + Gdx.app.log(TAG, CLASS_NAME + ".processCombinationBomb(): Combination bomb disabled."); + disableBomb(marker.code); + } + } + }catch(IllegalArgumentException e){ + Gdx.app.error(TAG, CLASS_NAME + ".processCombinationBomb(): IllegalArgumentException caught: " + e.getMessage()); + } + } + + /** + *Checks if the current player interaction disables an inclination bomb.
+ * + * @param b An Artemis {@link Entity} that possibly represents an Inclination Bomb's big button. + */ + private void processInclinationBomb(Entity b){ + // Get the components of the big button. + CollisionDetectionComponent collision = collisionMapper.getSafe(b); + MarkerCodeComponent marker = markerMapper.getSafe(b); + + // If any of the components is missing, skip this entity. + if(marker == null || collision == null ){ + Gdx.app.log(TAG, CLASS_NAME + ".processInclinationBomb(): Inclination bomb is missing some components."); + return; + } + + // If this bomb is still enabled and it's door is already open then process it. + try{ + if(marker.enabled && isDoorOpen(marker.code, manager) && collision.colliding){ + // Disable the bomb and remove it from collision detection. + marker.enabled = false; + manager.remove(b, CollisionDetectionSystem.COLLIDABLE_OBJECTS_GROUP); + manager.remove(b, Integer.toString(marker.code)); + b.deleteFromWorld(); + + if(Utils.deviceHasOrientationSensors()){ + if(!Utils.isDeviceRollValid() || (Utils.isDeviceRollValid() && Math.abs(Gdx.input.getRoll()) > ProjectConstants.MAX_ABS_ROLL)){ + Gdx.app.log(TAG, CLASS_NAME + ".processInclinationBomb(): Inclination bomb exploded."); + createFadeOutEffect(); + reducePlayerLivesByOne(); + } + } + + // Disable all related entities. + disableBomb(marker.code); + + Gdx.app.log(TAG, CLASS_NAME + ".processInclinationBomb(): Inclination bomb disabled."); + } + }catch(IllegalArgumentException e){ + Gdx.app.error(TAG, CLASS_NAME + ".processInclinationBomb(): IllegalArgumentException caught: " + e.getMessage()); + } + } + + /** + *Set's the animation for a door depending on it's collision and marker state.
+ * + * @param d An Artemis {@link Entity} possibly representing a door. + */ + private void processDoor(Entity d){ + // Get the components of the door. + CollisionDetectionComponent collision = collisionMapper.getSafe(d); + AnimationComponent animation = animationMapper.getSafe(d); + VisibilityComponent visibility = visibilityMapper.getSafe(d); + MarkerCodeComponent marker = markerMapper.getSafe(d); + + // If any of the components is missing, skip this entity. + if(marker == null || collision == null || animation == null || visibility == null){ + Gdx.app.log(TAG, CLASS_NAME + ".processDoor(): Door is missing some components."); + return; + } + + if(visibility.visible){ + if(marker.enabled){ + if(collision.colliding){ + // If the door is visible and enabled and the player is colliding with it then set + // it's opening animation; + animation.next = BombGameEntityCreator.DOOR_OPEN_ANIMATION; + animation.loop = false; + collision.colliding = false; + world.getManager(GroupManager.class).remove(d, CollisionDetectionSystem.COLLIDABLE_OBJECTS_GROUP); + Gdx.app.log(TAG, CLASS_NAME + ".processDoor(): Opening door."); + } + }else{ + // If the door is disabled and open, then set it's closing animation. + if(animation.current != BombGameEntityCreator.DOOR_CLOSE_ANIMATION){ + animation.next = BombGameEntityCreator.DOOR_CLOSE_ANIMATION; + animation.loop = false; + Gdx.app.log(TAG, CLASS_NAME + ".processDoor(): Closing door."); + } + } + } + } + + /** + *Checks if a door is either open or closed depending on the completeness of it's animation.
+ * + * @param markerCode The code of the door to check. Must be between 0 and 1023. + * @param manager An Artemis {@link GroupManager} to use to get all related entities. + * @return true if the opening animation of the door has finished playing. + * @throws IllegalArgumentException If marker code is not in the range [0, 1023], inclusive. + */ + private boolean isDoorOpen(int markerCode, GroupManager manager) throws IllegalArgumentException{ + AnimationComponent animation; + boolean doorOpen = false; + ImmutableBagUpdates the player's lives count.
+ */ + private void reducePlayerLivesByOne(){ + Entity player; + BombGamePlayerComponent playerComponent; + ImmutableBagUpdates the player's disabled bombs count.
+ */ + private void increasePlayerDisabledBombsByOne(){ + Entity player; + BombGamePlayerComponent playerComponent; + ImmutableBagDisables all entities associated with the corresponding marker code.
+ * + * @param markerCode + * @throws IllegalArgumentException If marker code is not in the range [0, 1023], inclusive. + */ + private void disableBomb(int markerCode) throws IllegalArgumentException{ + ImmutableBagChecks if a combination bomb is being disabled in the correct sequence.
+ * + * @param buttonType A number between {@link BombGameEntityTypeComponent.COM_BUTTON_1} and {@link BombGameEntityTypeComponent.COM_BUTTON_4}. + * @param markerCode A marker code between [0, 1023], inclusive. + * @return The current state of the bomb. + * @throws IllegalArgumentException If marker code is not in range or if buttonType is not valid. + */ + private combination_button_state_t checkCombinationBombButtons(int buttonType, int markerCode) throws IllegalArgumentException{ + combination_button_state_t state; + boolean correctSequence = true; + int remainingButtons = 0; + ImmutableBagAdds a new fade out entity to the {@link World}.
+ */ + private void createFadeOutEffect(){ + Entity effect = world.createEntity(); + effect.addComponent(new BombGameEntityTypeComponent(BombGameEntityTypeComponent.FADE_EFFECT)); + effect.addComponent(new FadeEffectComponent()); + effect.addToWorld(); + } + + /** + *Adds a new fade in entity to the {@link World}.
+ */ + private void createFadeInEffect(){ + Entity effect = world.createEntity(); + effect.addComponent(new BombGameEntityTypeComponent(BombGameEntityTypeComponent.FADE_EFFECT)); + effect.addComponent(new FadeEffectComponent(true)); + effect.addToWorld(); + } + + /** + *Updates a fade effect entity.
+ * + * @param f An Artemis {@link Entity} possibly referencing a fade effect. + */ + private void processFade(Entity f){ + FadeEffectComponent fade = fadeMapper.getSafe(f); + + if(fade != null){ + if(!fade.isEffectStarted()) + fade.startEffect(); + + if(!fade.isEffectFinished()){ + // If the fade has not finished then just update it. + Gdx.app.log(TAG, CLASS_NAME + ".processFade(): Updating fade."); + fade.update(Gdx.graphics.getDeltaTime()); + }else{ + // If the fade finished. + if(fade.isEffectFadeIn()){ + // If the effect was a fade in then just remove it. + Gdx.app.log(TAG, CLASS_NAME + ".processFade(): deleting fade in."); + f.deleteFromWorld(); + }else{ + // If the effect was a fade out then wait for one second and then remove it and start a fade in. + then += (int)(Gdx.graphics.getDeltaTime() * 1000.0f); + if(then >= 1500){ + Gdx.app.log(TAG, CLASS_NAME + ".processFade(): Deleting fade out."); + f.deleteFromWorld(); + Gdx.app.log(TAG, CLASS_NAME + ".processFade(): Creating fade in."); + createFadeInEffect(); + then = 0; + }else{ + Gdx.app.log(TAG, CLASS_NAME + ".processFade(): Waiting after fade out: " + Integer.toString(then)); + } + } + } + } + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGamePlayerComponent.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGamePlayerComponent.java new file mode 100644 index 0000000..af4f958 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGamePlayerComponent.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios.bombgame; + +import ve.ucv.ciens.ccg.nxtar.components.PlayerComponentBase; + +public class BombGamePlayerComponent extends PlayerComponentBase { + public static final int MIN_LIVES = 1; + public static final int MAX_LIVES = 5; + + public int startingLives; + public int lives; + public int disabledBombs; + + public BombGamePlayerComponent(int lives) throws IllegalArgumentException{ + super(); + + if(lives < MIN_LIVES || lives > MAX_LIVES) + throw new IllegalArgumentException("Lives number out of range: " + Integer.toString(lives)); + + startingLives = lives; + reset(); + } + + public BombGamePlayerComponent(){ + this(3); + } + + @Override + public void reset(){ + super.reset(); + + this.lives = startingLives; + this.disabledBombs = 0; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGamePlayerSystem.java b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGamePlayerSystem.java new file mode 100644 index 0000000..3c9f028 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/scenarios/bombgame/BombGamePlayerSystem.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.scenarios.bombgame; + +import ve.ucv.ciens.ccg.nxtar.scenarios.SummaryBase; +import ve.ucv.ciens.ccg.nxtar.systems.PlayerSystemBase; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; + +import com.artemis.ComponentMapper; +import com.artemis.Entity; +import com.artemis.annotations.Mapper; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; + +public class BombGamePlayerSystem extends PlayerSystemBase{ + public final class BombGamePlayerSummary extends SummaryBase{ + public int livesLeft; + public int disabledBombs; + public int detonatedBombs; + public boolean victory; + + public BombGamePlayerSummary(){ + reset(); + } + + @Override + public void reset() { + this.livesLeft = 0; + this.disabledBombs = 0; + this.detonatedBombs = 0; + this.victory = false; + } + } + + @Mapper ComponentMapperRepresents the two possible control modes for the robot.
+ */ + private enum robot_control_mode_t{ + WHEEL_CONTROL(0), ARM_CONTROL(1); + + private int value; + + private robot_control_mode_t(int value){ + this.value = value; + } + + public int getValue(){ + return this.value; + } + } // Background related fields. + private Sprite background; private float uScaling[]; - protected Sprite background; private Texture backgroundTexture; private ShaderProgram backgroundShader; // 3D rendering fields. private ModelBatch modelBatch; - private Matrix4 projectionMatrix; private FrameBuffer frameBuffer; private Sprite frameBufferSprite; - // Game world objects. + // Game related fields. private World gameWorld; - private EntityCreatorBase entityCreator; private MarkerRenderingSystem markerRenderingSystem; private ObjectRenderingSystem objectRenderingSystem; + private RobotArmPositioningSystem robotArmPositioningSystem; + private FadeEffectRenderingSystem fadeEffectRenderingSystem; + private PlayerSystemBase playerSystem; + private robot_control_mode_t controlMode; // Cameras. - private OrthographicCamera unitaryOrthoCamera; - private OrthographicCamera pixelPerfectOrthoCamera; + private OrthographicCamera unitaryOrthographicCamera; + private OrthographicCamera pixelPerfectOrthographicCamera; private CustomPerspectiveCamera perspectiveCamera; // Video stream graphics. @@ -97,70 +114,95 @@ public class InGameState extends BaseState{ private Sprite renderableVideoFrame; private Pixmap videoFrame; - // Interface buttons. - private Texture buttonTexture; - private Texture buttonTexture2; - private Sprite motorA; - private Sprite motorB; - private Sprite motorC; - private Sprite motorD; - private Sprite headA; - private Sprite headB; - private Sprite headC; + // Gui textures. + private Texture upControlButtonTexture; + private Texture downControlButtonTexture; + private Texture leftControlButtonTexture; + private Texture rightControlButtonTexture; + private Texture headControlButtonTexture; + private Texture wheelControlButtonTexture; + private Texture armControlButtonTexture; + private Texture correctAngleLedOnTexture; + private Texture correctAngleLedOffTexture; + private Texture orientationSliderTexture; + private Texture hintButtonTexture; + + // Gui renderable sprites. + private Sprite motorAButton; + private Sprite motorBButton; + private Sprite motorCButton; + private Sprite motorDButton; + private Sprite armAButton; + private Sprite armBButton; + private Sprite armCButton; + private Sprite armDButton; + private Sprite headAButton; + private Sprite headBButton; + private Sprite headCButton; + private Sprite wheelControlButton; + private Sprite armControlButton; + private Sprite correctAngleLedOnSprite; + private Sprite correctAngleLedOffSprite; + private Sprite orientationSlider; + private Sprite hintButton; // Button touch helper fields. - private boolean[] motorButtonsTouched; - private int[] motorButtonsPointers; - private boolean[] motorGamepadButtonPressed; + private boolean[] buttonsTouched; + private int[] buttonPointers; + private boolean[] gamepadButtonPressed; // Monitors. private VideoFrameMonitor frameMonitor; private MotorEventQueue queue; - public InGameState(final NxtARCore core){ + public InGameState(final NxtARCore core) throws IllegalStateException, IllegalArgumentException{ + if(core == null) + throw new IllegalArgumentException(CLASS_NAME + ": Core is null."); + this.core = core; frameMonitor = VideoFrameMonitor.getInstance(); queue = MotorEventQueue.getInstance(); + controlMode = robot_control_mode_t.WHEEL_CONTROL; // Set up rendering fields; videoFrame = null; // Set up the cameras. - pixelPerfectOrthoCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); - unitaryOrthoCamera = new OrthographicCamera(1.0f, Gdx.graphics.getHeight() / Gdx.graphics.getWidth()); - - if(!Ouya.runningOnOuya) setUpButtons(); + pixelPerfectOrthographicCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + unitaryOrthographicCamera = new OrthographicCamera(1.0f, Gdx.graphics.getHeight() / Gdx.graphics.getWidth()); // Set up input handling support fields. win2world = new Vector3(0.0f, 0.0f, 0.0f); touchPointWorldCoords = new Vector2(); - motorButtonsTouched = new boolean[7]; - motorButtonsTouched[0] = false; - motorButtonsTouched[1] = false; - motorButtonsTouched[2] = false; - motorButtonsTouched[3] = false; - motorButtonsTouched[4] = false; - motorButtonsTouched[5] = false; - motorButtonsTouched[6] = false; + buttonsTouched = new boolean[8]; + buttonsTouched[0] = false; + buttonsTouched[1] = false; + buttonsTouched[2] = false; + buttonsTouched[3] = false; + buttonsTouched[4] = false; + buttonsTouched[5] = false; + buttonsTouched[6] = false; + buttonsTouched[7] = false; - motorButtonsPointers = new int[7]; - motorButtonsPointers[0] = -1; - motorButtonsPointers[1] = -1; - motorButtonsPointers[2] = -1; - motorButtonsPointers[3] = -1; - motorButtonsPointers[4] = -1; - motorButtonsPointers[5] = -1; - motorButtonsPointers[6] = -1; + buttonPointers = new int[8]; + buttonPointers[0] = -1; + buttonPointers[1] = -1; + buttonPointers[2] = -1; + buttonPointers[3] = -1; + buttonPointers[4] = -1; + buttonPointers[5] = -1; + buttonPointers[6] = -1; + buttonPointers[7] = -1; - motorGamepadButtonPressed = new boolean[7]; - motorGamepadButtonPressed[0] = false; - motorGamepadButtonPressed[1] = false; - motorGamepadButtonPressed[2] = false; - motorGamepadButtonPressed[3] = false; - motorGamepadButtonPressed[4] = false; - motorGamepadButtonPressed[5] = false; - motorGamepadButtonPressed[6] = false; + gamepadButtonPressed = new boolean[7]; + gamepadButtonPressed[0] = false; + gamepadButtonPressed[1] = false; + gamepadButtonPressed[2] = false; + gamepadButtonPressed[3] = false; + gamepadButtonPressed[4] = false; + gamepadButtonPressed[5] = false; + gamepadButtonPressed[6] = false; // Set up the background. backgroundTexture = new Texture(Gdx.files.internal("data/gfx/textures/tile_aqua.png")); @@ -173,39 +215,46 @@ public class InGameState extends BaseState{ // Set up the shader. backgroundShader = new ShaderProgram(Gdx.files.internal(BACKGROUND_SHADER_PATH + "_vert.glsl"), Gdx.files.internal(BACKGROUND_SHADER_PATH + "_frag.glsl")); if(!backgroundShader.isCompiled()){ - Gdx.app.error(TAG, CLASS_NAME + ".MainMenuStateBase() :: Failed to compile the background shader."); + Gdx.app.error(TAG, CLASS_NAME + ".InGameState() :: Failed to compile the background shader."); Gdx.app.error(TAG, CLASS_NAME + backgroundShader.getLog()); backgroundShader = null; } - uScaling = new float[2]; + uScaling = new float[2]; uScaling[0] = Gdx.graphics.getWidth() > Gdx.graphics.getHeight() ? 16.0f : 9.0f; uScaling[1] = Gdx.graphics.getHeight() > Gdx.graphics.getWidth() ? 16.0f : 9.0f; // Set up the 3D rendering. - modelBatch = new ModelBatch(); - projectionMatrix = new Matrix4().idt(); - frameBuffer = null; + modelBatch = new ModelBatch(); + frameBuffer = null; perspectiveCamera = null; frameBufferSprite = null; - RenderParameters.setLightSource1(new LightSource(LIGHT_POSITION, AMBIENT_COLOR, DIFFUSE_COLOR, SPECULAR_COLOR, SHINYNESS)); + + // Set up he buttons. + if(!Ouya.runningOnOuya) + setUpButtons(); + + // Set up the hint button. + // Set up the correct angle leds. + hintButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/help.png")); + hintButton = new Sprite(hintButtonTexture); + hintButton.setSize(hintButton.getWidth() * (Ouya.runningOnOuya ? 0.5f : 0.25f), hintButton.getHeight() * (Ouya.runningOnOuya ? 0.5f : 0.25f)); + if(!Ouya.runningOnOuya) + hintButton.setPosition(-(Gdx.graphics.getWidth() / 2) + 5, (Gdx.graphics.getHeight() / 2) - hintButton.getHeight() - 5); + else + hintButton.setPosition(-(Utils.getScreenWidthWithOverscan() / 2) + 5, -hintButton.getHeight() / 2); // Set up the game world. - gameWorld = new World(); + gameWorld = ScenarioGlobals.getGameWorld(); - entityCreator = new MarkerTestEntityCreator(); - entityCreator.setWorld(gameWorld); - entityCreator.createAllEntities(); + robotArmPositioningSystem = gameWorld.getSystem(RobotArmPositioningSystem.class); + markerRenderingSystem = gameWorld.getSystem(MarkerRenderingSystem.class); + objectRenderingSystem = gameWorld.getSystem(ObjectRenderingSystem.class); + fadeEffectRenderingSystem = gameWorld.getSystem(FadeEffectRenderingSystem.class); + playerSystem = ScenarioGlobals.getPlayerSystem(); - gameWorld.setSystem(new MarkerPositioningSystem()); - gameWorld.setSystem(new AnimationSystem()); - - markerRenderingSystem = new MarkerRenderingSystem(modelBatch); - objectRenderingSystem = new ObjectRenderingSystem(modelBatch); - gameWorld.setSystem(markerRenderingSystem, true); - gameWorld.setSystem(objectRenderingSystem, true); - - gameWorld.initialize(); + if(robotArmPositioningSystem == null || markerRenderingSystem == null || objectRenderingSystem == null || fadeEffectRenderingSystem == null) + throw new IllegalStateException("One or more essential systems are null."); } /*;;;;;;;;;;;;;;;;;;;;;; @@ -214,7 +263,10 @@ public class InGameState extends BaseState{ @Override public void render(float delta){ + final float MIN_SLIDER_X = correctAngleLedOnSprite != null ? -(Utils.getScreenWidthWithOverscan() / 2) + 5 + correctAngleLedOnSprite.getWidth() : -(Utils.getScreenWidthWithOverscan() / 2) + 5; + final float MAX_SLIDER_X = correctAngleLedOnSprite != null ? (Utils.getScreenWidthWithOverscan() / 2) - 5 - correctAngleLedOnSprite.getWidth(): (Utils.getScreenWidthWithOverscan() / 2) - 5; int w, h; + float t, xSliderPos; byte[] frame; MarkerData data; TextureRegion region; @@ -225,7 +277,7 @@ public class InGameState extends BaseState{ Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Render the background. - core.batch.setProjectionMatrix(pixelPerfectOrthoCamera.combined); + core.batch.setProjectionMatrix(pixelPerfectOrthographicCamera.combined); core.batch.begin();{ if(backgroundShader != null){ core.batch.setShader(backgroundShader); @@ -258,135 +310,164 @@ public class InGameState extends BaseState{ // If a valid frame was fetched. if(data != null && data.outFrame != null){ - // Update the game state. - gameWorld.setDelta(Gdx.graphics.getDeltaTime() * 1000); - gameWorld.getSystem(MarkerPositioningSystem.class).setMarkerData(data); - gameWorld.process(); - - // Decode the video frame. - videoFrame = new Pixmap(data.outFrame, 0, w * h); - videoFrameTexture = new Texture(videoFrame); - videoFrameTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear); - videoFrame.dispose(); - - // Convert the decoded frame into a renderable texture. - region = new TextureRegion(videoFrameTexture, 0, 0, w, h); - if(renderableVideoFrame == null) - renderableVideoFrame = new Sprite(region); - else - renderableVideoFrame.setRegion(region); - renderableVideoFrame.setOrigin(renderableVideoFrame.getWidth() / 2, renderableVideoFrame.getHeight() / 2); - renderableVideoFrame.setPosition(0, 0); - - // Set the 3D frame buffer for rendering. - frameBuffer.begin();{ - // Set OpenGL state. - Gdx.gl.glClearColor(0, 0, 0, 0); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); - Gdx.gl.glDisable(GL20.GL_TEXTURE_2D); - - // Build the projection matrix. + try{ + // Set the camera to the correct projection. focalPointX = core.cvProc.getFocalPointX(); focalPointY = core.cvProc.getFocalPointY(); cameraCenterX = core.cvProc.getCameraCenterX(); cameraCenterY = core.cvProc.getCameraCenterY(); + perspectiveCamera.setCustomARProjectionMatrix(focalPointX, focalPointY, cameraCenterX, cameraCenterY, NEAR, FAR, w, h); + perspectiveCamera.update(perspectiveCamera.projection); - projectionMatrix.val[Matrix4.M00] = -2.0f * focalPointX / w; - projectionMatrix.val[Matrix4.M10] = 0.0f; - projectionMatrix.val[Matrix4.M20] = 0.0f; - projectionMatrix.val[Matrix4.M30] = 0.0f; + // Update the game state. + if(controlMode == robot_control_mode_t.ARM_CONTROL || Ouya.runningOnOuya) + gameWorld.getSystem(CollisionDetectionSystem.class).enableCollisions(); + else + gameWorld.getSystem(CollisionDetectionSystem.class).disableCollisions(); - projectionMatrix.val[Matrix4.M01] = 0.0f; - projectionMatrix.val[Matrix4.M11] = 2.0f * focalPointY / h; - projectionMatrix.val[Matrix4.M21] = 0.0f; - projectionMatrix.val[Matrix4.M31] = 0.0f; + gameWorld.setDelta(Gdx.graphics.getDeltaTime() * 1000); + gameWorld.getSystem(MarkerPositioningSystem.class).setMarkerData(data); + gameWorld.process(); - projectionMatrix.val[Matrix4.M02] = 2.0f * cameraCenterX / w - 1.0f; - projectionMatrix.val[Matrix4.M12] = 2.0f * cameraCenterY / h - 1.0f; - projectionMatrix.val[Matrix4.M22] = -FAR_PLUS_NEAR / FAR_LESS_NEAR; - projectionMatrix.val[Matrix4.M32] = -1.0f; + // Decode the video frame. + videoFrame = new Pixmap(data.outFrame, 0, w * h); + videoFrameTexture = new Texture(videoFrame); + videoFrameTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear); + videoFrame.dispose(); - projectionMatrix.val[Matrix4.M03] = 0.0f; - projectionMatrix.val[Matrix4.M13] = 0.0f; - projectionMatrix.val[Matrix4.M23] = -2.0f * FAR * NEAR / FAR_LESS_NEAR; - projectionMatrix.val[Matrix4.M33] = 0.0f; + // Convert the decoded frame into a renderable texture. + region = new TextureRegion(videoFrameTexture, 0, 0, w, h); + if(renderableVideoFrame == null) + renderableVideoFrame = new Sprite(region); + else + renderableVideoFrame.setRegion(region); + renderableVideoFrame.setOrigin(renderableVideoFrame.getWidth() / 2, renderableVideoFrame.getHeight() / 2); + renderableVideoFrame.setPosition(0, 0); - // Set rendering parameters. - perspectiveCamera.update(projectionMatrix, true); - RenderParameters.setModelViewProjectionMatrix(perspectiveCamera.combined); - RenderParameters.setEyePosition(perspectiveCamera.position); + // Set the 3D frame buffer for rendering. + frameBuffer.begin();{ + // Set OpenGL state. + Gdx.gl.glClearColor(0, 0, 0, 0); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); + Gdx.gl.glDisable(GL20.GL_TEXTURE_2D); - // Call rendering systems. - markerRenderingSystem.begin(perspectiveCamera, data); - markerRenderingSystem.process(); - markerRenderingSystem.end(); + // Call rendering systems. + markerRenderingSystem.begin(perspectiveCamera); + markerRenderingSystem.process(); + markerRenderingSystem.end(); - objectRenderingSystem.begin(perspectiveCamera); - objectRenderingSystem.process(); - objectRenderingSystem.end(); - }frameBuffer.end(); + if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue() || Ouya.runningOnOuya){ + objectRenderingSystem.begin(perspectiveCamera); + objectRenderingSystem.process(); + objectRenderingSystem.end(); + } + }frameBuffer.end(); - // Set the frame buffer object texture to a renderable sprite. - region = new TextureRegion(frameBuffer.getColorBufferTexture(), 0, 0, frameBuffer.getWidth(), frameBuffer.getHeight()); - region.flip(false, true); - if(frameBufferSprite == null) - frameBufferSprite = new Sprite(region); - else - frameBufferSprite.setRegion(region); - frameBufferSprite.setOrigin(frameBufferSprite.getWidth() / 2, frameBufferSprite.getHeight() / 2); - frameBufferSprite.setPosition(0, 0); + // Set the frame buffer object texture to a renderable sprite. + region = new TextureRegion(frameBuffer.getColorBufferTexture(), 0, 0, frameBuffer.getWidth(), frameBuffer.getHeight()); + region.flip(false, true); + if(frameBufferSprite == null) + frameBufferSprite = new Sprite(region); + else + frameBufferSprite.setRegion(region); + frameBufferSprite.setOrigin(frameBufferSprite.getWidth() / 2, frameBufferSprite.getHeight() / 2); + frameBufferSprite.setPosition(0, 0); - // Set the position and orientation of the renderable video frame and the frame buffer. - if(!Ouya.runningOnOuya){ - renderableVideoFrame.setSize(1.0f, renderableVideoFrame.getHeight() / renderableVideoFrame.getWidth() ); - renderableVideoFrame.rotate90(true); - renderableVideoFrame.translate(-renderableVideoFrame.getWidth() / 2, 0.5f - renderableVideoFrame.getHeight()); + // Set the position and orientation of the renderable video frame and the frame buffer. + if(!Ouya.runningOnOuya){ + renderableVideoFrame.setSize(1.0f, renderableVideoFrame.getHeight() / renderableVideoFrame.getWidth() ); + renderableVideoFrame.rotate90(true); + renderableVideoFrame.translate(-renderableVideoFrame.getWidth() / 2, 0.5f - renderableVideoFrame.getHeight()); - frameBufferSprite.setSize(1.0f, frameBufferSprite.getHeight() / frameBufferSprite.getWidth() ); - frameBufferSprite.rotate90(true); - frameBufferSprite.translate(-frameBufferSprite.getWidth() / 2, 0.5f - frameBufferSprite.getHeight()); - }else{ - float xSize = Gdx.graphics.getHeight() * (w / h); - renderableVideoFrame.setSize(xSize * ProjectConstants.OVERSCAN, Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN); - renderableVideoFrame.rotate90(true); - renderableVideoFrame.translate(-renderableVideoFrame.getWidth() / 2, -renderableVideoFrame.getHeight() / 2); + frameBufferSprite.setSize(1.0f, frameBufferSprite.getHeight() / frameBufferSprite.getWidth() ); + frameBufferSprite.rotate90(true); + frameBufferSprite.translate(-frameBufferSprite.getWidth() / 2, 0.5f - frameBufferSprite.getHeight()); - frameBufferSprite.setSize(xSize * ProjectConstants.OVERSCAN, Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN); - frameBufferSprite.rotate90(true); - frameBufferSprite.translate(-frameBufferSprite.getWidth() / 2, -frameBufferSprite.getHeight() / 2); + }else{ + float xSize = Gdx.graphics.getHeight() * (w / h); + renderableVideoFrame.setSize(xSize * ProjectConstants.OVERSCAN, Utils.getScreenHeightWithOverscan()); + renderableVideoFrame.rotate90(true); + renderableVideoFrame.translate(-renderableVideoFrame.getWidth() / 2, -renderableVideoFrame.getHeight() / 2); + + frameBufferSprite.setSize(xSize * ProjectConstants.OVERSCAN, Utils.getScreenHeightWithOverscan()); + frameBufferSprite.rotate90(true); + frameBufferSprite.translate(-frameBufferSprite.getWidth() / 2, -frameBufferSprite.getHeight() / 2); + } + + // Set the correct camera for the device. + if(!Ouya.runningOnOuya){ + core.batch.setProjectionMatrix(unitaryOrthographicCamera.combined); + }else{ + core.batch.setProjectionMatrix(pixelPerfectOrthographicCamera.combined); + } + + // Render the video frame and the frame buffer. + core.batch.begin();{ + renderableVideoFrame.draw(core.batch); + frameBufferSprite.draw(core.batch); + }core.batch.end(); + + // Clear the video frame from memory. + videoFrameTexture.dispose(); + }catch(GdxRuntimeException e){ + Gdx.app.error(TAG, CLASS_NAME + ".render(): Runtime exception caught: ", e); } - - // Set the correct camera for the device. - if(!Ouya.runningOnOuya){ - core.batch.setProjectionMatrix(unitaryOrthoCamera.combined); - }else{ - core.batch.setProjectionMatrix(pixelPerfectOrthoCamera.combined); - } - - // Render the video frame and the frame buffer. - core.batch.begin();{ - renderableVideoFrame.draw(core.batch); - frameBufferSprite.draw(core.batch); - }core.batch.end(); - - // Clear the video frame from memory. - videoFrameTexture.dispose(); } // Render the interface buttons. if(!Ouya.runningOnOuya){ - core.batch.setProjectionMatrix(pixelPerfectOrthoCamera.combined); + core.batch.setProjectionMatrix(pixelPerfectOrthographicCamera.combined); core.batch.begin();{ - motorA.draw(core.batch); - motorB.draw(core.batch); - motorC.draw(core.batch); - motorD.draw(core.batch); - headA.draw(core.batch); - headB.draw(core.batch); - headC.draw(core.batch); + // Draw control mode button. + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Draw motor control buttons. + motorAButton.draw(core.batch); + motorBButton.draw(core.batch); + motorCButton.draw(core.batch); + motorDButton.draw(core.batch); + wheelControlButton.draw(core.batch); + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + // Draw arm control buttons. + armAButton.draw(core.batch); + armBButton.draw(core.batch); + armCButton.draw(core.batch); + armDButton.draw(core.batch); + armControlButton.draw(core.batch); + }else{ + throw new IllegalStateException("Unrecognized control mode: " + Integer.toString(controlMode.getValue())); + } + + headAButton.draw(core.batch); + headBButton.draw(core.batch); + headCButton.draw(core.batch); + + // Draw device rotation led. + if(Utils.isDeviceRollValid()){ + if(Math.abs(Gdx.input.getRoll()) < ProjectConstants.MAX_ABS_ROLL) + correctAngleLedOnSprite.draw(core.batch); + else + correctAngleLedOffSprite.draw(core.batch); + + t = (Gdx.input.getRoll() + 60.0f) / 120.0f; + xSliderPos = (MIN_SLIDER_X * t) + (MAX_SLIDER_X * (1.0f - t)); + xSliderPos = xSliderPos < MIN_SLIDER_X ? MIN_SLIDER_X : (xSliderPos > MAX_SLIDER_X ? MAX_SLIDER_X : xSliderPos); + orientationSlider.setPosition(xSliderPos, orientationSlider.getY()); + orientationSlider.draw(core.batch); + }else{ + correctAngleLedOffSprite.draw(core.batch); + orientationSlider.draw(core.batch); + } }core.batch.end(); } + core.batch.setProjectionMatrix(pixelPerfectOrthographicCamera.combined); + core.batch.begin();{ + hintButton.draw(core.batch); + }core.batch.end(); + + fadeEffectRenderingSystem.process(); + playerSystem.process(); + data = null; } @@ -395,25 +476,50 @@ public class InGameState extends BaseState{ if(modelBatch != null) modelBatch.dispose(); - if(entityCreator != null) - entityCreator.dispose(); - if(videoFrameTexture != null) videoFrameTexture.dispose(); - if(buttonTexture != null) - buttonTexture.dispose(); + if(upControlButtonTexture != null) + upControlButtonTexture.dispose(); - if(buttonTexture2 != null) - buttonTexture2.dispose(); + if(downControlButtonTexture != null) + downControlButtonTexture.dispose(); - backgroundTexture.dispose(); + if(leftControlButtonTexture != null) + leftControlButtonTexture.dispose(); + + if(rightControlButtonTexture != null) + rightControlButtonTexture.dispose(); + + if(headControlButtonTexture != null) + headControlButtonTexture.dispose(); + + if(wheelControlButtonTexture != null) + wheelControlButtonTexture.dispose(); + + if(armControlButtonTexture != null) + armControlButtonTexture.dispose(); + + if(backgroundTexture != null) + backgroundTexture.dispose(); + + if(orientationSliderTexture != null) + orientationSliderTexture.dispose(); + + if(hintButtonTexture != null) + hintButtonTexture.dispose(); if(backgroundShader != null) backgroundShader.dispose(); if(frameBuffer != null) frameBuffer.dispose(); + + if(correctAngleLedOffTexture != null) + correctAngleLedOffTexture.dispose(); + + if(correctAngleLedOnTexture != null) + correctAngleLedOnTexture.dispose(); } /*;;;;;;;;;;;;;;;;;; @@ -437,42 +543,94 @@ public class InGameState extends BaseState{ } private void setUpButtons(){ - buttonTexture = new Texture(Gdx.files.internal("data/gfx/gui/PBCrichton_Flat_Button.png")); - buttonTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear); + // Set the main control buttons. + upControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/up_button.png")); + downControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/down_button.png")); + leftControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/left_button.png")); + rightControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/right_button.png")); - TextureRegion region = new TextureRegion(buttonTexture, 0, 0, buttonTexture.getWidth(), buttonTexture.getHeight()); + // Set the motor control buttons. + motorAButton = new Sprite(upControlButtonTexture); + motorAButton.setSize(motorAButton.getWidth() * 0.7f, motorAButton.getHeight() * 0.7f); - motorA = new Sprite(region); - motorA.setSize(motorA.getWidth() * 0.7f, motorA.getHeight() * 0.7f); + motorBButton = new Sprite(downControlButtonTexture); + motorBButton.setSize(motorBButton.getWidth() * 0.7f, motorBButton.getHeight() * 0.7f); - motorB = new Sprite(region); - motorB.setSize(motorB.getWidth() * 0.7f, motorB.getHeight() * 0.7f); + motorCButton = new Sprite(downControlButtonTexture); + motorCButton.setSize(motorCButton.getWidth() * 0.7f, motorCButton.getHeight() * 0.7f); - motorC = new Sprite(region); - motorC.setSize(motorC.getWidth() * 0.7f, motorC.getHeight() * 0.7f); + motorDButton = new Sprite(upControlButtonTexture); + motorDButton.setSize(motorDButton.getWidth() * 0.7f, motorDButton.getHeight() * 0.7f); - motorD = new Sprite(region); - motorD.setSize(motorD.getWidth() * 0.7f, motorD.getHeight() * 0.7f); + motorAButton.setPosition(-(Gdx.graphics.getWidth() / 2) + 10, -(Gdx.graphics.getHeight() / 2) + motorBButton.getHeight() + 20); + motorBButton.setPosition(-(Gdx.graphics.getWidth() / 2) + 20 + (motorAButton.getWidth() / 2), -(Gdx.graphics.getHeight() / 2) + 10); + motorCButton.setPosition((Gdx.graphics.getWidth() / 2) - (1.5f * (motorDButton.getWidth())) - 20, -(Gdx.graphics.getHeight() / 2) + 10); + motorDButton.setPosition((Gdx.graphics.getWidth() / 2) - motorDButton.getWidth() - 10, -(Gdx.graphics.getHeight() / 2) + 20 + motorCButton.getHeight()); - motorA.setPosition(-(Gdx.graphics.getWidth() / 2) + 10, -(Gdx.graphics.getHeight() / 2) + motorB.getHeight() + 20); - motorB.setPosition(-(Gdx.graphics.getWidth() / 2) + 20 + (motorA.getWidth() / 2), -(Gdx.graphics.getHeight() / 2) + 10); - motorC.setPosition((Gdx.graphics.getWidth() / 2) - (1.5f * (motorD.getWidth())) - 20, -(Gdx.graphics.getHeight() / 2) + 10); - motorD.setPosition((Gdx.graphics.getWidth() / 2) - motorD.getWidth() - 10, -(Gdx.graphics.getHeight() / 2) + 20 + motorC.getHeight()); + // Set up robot arm control buttons. + armAButton = new Sprite(upControlButtonTexture); + armAButton.setSize(armAButton.getWidth() * 0.7f, armAButton.getHeight() * 0.7f); - buttonTexture2 = new Texture(Gdx.files.internal("data/gfx/gui/orange_glowy_button.png")); + armBButton = new Sprite(leftControlButtonTexture); + armBButton.setSize(armBButton.getWidth() * 0.7f, armBButton.getHeight() * 0.7f); - headA = new Sprite(buttonTexture2); - headA.setSize(headA.getWidth() * 0.3f, headA.getHeight() * 0.6f); + armCButton = new Sprite(rightControlButtonTexture); + armCButton.setSize(armCButton.getWidth() * 0.7f, armCButton.getHeight() * 0.7f); - headB = new Sprite(buttonTexture2); - headB.setSize(headB.getWidth() * 0.3f, headB.getHeight() * 0.6f); + armDButton = new Sprite(downControlButtonTexture); + armDButton.setSize(armDButton.getWidth() * 0.7f, armDButton.getHeight() * 0.7f); - headA.setPosition(-headA.getWidth() - 10, motorA.getY() + (headA.getHeight() / 2)); - headB.setPosition(10, motorA.getY() + (headA.getHeight() / 2)); + armAButton.setPosition(-(Gdx.graphics.getWidth() / 2) + 10, -(Gdx.graphics.getHeight() / 2) + armBButton.getHeight() + 20); + armBButton.setPosition(-(Gdx.graphics.getWidth() / 2) + 20 + (armAButton.getWidth() / 2), -(Gdx.graphics.getHeight() / 2) + 10); + armCButton.setPosition((Gdx.graphics.getWidth() / 2) - (1.5f * (armDButton.getWidth())) - 20, -(Gdx.graphics.getHeight() / 2) + 10); + armDButton.setPosition((Gdx.graphics.getWidth() / 2) - armDButton.getWidth() - 10, -(Gdx.graphics.getHeight() / 2) + 20 + armCButton.getHeight()); - headC = new Sprite(buttonTexture2); - headC.setSize(headC.getWidth() * 0.3f, headC.getHeight() * 0.6f); - headC.setPosition(-(headC.getWidth() / 2), headA.getY() - headA.getHeight() - 10); + // Set the head control buttons. + headControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/orange_glowy_button.png")); + + headAButton = new Sprite(headControlButtonTexture); + headAButton.setSize(headAButton.getWidth() * 0.3f, headAButton.getHeight() * 0.6f); + headBButton = new Sprite(headControlButtonTexture); + headBButton.setSize(headBButton.getWidth() * 0.3f, headBButton.getHeight() * 0.6f); + + headAButton.setPosition(-headAButton.getWidth() - 10, motorAButton.getY() + (headAButton.getHeight() / 2)); + headBButton.setPosition(10, motorAButton.getY() + (headAButton.getHeight() / 2)); + + headCButton = new Sprite(headControlButtonTexture); + headCButton.setSize(headCButton.getWidth() * 0.3f, headCButton.getHeight() * 0.6f); + headCButton.setPosition(-(headCButton.getWidth() / 2), headAButton.getY() - headAButton.getHeight() - 10); + + // Set the control mode buttons. + wheelControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/wheel.png")); + armControlButtonTexture = new Texture(Gdx.files.internal("data/gfx/gui/arm.png")); + + wheelControlButton = new Sprite(wheelControlButtonTexture); + wheelControlButton.setSize(wheelControlButton.getWidth() * 0.3f, wheelControlButton.getHeight() * 0.3f); + + armControlButton = new Sprite(armControlButtonTexture); + armControlButton.setSize(armControlButton.getWidth() * 0.3f, armControlButton.getHeight() * 0.3f); + + wheelControlButton.setPosition(-(wheelControlButton.getWidth() / 2), headCButton.getY() - headCButton.getHeight() - 15); + armControlButton.setPosition(-(armControlButton.getWidth() / 2), headCButton.getY() - headCButton.getHeight() - 15); + + // Set up the correct angle leds. + correctAngleLedOnTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Button_Green.png")); + correctAngleLedOffTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Button_Red.png")); + + correctAngleLedOnSprite = new Sprite(correctAngleLedOnTexture); + correctAngleLedOffSprite = new Sprite(correctAngleLedOffTexture); + + correctAngleLedOnSprite.setSize(correctAngleLedOnSprite.getWidth() * 0.25f, correctAngleLedOnSprite.getHeight() * 0.25f); + correctAngleLedOffSprite.setSize(correctAngleLedOffSprite.getWidth() * 0.25f, correctAngleLedOffSprite.getHeight() * 0.25f); + + correctAngleLedOnSprite.setPosition((Gdx.graphics.getWidth() / 2) - correctAngleLedOnSprite.getWidth() - 5, (Gdx.graphics.getHeight() / 2) - correctAngleLedOnSprite.getHeight() - 5); + correctAngleLedOffSprite.setPosition((Gdx.graphics.getWidth() / 2) - correctAngleLedOffSprite.getWidth() - 5, (Gdx.graphics.getHeight() / 2) - correctAngleLedOffSprite.getHeight() - 5); + + // Set up orientation slider. + orientationSliderTexture = new Texture(Gdx.files.internal("data/gfx/gui/slider_black.png")); + orientationSlider = new Sprite(orientationSliderTexture); + orientationSlider.setSize(orientationSlider.getWidth() * 0.25f, orientationSlider.getHeight() * 0.25f); + orientationSlider.setPosition(-(orientationSlider.getWidth() / 2), (Utils.getScreenHeightWithOverscan() / 2) - orientationSlider.getHeight() - 5); } /*;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -482,95 +640,150 @@ public class InGameState extends BaseState{ @Override public boolean touchDown(int screenX, int screenY, int pointer, int button){ MotorEvent event; + UserInput input; if(!Ouya.runningOnOuya){ win2world.set(screenX, screenY, 0.0f); - unitaryOrthoCamera.unproject(win2world); + unitaryOrthographicCamera.unproject(win2world); touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight()); Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown(%d, %d, %d, %d)", screenX, screenY, pointer, button)); Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); - if(motorA.getBoundingRectangle().contains(touchPointWorldCoords)){ + if(motorAButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor A button pressed"); - motorButtonsTouched[0] = true; - motorButtonsPointers[0] = pointer; + buttonsTouched[0] = true; + buttonPointers[0] = pointer; - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_A); - event.setPower((byte)100); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_A); + event.setPower((byte)100); + queue.addEvent(event); - }else if(motorB.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + + ((KeyboardUserInput)input).keyUp = true; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); + } + + }else if(motorBButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor B button pressed"); - motorButtonsTouched[1] = true; - motorButtonsPointers[1] = pointer; + buttonsTouched[1] = true; + buttonPointers[1] = pointer; - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_A); - event.setPower((byte)-100); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_A); + event.setPower((byte)-100); + queue.addEvent(event); - }else if(motorC.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyLeft = true; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); + } + + }else if(motorCButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor C button pressed"); - motorButtonsTouched[2] = true; - motorButtonsPointers[2] = pointer; + buttonsTouched[2] = true; + buttonPointers[2] = pointer; - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_C); - event.setPower((byte)-100); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_C); + event.setPower((byte)-100); + queue.addEvent(event); - }else if(motorD.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyRight = true; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); + } + + }else if(motorDButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor D button pressed"); - motorButtonsTouched[3] = true; - motorButtonsPointers[3] = pointer; + buttonsTouched[3] = true; + buttonPointers[3] = pointer; - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_C); - event.setPower((byte)100); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_C); + event.setPower((byte)100); + queue.addEvent(event); - }else if(headA.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + + ((KeyboardUserInput)input).keyDown = true; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); + } + + }else if(headAButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Head A button pressed"); - motorButtonsTouched[4] = true; - motorButtonsPointers[4] = pointer; + buttonsTouched[4] = true; + buttonPointers[4] = pointer; event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); - event.setPower((byte)-40); + event.setPower((byte)-25); queue.addEvent(event); - }else if(headB.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(headBButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Head B button pressed"); - motorButtonsTouched[5] = true; - motorButtonsPointers[5] = pointer; + buttonsTouched[5] = true; + buttonPointers[5] = pointer; event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); - event.setPower((byte)40); + event.setPower((byte)25); queue.addEvent(event); - }else if(headC.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(headCButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Head C button pressed"); - if(!motorButtonsTouched[4] && !motorButtonsTouched[5]){ - motorButtonsTouched[6] = true; - motorButtonsPointers[6] = pointer; + if(!buttonsTouched[4] && !buttonsTouched[5]){ + buttonsTouched[6] = true; + buttonPointers[6] = pointer; event = new MotorEvent(); event.setMotor(motor_t.RECENTER); event.setPower((byte)0x00); queue.addEvent(event); } + + }else if(wheelControlButton.getBoundingRectangle().contains(touchPointWorldCoords) || armControlButton.getBoundingRectangle().contains(touchPointWorldCoords)){ + + buttonsTouched[7] = true; + buttonPointers[7] = pointer; + controlMode = controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue() ? robot_control_mode_t.ARM_CONTROL : robot_control_mode_t.WHEEL_CONTROL; + + }else if(hintButton.getBoundingRectangle().contains(touchPointWorldCoords)){ + core.nextState = game_states_t.HINTS; }else{ - // TODO: Send input to the input handler system. + if(controlMode == robot_control_mode_t.ARM_CONTROL){ + touchPointWorldCoords.set(win2world.x, win2world.y); + + if(frameBufferSprite != null && frameBufferSprite.getBoundingRectangle().contains(touchPointWorldCoords)){ + Gdx.app.log(TAG, CLASS_NAME + "touchDown(): Touch point inside framebuffer."); + input = new TouchUserInput(); + robotArmPositioningSystem.setUserInput(input); + + }else{ + Gdx.app.log(TAG, CLASS_NAME + "touchDown(): Touch point outside framebuffer."); + } + } } return true; @@ -582,106 +795,141 @@ public class InGameState extends BaseState{ @Override public boolean touchUp(int screenX, int screenY, int pointer, int button){ MotorEvent event; + UserInput input; if(!Ouya.runningOnOuya){ win2world.set(screenX, screenY, 0.0f); - unitaryOrthoCamera.unproject(win2world); + unitaryOrthographicCamera.unproject(win2world); touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight()); Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp(%d, %d, %d, %d)", screenX, screenY, pointer, button)); Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); - if(motorA.getBoundingRectangle().contains(touchPointWorldCoords)){ + if(motorAButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor A button released"); - motorButtonsPointers[0] = -1; - motorButtonsTouched[0] = false; + buttonPointers[0] = -1; + buttonsTouched[0] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[1]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_A); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[1]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_A); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyUp = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(motorB.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(motorBButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor B button released"); - motorButtonsPointers[1] = -1; - motorButtonsTouched[1] = false; + buttonPointers[1] = -1; + buttonsTouched[1] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[0]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_A); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[0]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_A); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyLeft = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(motorC.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(motorCButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor C button released"); - motorButtonsPointers[2] = -1; - motorButtonsTouched[2] = false; + buttonPointers[2] = -1; + buttonsTouched[2] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[3]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_C); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[3]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_C); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyRight = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(motorD.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(motorDButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor D button released"); - motorButtonsPointers[3] = -1; - motorButtonsTouched[3] = false; + buttonPointers[3] = -1; + buttonsTouched[3] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[2]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_C); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[2]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_C); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyDown = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(headA.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(headAButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Head A button released"); - motorButtonsPointers[4] = -1; - motorButtonsTouched[4] = false; + buttonPointers[4] = -1; + buttonsTouched[4] = false; // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[5]){ + if(!buttonsTouched[5]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); event.setPower((byte) 0); queue.addEvent(event); } - }else if(headB.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(headBButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Head B button released"); - motorButtonsPointers[5] = -1; - motorButtonsTouched[5] = false; + buttonPointers[5] = -1; + buttonsTouched[5] = false; // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[4]){ + if(!buttonsTouched[4]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); event.setPower((byte) 0); queue.addEvent(event); } - }else if(headC.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(headCButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Head C button released"); - motorButtonsPointers[6] = -1; - motorButtonsTouched[6] = false; - }else{ - // TODO: Pass input to the input handler system. + buttonPointers[6] = -1; + buttonsTouched[6] = false; + + }else if(wheelControlButton.getBoundingRectangle().contains(touchPointWorldCoords) || armControlButton.getBoundingRectangle().contains(touchPointWorldCoords)){ + buttonPointers[7] = -1; + buttonsTouched[7] = false; } return true; @@ -693,103 +941,138 @@ public class InGameState extends BaseState{ @Override public boolean touchDragged(int screenX, int screenY, int pointer){ MotorEvent event; + UserInput input; if(!Ouya.runningOnOuya){ win2world.set(screenX, screenY, 0.0f); - unitaryOrthoCamera.unproject(win2world); + unitaryOrthographicCamera.unproject(win2world); touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight()); - if(pointer == motorButtonsPointers[0] && !motorA.getBoundingRectangle().contains(touchPointWorldCoords)){ + if(pointer == buttonPointers[0] && !motorAButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor A button released"); - motorButtonsPointers[0] = -1; - motorButtonsTouched[0] = false; + buttonPointers[0] = -1; + buttonsTouched[0] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[1]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_A); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[1]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_A); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyUp = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(pointer == motorButtonsPointers[1] && !motorB.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(pointer == buttonPointers[1] && !motorBButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor B button released"); - motorButtonsPointers[1] = -1; - motorButtonsTouched[1] = false; + buttonPointers[1] = -1; + buttonsTouched[1] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[0]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_A); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[0]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_A); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyLeft = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(pointer == motorButtonsPointers[2] && !motorC.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(pointer == buttonPointers[2] && !motorCButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor C button released"); - motorButtonsPointers[2] = -1; - motorButtonsTouched[2] = false; + buttonPointers[2] = -1; + buttonsTouched[2] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[3]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_C); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[3]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_C); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyRight = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(pointer == motorButtonsPointers[3] && !motorD.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(pointer == buttonPointers[3] && !motorDButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor D button released"); - motorButtonsPointers[3] = -1; - motorButtonsTouched[3] = false; + buttonPointers[3] = -1; + buttonsTouched[3] = false; - // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[2]){ - event = new MotorEvent(); - event.setMotor(motor_t.MOTOR_C); - event.setPower((byte) 0); - queue.addEvent(event); + if(controlMode.getValue() == robot_control_mode_t.WHEEL_CONTROL.getValue()){ + // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. + if(!buttonsTouched[2]){ + event = new MotorEvent(); + event.setMotor(motor_t.MOTOR_C); + event.setPower((byte) 0); + queue.addEvent(event); + } + + }else if(controlMode.getValue() == robot_control_mode_t.ARM_CONTROL.getValue()){ + input = new KeyboardUserInput(); + ((KeyboardUserInput)input).keyDown = false; + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); } - }else if(pointer == motorButtonsPointers[4] && !headA.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(pointer == buttonPointers[4] && !headAButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Head A button released"); - motorButtonsPointers[4] = -1; - motorButtonsTouched[4] = false; + buttonPointers[4] = -1; + buttonsTouched[4] = false; // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[5]){ + if(!buttonsTouched[5]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); event.setPower((byte) 0); queue.addEvent(event); } - }else if(pointer == motorButtonsPointers[5] && !headB.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(pointer == buttonPointers[5] && !headBButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Head B button released"); - motorButtonsPointers[5] = -1; - motorButtonsTouched[5] = false; + buttonPointers[5] = -1; + buttonsTouched[5] = false; // Enqueue the event corresponding to releasing this button if the opposing button is not pressed already. - if(!motorButtonsTouched[4]){ + if(!buttonsTouched[4]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); event.setPower((byte) 0); queue.addEvent(event); } - }else if(pointer == motorButtonsPointers[6] && !headC.getBoundingRectangle().contains(touchPointWorldCoords)){ + }else if(pointer == buttonPointers[6] && !headCButton.getBoundingRectangle().contains(touchPointWorldCoords)){ Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Head C button released"); - motorButtonsPointers[6] = -1; - motorButtonsTouched[6] = false; - }else{ - // TODO: Pass input to the input handler system. + buttonPointers[6] = -1; + buttonsTouched[6] = false; + + }else if(pointer == buttonPointers[7] && !(wheelControlButton.getBoundingRectangle().contains(touchPointWorldCoords) || armControlButton.getBoundingRectangle().contains(touchPointWorldCoords))){ + buttonPointers[7] = -1; + buttonsTouched[7] = false; } return true; @@ -800,12 +1083,89 @@ public class InGameState extends BaseState{ @Override public boolean keyDown(int keycode){ + KeyboardUserInput input = null; + + if(keycode == Input.Keys.BACK){ + core.nextState = game_states_t.MAIN_MENU; + return true; + }else if(keycode == Input.Keys.F1){ + core.nextState = game_states_t.HINTS; + return true; + } + + switch(keycode){ + case Input.Keys.LEFT: + input = new KeyboardUserInput(); + input.keyLeft = true; + break; + case Input.Keys.RIGHT: + input = new KeyboardUserInput(); + input.keyRight = true; + break; + case Input.Keys.UP: + input = new KeyboardUserInput(); + input.keyUp = true; + break; + case Input.Keys.DOWN: + input = new KeyboardUserInput(); + input.keyDown = true; + break; + case Input.Keys.SPACE: + input = new KeyboardUserInput(); + input.keySpace = true; + break; + default: + return false; + } + + if(input != null){ + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); + } + + return true; + } + + @Override + public boolean keyUp(int keycode){ + KeyboardUserInput input = null; + if(keycode == Input.Keys.BACK){ core.nextState = game_states_t.MAIN_MENU; return true; } - return false; + switch(keycode){ + case Input.Keys.LEFT: + input = new KeyboardUserInput(); + input.keyLeft = false; + break; + case Input.Keys.RIGHT: + input = new KeyboardUserInput(); + input.keyRight = false; + break; + case Input.Keys.UP: + input = new KeyboardUserInput(); + input.keyUp = false; + break; + case Input.Keys.DOWN: + input = new KeyboardUserInput(); + input.keyDown = false; + break; + case Input.Keys.SPACE: + input = new KeyboardUserInput(); + input.keySpace = false; + break; + default: + return false; + } + + if(input != null){ + robotArmPositioningSystem.setUserInput(input); + robotArmPositioningSystem.process(); + } + + return true; } /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -814,15 +1174,16 @@ public class InGameState extends BaseState{ @Override public boolean buttonDown(Controller controller, int buttonCode){ - MotorEvent event; + MotorEvent event; + GamepadUserInput userInput; if(stateActive){ Gdx.app.log(TAG, CLASS_NAME + ".buttonDown() :: " + controller.getName() + " :: " + Integer.toString(buttonCode)); if(buttonCode == Ouya.BUTTON_L1){ - motorGamepadButtonPressed[0] = true; + gamepadButtonPressed[0] = true; - if(!motorGamepadButtonPressed[4]){ + if(!gamepadButtonPressed[4]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_A); event.setPower((byte)-100); @@ -830,9 +1191,9 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_R1){ - motorGamepadButtonPressed[1] = true; + gamepadButtonPressed[1] = true; - if(!motorGamepadButtonPressed[5]){ + if(!gamepadButtonPressed[5]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_C); event.setPower((byte)-100); @@ -840,29 +1201,29 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_DPAD_LEFT){ - motorGamepadButtonPressed[2] = false; + gamepadButtonPressed[2] = false; - if(!motorGamepadButtonPressed[3]){ + if(!gamepadButtonPressed[3]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); - event.setPower((byte)-40); + event.setPower((byte)-25); queue.addEvent(event); } }else if(buttonCode == Ouya.BUTTON_DPAD_RIGHT){ - motorGamepadButtonPressed[3] = false; + gamepadButtonPressed[3] = false; - if(!motorGamepadButtonPressed[2]){ + if(!gamepadButtonPressed[2]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); - event.setPower((byte)40); + event.setPower((byte)25); queue.addEvent(event); } }else if(buttonCode == Ouya.BUTTON_L2){ - motorGamepadButtonPressed[4] = false; + gamepadButtonPressed[4] = false; - if(!motorGamepadButtonPressed[0]){ + if(!gamepadButtonPressed[0]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_A); event.setPower((byte)100); @@ -870,9 +1231,9 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_R2){ - motorGamepadButtonPressed[5] = false; + gamepadButtonPressed[5] = false; - if(!motorGamepadButtonPressed[1]){ + if(!gamepadButtonPressed[1]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_C); event.setPower((byte)100); @@ -880,14 +1241,23 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_Y){ - motorGamepadButtonPressed[6] = true; + gamepadButtonPressed[6] = true; event = new MotorEvent(); event.setMotor(motor_t.RECENTER); event.setPower((byte)0x00); queue.addEvent(event); - }else{ - // TODO: Pass input to the input handler system. + + }else if(buttonCode == Ouya.BUTTON_O){ + userInput = new GamepadUserInput(); + userInput.oButton = true; + robotArmPositioningSystem.setUserInput(userInput); + robotArmPositioningSystem.process(); + + }else if(buttonCode == Ouya.BUTTON_U){ + core.nextState = game_states_t.HINTS; + }else if(buttonCode == Ouya.BUTTON_A || buttonCode == Ouya.BUTTON_MENU){ + core.nextState = game_states_t.MAIN_MENU; } return true; @@ -904,9 +1274,9 @@ public class InGameState extends BaseState{ Gdx.app.log(TAG, CLASS_NAME + ".buttonDown() :: " + controller.getName() + " :: " + Integer.toString(buttonCode)); if(buttonCode == Ouya.BUTTON_L1){ - motorGamepadButtonPressed[0] = false; + gamepadButtonPressed[0] = false; - if(!motorGamepadButtonPressed[4]){ + if(!gamepadButtonPressed[4]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_A); event.setPower((byte)0); @@ -914,9 +1284,9 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_R1){ - motorGamepadButtonPressed[1] = false; + gamepadButtonPressed[1] = false; - if(!motorGamepadButtonPressed[5]){ + if(!gamepadButtonPressed[5]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_C); event.setPower((byte)0); @@ -924,27 +1294,27 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_DPAD_LEFT){ - motorGamepadButtonPressed[2] = false; + gamepadButtonPressed[2] = false; - if(!motorGamepadButtonPressed[3]){ + if(!gamepadButtonPressed[3]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); event.setPower((byte)0); queue.addEvent(event); } }else if(buttonCode == Ouya.BUTTON_DPAD_RIGHT){ - motorGamepadButtonPressed[3] = false; + gamepadButtonPressed[3] = false; - if(!motorGamepadButtonPressed[2]){ + if(!gamepadButtonPressed[2]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_B); event.setPower((byte)0); queue.addEvent(event); } }else if(buttonCode == Ouya.BUTTON_L2){ - motorGamepadButtonPressed[4] = false; + gamepadButtonPressed[4] = false; - if(!motorGamepadButtonPressed[0]){ + if(!gamepadButtonPressed[0]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_A); event.setPower((byte)0); @@ -952,9 +1322,9 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_R2){ - motorGamepadButtonPressed[5] = false; + gamepadButtonPressed[5] = false; - if(!motorGamepadButtonPressed[1]){ + if(!gamepadButtonPressed[1]){ event = new MotorEvent(); event.setMotor(motor_t.MOTOR_C); event.setPower((byte)0); @@ -962,9 +1332,7 @@ public class InGameState extends BaseState{ } }else if(buttonCode == Ouya.BUTTON_Y){ - motorGamepadButtonPressed[6] = false; - }else{ - // TODO: Pass input to the input handler system. + gamepadButtonPressed[6] = false; } return true; @@ -975,7 +1343,26 @@ public class InGameState extends BaseState{ @Override public boolean axisMoved(Controller controller, int axisCode, float value){ - // TODO: Pass input to the input handler system. + GamepadUserInput userInput; + + if(Math.abs(value) > Ouya.STICK_DEADZONE){ + userInput = new GamepadUserInput(); + if(axisCode == Ouya.AXIS_LEFT_X){ + userInput.axisLeftX = value; + }else if(axisCode == Ouya.AXIS_LEFT_Y){ + userInput.axisLeftY = value; + }else if(axisCode == Ouya.AXIS_RIGHT_X){ + userInput.axisRightX = value; + }else if(axisCode == Ouya.AXIS_RIGHT_Y){ + userInput.axisRightY = value; + } + + robotArmPositioningSystem.setUserInput(userInput); + robotArmPositioningSystem.process(); + + return true; + } + return false; } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/states/InstructionsState.java b/src/ve/ucv/ciens/ccg/nxtar/states/InstructionsState.java new file mode 100644 index 0000000..e647165 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/states/InstructionsState.java @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.states; + +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.NxtARCore.game_states_t; +import ve.ucv.ciens.ccg.nxtar.scenarios.HintsOverlayBase; +import ve.ucv.ciens.ccg.nxtar.scenarios.ScenarioGlobals; +import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.controllers.Controller; +import com.badlogic.gdx.controllers.mappings.Ouya; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.Texture.TextureFilter; +import com.badlogic.gdx.graphics.Texture.TextureWrap; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.NinePatch; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle; +import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable; + +public class InstructionsState extends BaseState { + private static final String TAG = "HINTS_STATE"; + private static final String CLASS_NAME = InstructionsState.class.getSimpleName(); + private static final String SHADER_PATH = "shaders/movingBckg/movingBckg"; + + // Helper fields. + private float u_scaling[]; + private float u_displacement; + + // Buttons and other gui components. + private TextButton continueButton; + private Rectangle continueButtonBBox; + private Sprite background; + private Texture backgroundTexture; + private ShaderProgram backgroundShader; + private Texture ouyaOButtonTexture; + private Sprite ouyaOButton; + private boolean oButtonPressed; + + // Graphic data for the start button. + private Texture buttonEnabledTexture; + private Texture buttonDisabledTexture; + private Texture buttonPressedTexture; + private BitmapFont font; + + // Overlay related fields. + HintsOverlayBase hintsOverlay; + + // Button touch helper fields. + private boolean continueButtonTouched; + private int continueButtonTouchPointer; + + public InstructionsState(NxtARCore core) throws IllegalArgumentException{ + TextButtonStyle textButtonStyle; + FreeTypeFontGenerator fontGenerator; + FreeTypeFontParameter fontParameters; + NinePatch buttonEnabled9p; + NinePatch buttonDisabled9p; + NinePatch buttonPressed9p; + + if(core == null) + throw new IllegalArgumentException(CLASS_NAME + ": Core is null."); + + this.core = core; + this.pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + oButtonPressed = false; + hintsOverlay = ScenarioGlobals.getHintsOverlay(); + + // Create the start button background. + buttonEnabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Yellow.png")); + buttonEnabled9p = new NinePatch(new TextureRegion(buttonEnabledTexture, 0, 0, buttonEnabledTexture.getWidth(), buttonEnabledTexture.getHeight()), 49, 49, 45, 45); + buttonDisabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Cyan.png")); + buttonDisabled9p = new NinePatch(new TextureRegion(buttonDisabledTexture, 0, 0, buttonDisabledTexture.getWidth(), buttonDisabledTexture.getHeight()), 49, 49, 45, 45); + buttonPressedTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Blue.png")); + buttonPressed9p = new NinePatch(new TextureRegion(buttonPressedTexture, 0, 0, buttonPressedTexture.getWidth(), buttonPressedTexture.getHeight()), 49, 49, 45, 45); + + // Create the start button font. + fontParameters = new FreeTypeFontParameter(); + fontParameters.characters = ProjectConstants.FONT_CHARS; + fontParameters.size = ProjectConstants.MENU_BUTTON_FONT_SIZE; + fontParameters.flip = false; + fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/fonts/d-puntillas-B-to-tiptoe.ttf")); + font = fontGenerator.generateFont(fontParameters); + fontGenerator.dispose(); + + // Create the contine button. + textButtonStyle = new TextButtonStyle(); + textButtonStyle.font = font; + textButtonStyle.up = new NinePatchDrawable(buttonEnabled9p); + textButtonStyle.checked = new NinePatchDrawable(buttonPressed9p); + textButtonStyle.disabled = new NinePatchDrawable(buttonDisabled9p); + textButtonStyle.fontColor = new Color(Color.BLACK); + textButtonStyle.downFontColor = new Color(Color.WHITE); + textButtonStyle.disabledFontColor = new Color(Color.BLACK); + + continueButton = new TextButton("Continue", textButtonStyle); + continueButton.setText("Continue"); + continueButton.setPosition(-(continueButton.getWidth() / 2), -(Utils.getScreenHeightWithOverscan() / 2) + 10); + continueButtonBBox = new Rectangle(0, 0, continueButton.getWidth(), continueButton.getHeight()); + continueButtonBBox.setPosition(continueButton.getX(), continueButton.getY()); + + // Set OUYA's O button. + if(Ouya.runningOnOuya){ + ouyaOButtonTexture = new Texture("data/gfx/gui/OUYA_O.png"); + ouyaOButton = new Sprite(ouyaOButtonTexture); + ouyaOButton.setSize(ouyaOButton.getWidth() * 0.6f, ouyaOButton.getHeight() * 0.6f); + ouyaOButton.setPosition(continueButton.getX() - ouyaOButton.getWidth() - 20, continueButton.getY() + (ouyaOButton.getHeight() / 2)); + oButtonPressed = false; + }else{ + ouyaOButtonTexture = null; + } + + // Set up the background. + backgroundTexture = new Texture(Gdx.files.internal("data/gfx/textures/tile_aqua.png")); + backgroundTexture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat); + backgroundTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear); + background = new Sprite(backgroundTexture); + background.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + background.setPosition(-(Gdx.graphics.getWidth() / 2), -(Gdx.graphics.getHeight() / 2)); + + backgroundShader = new ShaderProgram(Gdx.files.internal(SHADER_PATH + "_vert.glsl"), Gdx.files.internal(SHADER_PATH + "_frag.glsl")); + if(!backgroundShader.isCompiled()){ + Gdx.app.error(TAG, CLASS_NAME + ".MainMenuStateBase() :: Failed to compile the background shader."); + Gdx.app.error(TAG, CLASS_NAME + backgroundShader.getLog()); + backgroundShader = null; + } + + u_scaling = new float[2]; + u_scaling[0] = Gdx.graphics.getWidth() > Gdx.graphics.getHeight() ? 16.0f : 9.0f; + u_scaling[1] = Gdx.graphics.getHeight() > Gdx.graphics.getWidth() ? 16.0f : 9.0f; + u_displacement = 1.0f; + + win2world = new Vector3(0.0f, 0.0f, 0.0f); + touchPointWorldCoords = new Vector2(); + continueButtonTouched = false; + continueButtonTouchPointer = -1; + stateActive = false; + } + + @Override + public void render(float delta){ + Gdx.gl.glClearColor(1, 1, 1, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + + core.batch.setProjectionMatrix(pixelPerfectCamera.combined); + core.batch.begin();{ + + // Render background. + core.batch.disableBlending(); + drawBackground(core.batch); + core.batch.enableBlending(); + + hintsOverlay.render(core.batch); + + // Render buttons. + continueButton.draw(core.batch, 1.0f); + if(Ouya.runningOnOuya) + ouyaOButton.draw(core.batch); + + }core.batch.end(); + } + + @Override + public void dispose(){ + buttonEnabledTexture.dispose(); + buttonDisabledTexture.dispose(); + buttonPressedTexture.dispose(); + if(ouyaOButtonTexture != null) + ouyaOButtonTexture.dispose(); + backgroundTexture.dispose(); + if(backgroundShader != null) backgroundShader.dispose(); + font.dispose(); + } + + private void drawBackground(SpriteBatch batch){ + if(backgroundShader != null){ + batch.setShader(backgroundShader); + backgroundShader.setUniform2fv("u_scaling", u_scaling, 0, 2); + backgroundShader.setUniformf("u_displacement", u_displacement); + } + background.draw(batch); + if(backgroundShader != null) batch.setShader(null); + u_displacement = u_displacement < 0.0f ? 1.0f : u_displacement - 0.0005f; + } + + @Override + public void onStateSet(){ + stateActive = true; + Gdx.input.setInputProcessor(this); + Gdx.input.setCatchBackKey(true); + Gdx.input.setCatchMenuKey(true); + } + + @Override + public void onStateUnset(){ + stateActive = false; + Gdx.input.setInputProcessor(null); + Gdx.input.setCatchBackKey(false); + Gdx.input.setCatchMenuKey(false); + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;; + ; INPUT LISTENER METHODS ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;*/ + + @Override + public boolean touchDown(int screenX, int screenY, int pointer, int button){ + unprojectTouch(screenX, screenY); + + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown(%d, %d, %d, %d)", screenX, screenY, pointer, button)); + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); + + if(!continueButton.isDisabled() && continueButtonBBox.contains(touchPointWorldCoords)){ + continueButton.setChecked(true); + continueButtonTouched = true; + continueButtonTouchPointer = pointer; + Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button pressed."); + } + + return true; + } + + @Override + public boolean touchUp(int screenX, int screenY, int pointer, int button){ + unprojectTouch(screenX, screenY); + + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp(%d, %d, %d, %d)", screenX, screenY, pointer, button)); + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); + + if(!continueButton.isDisabled() && continueButtonBBox.contains(touchPointWorldCoords) && continueButtonTouched){ + continueButton.setChecked(false); + continueButtonTouched = false; + continueButtonTouchPointer = -1; + core.nextState = game_states_t.IN_GAME; + Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button released."); + } + + return true; + } + + @Override + public boolean touchDragged(int screenX, int screenY, int pointer){ + unprojectTouch(screenX, screenY); + + if(!continueButton.isDisabled() && continueButtonTouched && pointer == continueButtonTouchPointer && !continueButtonBBox.contains(touchPointWorldCoords)){ + continueButtonTouchPointer = -1; + continueButtonTouched = false; + continueButton.setChecked(false); + Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Start button released."); + } + + return true; + } + + @Override + public boolean keyDown(int keycode){ + if(keycode == Input.Keys.BACK){ + core.nextState = game_states_t.IN_GAME; + return true; + } + return false; + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; CONTROLLER LISTENER METHODS ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ + + @Override + public boolean buttonDown(Controller controller, int buttonCode){ + if(stateActive){ + if(buttonCode == Ouya.BUTTON_O && !continueButton.isDisabled()){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button pressed."); + oButtonPressed = true; + continueButton.setChecked(true); + } + return true; + }else{ + return false; + } + } + + @Override + public boolean buttonUp(Controller controller, int buttonCode){ + if(stateActive){ + if(buttonCode == Ouya.BUTTON_O){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button released."); + if(oButtonPressed){ + oButtonPressed = false; + continueButton.setChecked(false); + core.nextState = game_states_t.IN_GAME; + } + } + return true; + }else{ + return false; + } + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/states/MainMenuStateBase.java b/src/ve/ucv/ciens/ccg/nxtar/states/MainMenuStateBase.java index db558f4..2b03546 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/states/MainMenuStateBase.java +++ b/src/ve/ucv/ciens/ccg/nxtar/states/MainMenuStateBase.java @@ -43,112 +43,115 @@ import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable; public abstract class MainMenuStateBase extends BaseState{ protected static final String TAG = "MAIN_MENU"; private static final String CLASS_NAME = MainMenuStateBase.class.getSimpleName(); - private static final String SHADER_PATH = "shaders/bckg/bckg"; + private static final String SHADER_PATH = "shaders/movingBckg/movingBckg"; - protected final int NUM_MENU_BUTTONS = 2; + protected final int NUM_MENU_BUTTONS = 3; // Helper fields. protected boolean clientConnected; - private float u_scaling[]; + protected boolean cameraCalibrated; + protected boolean assetsLoaded; + private float u_scaling[]; + private float u_displacement; // Buttons and other gui components. protected TextButton startButton; - protected Rectangle startButtonBBox; - protected Sprite clientConnectedLedOn; - protected Sprite clientConnectedLedOff; - + protected Rectangle startButtonBBox; protected TextButton calibrationButton; - protected Rectangle calibrationButtonBBox; - protected Sprite cameraCalibratedLedOn; - protected Sprite cameraCalibratedLedOff; - - protected Sprite background; + protected Rectangle calibrationButtonBBox; + protected TextButton autoButton; + protected Rectangle autoButtonBBox; + protected Sprite cameraCalibratedLedOn; + protected Sprite cameraCalibratedLedOff; + protected Sprite assetsLoadedLedOn; + protected Sprite assetsLoadedLedOff; + protected Sprite background; // Graphic data for the start button. - private Texture menuButtonEnabledTexture; - private Texture menuButtonDisabledTexture; - private Texture menuButtonPressedTexture; - private NinePatch menuButtonEnabled9p; - private NinePatch menuButtonDisabled9p; - private NinePatch menuButtonPressed9p; + private Texture menuButtonEnabledTexture; + private Texture menuButtonDisabledTexture; + private Texture menuButtonPressedTexture; + private NinePatch menuButtonEnabled9p; + private NinePatch menuButtonDisabled9p; + private NinePatch menuButtonPressed9p; private BitmapFont font; // Other graphics. - private Texture cameraCalibratedLedOffTexture; - private Texture cameraCalibratedLedOnTexture; - private Texture clientConnectedLedOffTexture; - private Texture clientConnectedLedOnTexture; - private Texture backgroundTexture; + private Texture ledOffTexture; + private Texture ledOnTexture; + private Texture backgroundTexture; private ShaderProgram backgroundShader; // Button touch helper fields. protected boolean startButtonTouched; - protected int startButtonTouchPointer; + protected int startButtonTouchPointer; protected boolean calibrationButtonTouched; - protected int calibrationButtonTouchPointer; + protected int calibrationButtonTouchPointer; + protected boolean autoButtonTouched; + protected int autoButtonTouchPointer; public MainMenuStateBase(){ - TextureRegion region; - TextButtonStyle tbs; - FreeTypeFontGenerator generator; - FreeTypeFontParameter param; + TextureRegion region; + TextButtonStyle textButtonStyle; + FreeTypeFontGenerator fontGenerator; + FreeTypeFontParameter fontParameters; this.pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); // Create the start button background. menuButtonEnabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Yellow.png")); menuButtonEnabled9p = new NinePatch(new TextureRegion(menuButtonEnabledTexture, 0, 0, menuButtonEnabledTexture.getWidth(), menuButtonEnabledTexture.getHeight()), 49, 49, 45, 45); - menuButtonDisabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Cyan.png")); menuButtonDisabled9p = new NinePatch(new TextureRegion(menuButtonDisabledTexture, 0, 0, menuButtonDisabledTexture.getWidth(), menuButtonDisabledTexture.getHeight()), 49, 49, 45, 45); - menuButtonPressedTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Blue.png")); menuButtonPressed9p = new NinePatch(new TextureRegion(menuButtonPressedTexture, 0, 0, menuButtonPressedTexture.getWidth(), menuButtonPressedTexture.getHeight()), 49, 49, 45, 45); // Create the start button font. - param = new FreeTypeFontParameter(); - param.characters = ProjectConstants.FONT_CHARS; - param.size = ProjectConstants.MENU_BUTTON_FONT_SIZE; - param.flip = false; - generator = new FreeTypeFontGenerator(Gdx.files.internal("data/fonts/d-puntillas-B-to-tiptoe.ttf")); - font = generator.generateFont(param); - generator.dispose(); + fontParameters = new FreeTypeFontParameter(); + fontParameters.characters = ProjectConstants.FONT_CHARS; + fontParameters.size = ProjectConstants.MENU_BUTTON_FONT_SIZE; + fontParameters.flip = false; + fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/fonts/d-puntillas-B-to-tiptoe.ttf")); + font = fontGenerator.generateFont(fontParameters); + fontGenerator.dispose(); - // Create the start button. - tbs = new TextButtonStyle(); - tbs.font = font; - tbs.up = new NinePatchDrawable(menuButtonEnabled9p); - tbs.checked = new NinePatchDrawable(menuButtonPressed9p); - tbs.disabled = new NinePatchDrawable(menuButtonDisabled9p); - tbs.disabledFontColor = new Color(0, 0, 0, 1); + // Create the buttons. + textButtonStyle = new TextButtonStyle(); + textButtonStyle.font = font; + textButtonStyle.up = new NinePatchDrawable(menuButtonEnabled9p); + textButtonStyle.checked = new NinePatchDrawable(menuButtonPressed9p); + textButtonStyle.disabled = new NinePatchDrawable(menuButtonDisabled9p); + textButtonStyle.fontColor = new Color(Color.BLACK); + textButtonStyle.downFontColor = new Color(Color.WHITE); + textButtonStyle.disabledFontColor = new Color(Color.BLACK); - startButton = new TextButton("Start server", tbs); - startButton.setText("Start game"); + startButton = new TextButton("Manual control", textButtonStyle); + startButton.setText("Manual control"); startButton.setDisabled(true); startButtonBBox = new Rectangle(0, 0, startButton.getWidth(), startButton.getHeight()); - // Create the calibration button. - calibrationButton = new TextButton("Calibrate camera", tbs); + calibrationButton = new TextButton("Calibrate camera", textButtonStyle); calibrationButton.setText("Calibrate camera"); calibrationButton.setDisabled(true); calibrationButtonBBox = new Rectangle(0, 0, calibrationButton.getWidth(), calibrationButton.getHeight()); + autoButton = new TextButton("Automatic action", textButtonStyle); + autoButton.setText("Automatic action"); + autoButton.setDisabled(true); + autoButtonBBox = new Rectangle(0, 0, autoButton.getWidth(), autoButton.getHeight()); + // Create the connection leds. - clientConnectedLedOnTexture = new Texture("data/gfx/gui/Anonymous_Button_Green.png"); - region = new TextureRegion(clientConnectedLedOnTexture); - clientConnectedLedOn = new Sprite(region); + ledOnTexture = new Texture("data/gfx/gui/Anonymous_Button_Green.png"); + ledOffTexture = new Texture("data/gfx/gui/Anonymous_Button_Red.png"); - clientConnectedLedOffTexture = new Texture("data/gfx/gui/Anonymous_Button_Red.png"); - region = new TextureRegion(clientConnectedLedOffTexture); - clientConnectedLedOff = new Sprite(region); - - cameraCalibratedLedOnTexture = new Texture("data/gfx/gui/Anonymous_Button_Green.png"); - region = new TextureRegion(cameraCalibratedLedOnTexture); + region = new TextureRegion(ledOnTexture); cameraCalibratedLedOn = new Sprite(region); - - cameraCalibratedLedOffTexture = new Texture("data/gfx/gui/Anonymous_Button_Red.png"); - region = new TextureRegion(cameraCalibratedLedOffTexture); + region = new TextureRegion(ledOffTexture); cameraCalibratedLedOff = new Sprite(region); + region = new TextureRegion(ledOnTexture); + assetsLoadedLedOn = new Sprite(region); + region = new TextureRegion(ledOffTexture); + assetsLoadedLedOff = new Sprite(region); // Set up the background. backgroundTexture = new Texture(Gdx.files.internal("data/gfx/textures/tile_aqua.png")); @@ -170,6 +173,7 @@ public abstract class MainMenuStateBase extends BaseState{ u_scaling[0] = Gdx.graphics.getWidth() > Gdx.graphics.getHeight() ? 16.0f : 9.0f; u_scaling[1] = Gdx.graphics.getHeight() > Gdx.graphics.getWidth() ? 16.0f : 9.0f; + u_displacement = 1.0f; win2world = new Vector3(0.0f, 0.0f, 0.0f); touchPointWorldCoords = new Vector2(); @@ -177,37 +181,25 @@ public abstract class MainMenuStateBase extends BaseState{ startButtonTouchPointer = -1; calibrationButtonTouched = false; calibrationButtonTouchPointer = -1; + autoButtonTouched = false; + autoButtonTouchPointer = -1; clientConnected = false; + cameraCalibrated = false; + assetsLoaded = false; stateActive = false; } @Override public abstract void render(float delta); - @Override - public void resize(int width, int height){ } - - @Override - public void show(){ } - @Override - public void hide(){ } - - @Override - public void pause(){ } - - @Override - public void resume(){ } - @Override public void dispose(){ menuButtonEnabledTexture.dispose(); menuButtonDisabledTexture.dispose(); menuButtonPressedTexture.dispose(); - clientConnectedLedOnTexture.dispose(); - clientConnectedLedOffTexture.dispose(); - cameraCalibratedLedOnTexture.dispose(); - cameraCalibratedLedOffTexture.dispose(); + ledOnTexture.dispose(); + ledOffTexture.dispose(); backgroundTexture.dispose(); if(backgroundShader != null) backgroundShader.dispose(); font.dispose(); @@ -217,9 +209,11 @@ public abstract class MainMenuStateBase extends BaseState{ if(backgroundShader != null){ batch.setShader(backgroundShader); backgroundShader.setUniform2fv("u_scaling", u_scaling, 0, 2); + backgroundShader.setUniformf("u_displacement", u_displacement); } background.draw(batch); if(backgroundShader != null) batch.setShader(null); + u_displacement = u_displacement < 0.0f ? 1.0f : u_displacement - 0.0005f; } @Override @@ -240,10 +234,24 @@ public abstract class MainMenuStateBase extends BaseState{ public void onClientConnected(){ clientConnected = true; - startButton.setDisabled(false); calibrationButton.setDisabled(false); } + public void onCameraCalibrated(){ + cameraCalibrated = true; + enableGameButtons(); + } + + public void onAssetsLoaded(){ + assetsLoaded = true; + enableGameButtons(); + } + + private void enableGameButtons(){ + startButton.setDisabled(!(cameraCalibrated && assetsLoaded)); + autoButton.setDisabled(!(cameraCalibrated && assetsLoaded)); + } + /*;;;;;;;;;;;;;;;;;;;;;;;;;; ; INPUT LISTENER METHODS ; ;;;;;;;;;;;;;;;;;;;;;;;;;;*/ @@ -255,16 +263,21 @@ public abstract class MainMenuStateBase extends BaseState{ Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown(%d, %d, %d, %d)", screenX, screenY, pointer, button)); Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); - if(!startButton.isDisabled() && startButtonBBox.contains(touchPointWorldCoords) && !calibrationButtonTouched){ + if(!startButton.isDisabled() && startButtonBBox.contains(touchPointWorldCoords) && (!calibrationButtonTouched && !autoButtonTouched)){ startButton.setChecked(true); startButtonTouched = true; startButtonTouchPointer = pointer; Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button pressed."); - }else if(!calibrationButton.isDisabled() && calibrationButtonBBox.contains(touchPointWorldCoords) && !startButtonTouched){ + }else if(!calibrationButton.isDisabled() && calibrationButtonBBox.contains(touchPointWorldCoords) && (!startButtonTouched && !autoButtonTouched)){ calibrationButton.setChecked(true); calibrationButtonTouched = true; calibrationButtonTouchPointer = pointer; Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Calibration button pressed."); + }else if(!autoButton.isDisabled() && autoButtonBBox.contains(touchPointWorldCoords) && (!startButtonTouched && !calibrationButtonTouched)){ + autoButton.setChecked(true); + autoButtonTouched = true; + autoButtonTouchPointer = pointer; + Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Auto button pressed."); } return true; @@ -289,6 +302,12 @@ public abstract class MainMenuStateBase extends BaseState{ calibrationButtonTouchPointer = -1; core.nextState = game_states_t.CALIBRATION; Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Calibration button released."); + }else if(!autoButton.isDisabled() && autoButtonBBox.contains(touchPointWorldCoords) && autoButtonTouched){ + autoButton.setChecked(false); + autoButtonTouched = false; + autoButtonTouchPointer = -1; + core.nextState = game_states_t.AUTOMATIC_ACTION; + Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Auto button released."); } return true; @@ -308,6 +327,11 @@ public abstract class MainMenuStateBase extends BaseState{ calibrationButtonTouched = false; calibrationButton.setChecked(false); Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Start button released."); + }else if(!autoButton.isDisabled() && autoButtonTouched && pointer == autoButtonTouchPointer && !autoButtonBBox.contains(touchPointWorldCoords)){ + autoButtonTouchPointer = -1; + autoButtonTouched = false; + autoButton.setChecked(false); + Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Auto button released."); } return true; @@ -316,7 +340,7 @@ public abstract class MainMenuStateBase extends BaseState{ @Override public boolean keyDown(int keycode){ if(keycode == Input.Keys.BACK){ - // Ignore. + Gdx.app.exit(); return true; } return false; diff --git a/src/ve/ucv/ciens/ccg/nxtar/states/OuyaMainMenuState.java b/src/ve/ucv/ciens/ccg/nxtar/states/OuyaMainMenuState.java index 223c3a8..3611e84 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/states/OuyaMainMenuState.java +++ b/src/ve/ucv/ciens/ccg/nxtar/states/OuyaMainMenuState.java @@ -1,171 +1,209 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.states; - -import ve.ucv.ciens.ccg.nxtar.NxtARCore; -import ve.ucv.ciens.ccg.nxtar.NxtARCore.game_states_t; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.controllers.Controller; -import com.badlogic.gdx.controllers.mappings.Ouya; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Sprite; -import com.badlogic.gdx.graphics.g2d.TextureRegion; - -public class OuyaMainMenuState extends MainMenuStateBase{ - private static final String CLASS_NAME = OuyaMainMenuState.class.getSimpleName(); - - private Texture ouyaOButtonTexture; - private Sprite ouyaOButton; - private boolean oButtonPressed; - private int oButtonSelection; - - public OuyaMainMenuState(final NxtARCore core){ - super(); - - this.core = core; - - startButton.setPosition(-(startButton.getWidth() / 2), -(startButton.getHeight() / 2)); - startButtonBBox.setPosition(startButton.getX(), startButton.getY()); - - calibrationButton.setPosition(-(calibrationButton.getWidth() / 2), (startButton.getY() + startButton.getHeight()) + 10); - calibrationButtonBBox.setPosition(calibrationButton.getX(), calibrationButton.getY()); - - float ledYPos = (-(Gdx.graphics.getHeight() / 2) * 0.5f) + (calibrationButton.getY() * 0.5f); - clientConnectedLedOn.setSize(clientConnectedLedOn.getWidth() * 0.5f, clientConnectedLedOn.getHeight() * 0.5f); - clientConnectedLedOn.setPosition(-(clientConnectedLedOn.getWidth() / 2), ledYPos); - - clientConnectedLedOff.setSize(clientConnectedLedOff.getWidth() * 0.5f, clientConnectedLedOff.getHeight() * 0.5f); - clientConnectedLedOff.setPosition(-(clientConnectedLedOff.getWidth() / 2), ledYPos); - - // TODO: Set calibration led attributes. - - ouyaOButtonTexture = new Texture("data/gfx/gui/OUYA_O.png"); - TextureRegion region = new TextureRegion(ouyaOButtonTexture, ouyaOButtonTexture.getWidth(), ouyaOButtonTexture.getHeight()); - ouyaOButton = new Sprite(region); - ouyaOButton.setSize(ouyaOButton.getWidth() * 0.6f, ouyaOButton.getHeight() * 0.6f); - - oButtonSelection = 0; - oButtonPressed = false; - } - - @Override - public void render(float delta) { - Gdx.gl.glClearColor(1, 1, 1, 1); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - - core.batch.setProjectionMatrix(pixelPerfectCamera.combined); - core.batch.begin();{ - core.batch.disableBlending(); - drawBackground(core.batch); - core.batch.enableBlending(); - - if(clientConnected){ - clientConnectedLedOn.draw(core.batch); - }else{ - clientConnectedLedOff.draw(core.batch); - } - - // TODO: Render calibration leds. - - startButton.draw(core.batch, 1.0f); - calibrationButton.draw(core.batch, 1.0f); - - if(oButtonSelection == 0){ - ouyaOButton.setPosition(startButton.getX() - ouyaOButton.getWidth() - 20, startButton.getY() + (ouyaOButton.getHeight() / 2)); - }else if(oButtonSelection == 1){ - ouyaOButton.setPosition(calibrationButton.getX() - ouyaOButton.getWidth() - 20, calibrationButton.getY() + (ouyaOButton.getHeight() / 2)); - } - ouyaOButton.draw(core.batch); - - }core.batch.end(); - } - - @Override - public void dispose(){ - super.dispose(); - ouyaOButtonTexture.dispose(); - } - - /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ; BEGIN CONTROLLER LISTENER METHODS ; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ - - @Override - public boolean buttonDown(Controller controller, int buttonCode){ - // TODO: Test this. - - if(stateActive){ - if(buttonCode == Ouya.BUTTON_O){ - Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button pressed."); - - if(oButtonSelection == 0){ - if(!clientConnected){ - core.toast("Can't start the game. No client is connected.", true); - }else{ - oButtonPressed = true; - startButton.setChecked(true); - } - }else if(oButtonSelection == 1){ - if(!clientConnected){ - core.toast("Can't calibrate the camera. No client is connected.", true); - }else{ - oButtonPressed = true; - calibrationButton.setChecked(true); - } - } - }else if(buttonCode == Ouya.BUTTON_DPAD_UP){ - Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): Dpad up button pressed."); - oButtonSelection = oButtonSelection - 1 < 0 ? NUM_MENU_BUTTONS - 1 : oButtonSelection - 1; - }else if(buttonCode == Ouya.BUTTON_DPAD_DOWN){ - Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): Dpad down button pressed."); - oButtonSelection = (oButtonSelection + 1) % NUM_MENU_BUTTONS; - } - - return true; - }else{ - return false; - } - } - - @Override - public boolean buttonUp(Controller controller, int buttonCode){ - // TODO: Test this. - - if(stateActive){ - if(buttonCode == Ouya.BUTTON_O){ - Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button released."); - - if(oButtonPressed){ - oButtonPressed = false; - - if(oButtonSelection == 0){ - startButton.setChecked(false); - core.nextState = game_states_t.IN_GAME; - }else if(oButtonSelection == 1){ - calibrationButton.setChecked(false); - core.nextState = game_states_t.IN_GAME; - } - } - } - - return true; - }else{ - return false; - } - } -} +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.states; + +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.NxtARCore.game_states_t; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.controllers.Controller; +import com.badlogic.gdx.controllers.mappings.Ouya; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.TextureRegion; + +public class OuyaMainMenuState extends MainMenuStateBase{ + private static final String CLASS_NAME = OuyaMainMenuState.class.getSimpleName(); + + private Texture ouyaOButtonTexture; + private Sprite ouyaOButton; + private boolean oButtonPressed; + private int oButtonSelection; + private float ledYPos; + + public OuyaMainMenuState(final NxtARCore core) throws IllegalArgumentException{ + super(); + + if(core == null) + throw new IllegalArgumentException(CLASS_NAME + ": Core is null."); + + this.core = core; + + // Set buttons. + startButton.setPosition(-(startButton.getWidth() / 2), -(startButton.getHeight() / 2)); + startButtonBBox.setPosition(startButton.getX(), startButton.getY()); + calibrationButton.setPosition(-(calibrationButton.getWidth() / 2), (startButton.getY() + startButton.getHeight()) + 10); + calibrationButtonBBox.setPosition(calibrationButton.getX(), calibrationButton.getY()); + autoButton.setPosition(-(autoButton.getWidth() / 2), (startButton.getY() - startButton.getHeight()) - 10); + autoButtonBBox.setPosition(autoButton.getX(), autoButton.getY()); + + //Set leds. + ledYPos = -(Utils.getScreenHeightWithOverscan() / 2) + 10; + cameraCalibratedLedOn.setSize(cameraCalibratedLedOn.getWidth() * 0.5f, cameraCalibratedLedOn.getHeight() * 0.5f); + cameraCalibratedLedOn.setPosition(-cameraCalibratedLedOn.getWidth() - 5, ledYPos); + cameraCalibratedLedOff.setSize(cameraCalibratedLedOff.getWidth() * 0.5f, cameraCalibratedLedOff.getHeight() * 0.5f); + cameraCalibratedLedOff.setPosition(-cameraCalibratedLedOff.getWidth() - 5, ledYPos); + assetsLoadedLedOn.setSize(assetsLoadedLedOn.getWidth() * 0.5f, assetsLoadedLedOn.getHeight() * 0.5f); + assetsLoadedLedOn.setPosition(5, ledYPos); + assetsLoadedLedOff.setSize(assetsLoadedLedOff.getWidth() * 0.5f, assetsLoadedLedOff.getHeight() * 0.5f); + assetsLoadedLedOff.setPosition(5, ledYPos); + + // Set OUYA's O button. + ouyaOButtonTexture = new Texture("data/gfx/gui/OUYA_O.png"); + TextureRegion region = new TextureRegion(ouyaOButtonTexture, ouyaOButtonTexture.getWidth(), ouyaOButtonTexture.getHeight()); + ouyaOButton = new Sprite(region); + ouyaOButton.setSize(ouyaOButton.getWidth() * 0.6f, ouyaOButton.getHeight() * 0.6f); + oButtonSelection = 0; + oButtonPressed = false; + } + + @Override + public void render(float delta) { + Gdx.gl.glClearColor(1, 1, 1, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + + core.batch.setProjectionMatrix(pixelPerfectCamera.combined); + core.batch.begin();{ + // Render background. + core.batch.disableBlending(); + drawBackground(core.batch); + core.batch.enableBlending(); + + // Render leds. + if(cameraCalibrated) cameraCalibratedLedOn.draw(core.batch); + else cameraCalibratedLedOff.draw(core.batch); + if(assetsLoaded) assetsLoadedLedOn.draw(core.batch); + else assetsLoadedLedOff.draw(core.batch); + + // Render buttons. + startButton.draw(core.batch, 1.0f); + calibrationButton.draw(core.batch, 1.0f); + autoButton.draw(core.batch, 1.0f); + + // Render O button. + if(oButtonSelection == 0){ + ouyaOButton.setPosition(startButton.getX() - ouyaOButton.getWidth() - 20, startButton.getY() + (ouyaOButton.getHeight() / 2)); + }else if(oButtonSelection == 1){ + ouyaOButton.setPosition(calibrationButton.getX() - ouyaOButton.getWidth() - 20, calibrationButton.getY() + (ouyaOButton.getHeight() / 2)); + }else if(oButtonSelection == 2){ + ouyaOButton.setPosition(autoButton.getX() - ouyaOButton.getWidth() - 20, autoButton.getY() + (ouyaOButton.getHeight() / 2)); + } + ouyaOButton.draw(core.batch); + + }core.batch.end(); + } + + @Override + public void dispose(){ + super.dispose(); + ouyaOButtonTexture.dispose(); + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; CONTROLLER LISTENER METHODS ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ + + @Override + public boolean buttonDown(Controller controller, int buttonCode){ + if(stateActive){ + if(buttonCode == Ouya.BUTTON_O){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button pressed."); + + if(oButtonSelection == 0){ + if(!clientConnected){ + core.toast("Can't start the game. No client is connected.", true); + }else if(!core.cvProc.isCameraCalibrated()){ + core.toast("Can't start the game. Camera is not calibrated.", true); + }else{ + oButtonPressed = true; + startButton.setChecked(true); + } + }else if(oButtonSelection == 1){ + if(!clientConnected){ + core.toast("Can't calibrate the camera. No client is connected.", true); + }else{ + oButtonPressed = true; + calibrationButton.setChecked(true); + } + }else if(oButtonSelection == 2){ + if(!clientConnected){ + core.toast("Can't launch automatic action. No client is connected.", true); + }else{ + oButtonPressed = true; + autoButton.setChecked(true); + } + } + }else if(buttonCode == Ouya.BUTTON_DPAD_UP){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): Dpad up button pressed."); + oButtonSelection = (oButtonSelection + 1) % NUM_MENU_BUTTONS; + }else if(buttonCode == Ouya.BUTTON_DPAD_DOWN){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): Dpad down button pressed."); + oButtonSelection = oButtonSelection - 1 < 0 ? NUM_MENU_BUTTONS - 1 : oButtonSelection - 1; + } + + return true; + }else{ + return false; + } + } + + @Override + public boolean buttonUp(Controller controller, int buttonCode){ + if(stateActive){ + if(buttonCode == Ouya.BUTTON_O){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button released."); + + if(oButtonPressed){ + oButtonPressed = false; + + if(oButtonSelection == 0){ + startButton.setChecked(false); + core.nextState = game_states_t.IN_GAME; + }else if(oButtonSelection == 1){ + calibrationButton.setChecked(false); + core.nextState = game_states_t.CALIBRATION; + }else if(oButtonSelection == 2){ + autoButton.setChecked(false); + core.nextState = game_states_t.AUTOMATIC_ACTION; + } + } + } + + return true; + }else{ + return false; + } + } + + @Override + public boolean axisMoved(Controller controller, int axisCode, float value){ + if(Math.abs(value) > 0.99f){ + if(axisCode == Ouya.AXIS_LEFT_Y && value < 0.0f){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): Dpad up button pressed."); + oButtonSelection = (oButtonSelection + 1) % NUM_MENU_BUTTONS; + }else if(axisCode == Ouya.AXIS_LEFT_Y && value >= 0.0f){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): Dpad down button pressed."); + oButtonSelection = oButtonSelection - 1 < 0 ? NUM_MENU_BUTTONS - 1 : oButtonSelection - 1; + } + + return true; + } + + return false; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/states/PauseState.java b/src/ve/ucv/ciens/ccg/nxtar/states/PauseState.java deleted file mode 100644 index a7c51bb..0000000 --- a/src/ve/ucv/ciens/ccg/nxtar/states/PauseState.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.states; - -import ve.ucv.ciens.ccg.nxtar.NxtARCore; - -import com.badlogic.gdx.controllers.Controller; -import com.badlogic.gdx.controllers.PovDirection; -import com.badlogic.gdx.math.Vector3; - -public class PauseState extends BaseState { - - public PauseState(final NxtARCore core){ - this.core = core; - } - - @Override - public void render(float delta) { - // TODO Auto-generated method stub - - } - - @Override - public void resize(int width, int height) { - // TODO Auto-generated method stub - - } - - @Override - public void show() { - // TODO Auto-generated method stub - - } - - @Override - public void hide() { - // TODO Auto-generated method stub - - } - - @Override - public void pause() { - // TODO Auto-generated method stub - - } - - @Override - public void resume() { - // TODO Auto-generated method stub - - } - - @Override - public void dispose() { - // TODO Auto-generated method stub - - } - - /*;;;;;;;;;;;;;;;;;; - ; HELPER METHODS ; - ;;;;;;;;;;;;;;;;;;*/ - - @Override - public void onStateSet(){ - } - - @Override - public void onStateUnset(){ - } - - /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ; BEGIN INPUT PROCESSOR METHODS ; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ - - @Override - public boolean keyDown(int keycode) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean keyUp(int keycode) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean keyTyped(char character) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean touchDown(int screenX, int screenY, int pointer, int button) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean touchUp(int screenX, int screenY, int pointer, int button) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean touchDragged(int screenX, int screenY, int pointer) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean mouseMoved(int screenX, int screenY) { - // TODO Auto-generated method stub - return false; - } - /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ; END INPUT PROCESSOR METHODS ; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ - - /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ; BEGIN CONTROLLER LISTENER METHODS ; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ - @Override - public boolean scrolled(int amount) { - // TODO Auto-generated method stub - return false; - } - - @Override - public void connected(Controller controller) { - // TODO Auto-generated method stub - - } - - @Override - public void disconnected(Controller controller) { - // TODO Auto-generated method stub - - } - - @Override - public boolean buttonDown(Controller controller, int buttonCode) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean buttonUp(Controller controller, int buttonCode) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean axisMoved(Controller controller, int axisCode, float value) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean povMoved(Controller controller, int povCode, - PovDirection value) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean xSliderMoved(Controller controller, int sliderCode, - boolean value) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean ySliderMoved(Controller controller, int sliderCode, - boolean value) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean accelerometerMoved(Controller controller, - int accelerometerCode, Vector3 value) { - // TODO Auto-generated method stub - return false; - } - /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ; END CONTROLLER LISTENER METHODS ; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ -} diff --git a/src/ve/ucv/ciens/ccg/nxtar/states/ScenarioEndSummaryState.java b/src/ve/ucv/ciens/ccg/nxtar/states/ScenarioEndSummaryState.java new file mode 100644 index 0000000..51b5336 --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/states/ScenarioEndSummaryState.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.states; + +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.NxtARCore.game_states_t; +import ve.ucv.ciens.ccg.nxtar.scenarios.ScenarioGlobals; +import ve.ucv.ciens.ccg.nxtar.scenarios.SummaryOverlayBase; +import ve.ucv.ciens.ccg.nxtar.systems.PlayerSystemBase; +import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Input; +import com.badlogic.gdx.controllers.Controller; +import com.badlogic.gdx.controllers.mappings.Ouya; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.Texture.TextureFilter; +import com.badlogic.gdx.graphics.Texture.TextureWrap; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.NinePatch; +import com.badlogic.gdx.graphics.g2d.Sprite; +import com.badlogic.gdx.graphics.g2d.SpriteBatch; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; +import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import com.badlogic.gdx.math.Rectangle; +import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.math.Vector3; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton; +import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle; +import com.badlogic.gdx.scenes.scene2d.utils.NinePatchDrawable; + +public class ScenarioEndSummaryState extends BaseState { + private static final String TAG = "SCENARIO_SUMMARY"; + private static final String CLASS_NAME = ScenarioEndSummaryState.class.getSimpleName(); + private static final String SHADER_PATH = "shaders/movingBckg/movingBckg"; + + // Helper fields. + private float u_scaling[]; + private float u_displacement; + + // Buttons and other gui components. + private TextButton continueButton; + private Rectangle continueButtonBBox; + private Sprite background; + private Texture backgroundTexture; + private ShaderProgram backgroundShader; + private Texture ouyaOButtonTexture; + private Sprite ouyaOButton; + private boolean oButtonPressed; + + // Graphic data for the start button. + private Texture buttonEnabledTexture; + private Texture buttonDisabledTexture; + private Texture buttonPressedTexture; + private BitmapFont font; + + // Summary overlay related fields. + PlayerSystemBase playerSystem; + SummaryOverlayBase summaryOverlay; + + // Button touch helper fields. + private boolean continueButtonTouched; + private int continueButtonTouchPointer; + + public ScenarioEndSummaryState(NxtARCore core) throws IllegalArgumentException{ + TextButtonStyle textButtonStyle; + FreeTypeFontGenerator fontGenerator; + FreeTypeFontParameter fontParameters; + NinePatch buttonEnabled9p; + NinePatch buttonDisabled9p; + NinePatch buttonPressed9p; + + if(core == null) + throw new IllegalArgumentException(CLASS_NAME + ": Core is null."); + + this.core = core; + this.pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + oButtonPressed = false; + playerSystem = ScenarioGlobals.getPlayerSystem(); + summaryOverlay = ScenarioGlobals.getScenarioSummaryOverlay(); + + // Create the start button background. + buttonEnabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Yellow.png")); + buttonEnabled9p = new NinePatch(new TextureRegion(buttonEnabledTexture, 0, 0, buttonEnabledTexture.getWidth(), buttonEnabledTexture.getHeight()), 49, 49, 45, 45); + buttonDisabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Cyan.png")); + buttonDisabled9p = new NinePatch(new TextureRegion(buttonDisabledTexture, 0, 0, buttonDisabledTexture.getWidth(), buttonDisabledTexture.getHeight()), 49, 49, 45, 45); + buttonPressedTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Blue.png")); + buttonPressed9p = new NinePatch(new TextureRegion(buttonPressedTexture, 0, 0, buttonPressedTexture.getWidth(), buttonPressedTexture.getHeight()), 49, 49, 45, 45); + + // Create the start button font. + fontParameters = new FreeTypeFontParameter(); + fontParameters.characters = ProjectConstants.FONT_CHARS; + fontParameters.size = ProjectConstants.MENU_BUTTON_FONT_SIZE; + fontParameters.flip = false; + fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("data/fonts/d-puntillas-B-to-tiptoe.ttf")); + font = fontGenerator.generateFont(fontParameters); + fontGenerator.dispose(); + + // Create the contine button. + textButtonStyle = new TextButtonStyle(); + textButtonStyle.font = font; + textButtonStyle.up = new NinePatchDrawable(buttonEnabled9p); + textButtonStyle.checked = new NinePatchDrawable(buttonPressed9p); + textButtonStyle.disabled = new NinePatchDrawable(buttonDisabled9p); + textButtonStyle.fontColor = new Color(Color.BLACK); + textButtonStyle.downFontColor = new Color(Color.WHITE); + textButtonStyle.disabledFontColor = new Color(Color.BLACK); + + continueButton = new TextButton("Continue", textButtonStyle); + continueButton.setText("Continue"); + continueButton.setPosition(-(continueButton.getWidth() / 2), -(Utils.getScreenHeightWithOverscan() / 2) + 10); + continueButtonBBox = new Rectangle(0, 0, continueButton.getWidth(), continueButton.getHeight()); + continueButtonBBox.setPosition(continueButton.getX(), continueButton.getY()); + + // Set OUYA's O button. + if(Ouya.runningOnOuya){ + ouyaOButtonTexture = new Texture("data/gfx/gui/OUYA_O.png"); + ouyaOButton = new Sprite(ouyaOButtonTexture); + ouyaOButton.setSize(ouyaOButton.getWidth() * 0.6f, ouyaOButton.getHeight() * 0.6f); + ouyaOButton.setPosition(continueButton.getX() - ouyaOButton.getWidth() - 20, continueButton.getY() + (ouyaOButton.getHeight() / 2)); + oButtonPressed = false; + }else{ + ouyaOButtonTexture = null; + } + + // Set up the background. + backgroundTexture = new Texture(Gdx.files.internal("data/gfx/textures/tile_aqua.png")); + backgroundTexture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat); + backgroundTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear); + background = new Sprite(backgroundTexture); + background.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + background.setPosition(-(Gdx.graphics.getWidth() / 2), -(Gdx.graphics.getHeight() / 2)); + + backgroundShader = new ShaderProgram(Gdx.files.internal(SHADER_PATH + "_vert.glsl"), Gdx.files.internal(SHADER_PATH + "_frag.glsl")); + if(!backgroundShader.isCompiled()){ + Gdx.app.error(TAG, CLASS_NAME + ".MainMenuStateBase() :: Failed to compile the background shader."); + Gdx.app.error(TAG, CLASS_NAME + backgroundShader.getLog()); + backgroundShader = null; + } + + u_scaling = new float[2]; + u_scaling[0] = Gdx.graphics.getWidth() > Gdx.graphics.getHeight() ? 16.0f : 9.0f; + u_scaling[1] = Gdx.graphics.getHeight() > Gdx.graphics.getWidth() ? 16.0f : 9.0f; + u_displacement = 1.0f; + + win2world = new Vector3(0.0f, 0.0f, 0.0f); + touchPointWorldCoords = new Vector2(); + continueButtonTouched = false; + continueButtonTouchPointer = -1; + stateActive = false; + } + + @Override + public void render(float delta){ + Gdx.gl.glClearColor(1, 1, 1, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + + core.batch.setProjectionMatrix(pixelPerfectCamera.combined); + core.batch.begin();{ + + // Render background. + core.batch.disableBlending(); + drawBackground(core.batch); + core.batch.enableBlending(); + + summaryOverlay.render(core.batch, playerSystem.getPlayerSummary()); + + // Render buttons. + continueButton.draw(core.batch, 1.0f); + if(Ouya.runningOnOuya) + ouyaOButton.draw(core.batch); + + }core.batch.end(); + } + + @Override + public void dispose(){ + buttonEnabledTexture.dispose(); + buttonDisabledTexture.dispose(); + buttonPressedTexture.dispose(); + if(ouyaOButtonTexture != null) + ouyaOButtonTexture.dispose(); + backgroundTexture.dispose(); + if(backgroundShader != null) backgroundShader.dispose(); + font.dispose(); + } + + private void drawBackground(SpriteBatch batch){ + if(backgroundShader != null){ + batch.setShader(backgroundShader); + backgroundShader.setUniform2fv("u_scaling", u_scaling, 0, 2); + backgroundShader.setUniformf("u_displacement", u_displacement); + } + background.draw(batch); + if(backgroundShader != null) batch.setShader(null); + u_displacement = u_displacement < 0.0f ? 1.0f : u_displacement - 0.0005f; + } + + @Override + public void onStateSet(){ + stateActive = true; + Gdx.input.setInputProcessor(this); + Gdx.input.setCatchBackKey(true); + Gdx.input.setCatchMenuKey(true); + } + + @Override + public void onStateUnset(){ + stateActive = false; + Gdx.input.setInputProcessor(null); + Gdx.input.setCatchBackKey(false); + Gdx.input.setCatchMenuKey(false); + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;; + ; INPUT LISTENER METHODS ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;*/ + + @Override + public boolean touchDown(int screenX, int screenY, int pointer, int button){ + unprojectTouch(screenX, screenY); + + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown(%d, %d, %d, %d)", screenX, screenY, pointer, button)); + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchDown() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); + + if(!continueButton.isDisabled() && continueButtonBBox.contains(touchPointWorldCoords)){ + continueButton.setChecked(true); + continueButtonTouched = true; + continueButtonTouchPointer = pointer; + Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button pressed."); + } + + return true; + } + + @Override + public boolean touchUp(int screenX, int screenY, int pointer, int button){ + unprojectTouch(screenX, screenY); + + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp(%d, %d, %d, %d)", screenX, screenY, pointer, button)); + Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y)); + + if(!continueButton.isDisabled() && continueButtonBBox.contains(touchPointWorldCoords) && continueButtonTouched){ + continueButton.setChecked(false); + continueButtonTouched = false; + continueButtonTouchPointer = -1; + core.nextState = game_states_t.MAIN_MENU; + Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button released."); + } + + return true; + } + + @Override + public boolean touchDragged(int screenX, int screenY, int pointer){ + unprojectTouch(screenX, screenY); + + if(!continueButton.isDisabled() && continueButtonTouched && pointer == continueButtonTouchPointer && !continueButtonBBox.contains(touchPointWorldCoords)){ + continueButtonTouchPointer = -1; + continueButtonTouched = false; + continueButton.setChecked(false); + Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Start button released."); + } + + return true; + } + + @Override + public boolean keyDown(int keycode){ + if(keycode == Input.Keys.BACK){ + core.nextState = game_states_t.MAIN_MENU; + return true; + } + return false; + } + + /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ; CONTROLLER LISTENER METHODS ; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/ + + @Override + public boolean buttonDown(Controller controller, int buttonCode){ + if(stateActive){ + if(buttonCode == Ouya.BUTTON_O && !continueButton.isDisabled()){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button pressed."); + oButtonPressed = true; + continueButton.setChecked(true); + } + return true; + }else{ + return false; + } + } + + @Override + public boolean buttonUp(Controller controller, int buttonCode){ + if(stateActive){ + if(buttonCode == Ouya.BUTTON_O){ + Gdx.app.log(TAG, CLASS_NAME + ".buttonDown(): O button released."); + if(oButtonPressed){ + oButtonPressed = false; + continueButton.setChecked(false); + core.nextState = game_states_t.MAIN_MENU; + } + } + return true; + }else{ + return false; + } + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/states/TabletMainMenuState.java b/src/ve/ucv/ciens/ccg/nxtar/states/TabletMainMenuState.java index 84ebb63..763a1c1 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/states/TabletMainMenuState.java +++ b/src/ve/ucv/ciens/ccg/nxtar/states/TabletMainMenuState.java @@ -1,71 +1,83 @@ -/* - * Copyright (C) 2014 Miguel Angel Astor Romero - * - * 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 ve.ucv.ciens.ccg.nxtar.states; - -import ve.ucv.ciens.ccg.nxtar.NxtARCore; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.GL20; - -public class TabletMainMenuState extends MainMenuStateBase{ - - public TabletMainMenuState(final NxtARCore core){ - super(); - - this.core = core; - - startButton.setPosition(-(startButton.getWidth() / 2), -(startButton.getHeight() / 2)); - startButtonBBox.setPosition(startButton.getX(), startButton.getY()); - - calibrationButton.setPosition(-(calibrationButton.getWidth() / 2), (startButton.getY() + startButton.getHeight()) + 10); - calibrationButtonBBox.setPosition(calibrationButton.getX(), calibrationButton.getY()); - - float ledYPos = (-(Gdx.graphics.getHeight() / 2) * 0.5f) + (calibrationButton.getY() * 0.5f); - clientConnectedLedOn.setSize(clientConnectedLedOn.getWidth() * 0.5f, clientConnectedLedOn.getHeight() * 0.5f); - clientConnectedLedOn.setPosition(-(clientConnectedLedOn.getWidth() / 2), ledYPos); - - clientConnectedLedOff.setSize(clientConnectedLedOff.getWidth() * 0.5f, clientConnectedLedOff.getHeight() * 0.5f); - clientConnectedLedOff.setPosition(-(clientConnectedLedOff.getWidth() / 2), ledYPos); - - // TODO: Set calibration led attributes. - } - - @Override - public void render(float delta){ - Gdx.gl.glClearColor(1, 1, 1, 1); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - - core.batch.setProjectionMatrix(pixelPerfectCamera.combined); - core.batch.begin();{ - - core.batch.disableBlending(); - drawBackground(core.batch); - core.batch.enableBlending(); - - if(clientConnected){ - clientConnectedLedOn.draw(core.batch); - }else{ - clientConnectedLedOff.draw(core.batch); - } - - // TODO: Render calibration led. - - startButton.draw(core.batch, 1.0f); - calibrationButton.draw(core.batch, 1.0f); - - }core.batch.end(); - } -} +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.states; + +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.GL20; + +public class TabletMainMenuState extends MainMenuStateBase{ + private static final String CLASS_NAME = TabletMainMenuState.class.getSimpleName(); + + private float ledYPos; + + public TabletMainMenuState(final NxtARCore core) throws IllegalArgumentException{ + super(); + + if(core == null) + throw new IllegalArgumentException(CLASS_NAME + ": Core is null."); + + this.core = core; + + // Set buttons. + startButton.setPosition(-(startButton.getWidth() / 2), -(startButton.getHeight() / 2)); + startButtonBBox.setPosition(startButton.getX(), startButton.getY()); + calibrationButton.setPosition(-(calibrationButton.getWidth() / 2), (startButton.getY() + startButton.getHeight()) + 10); + calibrationButtonBBox.setPosition(calibrationButton.getX(), calibrationButton.getY()); + autoButton.setPosition(-(autoButton.getWidth() / 2), (startButton.getY() - startButton.getHeight()) - 10); + autoButtonBBox.setPosition(autoButton.getX(), autoButton.getY()); + + // Set leds. + ledYPos = -(Utils.getScreenHeightWithOverscan() / 2) + 10; + cameraCalibratedLedOn.setSize(cameraCalibratedLedOn.getWidth() * 0.5f, cameraCalibratedLedOn.getHeight() * 0.5f); + cameraCalibratedLedOn.setPosition(-cameraCalibratedLedOn.getWidth() - 5, ledYPos); + cameraCalibratedLedOff.setSize(cameraCalibratedLedOff.getWidth() * 0.5f, cameraCalibratedLedOff.getHeight() * 0.5f); + cameraCalibratedLedOff.setPosition(-cameraCalibratedLedOff.getWidth() - 5, ledYPos); + assetsLoadedLedOn.setSize(assetsLoadedLedOn.getWidth() * 0.5f, assetsLoadedLedOn.getHeight() * 0.5f); + assetsLoadedLedOn.setPosition(5, ledYPos); + assetsLoadedLedOff.setSize(assetsLoadedLedOff.getWidth() * 0.5f, assetsLoadedLedOff.getHeight() * 0.5f); + assetsLoadedLedOff.setPosition(5, ledYPos); + } + + @Override + public void render(float delta){ + Gdx.gl.glClearColor(1, 1, 1, 1); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + + core.batch.setProjectionMatrix(pixelPerfectCamera.combined); + core.batch.begin();{ + + // Render background. + core.batch.disableBlending(); + drawBackground(core.batch); + core.batch.enableBlending(); + + // Render leds. + if(cameraCalibrated) cameraCalibratedLedOn.draw(core.batch); + else cameraCalibratedLedOff.draw(core.batch); + if(assetsLoaded) assetsLoadedLedOn.draw(core.batch); + else assetsLoadedLedOff.draw(core.batch); + + // Render buttons. + startButton.draw(core.batch, 1.0f); + calibrationButton.draw(core.batch, 1.0f); + autoButton.draw(core.batch, 1.0f); + + }core.batch.end(); + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/systems/AnimationSystem.java b/src/ve/ucv/ciens/ccg/nxtar/systems/AnimationSystem.java index 5e2f2a0..f314e9b 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/systems/AnimationSystem.java +++ b/src/ve/ucv/ciens/ccg/nxtar/systems/AnimationSystem.java @@ -16,6 +16,7 @@ package ve.ucv.ciens.ccg.nxtar.systems; import ve.ucv.ciens.ccg.nxtar.components.AnimationComponent; +import ve.ucv.ciens.ccg.nxtar.components.VisibilityComponent; import com.artemis.Aspect; import com.artemis.ComponentMapper; @@ -25,25 +26,44 @@ import com.artemis.systems.EntityProcessingSystem; import com.badlogic.gdx.Gdx; public class AnimationSystem extends EntityProcessingSystem { + public static final int NO_ANIMATION = -1; + @Mapper ComponentMapperA matrix representing 3D translations.
+ */ + private Matrix4 translationMatrix; + + /** + *A matrix representing 3D rotations.
+ */ + private Matrix4 rotationMatrix; + + /** + *A matrix representing 3D scalings.
+ */ + private Matrix4 scalingMatrix; + + @SuppressWarnings("unchecked") + public GeometrySystem(){ + super(Aspect.getAspectForAll(GeometryComponent.class).one(RenderModelComponent.class, CollisionModelComponent.class)); + + translationMatrix = new Matrix4().setToTranslation(0.0f, 0.0f, 0.0f); + rotationMatrix = new Matrix4().idt(); + scalingMatrix = new Matrix4().setToScaling(0.0f, 0.0f, 0.0f); + } + + @Override + protected void process(Entity e) { + GeometryComponent geometry; + RenderModelComponent renderModel; + CollisionModelComponent colModel; + + geometry = geometryMapper.get(e); + renderModel = renderModelMapper.getSafe(e); + colModel = colModelMapper.getSafe(e); + + if(renderModel != null) + applyWorldTransform(renderModel.instance, geometry); + if(colModel != null) + applyWorldTransform(colModel.instance, geometry); + } + + private void applyWorldTransform(ModelInstance model, GeometryComponent geometry){ + translationMatrix.setToTranslation(geometry.position); + + rotationMatrix.val[Matrix4.M00] = geometry.rotation.val[0]; + rotationMatrix.val[Matrix4.M10] = geometry.rotation.val[1]; + rotationMatrix.val[Matrix4.M20] = geometry.rotation.val[2]; + rotationMatrix.val[Matrix4.M30] = 0; + + rotationMatrix.val[Matrix4.M01] = geometry.rotation.val[3]; + rotationMatrix.val[Matrix4.M11] = geometry.rotation.val[4]; + rotationMatrix.val[Matrix4.M21] = geometry.rotation.val[5]; + rotationMatrix.val[Matrix4.M31] = 0; + + rotationMatrix.val[Matrix4.M02] = geometry.rotation.val[6]; + rotationMatrix.val[Matrix4.M12] = geometry.rotation.val[7]; + rotationMatrix.val[Matrix4.M22] = geometry.rotation.val[8]; + rotationMatrix.val[Matrix4.M32] = 0; + + rotationMatrix.val[Matrix4.M03] = 0; + rotationMatrix.val[Matrix4.M13] = 0; + rotationMatrix.val[Matrix4.M23] = 0; + rotationMatrix.val[Matrix4.M33] = 1; + + scalingMatrix.setToScaling(geometry.scaling); + + model.transform.idt().mul(translationMatrix).mul(rotationMatrix).mul(scalingMatrix); + model.calculateTransforms(); + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/systems/MarkerPositioningSystem.java b/src/ve/ucv/ciens/ccg/nxtar/systems/MarkerPositioningSystem.java index bfde1da..8f71a94 100644 --- a/src/ve/ucv/ciens/ccg/nxtar/systems/MarkerPositioningSystem.java +++ b/src/ve/ucv/ciens/ccg/nxtar/systems/MarkerPositioningSystem.java @@ -17,6 +17,7 @@ package ve.ucv.ciens.ccg.nxtar.systems; import ve.ucv.ciens.ccg.nxtar.components.GeometryComponent; import ve.ucv.ciens.ccg.nxtar.components.MarkerCodeComponent; +import ve.ucv.ciens.ccg.nxtar.components.VisibilityComponent; import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor.MarkerData; import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants; @@ -25,20 +26,17 @@ import com.artemis.ComponentMapper; import com.artemis.Entity; import com.artemis.annotations.Mapper; import com.artemis.systems.EntityProcessingSystem; -import com.badlogic.gdx.Gdx; public class MarkerPositioningSystem extends EntityProcessingSystem { @Mapper ComponentMapperA matrix representing 3D translations.
- */ - private Matrix4 translationMatrix; - - /** - *A matrix representing 3D rotations.
- */ - private Matrix4 rotationMatrix; - - /** - *A matrix representing 3D scalings.
- */ - private Matrix4 scalingMatrix; - - private MarkerData markers; + @Mapper ComponentMapperEntity processing system in charge of rendering 3D objects using OpenGL. The * entities to be rendered must have a geometry, shader and mesh component associated.
*/ public class ObjectRenderingSystem extends EntityProcessingSystem { - @Mapper ComponentMapperA matrix representing 3D translations.
- */ - private Matrix4 translationMatrix; - - /** - *A matrix representing 3D rotations.
- */ - private Matrix4 rotationMatrix; - - /** - *A matrix representing 3D scalings.
- */ - private Matrix4 scalingMatrix; - private PerspectiveCamera camera; - private ModelBatch batch; @SuppressWarnings("unchecked") public ObjectRenderingSystem(ModelBatch batch) { - super(Aspect.getAspectForAll(GeometryComponent.class, ShaderComponent.class, ModelComponent.class, EnvironmentComponent.class).exclude(MarkerCodeComponent.class)); + super(Aspect.getAspectForAll(ShaderComponent.class, RenderModelComponent.class, EnvironmentComponent.class).exclude(MarkerCodeComponent.class)); - camera = null; - this.batch = batch; - translationMatrix = new Matrix4().setToTranslation(0.0f, 0.0f, 0.0f); - rotationMatrix = new Matrix4().idt(); - scalingMatrix = new Matrix4().setToScaling(0.0f, 0.0f, 0.0f); + camera = null; + this.batch = batch; } public void begin(PerspectiveCamera camera) throws RuntimeException{ @@ -83,33 +61,18 @@ public class ObjectRenderingSystem extends EntityProcessingSystem { camera = null; } - /** - *Renders the entity passed by parameter, calculating it's corresponding geometric - * transformation and setting and calling it's associated shader program.
- * - * @param e The entity to be processed. - */ @Override protected void process(Entity e) { EnvironmentComponent environment; - GeometryComponent geometryComponent; ShaderComponent shaderComponent; - ModelComponent modelComponent; + RenderModelComponent renderModelComponent; // Get the necessary components. - geometryComponent = geometryMapper.get(e); - modelComponent = modelMapper.get(e); - shaderComponent = shaderMapper.get(e); - environment = environmentMapper.get(e); - - // Calculate the geometric transformation for this entity. - translationMatrix.setToTranslation(geometryComponent.position); - rotationMatrix.set(geometryComponent.rotation); - scalingMatrix.setToScaling(geometryComponent.scaling); - modelComponent.instance.transform.idt().mul(translationMatrix).mul(rotationMatrix).mul(scalingMatrix); - modelComponent.instance.calculateTransforms(); + renderModelComponent = modelMapper.get(e); + shaderComponent = shaderMapper.get(e); + environment = environmentMapper.get(e); // Render this entity. - batch.render(modelComponent.instance, environment.environment, shaderComponent.shader); + batch.render(renderModelComponent.instance, environment.environment, shaderComponent.shader); } } diff --git a/src/ve/ucv/ciens/ccg/nxtar/systems/PlayerSystemBase.java b/src/ve/ucv/ciens/ccg/nxtar/systems/PlayerSystemBase.java new file mode 100644 index 0000000..a3e6caf --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/systems/PlayerSystemBase.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.systems; + +import ve.ucv.ciens.ccg.nxtar.NxtARCore; +import ve.ucv.ciens.ccg.nxtar.components.PlayerComponentBase; +import ve.ucv.ciens.ccg.nxtar.scenarios.ScenarioGlobals; +import ve.ucv.ciens.ccg.nxtar.scenarios.SummaryBase; + +import com.artemis.Aspect; +import com.artemis.systems.EntityProcessingSystem; +import com.badlogic.gdx.utils.Disposable; + +public abstract class PlayerSystemBase extends EntityProcessingSystem implements Disposable{ + protected NxtARCore core; + + @SuppressWarnings("unchecked") + public PlayerSystemBase(Class extends PlayerComponentBase> component){ + super(Aspect.getAspectForAll(component)); + } + + public abstract SummaryBase getPlayerSummary(); + + public final void setCore(NxtARCore core) throws IllegalArgumentException{ + if(core == null) + throw new IllegalArgumentException("Core is null."); + + this.core = core; + } + + protected final void finishGame(boolean victory) throws IllegalStateException{ + if(core == null) + throw new IllegalStateException("Core is null."); + + ScenarioGlobals.getEntityCreator().resetAllEntities(); + core.nextState = NxtARCore.game_states_t.SCENARIO_END_SUMMARY; + } +} diff --git a/src/ve/ucv/ciens/ccg/nxtar/systems/RobotArmPositioningSystem.java b/src/ve/ucv/ciens/ccg/nxtar/systems/RobotArmPositioningSystem.java new file mode 100644 index 0000000..daab97a --- /dev/null +++ b/src/ve/ucv/ciens/ccg/nxtar/systems/RobotArmPositioningSystem.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2014 Miguel Angel Astor Romero + * + * 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 ve.ucv.ciens.ccg.nxtar.systems; + +import ve.ucv.ciens.ccg.nxtar.components.AutomaticMovementComponent; +import ve.ucv.ciens.ccg.nxtar.components.CollisionDetectionComponent; +import ve.ucv.ciens.ccg.nxtar.components.GeometryComponent; +import ve.ucv.ciens.ccg.nxtar.components.MarkerCodeComponent; +import ve.ucv.ciens.ccg.nxtar.input.GamepadUserInput; +import ve.ucv.ciens.ccg.nxtar.input.KeyboardUserInput; +import ve.ucv.ciens.ccg.nxtar.input.TouchUserInput; +import ve.ucv.ciens.ccg.nxtar.input.UserInput; +import ve.ucv.ciens.ccg.nxtar.utils.Utils; + +import com.artemis.Aspect; +import com.artemis.ComponentMapper; +import com.artemis.Entity; +import com.artemis.annotations.Mapper; +import com.artemis.systems.EntityProcessingSystem; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.controllers.mappings.Ouya; +import com.badlogic.gdx.math.Vector3; + +public class RobotArmPositioningSystem extends EntityProcessingSystem { + private static final String TAG = "ROBOT_ARM_POSITIONING_SYSTEM"; + private static final String CLASS_NAME = RobotArmPositioningSystem.class.getSimpleName(); + private static final float INTERPOLATION_STEP = 0.05f; + private static final float STEP_SIZE = 0.05f; + private static final float MAX_Z = -4.5f; + private static final float MIN_Z = -1.0f; + + @Mapper ComponentMapperConverts a libGDX {@link Vector3} to a String representation form easy logging.
+ * + * @param v The vector to convert. + * @return A string representation of the form "(v.x, v.y, v.z)". + */ + public static String vector2String(Vector3 v){ + return "(" + Float.toString(v.x) + ", " + Float.toString(v.y) + ", " + Float.toString(v.z) + ")"; + } + + /** + * @return The width of the screen accounting for screen overscan. + */ + public static int getScreenWidthWithOverscan(){ + return (int)(Gdx.graphics.getWidth() * ProjectConstants.OVERSCAN); + } + + /** + * @return The height of the screen accounting for screen overscan. + */ + public static int getScreenHeightWithOverscan(){ + return (int)(Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN); + } + + /** + *Checks if the running device posseses and accelerometer and compass.
+ * + * @return True when the device supports both sensors. False otherwise. + */ + public static boolean deviceHasOrientationSensors(){ + return Gdx.input.isPeripheralAvailable(Peripheral.Accelerometer) && Gdx.input.isPeripheralAvailable(Peripheral.Compass); + } + + /** + *Checks if the device's orientation is available and wihtin some arbitrary ranges.
+ * + * @return True if the device can detect it's orientation and it's within range. False otherwise. + */ + public static boolean isDeviceRollValid(){ + boolean rollValid = true; + float azimuth, pitch; + + if(deviceHasOrientationSensors()){ + azimuth = Gdx.input.getAzimuth(); + pitch = Gdx.input.getPitch(); + + if(pitch < MIN_PITCH || pitch > MAX_PITCH) + rollValid = false; + + if(rollValid && (azimuth < MIN_AZIMUTH || azimuth > MAX_AZIMUTH)) + rollValid = false; + }else + rollValid = false; + + return rollValid; + } +}