60 Commits

Author SHA1 Message Date
fa6950d5c9 Merge branch 'develop' 2014-05-27 14:42:38 -04:30
5e66273396 Added animation support. 2014-05-27 14:41:06 -04:30
686f263b0d Added support for gpu skinning. Added animation system. 2014-05-27 13:45:58 -04:30
5ef7928706 Refactored rendering to use modelbatch everywhere. 2014-05-26 16:42:57 -04:30
169d0e7c43 Started using custom shaders with model batch. 2014-05-23 17:00:14 -04:30
9a67df8f8a Removed old code. 2014-05-22 18:03:48 -04:30
76a6d8485b Removed libs. 2014-05-22 17:42:35 -04:30
653adc91a7 Updated libgdx to 1.0.1 2014-05-22 17:41:58 -04:30
a1891f53d2 Changed model rendering to model batch. 2014-05-22 16:50:24 -04:30
a942b0ebd1 Changed references to GL10. 2014-05-21 16:18:49 -04:30
e6085e7a79 Testing model loading. 2014-05-20 20:15:34 -04:30
3c65f52e9e Added some more test cases. Fixed a bug when rendering markers. 2014-05-15 16:58:23 -04:30
0df8b37ddd Merge branch 'develop' 2014-05-15 12:10:47 -04:30
87295031dc Rendering of objects on top of markers is complete. 2014-05-15 12:10:14 -04:30
a9fa76cb68 Markers rendered almost sucessfully. 2014-05-14 16:56:31 -04:30
fbb25ead08 Added marker positioning and rendering. Not tested yet. 2014-05-13 18:29:25 -04:30
82e95ed0f7 Added aditional geometric transformation info to rendering. Added entity creator. 2014-05-12 14:30:24 -04:30
b7367427f6 Successfully incorporated Artemis. 2014-05-12 11:37:58 -04:30
e6ea6ab4a1 Added comments. 2014-05-09 17:00:21 -04:30
167b5d644b Some refactorings and touch ups. 2014-05-09 11:30:45 -04:30
e82d49f1f1 Testing the shader. 2014-05-09 10:46:47 -04:30
6b54b1364f Fixed test rendering problems. 2014-05-08 16:21:30 -04:30
de64cd972b Successfully rendered a 3D sphere. 2014-05-07 16:41:25 -04:30
90427419e0 Frame buffer object correctly created and positioned. 2014-05-06 16:35:19 -04:30
271b7d003a Started sketching the 3D rendering. 2014-05-05 15:25:01 -04:30
1146a260b6 Added intradocumentation. 2014-05-05 12:32:06 -04:30
d8922182e0 Camera calibration successfully ported. 2014-05-02 13:59:15 -04:30
e976a17de0 Added menu transition to calibration state. 2014-05-02 11:16:40 -04:30
11ebf2b96e Continued implementation of the calibration state and interface. 2014-04-30 16:21:03 -04:30
139bb62067 Started programming the calibration state. 2014-04-28 10:14:01 -04:30
e93d227b62 Updated the interfaces and renamed some methods. 2014-04-10 17:57:31 -04:30
06d74a6474 Added some logging. 2014-04-05 15:05:20 -04:30
d481df0f96 Merge branch 'develop' 2014-04-04 10:27:33 -04:30
1e2de5ee55 Added recenter button and corresponding event handling. 2014-04-04 10:26:56 -04:30
a37ff0fba5 Added a sensor data receiving thread. 2014-04-03 11:28:30 -04:30
ebb943925f Renamed the project. 2014-03-31 11:02:20 -04:30
ae93dca1b9 Merge branch 'develop' 2014-03-14 15:16:50 -04:30
ddbadba3ea Added OUYA gamepad support. 2014-03-14 15:16:21 -04:30
28be9c8a81 Flipped motor buttons on tablet iface. Added NO_DELAY to robot control. 2014-03-13 16:12:53 -04:30
8ed8be0a29 OpenCV marker detection successfully ported. 2014-03-12 16:35:52 -04:30
c7ee0e0af6 Added CVProcessor interface. 2014-03-11 18:32:46 -04:30
0fdef7c01c Merge branch 'develop' 2014-02-11 17:46:28 -04:30
6690d7b3fc Merge branch 'robot_control' into develop 2014-02-11 17:45:37 -04:30
97b27f736a Fixed robot control logic. Added gamepad support (not tested). 2014-02-11 17:44:54 -04:30
5816e77e36 RobotControlThread sends data. 2014-02-10 15:27:27 -04:30
5d80d5f866 Added state transitions with Universal Tween Library. 2014-02-07 17:02:53 -04:30
be1a392c4a Basic main menu finished. 2014-02-07 14:10:10 -04:30
39426cd2a6 Fixed OuyaMainMenuState. 2014-02-06 22:34:37 -04:30
e88305f0ff Refactoring finished. 2014-02-06 18:56:20 -04:30
40d390baec Started refactoring the app to use State Pattern. 2014-02-05 17:06:29 -04:30
5bd79f0d58 Deleted useless Artemis classes. 2014-02-05 15:10:02 -04:30
4893313116 Merge branch 'robot_control' into develop 2014-02-05 15:07:38 -04:30
unknown
c5fe36116f Minor refactorings. 2014-02-04 17:14:35 -04:30
5c795ce8e3 Got this thing to render on Ouya. 2014-01-29 03:30:40 -04:30
e5b43beaea VideoStreamingThread now reports the number of lost frames per second. 2014-01-28 19:14:35 -04:30
bfea25e95e Added gui buttons. Removed tcp connection for video code. 2014-01-24 12:25:58 -04:30
0b790bce8f Removed the TCP video streaming code. 2014-01-21 12:20:38 -04:30
b937b93767 Minor cosmetic touches. 2014-01-20 13:00:29 -04:30
da044c5b1c Changed the streaming to use UDP. 2014-01-15 14:50:54 -04:30
c6f310884a Testing video streaming framerate. 2014-01-13 14:33:35 -04:30
50 changed files with 4926 additions and 485 deletions

1
.gitignore vendored
View File

@@ -18,6 +18,7 @@ local.properties
# Eclipse project files
.classpath
.project
libs/
# Proguard folder generated by Eclipse
proguard/

4
README.md Normal file
View File

@@ -0,0 +1,4 @@
NxtAR-core
==========
Modulo 2 de mi trabajo especial de grado.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,37 @@
package ve.ucv.ciens.ccg.networkdata;
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};
private motor_t motor;
private byte power;
public MotorEvent(){
motor = motor_t.NONE;
power = 0;
}
public void setMotor(motor_t motor){
this.motor = motor;
}
public void setPower(byte power) throws IllegalArgumentException{
if(power > 100 || power < -100){
throw new IllegalArgumentException("Motor power must be a number between -100 and 100");
}else{
this.power = power;
}
}
public motor_t getMotor(){
return this.motor;
}
public byte getPower(){
return this.power;
}
}

View File

@@ -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.networkdata;
import java.io.Serializable;
public class MotorEventACK implements Serializable {
private static final long serialVersionUID = 9989L;
private boolean clientQueueIsFull;
public MotorEventACK(boolean isQueueFull){
this.clientQueueIsFull = isQueueFull;
}
public boolean isClientQueueFull(){
return this.clientQueueIsFull;
}
}

View File

@@ -15,155 +15,468 @@
*/
package ve.ucv.ciens.ccg.nxtar;
import ve.ucv.ciens.ccg.nxtar.interfaces.MulticastEnabler;
import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
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.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.VideoFrameMonitor;
import ve.ucv.ciens.ccg.nxtar.network.VideoStreamingThread;
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.MainMenuStateBase;
import ve.ucv.ciens.ccg.nxtar.states.OuyaMainMenuState;
import ve.ucv.ciens.ccg.nxtar.states.PauseState;
import ve.ucv.ciens.ccg.nxtar.states.TabletMainMenuState;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import ve.ucv.ciens.ccg.nxtar.utils.Size;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenEquations;
import aurelienribon.tweenengine.primitives.MutableFloat;
import com.badlogic.gdx.Application;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.controllers.Controllers;
import com.badlogic.gdx.controllers.mappings.Ouya;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
public class NxtARCore implements ApplicationListener, NetworkConnectionListener {
/**
* <p>Core of the application.</p>
*
* <p>This class has three basic resposibilities:</p>
* <ul>
* <li> Handling the main game loop.</li>
* <li> Starting and destroying the networking threads.</li>
* <li> Rendering debug information.</li>
* </ul>
*/
public class NxtARCore extends Game implements ApplicationEventsListener{
/**
* Tag used for logging.
*/
private static final String TAG = "NXTAR_CORE_MAIN";
/**
* Class name used for logging.
*/
private static final String CLASS_NAME = NxtARCore.class.getSimpleName();
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture texture;
private Sprite sprite;
private Toaster toaster;
private MulticastEnabler mcastEnabler;
/**
* Valid game states.
*/
public enum game_states_t {
MAIN_MENU(0), IN_GAME(1), PAUSED(2), CALIBRATION(3);
private int value;
private game_states_t(int value){
this.value = value;
}
public int getValue(){
return this.value;
}
public static int getNumStates(){
return 4;
}
};
/**
* The current application state.
*/
private game_states_t currState;
/**
* <p>The state to change to.</p>
* <p> Usually null. A state change is scheduled by setting this field to a {@link game_states_t} value.</p>
*/
public game_states_t nextState;
// Screens.
/**
* <p>The application states.</p>
*/
private BaseState[] states;
// Assorted fields.
/**
* <p>Global sprite batch used for rendering trough the application.</p>
*/
public SpriteBatch batch;
/**
* <p>The OpenCV wrapper.</p>
*/
public ImageProcessor cvProc;
/**
* <p>Wrapper around the Operating System methods.</p>
*/
private ActionResolver osFunction;
// Networking related fields.
/**
* <p>The number of connections successfully established with the NxtAR-cam application.</p>
*/
private int connections;
private VideoFrameMonitor frameMonitor;
private ServiceDiscoveryThread udpThread;
/**
* <p>Worker thread used to broadcast this server over the network.</p>
*/
private ServiceDiscoveryThread serviceDiscoveryThread;
/**
* <p>Worker thread used to receive video frames over UDP.<p>
*/
private VideoStreamingThread videoThread;
/**
* <p>Worker thread used to send control commands to the NxtAR-cam application.
*/
private RobotControlThread robotThread;
/**
* <p>Worker thread used to receive sensor data from the NxtAR-cam application.</p>
*/
private SensorReportThread sensorThread;
// Overlays.
/**
* <p>Camera used to render the debugging overlay.</p>
*/
private OrthographicCamera pixelPerfectCamera;
/**
* <p>The base x coordinate for rendering the debugging overlay.</p>
*/
private float overlayX;
/**
* <p>The base y coordinate for rendering the debugging overlay.</p>
*/
private float overlayY;
/**
* <p>The font used to render the debugging overlay.</p>
*/
private BitmapFont font;
// Fade in/out effect fields.
/**
* <p>The graphic used to render the fading effect.</p>
*/
private Texture fadeTexture;
/**
* <p>The interpolation value for the fading effect.</p>
*/
private MutableFloat alpha;
/**
* <p>The fade out interpolator.</p>
*/
private Tween fadeOut;
/**
* <p>The fade in interpolator.</p>
*/
private Tween fadeIn;
/**
* <p>Flag used to indicate if a fading effect is active.</p>
*/
private boolean fading;
/**
* <p>Set up the basic application fields.</p>
*/
public NxtARCore(Application concreteApp){
super();
connections = 0;
// Check if the concrete application implements all required interfaces.
try{
this.toaster = (Toaster)concreteApp;
this.mcastEnabler = (MulticastEnabler)concreteApp;
this.osFunction = (ActionResolver)concreteApp;
}catch(ClassCastException cc){
Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement any of the required interfaces.");
System.exit(ProjectConstants.EXIT_FAILURE);
Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement the Toaster interface. Toasting disabled.");
this.osFunction = null;
}
try{
this.cvProc = (ImageProcessor)concreteApp;
}catch(ClassCastException cc){
Gdx.app.error(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement the CVProcessor interface. Quitting.");
Gdx.app.exit();
}
}
@Override
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GAME SUPERCLASS METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
/**
* <p>Initialize the member fields and launch the networking threads. Also creates and
* sets the application states.</p>
*/
public void create(){
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
// 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);
Gdx.app.setLogLevel(Application.LOG_DEBUG);
// Register controller listeners.
for(BaseState state : states){
Controllers.addListener(state);
}
camera = new OrthographicCamera(1, h/w);
// Set up rendering fields and settings.
batch = new SpriteBatch();
batch.enableBlending();
batch.setBlendFunction(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
texture = new Texture(Gdx.files.internal("data/libgdx.png"));
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
ShaderProgram.pedantic = false;
sprite = new Sprite(region);
sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
// Set up the overlay font.
if(ProjectConstants.DEBUG){
overlayX = -((Gdx.graphics.getWidth() * ProjectConstants.OVERSCAN) / 2) + 10;
overlayY = ((Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN) / 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();
Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads");
frameMonitor = VideoFrameMonitor.getInstance();
mcastEnabler.enableMulticast();
udpThread = ServiceDiscoveryThread.getInstance();
videoThread = VideoStreamingThread.getInstance().setToaster(toaster);
//robotThread = RobotControlThread.getInstance().setToaster(toaster);
serviceDiscoveryThread = ServiceDiscoveryThread.getInstance();
videoThread = VideoStreamingThread.getInstance();
robotThread = RobotControlThread.getInstance();
sensorThread = SensorReportThread.getInstance();
// Launch networking threads.
serviceDiscoveryThread.start();
udpThread.start();
videoThread.start();
videoThread.startStreaming();
//robotThread.start();
}
videoThread.addNetworkConnectionListener(this);
@Override
public void dispose() {
batch.dispose();
texture.dispose();
}
robotThread.addNetworkConnectionListener(this);
robotThread.start();
@Override
public void render(){
Pixmap image;
Pixmap temp;
byte[] frame;
Size dimensions;
sensorThread.addNetworkConnectionListener(this);
sensorThread.start();
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// Set the current and next states.
currState = game_states_t.MAIN_MENU;
nextState = null;
this.setScreen(states[currState.getValue()]);
states[currState.getValue()].onStateSet();
frame = frameMonitor.getCurrentFrame();
if(frame != null){
texture.dispose();
// Prepare the fading effect.
Pixmap pixmap = new Pixmap(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), Format.RGBA4444);
pixmap.setColor(0, 0, 0, 1);
pixmap.fill();
fadeTexture = new Texture(pixmap);
pixmap.dispose();
dimensions = frameMonitor.getFrameDimensions();
temp = new Pixmap(frame, 0, dimensions.getWidth() * dimensions.getHeight());
image = new Pixmap(1024, 512, temp.getFormat());
image.drawPixmap(temp, 0, 0);
texture = new Texture(image);
texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
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);
TextureRegion region = new TextureRegion(texture, 0, 0, dimensions.getWidth(), dimensions.getHeight());
fading = false;
sprite = new Sprite(region);
sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
// Set initial input handlers.
Gdx.input.setInputProcessor(states[currState.getValue()]);
Controllers.addListener(states[currState.getValue()]);
batch.setProjectionMatrix(camera.combined);
batch.begin();{
sprite.draw(batch);
}batch.end();
texture.dispose();
temp.dispose();
image.dispose();
// Set log level
if(ProjectConstants.DEBUG){
Gdx.app.setLogLevel(Application.LOG_DEBUG);
}else{
Gdx.app.setLogLevel(Application.LOG_NONE);
}
}
@Override
public void resize(int width, int height){
/**
* <p>Update and render the currently enabled application state. This method
* also handles state switching, rendering state transitions and global overlays.</p>
*/
public void render(){
super.render();
// If the current state set a value for nextState then switch to that state.
if(nextState != null){
states[currState.getValue()].onStateUnset();
if(!fadeOut.isStarted()){
// Start the fade out effect.
fadeOut.start();
fading = true;
}else{
// Update the fade out effect.
fadeOut.update(Gdx.graphics.getDeltaTime());
// When the fade out effect finishes, change to the requested state
// and launh the fade in effect.
if(fadeOut.isFinished()){
// Change to the requested state.
currState = nextState;
nextState = null;
states[currState.getValue()].onStateSet();
setScreen(states[currState.getValue()]);
// Reset the fade out effect and launch the fade in.
Gdx.app.log(TAG, CLASS_NAME + ".onRender() :: Freeing fade out.");
fadeOut.free();
fadeOut = Tween.to(alpha, 0, 0.5f).target(1.0f).ease(TweenEquations.easeInQuint);
fadeIn.start();
}
}
}
// If there is a fade in effect in progress.
if(fadeIn.isStarted()){
if(!fadeIn.isFinished()){
// Update it until finished.
fadeIn.update(Gdx.graphics.getDeltaTime());
}else{
// Stop and reset it when done.
fading = false;
fadeIn.free();
fadeIn = Tween.to(alpha, 0, 0.5f).target(0.0f).ease(TweenEquations.easeInQuint);
}
}
// Render the fading sprite with alpha blending.
if(fading){
batch.setProjectionMatrix(pixelPerfectCamera.combined);
batch.begin();{
batch.setColor(1, 1, 1, alpha.floatValue());
batch.draw(fadeTexture, -(Gdx.graphics.getWidth() / 2), -(Gdx.graphics.getHeight() / 2));
batch.setColor(1, 1, 1, 1);
}batch.end();
}
// Render the debug overlay.
if(ProjectConstants.DEBUG){
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("Light sensor data: %d", sensorThread.getLightSensorReading()), overlayX, overlayY - (3 * font.getCapHeight()) - 15);
}batch.end();
}
}
@Override
/**
* <p>Pause a currently running thread. Pausing an already paused thread is a
* no op.</p>
*/
public void pause(){
if(videoThread != null)
videoThread.pause();
// TODO: Ignore pausing paused threads.
// TODO: Pause the other threads.
}
@Override
/**
* <p>Resume a currently paused thread. Resuming an already resumed thread is a
* no op.</p>
*/
public void resume(){
if(videoThread != null)
videoThread.play();
// TODO: Ignore resuming resumed threads.
// TODO: Resume the other threads.
}
/**
* <p>Clear graphic resources</p>
*/
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();
}
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; APPLICATION EVENTS LISTENER INTERFACE METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
// TODO: Disable start game button until camera has been sucessfully calibrated.
// TODO: Add calibration listener callback.
/**
* <p>Callback used by the networking threads to notify sucessfull connections
* to the application</p>
*
* @param streamName The name of the thread notifying a connection.
*/
@Override
public synchronized void networkStreamConnected(String streamName){
if(streamName.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || streamName.compareTo(RobotControlThread.THREAD_NAME) == 0)
connections += 1;
if(connections >= 2){
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.");
udpThread.finish();
mcastEnabler.disableMulticast();
serviceDiscoveryThread.finish();
osFunction.disableMulticast();
osFunction.showShortToast("Client connected");
((MainMenuStateBase)states[game_states_t.MAIN_MENU.getValue()]).onClientConnected();
}
}
/*;;;;;;;;;;;;;;;;;;
; HELPER METHODS ;
;;;;;;;;;;;;;;;;;;*/
/**
* <p>Show a toast message on screen using the O.S. functionality
* provider.</p>
* @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);
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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 java.util.LinkedList;
import java.util.List;
import com.artemis.Component;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.utils.AnimationController;
public class AnimationComponent extends Component {
public AnimationController controller;
public List<String> animationsIds;
public int current;
public int next;
public boolean loop;
public AnimationComponent(ModelInstance instance) throws IllegalArgumentException{
this(instance, -1, false);
}
public AnimationComponent(ModelInstance instance, int next) throws IllegalArgumentException{
this(instance, next, false);
}
public AnimationComponent(ModelInstance instance, int next, boolean loop) throws IllegalArgumentException{
if(instance == null)
throw new IllegalArgumentException("Instance is null.");
else if(next < 0)
throw new IllegalArgumentException("Next is less than 0.");
else if(next > instance.animations.size)
throw new IllegalArgumentException("Next is greater than the number of animations for this model.");
controller = new AnimationController(instance);
animationsIds = new LinkedList<String>();
current = -1;
this.next = next;
this.loop = loop;
for(int i = 0; i < instance.animations.size; i++){
animationsIds.add(instance.animations.get(i).id);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Miguel Angel Astor Romero
* 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.
@@ -15,26 +15,17 @@
*/
package ve.ucv.ciens.ccg.nxtar.components;
import ve.ucv.ciens.ccg.nxtar.graphics.shaders.CustomShaderBase;
import com.artemis.Component;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
public class VideoFrame extends Component {
private Texture frame;
public class CustomShaderComponent extends Component {
public CustomShaderBase shader;
public VideoFrame(){ }
public CustomShaderComponent(CustomShaderBase shader) throws IllegalArgumentException{
if(shader == null)
throw new IllegalArgumentException("Shader cannot be null.");
public VideoFrame(byte[] imageData){
frame = new Texture(new Pixmap(imageData, 0, imageData.length));
}
public Texture getFrame(){
return frame;
}
public void setFrame(byte[] imageData){
if(frame != null)
frame.dispose();
frame = new Texture(new Pixmap(imageData, 0, imageData.length));
this.shader = shader;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Miguel Angel Astor Romero
* 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.
@@ -13,33 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ve.ucv.ciens.ccg.nxtar.states;
package ve.ucv.ciens.ccg.nxtar.components;
/**
* Empty state.
*
* Completely empty state for debugging purposes.
*
* @author miky
*/
public class DummyState implements NxtARState{
import com.artemis.Component;
import com.badlogic.gdx.graphics.g3d.Environment;
@Override
public void input(){ }
public class EnvironmentComponent extends Component {
public Environment environment;
@Override
public void update(){ }
@Override
public void render(){ }
@Override
public void pause(){ }
@Override
public void resume(){ }
@Override
public void dispose(){ }
public EnvironmentComponent(Environment environment) throws IllegalArgumentException{
if(environment == null)
throw new IllegalArgumentException("Environment is null.");
this.environment = environment;
}
}

View File

@@ -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;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Vector3;
public class GeometryComponent extends Component {
public Vector3 position;
public Matrix3 rotation;
public Vector3 scaling;
public GeometryComponent(){
this.position = new Vector3();
this.rotation = new Matrix3();
this.scaling = new Vector3(1.0f, 1.0f, 1.0f);
}
public GeometryComponent(Vector3 position, Matrix3 rotation, Vector3 scaling){
this.position = new Vector3(position);
this.rotation = new Matrix3(rotation);
this.scaling = new Vector3(scaling);
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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;
public class MarkerCodeComponent extends Component {
public int code;
public MarkerCodeComponent(int code) throws IllegalArgumentException{
if(code < 0 || code > 1024)
throw new IllegalArgumentException("Marker code must be between [0, 1024].");
this.code = code;
}
}

View File

@@ -0,0 +1,27 @@
/*
* 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;
import com.badlogic.gdx.graphics.Mesh;
public class MeshComponent extends Component {
public Mesh model;
public MeshComponent(Mesh model){
this.model = model;
}
}

View File

@@ -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.components;
import com.artemis.Component;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
public class ModelComponent extends Component {
public ModelInstance instance;
public ModelComponent(Model model) throws IllegalArgumentException{
if(model == null)
throw new IllegalArgumentException("Model is null.");
this.instance = new ModelInstance(model);
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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;
import com.badlogic.gdx.graphics.g3d.Shader;
public class ShaderComponent extends Component{
public Shader shader;
public ShaderComponent(Shader shader) throws IllegalArgumentException{
if(shader == null)
throw new IllegalArgumentException("Shader is null.");
this.shader = shader;
}
}

View File

@@ -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.entities;
public class BombGameEntityCreator extends EntityCreatorBase {
public BombGameEntityCreator(){
// TODO: Empty constructor.
}
@Override
public void createAllEntities() {
// TODO Auto-generated method stub
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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 com.artemis.World;
public abstract class EntityCreatorBase {
protected World world;
public void setWorld(World world) throws IllegalArgumentException{
if(world == null)
throw new IllegalArgumentException("World cannot be null.");
this.world = world;
}
public abstract void createAllEntities();
public abstract void dispose();
}

View File

@@ -0,0 +1,127 @@
/*
* 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();
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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();
}
}

View File

@@ -1,21 +1,24 @@
/*
* 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.interfaces;
public interface MulticastEnabler {
public void enableMulticast();
public void disableMulticast();
}
/*
* 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.exceptions;
public class ImageTooBigException extends Exception{
private static final long serialVersionUID = 9989L;
public ImageTooBigException(String msg){
super(msg);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Miguel Angel Astor Romero
* 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.
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ve.ucv.ciens.ccg.nxtar.states;
package ve.ucv.ciens.ccg.nxtar.exceptions;
public interface NxtARState{
public void input();
public void update();
public void render();
public void pause();
public void resume();
public void dispose();
public class ShaderFailedToLoadException extends Exception {
private static final long serialVersionUID = 9989L;
public ShaderFailedToLoadException(String msg){
super(msg);
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.PerspectiveCamera;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
/**
* <p>Extension of the standard LibGDX perspective camera that allows setting an
* arbitrary projection matrix when updating.</p>
*/
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;
update();
}
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);
if(updateFrustum){
invProjectionView.set(combined);
Matrix4.inv(invProjectionView.val);
frustum.update(invProjectionView);
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* 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;
/**
* <p>A 3D light source.</p>
*/
public class LightSource{
private Vector3 position;
private Color ambientColor;
private Color diffuseColor;
private Color specularColor;
private float shinyness;
/**
* <p>Creates a default white light source positioned at (0,0,0).</p>
*/
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;
}
/**
* <p>Creates a white light source at the specified position.</p>
*
* @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;
}
/**
* <p>Creates a custom light source.</p>
*
* @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;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
}
}

View File

@@ -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.graphics.shaders;
import ve.ucv.ciens.ccg.nxtar.exceptions.ShaderFailedToLoadException;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
public abstract class CustomShaderBase{
protected ShaderProgram shaderProgram;
public abstract CustomShaderBase loadShader() throws ShaderFailedToLoadException;
public abstract void setUniforms();
public ShaderProgram getShaderProgram(){
return this.shaderProgram;
}
}

View File

@@ -0,0 +1,230 @@
/*
* 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 com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g3d.Renderable;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.FloatAttribute;
import com.badlogic.gdx.graphics.g3d.utils.RenderContext;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.GdxRuntimeException;
public class SingleLightPerPixelShader 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 ShaderProgram skinningProgram;
private ShaderProgram baseProgram;
private Camera camera;
private RenderContext context;
private Matrix4 normalMatrix;
// Uniform locations.
private int[] u_geomTrans;
private int[] u_projTrans;
private int[] u_lightPos;
private int[] u_lightDiffuse;
private int[] u_specular;
private int[] u_ambient;
private int[] u_shiny;
private int[] u_cameraPos;
private int[] u_materialDiffuse;
private int[] u_normalMatrix;
private int[] u_bones;
public SingleLightPerPixelShader(){
skinningProgram = null;
baseProgram = null;
camera = null;
context = null;
}
@Override
public void init() throws GdxRuntimeException{
normalMatrix = new Matrix4().idt();
u_bones = new int[MAX_NUM_BONES];
// Compile the shader.
String vertexCode = Gdx.files.internal(VERTEX_SHADER_PATH).readString();
String fragmentCode = Gdx.files.internal(FRAGMENT_SHADER_PATH).readString();
skinningProgram = new ShaderProgram(INCLUDE_SKINNING + vertexCode, fragmentCode);
baseProgram = new ShaderProgram(vertexCode, fragmentCode);
if(!skinningProgram.isCompiled())
throw new GdxRuntimeException(skinningProgram.getLog());
if(!baseProgram.isCompiled())
throw new GdxRuntimeException(skinningProgram.getLog());
// Create uniform locations.
u_projTrans = new int[2];
u_geomTrans = new int[2];
u_lightPos = new int[2];
u_lightDiffuse = new int[2];
u_specular = new int[2];
u_ambient = new int[2];
u_shiny = new int[2];
u_cameraPos = new int[2];
u_materialDiffuse = new int[2];
u_normalMatrix = new int[2];
// Cache uniform locations.
u_projTrans [0] = skinningProgram.getUniformLocation("u_projTrans");
u_geomTrans [0] = skinningProgram.getUniformLocation("u_geomTrans");
u_lightPos [0] = skinningProgram.getUniformLocation("u_lightPos");
u_lightDiffuse [0] = skinningProgram.getUniformLocation("u_lightDiffuse");
u_specular [0] = skinningProgram.getUniformLocation("u_specular");
u_ambient [0] = skinningProgram.getUniformLocation("u_ambient");
u_shiny [0] = skinningProgram.getUniformLocation("u_shiny");
u_cameraPos [0] = skinningProgram.getUniformLocation("u_cameraPos");
u_materialDiffuse [0] = skinningProgram.getUniformLocation("u_materialDiffuse");
u_normalMatrix [0] = skinningProgram.getUniformLocation("u_normalMatrix");
u_projTrans [1] = baseProgram.getUniformLocation("u_projTrans");
u_geomTrans [1] = baseProgram.getUniformLocation("u_geomTrans");
u_lightPos [1] = baseProgram.getUniformLocation("u_lightPos");
u_lightDiffuse [1] = baseProgram.getUniformLocation("u_lightDiffuse");
u_specular [1] = baseProgram.getUniformLocation("u_specular");
u_ambient [1] = baseProgram.getUniformLocation("u_ambient");
u_shiny [1] = baseProgram.getUniformLocation("u_shiny");
u_cameraPos [1] = baseProgram.getUniformLocation("u_cameraPos");
u_materialDiffuse [1] = baseProgram.getUniformLocation("u_materialDiffuse");
u_normalMatrix [1] = baseProgram.getUniformLocation("u_normalMatrix");
for(int i = 0; i < MAX_NUM_BONES; i++){
u_bones[i] = skinningProgram.getUniformLocation("u_bone" + Integer.toString(i));
}
}
@Override
public void dispose(){
if(skinningProgram != null) skinningProgram.dispose();
if(baseProgram != null) baseProgram.dispose();
}
@Override
public int compareTo(Shader other){
return 0;
}
@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;
return true;
}
@Override
public void begin(Camera camera, RenderContext context) throws GdxRuntimeException{
if(this.camera != null || this.context != null)
throw new GdxRuntimeException("Called begin twice before calling end.");
this.camera = camera;
this.context = context;
// Set render context.
this.context.setDepthTest(GL20.GL_LEQUAL);
this.context.setDepthMask(true);
}
@Override
public void render(Renderable renderable){
ShaderProgram program;
int index;
boolean bonesEnabled;
// 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.mesh.getVertexAttribute(VertexAttributes.Usage.BoneWeight) != null){
program = skinningProgram;
index = 0;
bonesEnabled = true;
}else{
program = baseProgram;
index = 1;
bonesEnabled = false;
}
program.begin();
// Set camera dependant uniforms.
program.setUniformMatrix(u_projTrans[index], this.camera.combined);
program.setUniformf(u_cameraPos[index], this.camera.position);
// Set model dependant uniforms.
program.setUniformMatrix(u_geomTrans[index], renderable.worldTransform);
program.setUniformMatrix(u_normalMatrix[index], normalMatrix.idt().mul(renderable.worldTransform).inv().tra());
program.setUniformf(u_lightPos[index], lightPosition);
program.setUniformf(u_lightDiffuse[index], diffuseLightColor);
program.setUniformf(u_materialDiffuse[index], diffuseColor);
program.setUniformf(u_specular[index], specularColor);
program.setUniformf(u_ambient[index], ambientColor);
program.setUniformf(u_shiny[index], shininess);
// Set the bones uniforms.
if(bonesEnabled){
for(int i = 0; i < MAX_NUM_BONES; i++){
if(renderable.bones != null && i < renderable.bones.length && renderable.bones[i] != null)
skinningProgram.setUniformMatrix(u_bones[i], renderable.bones[i]);
else
skinningProgram.setUniformMatrix(u_bones[i], IDENTITY);
}
}
renderable.mesh.render(program, renderable.primitiveType, renderable.meshPartOffset, renderable.meshPartSize);
program.end();
}
@Override
public void end(){
this.camera = null;
this.context = null;
}
}

View File

@@ -0,0 +1,58 @@
/*
* 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());
}
}

View File

@@ -15,7 +15,9 @@
*/
package ve.ucv.ciens.ccg.nxtar.interfaces;
public interface Toaster {
public interface ActionResolver{
public void showShortToast(String msg);
public void showLongToast(String msg);
public void enableMulticast();
public void disableMulticast();
}

View File

@@ -15,6 +15,6 @@
*/
package ve.ucv.ciens.ccg.nxtar.interfaces;
public interface NetworkConnectionListener {
public interface ApplicationEventsListener {
public void networkStreamConnected(String streamName);
}

View File

@@ -0,0 +1,86 @@
/*
* 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.interfaces;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Vector3;
public interface ImageProcessor{
public class MarkerData{
public byte[] outFrame;
public int[] markerCodes;
public Vector3[] translationVectors;
public Matrix3[] rotationMatrices;
}
public class CalibrationData{
public byte[] outFrame;
public float[] calibrationPoints;
}
/**
* <p>Finds up to {@link ProjectConstants.MAXIMUM_NUMBER_OF_MARKERS} markers in the input
* image and returns their codes and pose estimation in the CVMarkerData structure. The
* markers are higlihted in the input image.</p>
*
* @param frame The JPEG encoded input image.
* @return A data structure containing the processed output image, the
* detected marker codes and their respective locations.
*/
public MarkerData findMarkersInFrame(byte[] frame);
/**
* <p>Attempts to detect a checkerboard calibration pattern in the input image.
* If the pattenr is found the method returns an image with the pattern
* highlighted and the spatial location of the calibration points in the
* output data structure.</p>
*
* @param frame The JPEG encoded input image.
* @return A data structure containing the processed output image and the
* location of the calibration points. If the pattern was not found, the returnd
* calibration points array is null.
*/
public CalibrationData findCalibrationPattern(byte[] frame);
/**
* <p>Obtains the intrinsic camera parameters necesary for calibration.</p>
*/
public void calibrateCamera(float[][] calibrationSamples, byte[] frame);
/**
* <p>Removes camera lens distortion from the input image using the
* camera parameters obtained by the calibrateCamera method.</p>
*
* @return A JPEG encoded image that is the input image after distortion correction. If the
* camera has not been calibrated or OpenCV failed to load returns null.
*/
public byte[] undistortFrame(byte[] frame);
/**
* <p>Indicates if OpenCV has been sucessfully initialized and used
* to obtain the camera parameters for calibration.</p>
*
* @return True if and only if OpenCV initialized succesfully and calibrateCamera has been called previously.
*/
public boolean isCameraCalibrated();
public float getFocalPointX();
public float getFocalPointY();
public float getCameraCenterX();
public float getCameraCenterY();
}

View File

@@ -16,11 +16,16 @@
package ve.ucv.ciens.ccg.nxtar.network;
import java.io.IOException;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
import ve.ucv.ciens.ccg.networkdata.MotorEvent;
import ve.ucv.ciens.ccg.networkdata.MotorEventACK;
import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener;
import ve.ucv.ciens.ccg.nxtar.network.monitors.MotorEventQueue;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx;
@@ -30,23 +35,32 @@ public class RobotControlThread extends Thread {
private static final String TAG = "NXTAR_CORE_ROBOTTHREAD";
private static final String CLASS_NAME = RobotControlThread.class.getSimpleName();
private NetworkConnectionListener netListener;
private ApplicationEventsListener netListener;
private ServerSocket server;
private Socket client;
private Toaster toaster;
private MotorEventQueue queue;
private Object pauseMonitor;
private boolean paused;
private boolean done;
private ObjectOutputStream os;
private ObjectInputStream is;
private RobotControlThread(){
super(THREAD_NAME);
netListener = null;
queue = MotorEventQueue.getInstance();
pauseMonitor = new Object();
paused = false;
done = false;
try{
server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_2);
server = new ServerSocket(ProjectConstants.MOTOR_CONTROL_PORT);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".RobotControlThread() :: Error creating server: " + io.getMessage(), io);
}
}
private static class SingletonHolder{
public static final RobotControlThread INSTANCE = new RobotControlThread();
}
@@ -55,25 +69,110 @@ public class RobotControlThread extends Thread {
return SingletonHolder.INSTANCE;
}
public RobotControlThread setToaster(Toaster toaster){
this.toaster = toaster;
return this;
public void addNetworkConnectionListener(ApplicationEventsListener listener){
netListener = listener;
}
public void addNetworkConnectionListener(NetworkConnectionListener listener){
netListener = listener;
public void pauseThread(){
synchronized(pauseMonitor){
paused = true;
}
}
public void resumeThread(){
synchronized(pauseMonitor){
paused = false;
}
}
public void finish(){
done = true;
}
@Override
public void run(){
MotorEvent message;
MotorEventACK ack;
try{
client = server.accept();
if(netListener != null)
netListener.networkStreamConnected(THREAD_NAME);
toaster.showShortToast("Client connected to RobotControlThread");
client.close();
client.setTcpNoDelay(true);
if(netListener != null) netListener.networkStreamConnected(THREAD_NAME);
os = new ObjectOutputStream(client.getOutputStream());
is = new ObjectInputStream(client.getInputStream());
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
return;
}
while(!paused){
if(done){
break;
}
// Send the motor event.
try{
message = queue.getNextEvent();
os.writeObject(message);
message = null;
}catch(InvalidClassException ic){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during transmission: " + ic.getMessage(), ic);
break;
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException during transmission: " + io.getMessage(), io);
break;
}
// Receive ack.
try{
ack = (MotorEventACK)is.readObject();
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during reception: " + cn.getMessage(), cn);
break;
}catch(ClassCastException cc){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during reception: " + cc.getMessage(), cc);
break;
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during reception: " + io.getMessage(), io);
break;
}
if(ack.isClientQueueFull()){
// Wait for client to notify.
// A client will never send two queue full acks in a row.
try{
ack = (MotorEventACK)is.readObject();
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during reception: " + cn.getMessage(), cn);
break;
}catch(ClassCastException cc){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during reception: " + cc.getMessage(), cc);
break;
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: InvalidClassException during reception: " + io.getMessage(), io);
break;
}
}else{
// Clean and continue.
ack = null;
message = null;
continue;
}
}
try{
client.close();
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client: " + io.getMessage(), io);
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* 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.network;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx;
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 ApplicationEventsListener netListener;
private ServerSocket server;
private Socket client;
private Object pauseMonitor;
private boolean paused;
private boolean done;
private InputStream reader;
private Byte lightReading;
private SensorReportThread(){
paused = false;
done = false;
netListener = null;
pauseMonitor = null;
client = null;
lightReading = -1;
try{
server = new ServerSocket(ProjectConstants.SENSOR_REPORT_PORT);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".RobotControlThread() :: Error creating server: " + io.getMessage(), io);
server = null;
}
}
private static class SingletonHolder{
public final static SensorReportThread INSTANCE = new SensorReportThread();
}
public static SensorReportThread getInstance(){
return SingletonHolder.INSTANCE;
}
public void addNetworkConnectionListener(ApplicationEventsListener listener){
netListener = listener;
}
public void pauseThread(){
synchronized(pauseMonitor){
paused = true;
}
}
public void resumeThread(){
synchronized(pauseMonitor){
paused = false;
}
}
public void finish(){
done = true;
}
public byte getLightSensorReading(){
byte data;
synchronized(lightReading){
data = lightReading.byteValue();
}
return data;
}
@Override
public void run(){
byte[] reading = new byte[1];
try{
client = server.accept();
client.setTcpNoDelay(true);
if(netListener != null) netListener.networkStreamConnected(THREAD_NAME);
reader = client.getInputStream();
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
return;
}
while(!paused){
if(done) break;
try{
reader.read(reading);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException during sensor read: " + io.getMessage(), io);
break;
}
synchronized (lightReading) {
lightReading = reading[0];
}
}
}
}

View File

@@ -31,7 +31,7 @@ import com.badlogic.gdx.Gdx;
* <p> This thread performs an ad hoc service discovery protocol. A multicast datagram packet is sent every
* 250 miliseconds carrying the string "NxtAR server is here!" on the multicast address defined
* in {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#MULTICAST_ADDRESS}. The port defined in
* {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#SERVER_UDP_PORT} is used for the transmissions. The server stops
* {@link ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants#SERVICE_DISCOVERY_PORT} is used for the transmissions. The server stops
* when another thread calls the {@link #finish()} method or the server fails to transmit {@link #MAX_RETRIES} packets in
* a row, whichever happens first.</p>
*
@@ -89,7 +89,7 @@ public class ServiceDiscoveryThread extends Thread {
// Create a UDP socket at the port defined in ProjectConstants.SERVER_UDP_PORT.
Gdx.app.debug(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Creating multicast server.");
try{
udpServer = new DatagramSocket(ProjectConstants.SERVER_UDP_PORT);
udpServer = new DatagramSocket(ProjectConstants.SERVICE_DISCOVERY_PORT);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".ServiceDiscoveryThread() :: Error creating UDP socket: " + io.getMessage());
udpServer = null;
@@ -150,10 +150,11 @@ public class ServiceDiscoveryThread extends Thread {
break;
}
// Send the packet and reset the retry counter.
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, ProjectConstants.SERVER_UDP_PORT);
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, group, ProjectConstants.SERVICE_DISCOVERY_PORT);
udpServer.send(packet);
retries = 0;
try{ sleep(250L); }catch(InterruptedException ie){ }
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending packet: " + io.getMessage());
retries += 1;

View File

@@ -15,57 +15,57 @@
*/
package ve.ucv.ciens.ccg.nxtar.network;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import ve.ucv.ciens.ccg.networkdata.VideoFrameDataMessage;
import ve.ucv.ciens.ccg.networkdata.VideoStreamingControlMessage;
import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
import ve.ucv.ciens.ccg.nxtar.network.protocols.VideoStreamingProtocol;
import ve.ucv.ciens.ccg.nxtar.interfaces.ApplicationEventsListener;
import ve.ucv.ciens.ccg.nxtar.network.monitors.VideoFrameMonitor;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx;
public class VideoStreamingThread extends Thread {
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 enum ProtocolState_t {WAIT_FOR_START, SEND_CONTINUE, RECEIVE_DATA, SEND_ACK_NEXT, SEND_ACK_WAIT, PAUSED, END_STREAM};
private NetworkConnectionListener netListener;
private ServerSocket server;
private Toaster toaster;
private ProtocolState_t protocolState;
private ApplicationEventsListener netListener;
private DatagramSocket socket;
private boolean protocolStarted;
private boolean pauseProtocol;
private boolean endProtocol;
private boolean done;
private boolean pause;
private boolean coreNotified;
private Object protocolPauseMonitor;
private Socket client;
private ObjectInputStream reader;
private ObjectOutputStream writer;
private VideoFrameMonitor frameMonitor;
private long then;
private long now;
private long delta;
private int fps;
private int lostFramesPerSecond;
private int lostFrames;
private Object pauseMonitor;
private VideoStreamingThread(){
super(THREAD_NAME);
pauseMonitor = new Object();
fps = 0;
lostFramesPerSecond = 0;
netListener = null;
toaster = null;
protocolStarted = false;
endProtocol = false;
pauseProtocol = false;
done = false;
protocolState = ProtocolState_t.WAIT_FOR_START;
coreNotified = false;
protocolPauseMonitor = new Object();
frameMonitor = VideoFrameMonitor.getInstance();
try{
server = new ServerSocket(ProjectConstants.SERVER_TCP_PORT_1);
socket = new DatagramSocket(ProjectConstants.VIDEO_STREAMING_PORT);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".VideoStreamingThread() :: Error creating server: " + io.getMessage(), io);
}
@@ -79,26 +79,15 @@ public class VideoStreamingThread extends Thread {
return SingletonHolder.INSTANCE;
}
public VideoStreamingThread setToaster(Toaster toaster){
this.toaster = toaster;
return this;
}
public void addNetworkConnectionListener(NetworkConnectionListener listener){
public void addNetworkConnectionListener(ApplicationEventsListener listener){
netListener = listener;
}
private void toast(String message){
if(toaster != null)
toaster.showShortToast(message);
}
public void startStreaming(){
if(!protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".startStreaming() :: Requesting protocol start.");
synchronized(protocolPauseMonitor){
protocolStarted = true;
protocolState = ProtocolState_t.SEND_CONTINUE;
protocolPauseMonitor.notifyAll();
}
}
@@ -107,7 +96,6 @@ public class VideoStreamingThread extends Thread {
public void pauseStreaming(){
if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".pauseStreaming() :: Requesting protocol pause.");
pauseProtocol = true;
}else
return;
}
@@ -116,7 +104,6 @@ public class VideoStreamingThread extends Thread {
if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".resumeStreaming() :: Requesting protocol resume.");
synchronized(protocolPauseMonitor){
pauseProtocol = false;
protocolPauseMonitor.notifyAll();
}
}else
@@ -126,7 +113,6 @@ public class VideoStreamingThread extends Thread {
public void finishStreaming(){
if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".finishStreaming() :: Requesting protocol end.");
endProtocol = true;
}else
return;
}
@@ -135,222 +121,119 @@ public class VideoStreamingThread extends Thread {
done = true;
}
/*@Override
public void run(){
Object tmpMessage;
VideoStreamingControlMessage controlMessage;
VideoFrameDataMessage dataMessage;
private int byteArray2Int(byte[] array){
int number = 0;
for(int i = 0; i < 4; i++){
number |= (array[3-i] & 0xff) << (i << 3);
}
return number;
}
// Listen on the server socket until a client successfully connects.
do{
private void receiveUdp(){
try{
int intSize;
byte[] size = new byte[4];
byte[] data;
DatagramPacket packet;
VideoFrameDataMessage dataMessage;
Object tmpMessage;
//Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message size from socket.");
try{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Listening for client.");
client = server.accept();
if(netListener != null)
netListener.networkStreamConnected(THREAD_NAME);
writer = new ObjectOutputStream(client.getOutputStream());
reader = new ObjectInputStream(client.getInputStream());
toast("Client connected");
packet = new DatagramPacket(size, size.length);
socket.receive(packet);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
client = null;
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving size " + io.getMessage());
lostFramesPerSecond += 1;
return;
}
}while(client != null && !client.isConnected());
while(!done){
switch(protocolState){
case WAIT_FOR_START:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is WAIT_FOR_START.");
// If the app has not started the protocol then wait.
synchronized(protocolPauseMonitor){
while(!protocolStarted){
try{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Protocol has not started, waiting.");
protocolPauseMonitor.wait();
}catch(InterruptedException ie){ }
}
}
break;
//Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Creating buffers.");
intSize = byteArray2Int(size);
data = new byte[intSize];
case SEND_CONTINUE:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is SEND_CONTINUE.");
// Prepare the message.
controlMessage = new VideoStreamingControlMessage();
if(!endProtocol){
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing STREAM_CONTROL_END message.");
controlMessage.message = VideoStreamingProtocol.STREAM_CONTROL_END;
}else{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing FLOW_CONTROL_CONTINUE message.");
controlMessage.message = VideoStreamingProtocol.FLOW_CONTROL_CONTINUE;
}
//Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Reading message from socket.");
try{
packet = new DatagramPacket(data, data.length);
socket.receive(packet);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving data " + io.getMessage());
lostFramesPerSecond += 1;
return;
}
// Send it!
try{
writer.writeObject(controlMessage);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending message: " + io.getMessage(), io);
}finally{
protocolState = ProtocolState_t.RECEIVE_DATA;
}
break;
ByteArrayInputStream bais = new ByteArrayInputStream(data);
case RECEIVE_DATA:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is RECEIVE_DATA.");
//Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Saving message in monitor.");
try{
ObjectInputStream ois = new ObjectInputStream(bais);
tmpMessage = ois.readObject();
try{
tmpMessage = reader.readObject();
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException while receiving message: " + io.getMessage(), io);
break;
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: ClassNotFoundException while receiving message: " + cn.getMessage(), cn);
break;
}
if(tmpMessage instanceof VideoStreamingControlMessage){
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received a control message.");
controlMessage = (VideoStreamingControlMessage) tmpMessage;
// TODO: handle this case correctly.
}else if(tmpMessage instanceof VideoFrameDataMessage){
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received a data message.");
if(tmpMessage instanceof VideoFrameDataMessage){
//Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received a data message.");
dataMessage = (VideoFrameDataMessage) tmpMessage;
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received frame dimensions are: " +
Integer.toString(dataMessage.imageWidth) + "x" + Integer.toString(dataMessage.imageHeight));
//Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received frame dimensions are: " + Integer.toString(dataMessage.imageWidth) + "x" + Integer.toString(dataMessage.imageHeight));
frameMonitor.setFrameDimensions(dataMessage.imageWidth, dataMessage.imageHeight);
frameMonitor.setNewFrame(dataMessage.data);
if(pauseProtocol)
protocolState = ProtocolState_t.SEND_ACK_WAIT;
else
protocolState = ProtocolState_t.SEND_ACK_NEXT;
}else{
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Unrecognized message received!.");
// TODO: handle this case correctly.
System.exit(ProjectConstants.EXIT_FAILURE);
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received something unknown.");
lostFramesPerSecond += 1;
}
break;
case SEND_ACK_NEXT:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is SEND_ACK_NEXT.");
// Prepare the message.
controlMessage = new VideoStreamingControlMessage();
if(!endProtocol){
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing STREAM_CONTROL_END message.");
controlMessage.message = VideoStreamingProtocol.STREAM_CONTROL_END;
}else{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Preparing ACK_SEND_NEXT message.");
controlMessage.message = VideoStreamingProtocol.ACK_SEND_NEXT;
}
// Send it!
try{
writer.writeObject(controlMessage);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending message: " + io.getMessage(), io);
}finally{
if(!endProtocol)
protocolState = ProtocolState_t.RECEIVE_DATA;
else
protocolState = ProtocolState_t.END_STREAM;
}
break;
case SEND_ACK_WAIT:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is SEND_ACK_WAIT.");
// Prepare the message.
controlMessage = new VideoStreamingControlMessage();
controlMessage.message = VideoStreamingProtocol.ACK_WAIT;
// Send it!
try{
writer.writeObject(controlMessage);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error sending message: " + io.getMessage(), io);
}finally{
protocolState = ProtocolState_t.PAUSED;
}
break;
case PAUSED:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is PAUSED.");
// The app requested to stop the protocol temporarily.
synchronized(protocolPauseMonitor){
while(pauseProtocol){
try{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Protocol pause requested, waiting.");
protocolPauseMonitor.wait();
}catch(InterruptedException ie){ }
}
}
protocolState = ProtocolState_t.SEND_CONTINUE;
break;
case END_STREAM:
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: State is END_STREAM.");
// Simply disconnect from the client and end the thread.
try{
client.close();
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client: " + io.getMessage(), io);
}
done = true;
break;
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException received deserializing message " + io.getMessage());
lostFramesPerSecond += 1;
return;
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: ClassNotFoundException received " + cn.getMessage());
lostFramesPerSecond += 1;
return;
}
}
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished.");
}*/
private void receiveImage(){
Object tmpMessage;
VideoFrameDataMessage dataMessage;
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Receiving data.");
try{
tmpMessage = (VideoFrameDataMessage)reader.readObject();
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: IOException while receiving message: " + io.getMessage());
return;
}catch(ClassNotFoundException cn){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: ClassNotFoundException while receiving message: " + cn.getMessage());
}catch(Exception e){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: Exception received " + e.getMessage());
lostFramesPerSecond += 1;
return;
}
}
if(tmpMessage instanceof VideoFrameDataMessage){
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received a data message.");
dataMessage = (VideoFrameDataMessage) tmpMessage;
frameMonitor.setFrameDimensions(dataMessage.imageWidth, dataMessage.imageHeight);
frameMonitor.setNewFrame(dataMessage.data);
}else{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Received something unknown.");
}
public int getFps(){
return fps;
}
public int getLostFrames(){
return lostFrames;
}
@Override
public void run(){
// Listen on the server socket until a client successfully connects.
do{
try{
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Listening for client.");
client = server.accept();
if(netListener != null)
netListener.networkStreamConnected(THREAD_NAME);
writer = new ObjectOutputStream(client.getOutputStream());
reader = new ObjectInputStream(client.getInputStream());
toast("Client connected");
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
client = null;
}
}while(client != null && !client.isConnected());
int frames = 0;
lostFrames = 0;
then = System.currentTimeMillis();
while(!done){
receiveImage();
synchronized (pauseMonitor) {
while(pause){
try{ pauseMonitor.wait(); }catch(InterruptedException ie){ }
}
}
//Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Receiving.");
if(netListener != null && !coreNotified && frameMonitor.getCurrentFrame() != null){
coreNotified = true;
netListener.networkStreamConnected(THREAD_NAME);
}
receiveUdp();
frames++;
now = System.currentTimeMillis();
delta = now - then;
if(delta >= 1000){
fps = frames;
frames = 0;
lostFrames = lostFramesPerSecond;
lostFramesPerSecond = 0;
then = now;
delta = 0;
}
}
try{
@@ -362,4 +245,16 @@ public class VideoStreamingThread extends Thread {
Gdx.app.debug(TAG, CLASS_NAME + ".run() :: Thread finished.");
}
public void pause(){
synchronized (pauseMonitor){
pause = true;
}
}
public void play(){
synchronized (pauseMonitor){
pause = false;
pauseMonitor.notifyAll();
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.network.monitors;
import java.util.LinkedList;
import java.util.Queue;
import ve.ucv.ciens.ccg.networkdata.MotorEvent;
/**
* <p>A simple monitor class that encapsulates a queue.</p>
* <p>As it name says it stores motor events to be forwarded to the NXT robot.</p>
* <p>This class implements the singleton design pattern.<p>
*
* @author Miguel Angel Astor Romero
*/
public class MotorEventQueue{
/**
* The event queue implemented as a linked list.
*/
private Queue<MotorEvent> motorEvents;
private MotorEventQueue(){
motorEvents = new LinkedList<MotorEvent>();
}
private static class SingletonHolder{
public static final MotorEventQueue instance = new MotorEventQueue();
}
/**
* Return the singleton instance of this class.
* @return The singleton instance.
*/
public static MotorEventQueue getInstance(){
return SingletonHolder.instance;
}
/**
* <p>Get the first event on the queue.</p>
* <p> If there are no events to return this method blocks until some thread calls the addEvent() method.</p>
* @return The event at the front of the queue.
*/
public synchronized MotorEvent getNextEvent(){
while(motorEvents.size() == 0){
try{ wait(); }catch(InterruptedException ie){ }
}
return motorEvents.poll();
}
/**
* <p>Adds an event to the back of the queue.</p>
* @param event The event to add.
*/
public synchronized void addEvent(MotorEvent event){
motorEvents.add(event);
notifyAll();
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ve.ucv.ciens.ccg.nxtar.network;
package ve.ucv.ciens.ccg.nxtar.network.monitors;
import ve.ucv.ciens.ccg.nxtar.utils.Size;
@@ -63,9 +63,9 @@ public class VideoFrameMonitor{
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.");
temp = frameA;
frameA = frameB;
frameB = temp;
Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping done.");

View File

@@ -1,46 +0,0 @@
/*
* 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.network.protocols;
public final class VideoStreamingProtocol{
public static final byte STREAM_CONTROL_END = 0x10;
public static final byte ACK_SEND_NEXT = 0x20;
public static final byte ACK_WAIT = 0x30;
public static final byte FLOW_CONTROL_WAIT = 0x40;
public static final byte FLOW_CONTROL_CONTINUE = 0x50;
public static final byte IMAGE_DATA = 0x60;
public static final byte UNRECOGNIZED = (byte)0xFF;
public static boolean checkValidityOfMessage(byte message){
boolean validity;
switch(message){
case STREAM_CONTROL_END:
case ACK_SEND_NEXT:
case ACK_WAIT:
case FLOW_CONTROL_WAIT:
case FLOW_CONTROL_CONTINUE:
case IMAGE_DATA:
case UNRECOGNIZED:
validity = true;
break;
default:
validity = false;
}
return validity;
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.controllers.ControllerListener;
import com.badlogic.gdx.controllers.PovDirection;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
public abstract class BaseState implements Screen, ControllerListener, InputProcessor{
protected NxtARCore core;
protected boolean stateActive;
protected OrthographicCamera pixelPerfectCamera;
protected Vector3 win2world;
protected Vector2 touchPointWorldCoords;
/*;;;;;;;;;;;;;;;;;
; STATE METHODS ;
;;;;;;;;;;;;;;;;;*/
public abstract void onStateSet();
public abstract void onStateUnset();
/*;;;;;;;;;;;;;;;;;;
; SCREEN METHODS ;
;;;;;;;;;;;;;;;;;;*/
@Override
public abstract void render(float delta);
@Override
public abstract void dispose();
@Override
public void resize(int width, int height){ }
@Override
public void show(){ }
@Override
public void hide(){ }
@Override
public void pause(){ }
@Override
public void resume(){ }
/*;;;;;;;;;;;;;;;;;;
; HELPER METHODS ;
;;;;;;;;;;;;;;;;;;*/
protected final void unprojectTouch(int screenX, int screenY){
win2world.set(screenX, screenY, 0.0f);
pixelPerfectCamera.unproject(win2world);
touchPointWorldCoords.set(win2world.x, win2world.y);
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;
; INPUT PROCESSOR METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public boolean keyDown(int keycode){
return false;
};
@Override
public boolean keyUp(int keycode){
return false;
};
@Override
public boolean keyTyped(char character){
return false;
};
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button){
return false;
};
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button){
return false;
};
@Override
public boolean touchDragged(int screenX, int screenY, int pointer){
return false;
};
@Override
public boolean mouseMoved(int screenX, int screenY){
return false;
};
@Override
public boolean scrolled(int amount){
return false;
};
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; CONTROLLER LISTENER METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public void connected(Controller controller){ };
@Override
public void disconnected(Controller controller){ };
@Override
public boolean buttonDown(Controller controller, int buttonCode){
return false;
};
@Override
public boolean buttonUp(Controller controller, int buttonCode){
return false;
};
@Override
public boolean axisMoved(Controller controller, int axisCode, float value){
return false;
};
@Override
public boolean povMoved(Controller controller, int povCode, PovDirection value){
return false;
};
@Override
public boolean xSliderMoved(Controller controller, int sliderCode, boolean value){
return false;
};
@Override
public boolean ySliderMoved(Controller controller, int sliderCode, boolean value){
return false;
};
@Override
public boolean accelerometerMoved(Controller controller, int accelerometerCode, Vector3 value){
return false;
};
}

View File

@@ -0,0 +1,420 @@
/*
* 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 java.util.Arrays;
import ve.ucv.ciens.ccg.nxtar.NxtARCore;
import ve.ucv.ciens.ccg.nxtar.NxtARCore.game_states_t;
import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor.CalibrationData;
import ve.ucv.ciens.ccg.nxtar.network.monitors.VideoFrameMonitor;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import ve.ucv.ciens.ccg.nxtar.utils.Size;
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.Pixmap;
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.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 CameraCalibrationState extends BaseState{
private static final String TAG = "CAMERA_CALIBRATION_STATE";
private static final String CLASS_NAME = CameraCalibrationState.class.getSimpleName();
private static final String SHADER_PATH = "shaders/bckg/bckg";
private NxtARCore core;
private float u_scaling[];
protected Sprite background;
private Texture backgroundTexture;
private ShaderProgram backgroundShader;
// Cameras.
private OrthographicCamera camera;
// Video stream graphics.
private Texture videoFrameTexture;
private Sprite renderableVideoFrame;
private Pixmap videoFrame;
// Gui components.
private TextButton takeSampleButton;
private Rectangle takeSampleButtonBBox;
private Texture buttonEnabledTexture;
private Texture buttonDisabledTexture;
private Texture buttonPressedTexture;
private NinePatch buttonEnabled9p;
private NinePatch buttonDisabled9p;
private NinePatch buttonPressed9p;
private BitmapFont font;
// Button touch helper fields.
private boolean takeSampleButtonTouched;
private int takeSampleButtonPointer;
// Monitors.
private VideoFrameMonitor frameMonitor;
private float[][] calibrationSamples;
private boolean takeSample;
private int lastSampleTaken;
public CameraCalibrationState(final NxtARCore core){
TextButtonStyle tbs;
FreeTypeFontGenerator generator;
FreeTypeFontParameter param;
this.core = core;
frameMonitor = VideoFrameMonitor.getInstance();
// Set up the cameras.
pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera = new OrthographicCamera(1.0f, Gdx.graphics.getHeight() / Gdx.graphics.getWidth());
// 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));
// Load the background shader.
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 + ".CameraCalibrationState() :: Failed to compile the background shader.");
Gdx.app.error(TAG, CLASS_NAME + backgroundShader.getLog());
backgroundShader = null;
}
// Set up the background scaling.
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;
// Set up the sampling button.
// Create the 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();
// Load the textures.
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 button style.
tbs = new TextButtonStyle();
tbs.font = font;
tbs.up = new NinePatchDrawable(buttonEnabled9p);
tbs.checked = new NinePatchDrawable(buttonPressed9p);
tbs.disabled = new NinePatchDrawable(buttonDisabled9p);
tbs.disabledFontColor = new Color(0, 0, 0, 1);
// Create the button itself.
takeSampleButton = new TextButton("Take calibration sample", tbs);
takeSampleButton.setText("Take calibration sample");
takeSampleButton.setDisabled(true);
takeSampleButtonBBox = new Rectangle(0, 0, takeSampleButton.getWidth(), takeSampleButton.getHeight());
takeSampleButton.setPosition(-(takeSampleButton.getWidth() / 2), -(Gdx.graphics.getHeight()/2) - 1 + (takeSampleButton.getHeight() / 2));
takeSampleButtonBBox.setPosition(takeSampleButton.getX(), takeSampleButton.getY());
// Set up the touch collision detection variables.
win2world = new Vector3(0.0f, 0.0f, 0.0f);
touchPointWorldCoords = new Vector2();
takeSampleButtonTouched = false;
takeSampleButtonPointer = -1;
// Initialize the calibration samples vector.
calibrationSamples = new float[ProjectConstants.CALIBRATION_SAMPLES][];
for(int i = 0; i < calibrationSamples.length; i++){
calibrationSamples[i] = new float[ProjectConstants.CALIBRATION_PATTERN_POINTS * 2];
}
}
@Override
public void onStateSet(){
Gdx.input.setInputProcessor(this);
Gdx.input.setCatchBackKey(true);
Gdx.input.setCatchMenuKey(true);
takeSample = false;
lastSampleTaken = 0;
for(int i = 0; i < calibrationSamples.length; i++){
for(int j = 0; j < calibrationSamples[i].length; j++){
calibrationSamples[i][j] = 0.0f;
}
}
}
@Override
public void onStateUnset(){ }
@Override
public void render(float delta){
String msg;
byte[] frame;
byte[] prevFrame = null;
Size dimensions = null;
// Clear the screen.
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render the background.
core.batch.setProjectionMatrix(pixelPerfectCamera.combined);
core.batch.begin();{
if(backgroundShader != null){
core.batch.setShader(backgroundShader);
backgroundShader.setUniform2fv("u_scaling", u_scaling, 0, 2);
}
background.draw(core.batch);
if(backgroundShader != null) core.batch.setShader(null);
}core.batch.end();
// Fetch the current video frame.
frame = frameMonitor.getCurrentFrame();
// Apply the undistortion method if the camera has been calibrated already.
/*if(core.cvProc.isCameraCalibrated()){
frame = core.cvProc.undistortFrame(frame);
}*/
// Find the calibration points in the video frame.
CalibrationData data = core.cvProc.findCalibrationPattern(frame);
// Disable the sampling button if the calibration pattern was not found.
if(data.calibrationPoints != null && !core.cvProc.isCameraCalibrated()){
takeSampleButton.setDisabled(false);
}else{
takeSampleButton.setDisabled(true);
}
// If the user requested a sample be taken.
if(takeSample && !core.cvProc.isCameraCalibrated() && data.calibrationPoints != null){
// Disable sample taking.
takeSample = false;
Gdx.app.log(TAG, CLASS_NAME + ".render(): Sample taken.");
// Save the calibration points to the samples array.
for(int i = 0; i < data.calibrationPoints.length; i += 2){
Gdx.app.log(TAG, CLASS_NAME + ".render(): Value " + Integer.toString(i) + " = (" + Float.toString(data.calibrationPoints[i]) + ", " + Float.toString(data.calibrationPoints[i + 1]) + ")");
calibrationSamples[lastSampleTaken][i] = data.calibrationPoints[i];
calibrationSamples[lastSampleTaken][i + 1] = data.calibrationPoints[i + 1];
}
// Move to the next sample.
lastSampleTaken++;
msg = Integer.toString(lastSampleTaken) + " samples taken. ";
msg += Integer.toString(ProjectConstants.CALIBRATION_SAMPLES - lastSampleTaken) + " samples left.";
core.toast(msg, false);
// If enough samples has been taken then calibrate the camera.
if(lastSampleTaken == ProjectConstants.CALIBRATION_SAMPLES){
Gdx.app.log(TAG, CLASS_NAME + "render(): Last sample taken.");
core.cvProc.calibrateCamera(calibrationSamples, frame);
msg = "Camera successfully calibrated";
core.toast(msg, true);
}
}
if(frame != null && data != null && data.outFrame != null && !Arrays.equals(frame, prevFrame)){
// If the received frame is valid and is different from the previous frame.
// Make a texture from the frame.
dimensions = frameMonitor.getFrameDimensions();
videoFrame = new Pixmap(data.outFrame, 0, dimensions.getWidth() * dimensions.getHeight());
videoFrameTexture = new Texture(videoFrame);
videoFrameTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
videoFrame.dispose();
// Set up the frame texture as a rendereable sprite.
TextureRegion region = new TextureRegion(videoFrameTexture, 0, 0, dimensions.getWidth(), dimensions.getHeight());
renderableVideoFrame = new Sprite(region);
renderableVideoFrame.setOrigin(renderableVideoFrame.getWidth() / 2, renderableVideoFrame.getHeight() / 2);
if(!Ouya.runningOnOuya){
renderableVideoFrame.setSize(1.0f, renderableVideoFrame.getHeight() / renderableVideoFrame.getWidth() );
renderableVideoFrame.rotate90(true);
renderableVideoFrame.translate(-renderableVideoFrame.getWidth() / 2, 0.5f - renderableVideoFrame.getHeight());
}else{
float xSize = Gdx.graphics.getHeight() * (dimensions.getWidth() / dimensions.getHeight());
renderableVideoFrame.setSize(xSize * ProjectConstants.OVERSCAN, Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN);
renderableVideoFrame.rotate90(true);
renderableVideoFrame.translate(-renderableVideoFrame.getWidth() / 2, -renderableVideoFrame.getHeight() / 2);
}
// Render the frame.
if(!Ouya.runningOnOuya){
core.batch.setProjectionMatrix(camera.combined);
}else{
core.batch.setProjectionMatrix(pixelPerfectCamera.combined);
}
core.batch.begin();{
renderableVideoFrame.draw(core.batch);
}core.batch.end();
// Clear texture memory.
videoFrameTexture.dispose();
}
// Render the user interface.
if(!Ouya.runningOnOuya){
core.batch.setProjectionMatrix(pixelPerfectCamera.combined);
core.batch.begin();{
takeSampleButton.draw(core.batch, 1.0f);
}core.batch.end();
}else{
// TODO: Render OUYA gui.
}
// Save this frame as previous to avoid processing the same frame twice when network latency is high.
prevFrame = frame;
}
@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(){
if(videoFrameTexture != null)
videoFrameTexture.dispose();
backgroundTexture.dispose();
if(backgroundShader != null) backgroundShader.dispose();
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;
; INPUT LISTENER METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public boolean keyDown(int keycode){
if(keycode == Input.Keys.BACK){
core.nextState = game_states_t.MAIN_MENU;
return true;
}
return false;
}
@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(!takeSampleButton.isDisabled() && takeSampleButtonBBox.contains(touchPointWorldCoords) && !takeSampleButtonTouched){
takeSampleButton.setChecked(true);
takeSampleButtonTouched = true;
takeSampleButtonPointer = pointer;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Sample 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(!takeSampleButton.isDisabled() && takeSampleButtonBBox.contains(touchPointWorldCoords) && takeSampleButtonTouched){
takeSampleButton.setChecked(false);
takeSampleButtonTouched = false;
takeSampleButtonPointer = -1;
takeSample = true;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Sample button released.");
}
return true;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer){
unprojectTouch(screenX, screenY);
if(!takeSampleButton.isDisabled() && takeSampleButtonTouched && pointer == takeSampleButtonPointer && !takeSampleButtonBBox.contains(touchPointWorldCoords)){
takeSampleButtonPointer = -1;
takeSampleButtonTouched = false;
takeSampleButton.setChecked(false);
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Sample button released.");
}
return true;
}
@Override
public boolean buttonDown(Controller controller, int buttonCode){
// TODO: Handle OUYA controls.
return false;
}
@Override
public boolean buttonUp(Controller controller, int buttonCode){
// TODO: Handle OUYA controls.
return false;
}
@Override
public boolean axisMoved(Controller controller, int axisCode, float value){
// TODO: Handle OUYA controls.
return false;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Miguel Angel Astor Romero
* 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.
@@ -15,42 +15,967 @@
*/
package ve.ucv.ciens.ccg.nxtar.states;
public class InGameState implements NxtARState{
import ve.ucv.ciens.ccg.networkdata.MotorEvent;
import ve.ucv.ciens.ccg.networkdata.MotorEvent.motor_t;
import ve.ucv.ciens.ccg.nxtar.NxtARCore;
import ve.ucv.ciens.ccg.nxtar.NxtARCore.game_states_t;
import ve.ucv.ciens.ccg.nxtar.entities.EntityCreatorBase;
import ve.ucv.ciens.ccg.nxtar.entities.MarkerTestEntityCreator;
import ve.ucv.ciens.ccg.nxtar.graphics.CustomPerspectiveCamera;
import ve.ucv.ciens.ccg.nxtar.graphics.LightSource;
import ve.ucv.ciens.ccg.nxtar.graphics.RenderParameters;
import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor.MarkerData;
import ve.ucv.ciens.ccg.nxtar.network.monitors.MotorEventQueue;
import ve.ucv.ciens.ccg.nxtar.network.monitors.VideoFrameMonitor;
import ve.ucv.ciens.ccg.nxtar.systems.AnimationSystem;
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.utils.ProjectConstants;
import com.artemis.World;
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.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
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.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
public class InGameState extends BaseState{
private static final String TAG = "IN_GAME_STATE";
private static final String CLASS_NAME = InGameState.class.getSimpleName();
private static final String BACKGROUND_SHADER_PATH = "shaders/bckg/bckg";
private static final float NEAR = 0.01f;
private static final float FAR = 100.0f;
private static final float FAR_PLUS_NEAR = FAR + NEAR;
private static final float FAR_LESS_NEAR = FAR - NEAR;
private static final Vector3 LIGHT_POSITION = new Vector3(2.0f, 2.0f, 4.0f);
private static final Color AMBIENT_COLOR = new Color(0.0f, 0.1f, 0.3f, 1.0f);
private static final Color DIFFUSE_COLOR = new Color(1.0f, 1.0f, 1.0f, 1.0f);
private static final Color SPECULAR_COLOR = new Color(1.0f, 0.8f, 0.0f, 1.0f);
private static final float SHINYNESS = 50.0f;
// Background related fields.
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.
private World gameWorld;
private EntityCreatorBase entityCreator;
private MarkerRenderingSystem markerRenderingSystem;
private ObjectRenderingSystem objectRenderingSystem;
// Cameras.
private OrthographicCamera unitaryOrthoCamera;
private OrthographicCamera pixelPerfectOrthoCamera;
private CustomPerspectiveCamera perspectiveCamera;
// Video stream graphics.
private Texture videoFrameTexture;
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;
// Button touch helper fields.
private boolean[] motorButtonsTouched;
private int[] motorButtonsPointers;
private boolean[] motorGamepadButtonPressed;
// Monitors.
private VideoFrameMonitor frameMonitor;
private MotorEventQueue queue;
public InGameState(final NxtARCore core){
this.core = core;
frameMonitor = VideoFrameMonitor.getInstance();
queue = MotorEventQueue.getInstance();
// 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();
// 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;
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;
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;
// 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));
// 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 + backgroundShader.getLog());
backgroundShader = null;
}
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;
perspectiveCamera = null;
frameBufferSprite = null;
RenderParameters.setLightSource1(new LightSource(LIGHT_POSITION, AMBIENT_COLOR, DIFFUSE_COLOR, SPECULAR_COLOR, SHINYNESS));
// Set up the game world.
gameWorld = new World();
entityCreator = new MarkerTestEntityCreator();
entityCreator.setWorld(gameWorld);
entityCreator.createAllEntities();
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();
}
/*;;;;;;;;;;;;;;;;;;;;;;
; BASE STATE METHODS ;
;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public void input() {
// TODO Auto-generated method stub
public void render(float delta){
int w, h;
byte[] frame;
MarkerData data;
TextureRegion region;
float focalPointX, focalPointY, cameraCenterX, cameraCenterY;
// Clear the screen.
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render the background.
core.batch.setProjectionMatrix(pixelPerfectOrthoCamera.combined);
core.batch.begin();{
if(backgroundShader != null){
core.batch.setShader(backgroundShader);
backgroundShader.setUniform2fv("u_scaling", uScaling, 0, 2);
}
background.draw(core.batch);
if(backgroundShader != null) core.batch.setShader(null);
}core.batch.end();
// Fetch the current video frame.
frame = frameMonitor.getCurrentFrame();
w = frameMonitor.getFrameDimensions().getWidth();
h = frameMonitor.getFrameDimensions().getHeight();
// Create the 3D perspective camera and the frame buffer object if they don't exist.
if(perspectiveCamera == null && frameBuffer == null){
frameBuffer = new FrameBuffer(Format.RGBA8888, w, h, true);
frameBuffer.getColorBufferTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear);
perspectiveCamera = new CustomPerspectiveCamera(67, w, h);
perspectiveCamera.translate(0.0f, 0.0f, 0.0f);
perspectiveCamera.near = NEAR;
perspectiveCamera.far = FAR;
perspectiveCamera.lookAt(0.0f, 0.0f, -1.0f);
perspectiveCamera.update();
}
// Attempt to find the markers in the current video frame.
data = core.cvProc.findMarkersInFrame(frame);
// 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.
focalPointX = core.cvProc.getFocalPointX();
focalPointY = core.cvProc.getFocalPointY();
cameraCenterX = core.cvProc.getCameraCenterX();
cameraCenterY = core.cvProc.getCameraCenterY();
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;
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;
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;
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;
// Set rendering parameters.
perspectiveCamera.update(projectionMatrix, true);
RenderParameters.setModelViewProjectionMatrix(perspectiveCamera.combined);
RenderParameters.setEyePosition(perspectiveCamera.position);
// Call rendering systems.
markerRenderingSystem.begin(perspectiveCamera, data);
markerRenderingSystem.process();
markerRenderingSystem.end();
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 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(xSize * ProjectConstants.OVERSCAN, Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN);
frameBufferSprite.rotate90(true);
frameBufferSprite.translate(-frameBufferSprite.getWidth() / 2, -frameBufferSprite.getHeight() / 2);
}
// 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.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);
}core.batch.end();
}
data = null;
}
@Override
public void update() {
// TODO Auto-generated method stub
public void dispose(){
if(modelBatch != null)
modelBatch.dispose();
if(entityCreator != null)
entityCreator.dispose();
if(videoFrameTexture != null)
videoFrameTexture.dispose();
if(buttonTexture != null)
buttonTexture.dispose();
if(buttonTexture2 != null)
buttonTexture2.dispose();
backgroundTexture.dispose();
if(backgroundShader != null)
backgroundShader.dispose();
if(frameBuffer != null)
frameBuffer.dispose();
}
/*;;;;;;;;;;;;;;;;;;
; HELPER METHODS ;
;;;;;;;;;;;;;;;;;;*/
@Override
public void onStateSet(){
stateActive = true;
Gdx.input.setInputProcessor(this);
Gdx.input.setCatchBackKey(true);
Gdx.input.setCatchMenuKey(true);
}
@Override
public void render() {
// TODO Auto-generated method stub
public void onStateUnset(){
stateActive = false;
Gdx.input.setInputProcessor(null);
Gdx.input.setCatchBackKey(false);
Gdx.input.setCatchMenuKey(false);
}
private void setUpButtons(){
buttonTexture = new Texture(Gdx.files.internal("data/gfx/gui/PBCrichton_Flat_Button.png"));
buttonTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
TextureRegion region = new TextureRegion(buttonTexture, 0, 0, buttonTexture.getWidth(), buttonTexture.getHeight());
motorA = new Sprite(region);
motorA.setSize(motorA.getWidth() * 0.7f, motorA.getHeight() * 0.7f);
motorB = new Sprite(region);
motorB.setSize(motorB.getWidth() * 0.7f, motorB.getHeight() * 0.7f);
motorC = new Sprite(region);
motorC.setSize(motorC.getWidth() * 0.7f, motorC.getHeight() * 0.7f);
motorD = new Sprite(region);
motorD.setSize(motorD.getWidth() * 0.7f, motorD.getHeight() * 0.7f);
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());
buttonTexture2 = new Texture(Gdx.files.internal("data/gfx/gui/orange_glowy_button.png"));
headA = new Sprite(buttonTexture2);
headA.setSize(headA.getWidth() * 0.3f, headA.getHeight() * 0.6f);
headB = new Sprite(buttonTexture2);
headB.setSize(headB.getWidth() * 0.3f, headB.getHeight() * 0.6f);
headA.setPosition(-headA.getWidth() - 10, motorA.getY() + (headA.getHeight() / 2));
headB.setPosition(10, motorA.getY() + (headA.getHeight() / 2));
headC = new Sprite(buttonTexture2);
headC.setSize(headC.getWidth() * 0.3f, headC.getHeight() * 0.6f);
headC.setPosition(-(headC.getWidth() / 2), headA.getY() - headA.getHeight() - 10);
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;
; INPUT PROCESSOR METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button){
MotorEvent event;
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
unitaryOrthoCamera.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)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor A button pressed");
motorButtonsTouched[0] = true;
motorButtonsPointers[0] = pointer;
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_A);
event.setPower((byte)100);
queue.addEvent(event);
}else if(motorB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor B button pressed");
motorButtonsTouched[1] = true;
motorButtonsPointers[1] = pointer;
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_A);
event.setPower((byte)-100);
queue.addEvent(event);
}else if(motorC.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor C button pressed");
motorButtonsTouched[2] = true;
motorButtonsPointers[2] = pointer;
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_C);
event.setPower((byte)-100);
queue.addEvent(event);
}else if(motorD.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Motor D button pressed");
motorButtonsTouched[3] = true;
motorButtonsPointers[3] = pointer;
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_C);
event.setPower((byte)100);
queue.addEvent(event);
}else if(headA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Head A button pressed");
motorButtonsTouched[4] = true;
motorButtonsPointers[4] = pointer;
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_B);
event.setPower((byte)-40);
queue.addEvent(event);
}else if(headB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Head B button pressed");
motorButtonsTouched[5] = true;
motorButtonsPointers[5] = pointer;
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_B);
event.setPower((byte)40);
queue.addEvent(event);
}else if(headC.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;
event = new MotorEvent();
event.setMotor(motor_t.RECENTER);
event.setPower((byte)0x00);
queue.addEvent(event);
}
}else{
// TODO: Send input to the input handler system.
}
return true;
}
return false;
}
@Override
public void pause() {
// TODO Auto-generated method stub
public boolean touchUp(int screenX, int screenY, int pointer, int button){
MotorEvent event;
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
unitaryOrthoCamera.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)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor A button released");
motorButtonsPointers[0] = -1;
motorButtonsTouched[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);
}
}else if(motorB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor B button released");
motorButtonsPointers[1] = -1;
motorButtonsTouched[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);
}
}else if(motorC.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor C button released");
motorButtonsPointers[2] = -1;
motorButtonsTouched[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);
}
}else if(motorD.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Motor D button released");
motorButtonsPointers[3] = -1;
motorButtonsTouched[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);
}
}else if(headA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Head A button released");
motorButtonsPointers[4] = -1;
motorButtonsTouched[4] = false;
// Enqueue the event corresponding to releasing this button if the opposing button is not pressed already.
if(!motorButtonsTouched[5]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_B);
event.setPower((byte) 0);
queue.addEvent(event);
}
}else if(headB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchUp() :: Head B button released");
motorButtonsPointers[5] = -1;
motorButtonsTouched[5] = false;
// Enqueue the event corresponding to releasing this button if the opposing button is not pressed already.
if(!motorButtonsTouched[4]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_B);
event.setPower((byte) 0);
queue.addEvent(event);
}
}else if(headC.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.
}
return true;
}
return false;
}
@Override
public void resume() {
// TODO Auto-generated method stub
public boolean touchDragged(int screenX, int screenY, int pointer){
MotorEvent event;
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
unitaryOrthoCamera.unproject(win2world);
touchPointWorldCoords.set(win2world.x * Gdx.graphics.getWidth(), win2world.y * Gdx.graphics.getHeight());
if(pointer == motorButtonsPointers[0] && !motorA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor A button released");
motorButtonsPointers[0] = -1;
motorButtonsTouched[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);
}
}else if(pointer == motorButtonsPointers[1] && !motorB.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor B button released");
motorButtonsPointers[1] = -1;
motorButtonsTouched[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);
}
}else if(pointer == motorButtonsPointers[2] && !motorC.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor C button released");
motorButtonsPointers[2] = -1;
motorButtonsTouched[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);
}
}else if(pointer == motorButtonsPointers[3] && !motorD.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Motor D button released");
motorButtonsPointers[3] = -1;
motorButtonsTouched[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);
}
}else if(pointer == motorButtonsPointers[4] && !headA.getBoundingRectangle().contains(touchPointWorldCoords)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Head A button released");
motorButtonsPointers[4] = -1;
motorButtonsTouched[4] = false;
// Enqueue the event corresponding to releasing this button if the opposing button is not pressed already.
if(!motorButtonsTouched[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)){
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Head B button released");
motorButtonsPointers[5] = -1;
motorButtonsTouched[5] = false;
// Enqueue the event corresponding to releasing this button if the opposing button is not pressed already.
if(!motorButtonsTouched[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)){
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.
}
return true;
}
return false;
}
@Override
public void dispose() {
// TODO Auto-generated method stub
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){
MotorEvent event;
if(stateActive){
Gdx.app.log(TAG, CLASS_NAME + ".buttonDown() :: " + controller.getName() + " :: " + Integer.toString(buttonCode));
if(buttonCode == Ouya.BUTTON_L1){
motorGamepadButtonPressed[0] = true;
if(!motorGamepadButtonPressed[4]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_A);
event.setPower((byte)-100);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_R1){
motorGamepadButtonPressed[1] = true;
if(!motorGamepadButtonPressed[5]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_C);
event.setPower((byte)-100);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_DPAD_LEFT){
motorGamepadButtonPressed[2] = false;
if(!motorGamepadButtonPressed[3]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_B);
event.setPower((byte)-40);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_DPAD_RIGHT){
motorGamepadButtonPressed[3] = false;
if(!motorGamepadButtonPressed[2]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_B);
event.setPower((byte)40);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_L2){
motorGamepadButtonPressed[4] = false;
if(!motorGamepadButtonPressed[0]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_A);
event.setPower((byte)100);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_R2){
motorGamepadButtonPressed[5] = false;
if(!motorGamepadButtonPressed[1]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_C);
event.setPower((byte)100);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_Y){
motorGamepadButtonPressed[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.
}
return true;
}else{
return false;
}
}
@Override
public boolean buttonUp(Controller controller, int buttonCode){
MotorEvent event;
if(stateActive){
Gdx.app.log(TAG, CLASS_NAME + ".buttonDown() :: " + controller.getName() + " :: " + Integer.toString(buttonCode));
if(buttonCode == Ouya.BUTTON_L1){
motorGamepadButtonPressed[0] = false;
if(!motorGamepadButtonPressed[4]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_A);
event.setPower((byte)0);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_R1){
motorGamepadButtonPressed[1] = false;
if(!motorGamepadButtonPressed[5]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_C);
event.setPower((byte)0);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_DPAD_LEFT){
motorGamepadButtonPressed[2] = false;
if(!motorGamepadButtonPressed[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;
if(!motorGamepadButtonPressed[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;
if(!motorGamepadButtonPressed[0]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_A);
event.setPower((byte)0);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_R2){
motorGamepadButtonPressed[5] = false;
if(!motorGamepadButtonPressed[1]){
event = new MotorEvent();
event.setMotor(motor_t.MOTOR_C);
event.setPower((byte)0);
queue.addEvent(event);
}
}else if(buttonCode == Ouya.BUTTON_Y){
motorGamepadButtonPressed[6] = false;
}else{
// TODO: Pass input to the input handler system.
}
return true;
}else{
return false;
}
}
@Override
public boolean axisMoved(Controller controller, int axisCode, float value){
// TODO: Pass input to the input handler system.
return false;
}
}

View File

@@ -0,0 +1,324 @@
/*
* 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.game_states_t;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
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 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";
protected final int NUM_MENU_BUTTONS = 2;
// Helper fields.
protected boolean clientConnected;
private float u_scaling[];
// Buttons and other gui components.
protected TextButton startButton;
protected Rectangle startButtonBBox;
protected Sprite clientConnectedLedOn;
protected Sprite clientConnectedLedOff;
protected TextButton calibrationButton;
protected Rectangle calibrationButtonBBox;
protected Sprite cameraCalibratedLedOn;
protected Sprite cameraCalibratedLedOff;
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 BitmapFont font;
// Other graphics.
private Texture cameraCalibratedLedOffTexture;
private Texture cameraCalibratedLedOnTexture;
private Texture clientConnectedLedOffTexture;
private Texture clientConnectedLedOnTexture;
private Texture backgroundTexture;
private ShaderProgram backgroundShader;
// Button touch helper fields.
protected boolean startButtonTouched;
protected int startButtonTouchPointer;
protected boolean calibrationButtonTouched;
protected int calibrationButtonTouchPointer;
public MainMenuStateBase(){
TextureRegion region;
TextButtonStyle tbs;
FreeTypeFontGenerator generator;
FreeTypeFontParameter param;
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();
// 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);
startButton = new TextButton("Start server", tbs);
startButton.setText("Start game");
startButton.setDisabled(true);
startButtonBBox = new Rectangle(0, 0, startButton.getWidth(), startButton.getHeight());
// Create the calibration button.
calibrationButton = new TextButton("Calibrate camera", tbs);
calibrationButton.setText("Calibrate camera");
calibrationButton.setDisabled(true);
calibrationButtonBBox = new Rectangle(0, 0, calibrationButton.getWidth(), calibrationButton.getHeight());
// Create the connection leds.
clientConnectedLedOnTexture = new Texture("data/gfx/gui/Anonymous_Button_Green.png");
region = new TextureRegion(clientConnectedLedOnTexture);
clientConnectedLedOn = new Sprite(region);
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);
cameraCalibratedLedOn = new Sprite(region);
cameraCalibratedLedOffTexture = new Texture("data/gfx/gui/Anonymous_Button_Red.png");
region = new TextureRegion(cameraCalibratedLedOffTexture);
cameraCalibratedLedOff = new Sprite(region);
// 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);
region = new TextureRegion(backgroundTexture);
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;
win2world = new Vector3(0.0f, 0.0f, 0.0f);
touchPointWorldCoords = new Vector2();
startButtonTouched = false;
startButtonTouchPointer = -1;
calibrationButtonTouched = false;
calibrationButtonTouchPointer = -1;
clientConnected = 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();
backgroundTexture.dispose();
if(backgroundShader != null) backgroundShader.dispose();
font.dispose();
}
protected void drawBackground(SpriteBatch batch){
if(backgroundShader != null){
batch.setShader(backgroundShader);
backgroundShader.setUniform2fv("u_scaling", u_scaling, 0, 2);
}
background.draw(batch);
if(backgroundShader != null) batch.setShader(null);
}
@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);
}
public void onClientConnected(){
clientConnected = true;
startButton.setDisabled(false);
calibrationButton.setDisabled(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(!startButton.isDisabled() && startButtonBBox.contains(touchPointWorldCoords) && !calibrationButtonTouched){
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){
calibrationButton.setChecked(true);
calibrationButtonTouched = true;
calibrationButtonTouchPointer = pointer;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Calibration 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(!startButton.isDisabled() && startButtonBBox.contains(touchPointWorldCoords) && startButtonTouched){
startButton.setChecked(false);
startButtonTouched = false;
startButtonTouchPointer = -1;
core.nextState = game_states_t.IN_GAME;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button released.");
}else if(!calibrationButton.isDisabled() && calibrationButtonBBox.contains(touchPointWorldCoords) && calibrationButtonTouched){
calibrationButton.setChecked(false);
calibrationButtonTouched = false;
calibrationButtonTouchPointer = -1;
core.nextState = game_states_t.CALIBRATION;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Calibration button released.");
}
return true;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer){
unprojectTouch(screenX, screenY);
if(!startButton.isDisabled() && startButtonTouched && pointer == startButtonTouchPointer && !startButtonBBox.contains(touchPointWorldCoords)){
startButtonTouchPointer = -1;
startButtonTouched = false;
startButton.setChecked(false);
Gdx.app.log(TAG, CLASS_NAME + ".touchDragged() :: Start button released.");
}else if(!calibrationButton.isDisabled() && calibrationButtonTouched && pointer == calibrationButtonTouchPointer && !calibrationButtonBBox.contains(touchPointWorldCoords)){
calibrationButtonTouchPointer = -1;
calibrationButtonTouched = false;
calibrationButton.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){
// Ignore.
return true;
}
return false;
}
}

View File

@@ -0,0 +1,171 @@
/*
* 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;
}
}
}

View File

@@ -0,0 +1,202 @@
/*
* 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 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
}

View File

@@ -0,0 +1,71 @@
/*
* 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();
}
}

View File

@@ -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.systems;
import ve.ucv.ciens.ccg.nxtar.components.AnimationComponent;
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;
public class AnimationSystem extends EntityProcessingSystem {
@Mapper ComponentMapper<AnimationComponent> animationMapper;
@SuppressWarnings("unchecked")
public AnimationSystem(){
super(Aspect.getAspectForAll(AnimationComponent.class));
}
@Override
protected void process(Entity e) {
AnimationComponent animation = animationMapper.get(e);
if(animation.current != animation.next && animation.next >= 0 && animation.next < animation.animationsIds.size()){
if(animation.loop)
animation.controller.setAnimation(animation.animationsIds.get(animation.next), -1);
else
animation.controller.setAnimation(animation.animationsIds.get(animation.next));
}
animation.controller.update(Gdx.graphics.getDeltaTime());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Miguel Angel Astor Romero
* 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.
@@ -15,22 +15,23 @@
*/
package ve.ucv.ciens.ccg.nxtar.systems;
import ve.ucv.ciens.ccg.nxtar.components.VideoFrame;
import ve.ucv.ciens.ccg.nxtar.components.ModelComponent;
import com.artemis.Aspect;
import com.artemis.Entity;
import com.artemis.systems.EntityProcessingSystem;
public class RenderSystem extends EntityProcessingSystem {
public class CollisionDetectionSystem extends EntityProcessingSystem {
@SuppressWarnings("unchecked")
public RenderSystem(Aspect aspect) {
super(Aspect.getAspectForAll(VideoFrame.class));
public CollisionDetectionSystem(){
super(Aspect.getAspectForAll(ModelComponent.class));
}
@Override
protected void process(Entity arg0) {
protected void process(Entity e) {
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,77 @@
/*
* 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.GeometryComponent;
import ve.ucv.ciens.ccg.nxtar.components.MarkerCodeComponent;
import ve.ucv.ciens.ccg.nxtar.interfaces.ImageProcessor.MarkerData;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
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;
public class MarkerPositioningSystem extends EntityProcessingSystem {
@Mapper ComponentMapper<MarkerCodeComponent> markerMapper;
@Mapper ComponentMapper<GeometryComponent> geometryMapper;
private static final String TAG = "MARKER_POSITIONING_SYSTEM";
private static final String CLASS_NAME = MarkerPositioningSystem.class.getSimpleName();
private MarkerData markers;
@SuppressWarnings("unchecked")
public MarkerPositioningSystem(){
super(Aspect.getAspectForAll(MarkerCodeComponent.class, GeometryComponent.class));
markers = null;
}
public void setMarkerData(MarkerData markers){
this.markers = markers;
}
@Override
protected void process(Entity e) {
MarkerCodeComponent marker;
GeometryComponent geometry;
if(markers == null)
return;
Gdx.app.log(TAG, CLASS_NAME + ".process(): Getting components.");
marker = markerMapper.get(e);
geometry = geometryMapper.get(e);
Gdx.app.log(TAG, CLASS_NAME + ".process(): Processing markers.");
for(int i = 0; i < ProjectConstants.MAXIMUM_NUMBER_OF_MARKERS; i++){
if(markers.markerCodes[i] != 1){
Gdx.app.log(TAG, CLASS_NAME + ".process(): Checking marker code: " + Integer.toString(markers.markerCodes[i]));
Gdx.app.log(TAG, CLASS_NAME + ".process(): This entity's code is: " + Integer.toString(marker.code));
if(markers.markerCodes[i] == marker.code){
Gdx.app.log(TAG, CLASS_NAME + ".process(): Processing marker code " + Integer.toString(markers.markerCodes[i]) + ".");
geometry.position.set(markers.translationVectors[i]);
geometry.rotation.set(markers.rotationMatrices[i]);
}
}else{
Gdx.app.log(TAG, CLASS_NAME + ".process(): Skipping marker number " + Integer.toString(i) + ".");
}
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.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.interfaces.ImageProcessor.MarkerData;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
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.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.math.Matrix4;
public class MarkerRenderingSystem extends EntityProcessingSystem {
@Mapper ComponentMapper<MarkerCodeComponent> markerMapper;
@Mapper ComponentMapper<GeometryComponent> geometryMapper;
@Mapper ComponentMapper<ModelComponent> modelMapper;
@Mapper ComponentMapper<EnvironmentComponent> environmentMapper;
@Mapper ComponentMapper<ShaderComponent> shaderMapper;
private static final String TAG = "MODEL_BATCH_MARKER_RENDERING_SYSTEM";
private static final String CLASS_NAME = MarkerRenderingSystem.class.getSimpleName();
/**
* <p>A matrix representing 3D translations.</p>
*/
private Matrix4 translationMatrix;
/**
* <p>A matrix representing 3D rotations.</p>
*/
private Matrix4 rotationMatrix;
/**
* <p>A matrix representing 3D scalings.</p>
*/
private Matrix4 scalingMatrix;
private MarkerData markers;
private PerspectiveCamera camera;
private ModelBatch batch;
@SuppressWarnings("unchecked")
public MarkerRenderingSystem(ModelBatch batch){
super(Aspect.getAspectForAll(MarkerCodeComponent.class, GeometryComponent.class, ShaderComponent.class, EnvironmentComponent.class, ModelComponent.class));
markers = null;
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);
}
public void begin(PerspectiveCamera camera, MarkerData markers) throws RuntimeException{
if(this.camera != null)
throw new RuntimeException("Begin called twice without calling end.");
if(this.markers != null)
throw new RuntimeException("Begin called twice without calling end.");
this.markers = markers;
this.camera = camera;
batch.begin(camera);
}
public void end(){
batch.end();
camera = null;
markers = null;
}
@Override
protected void process(Entity e) {
MarkerCodeComponent marker;
GeometryComponent geometry;
EnvironmentComponent environment;
ModelComponent model;
ShaderComponent shader;
if(markers == null || camera == null)
return;
Gdx.app.log(TAG, CLASS_NAME + ".process(): Getting components.");
marker = markerMapper.get(e);
geometry = geometryMapper.get(e);
model = modelMapper.get(e);
environment = environmentMapper.get(e);
shader = shaderMapper.get(e);
Gdx.app.log(TAG, CLASS_NAME + ".process(): Processing markers.");
for(int i = 0; i < ProjectConstants.MAXIMUM_NUMBER_OF_MARKERS; i++){
if(markers.markerCodes[i] != 1){
if(markers.markerCodes[i] == marker.code){
Gdx.app.log(TAG, CLASS_NAME + ".process(): Rendering marker code " + Integer.toString(markers.markerCodes[i]) + ".");
// Set the geometric transformations.
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);
// Apply the geometric transformations to the model.
model.instance.transform.idt().mul(translationMatrix).mul(rotationMatrix).mul(scalingMatrix);
model.instance.calculateTransforms();
// Render the marker;
batch.render(model.instance, environment.environment, shader.shader);
}
}else{
Gdx.app.log(TAG, CLASS_NAME + ".process(): Skipping marker number " + Integer.toString(i) + ".");
}
}
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.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 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.graphics.PerspectiveCamera;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.math.Matrix4;
/**
* <p>Entity processing system in charge of rendering 3D objects using OpenGL. The
* entities to be rendered must have a geometry, shader and mesh component associated.</p>
*/
public class ObjectRenderingSystem extends EntityProcessingSystem {
@Mapper ComponentMapper<GeometryComponent> geometryMapper;
@Mapper ComponentMapper<ShaderComponent> shaderMapper;
@Mapper ComponentMapper<ModelComponent> modelMapper;
@Mapper ComponentMapper<EnvironmentComponent> environmentMapper;
/**
* <p>A matrix representing 3D translations.</p>
*/
private Matrix4 translationMatrix;
/**
* <p>A matrix representing 3D rotations.</p>
*/
private Matrix4 rotationMatrix;
/**
* <p>A matrix representing 3D scalings.</p>
*/
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));
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);
}
public void begin(PerspectiveCamera camera) throws RuntimeException{
if(this.camera != null)
throw new RuntimeException("Begin called twice without calling end.");
this.camera = camera;
batch.begin(camera);
}
public void end(){
batch.end();
camera = null;
}
/**
* <p>Renders the entity passed by parameter, calculating it's corresponding geometric
* transformation and setting and calling it's associated shader program.</p>
*
* @param e The entity to be processed.
*/
@Override
protected void process(Entity e) {
EnvironmentComponent environment;
GeometryComponent geometryComponent;
ShaderComponent shaderComponent;
ModelComponent modelComponent;
// 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();
// Render this entity.
batch.render(modelComponent.instance, environment.environment, shaderComponent.shader);
}
}

View File

@@ -15,13 +15,33 @@
*/
package ve.ucv.ciens.ccg.nxtar.utils;
public abstract class ProjectConstants {
public static final int SERVER_UDP_PORT = 8889;
public static final int SERVER_TCP_PORT_1 = 9989;
public static final int SERVER_TCP_PORT_2 = 9990;
public static final String MULTICAST_ADDRESS = "230.0.0.1";
public static final int EXIT_SUCCESS = 0;
public static final int EXIT_FAILURE = 1;
import com.badlogic.gdx.controllers.mappings.Ouya;
public static final boolean DEBUG = true;
public abstract class ProjectConstants{
public static final int SERVICE_DISCOVERY_PORT = 9988;
public static final int VIDEO_STREAMING_PORT = 9989;
public static final int MOTOR_CONTROL_PORT = 9990;
public static final int SENSOR_REPORT_PORT = 9991;
public static final int APP_CONTROL_PORT = 9992;
public static final String MULTICAST_ADDRESS = "230.0.0.1";
public static final int EXIT_SUCCESS = 0;
public static final int EXIT_FAILURE = 1;
public static final boolean DEBUG = true;
public static final int[] POWERS_OF_2 = {64, 128, 256, 512, 1024, 2048};
public static final float OVERSCAN;
public static final int MENU_BUTTON_FONT_SIZE;
public static final String FONT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
public static final int MAXIMUM_NUMBER_OF_MARKERS = 5;
public static final int CALIBRATION_PATTERN_POINTS = 54;
public static final int CALIBRATION_SAMPLES = 10;
static{
OVERSCAN = Ouya.runningOnOuya ? 0.9f : 1.0f;
MENU_BUTTON_FONT_SIZE = Ouya.runningOnOuya ? 60 : 40;
}
}