31 Commits

Author SHA1 Message Date
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
481fe1f428 Merge branch 'release-14.01.10' 2014-01-09 08:24:50 -04:30
1dc0f5b47f Merge branch 'image_transfer' into develop 2014-01-08 15:30:14 -04:30
689721d75b Fixed some problems receiving the image from NxtCAM. 2014-01-08 15:29:54 -04:30
4a509b0920 Added LICENSE file. 2014-01-07 18:14:20 -04:30
0e51d0ea22 Started fixing the video streaming code. 2014-01-07 18:13:55 -04:30
3fc5ce58c3 Started programming app states. Incorporated Artemis. 2013-12-16 16:02:29 -04:30
c6de50df7f Added licensing information. 2013-12-16 14:47:00 -04:30
27 changed files with 2998 additions and 95 deletions

1
.gitignore vendored
View File

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

202
LICENSE Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,5 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<module>
<source path="ve/ucv/ciens/ccg/nxtar" />
<?xml version="1.0" encoding="UTF-8"?>
<!--
* 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.
-->
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<module>
<source path="ve/ucv/ciens/ccg/nxtar" />
</module>

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};
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

@@ -0,0 +1,33 @@
/*
* 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.networkdata;
import java.io.Serializable;
public final class VideoFrameDataMessage implements Serializable{
private static final long serialVersionUID = 9989L;
public static final int magicNumber = 0x10;
public int imageWidth;
public int imageHeight;
public byte[] data;
public VideoFrameDataMessage(){
imageWidth = -1;
imageHeight = -1;
data = null;
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.networkdata;
import java.io.Serializable;
public final class VideoStreamingControlMessage implements Serializable{
private static final long serialVersionUID = 8898L;
public static final int magicNumber = 0x20;
public byte message;
public VideoStreamingControlMessage(){
message = -1;
}
}

View File

@@ -1,120 +1,330 @@
/*
* 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;
import ve.ucv.ciens.ccg.nxtar.interfaces.CVProcessor;
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.network.RobotControlThread;
import ve.ucv.ciens.ccg.nxtar.network.ServiceDiscoveryThread;
import ve.ucv.ciens.ccg.nxtar.network.VideoStreamingThread;
import ve.ucv.ciens.ccg.nxtar.states.BaseState;
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 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.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;
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>
* @author Miguel Angel Astor Romero
*/
public class NxtARCore extends Game implements NetworkConnectionListener{
private static final String TAG = "NXTAR_CORE_MAIN";
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;
private int connections;
/**
* Valid game states.
*/
public enum game_states_t {
MAIN_MENU(0), IN_GAME(1), PAUSED(2);
private ServiceDiscoveryThread udpThread;
private int value;
private game_states_t(int value){
this.value = value;
}
public int getValue(){
return this.value;
}
};
/**
* 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.
private BaseState[] states;
// Assorted fields.
public SpriteBatch batch;
public CVProcessor cvProc;
private Toaster toaster;
// Networking related fields.
private int connections;
private MulticastEnabler mcastEnabler;
private ServiceDiscoveryThread serviceDiscoveryThread;
private VideoStreamingThread videoThread;
private RobotControlThread robotThread;
// Overlays.
private OrthographicCamera pixelPerfectCamera;
private float fontX;
private float fontY;
private BitmapFont font;
private Texture fadeTexture;
private MutableFloat alpha;
private Tween fadeOut;
private Tween fadeIn;
private boolean fading;
/**
* <p>Set up the basic application fields.</p>
*/
public NxtARCore(Application concreteApp){
super();
connections = 0;
try{
this.toaster = (Toaster)concreteApp;
}catch(ClassCastException cc){
Gdx.app.debug(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement the Toaster interface. Toasting disabled.");
this.toaster = null;
}
try{
this.mcastEnabler = (MulticastEnabler)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.error(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement MulticastEnabler. Quitting.");
Gdx.app.exit();
}
try{
this.cvProc = (CVProcessor)concreteApp;
}catch(ClassCastException cc){
Gdx.app.error(TAG, CLASS_NAME + ".Main() :: concreteApp does not implement the CVProcessor interface. Quitting.");
Gdx.app.exit();
}
}
@Override
public void create(){
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
// Create the state objects.
states = new BaseState[3];
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);
Gdx.app.setLogLevel(Application.LOG_DEBUG);
for(BaseState state : states){
Controllers.addListener(state);
}
camera = new OrthographicCamera(1, h/w);
// Set up fields.
batch = new SpriteBatch();
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);
if(ProjectConstants.DEBUG){
// Set up the overlay font.
fontX = -((Gdx.graphics.getWidth() * ProjectConstants.OVERSCAN) / 2) + 10;
fontY = ((Gdx.graphics.getHeight() * ProjectConstants.OVERSCAN) / 2) - 10;
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);
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.
mcastEnabler.enableMulticast();
Gdx.app.debug(TAG, CLASS_NAME + ".create() :: Creating network threads");
mcastEnabler.enableMulticast();
udpThread = ServiceDiscoveryThread.getInstance();
videoThread = VideoStreamingThread.getInstance().setToaster(toaster);
robotThread = RobotControlThread.getInstance().setToaster(toaster);
serviceDiscoveryThread = ServiceDiscoveryThread.getInstance();
videoThread = VideoStreamingThread.getInstance();
robotThread = RobotControlThread.getInstance();
serviceDiscoveryThread.start();
udpThread.start();
videoThread.start();
videoThread.startStreaming();
videoThread.addNetworkConnectionListener(this);
robotThread.addNetworkConnectionListener(this);
robotThread.start();
// Set the current and next states.
currState = game_states_t.MAIN_MENU;
nextState = null;
this.setScreen(states[currState.getValue()]);
states[currState.getValue()].onStateSet();
// Prepare the fadeToBlack sprite;
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();
alpha = new MutableFloat(0.0f);
fadeOut = Tween.to(alpha, 0, 0.5f).target(1.0f).ease(TweenEquations.easeInQuint);
fadeIn = Tween.to(alpha, 0, 0.5f).target(0.0f).ease(TweenEquations.easeInQuint);
fading = false;
// Set initial input handlers.
Gdx.input.setInputProcessor(states[currState.getValue()]);
Controllers.addListener(states[currState.getValue()]);
// Anything else.
//Gdx.app.setLogLevel(Application.LOG_INFO);
//Gdx.app.setLogLevel(Application.LOG_DEBUG);
Gdx.app.setLogLevel(Application.LOG_NONE);
}
@Override
public void dispose() {
batch.dispose();
texture.dispose();
}
@Override
public void render(){
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
super.render();
batch.setProjectionMatrix(camera.combined);
batch.begin();
sprite.draw(batch);
batch.end();
// If the current state set a value for nextState then switch to that state.
if(nextState != null){
states[currState.getValue()].onStateUnset();
if(!fadeOut.isStarted()){
Gdx.app.log(TAG, CLASS_NAME + ".onRender() :: Starting fade out.");
fadeOut.start();
fading = true;
}else{
Gdx.app.log(TAG, CLASS_NAME + ".onRender() :: Updating fade out.");
fadeOut.update(Gdx.graphics.getDeltaTime());
if(fadeOut.isFinished()){
currState = nextState;
nextState = null;
states[currState.getValue()].onStateSet();
setScreen(states[currState.getValue()]);
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(fadeIn.isStarted()){
if(!fadeIn.isFinished()){
fadeIn.update(Gdx.graphics.getDeltaTime());
}else{
fading = false;
fadeIn.free();
fadeIn = Tween.to(alpha, 0, 0.5f).target(0.0f).ease(TweenEquations.easeInQuint);
}
}
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();
}
if(ProjectConstants.DEBUG){
batch.setProjectionMatrix(pixelPerfectCamera.combined);
batch.begin();{
// Draw the FPS overlay.
font.draw(batch, String.format("Render FPS: %d", Gdx.graphics.getFramesPerSecond()), fontX, fontY);
font.draw(batch, String.format("Total stream FPS: %d", videoThread.getFps()), fontX, fontY - font.getCapHeight() - 5);
font.draw(batch, String.format("Lost stream FPS: %d", videoThread.getLostFrames()), fontX, fontY - (2 * font.getCapHeight()) - 10);
}batch.end();
}
}
@Override
public void resize(int width, int height){
}
@Override
public void pause(){
if(videoThread != null)
videoThread.pause();
}
@Override
public void resume(){
if(videoThread != null)
videoThread.play();
}
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();
}
}
@Override
public synchronized void networkStreamConnected(String streamName){
if(streamName.compareTo(VideoStreamingThread.THREAD_NAME) == 0 || streamName.compareTo(RobotControlThread.THREAD_NAME) == 0)
connections += 1;
//if(streamName.equals(VideoStreamingThread.THREAD_NAME) || streamName.equals(RobotControlThread.THREAD_NAME))
Gdx.app.log(TAG, CLASS_NAME + ".networkStreamConnected() :: Stream " + streamName + " connected.");
connections += 1;
if(connections >= 2){
Gdx.app.debug(TAG, CLASS_NAME + ".networkStreamConnected() :: Stopping service broadcast.");
udpThread.finish();
serviceDiscoveryThread.finish();
mcastEnabler.disableMulticast();
toaster.showShortToast("Client connected");
((MainMenuStateBase)states[game_states_t.MAIN_MENU.getValue()]).onClientConnected();
}
}
public void toast(String msg, boolean longToast){
if(toaster != null){
if(longToast) toaster.showLongToast(msg);
else toaster.showShortToast(msg);
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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

@@ -0,0 +1,26 @@
/*
* 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;
public interface CVProcessor {
public class CVData{
public byte[] outFrame;
public int[] markerCodes;
// TODO: Add marker location data.
}
public CVData processFrame(byte[] frame, int w, int h);
}

View File

@@ -1,3 +1,18 @@
/*
* 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 {

View File

@@ -1,3 +1,18 @@
/*
* 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 NetworkConnectionListener {

View File

@@ -1,3 +1,18 @@
/*
* 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 Toaster {

View File

@@ -1,11 +1,31 @@
/*
* 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;
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.networkdata.MotorEvent;
import ve.ucv.ciens.ccg.networkdata.MotorEventACK;
import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
import ve.ucv.ciens.ccg.nxtar.network.monitors.MotorEventQueue;
import ve.ucv.ciens.ccg.nxtar.utils.ProjectConstants;
import com.badlogic.gdx.Gdx;
@@ -18,20 +38,29 @@ public class RobotControlThread extends Thread {
private NetworkConnectionListener 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();
}
@@ -40,25 +69,110 @@ public class RobotControlThread extends Thread {
return SingletonHolder.INSTANCE;
}
public RobotControlThread setToaster(Toaster toaster){
this.toaster = toaster;
return this;
}
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,36 @@
/*
* 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;
public class SensorReportThread extends Thread {
private SensorReportThread(){
}
private static class SingletonHolder{
public final static SensorReportThread INSTANCE = new SensorReportThread();
}
public static SensorReportThread getInstance(){
return SingletonHolder.INSTANCE;
}
@Override
public void run(){
}
}

View File

@@ -1,3 +1,18 @@
/*
* 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;
import java.io.IOException;
@@ -16,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>
*
@@ -74,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;
@@ -135,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

@@ -1,31 +1,71 @@
/*
* 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;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import ve.ucv.ciens.ccg.networkdata.VideoFrameDataMessage;
import ve.ucv.ciens.ccg.nxtar.interfaces.NetworkConnectionListener;
import ve.ucv.ciens.ccg.nxtar.interfaces.Toaster;
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 NetworkConnectionListener netListener;
private ServerSocket server;
private DatagramSocket socket;
private boolean protocolStarted;
private boolean done;
private boolean pause;
private boolean coreNotified;
private Object protocolPauseMonitor;
private Socket client;
private Toaster toaster;
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;
protocolStarted = false;
done = false;
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);
}
@@ -39,25 +79,183 @@ public class VideoStreamingThread extends Thread {
return SingletonHolder.INSTANCE;
}
public VideoStreamingThread setToaster(Toaster toaster){
this.toaster = toaster;
return this;
}
public void addNetworkConnectionListener(NetworkConnectionListener listener){
netListener = listener;
}
public void startStreaming(){
if(!protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".startStreaming() :: Requesting protocol start.");
synchronized(protocolPauseMonitor){
protocolStarted = true;
protocolPauseMonitor.notifyAll();
}
}
}
public void pauseStreaming(){
if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".pauseStreaming() :: Requesting protocol pause.");
}else
return;
}
public void resumeStreaming(){
if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".resumeStreaming() :: Requesting protocol resume.");
synchronized(protocolPauseMonitor){
protocolPauseMonitor.notifyAll();
}
}else
return;
}
public void finishStreaming(){
if(protocolStarted){
Gdx.app.debug(TAG, CLASS_NAME + ".finishStreaming() :: Requesting protocol end.");
}else
return;
}
public void finish(){
done = true;
}
private int byteArray2Int(byte[] array){
int number = 0;
for(int i = 0; i < 4; i++){
number |= (array[3-i] & 0xff) << (i << 3);
}
return number;
}
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{
packet = new DatagramPacket(size, size.length);
socket.receive(packet);
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: IOException receiving size " + io.getMessage());
lostFramesPerSecond += 1;
return;
}
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Creating buffers.");
intSize = byteArray2Int(size);
data = new byte[intSize];
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;
}
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Saving message in monitor.");
try{
ObjectInputStream ois = new ObjectInputStream(bais);
tmpMessage = ois.readObject();
if(tmpMessage instanceof VideoFrameDataMessage){
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received a data message.");
dataMessage = (VideoFrameDataMessage) tmpMessage;
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);
}else{
Gdx.app.debug(TAG, CLASS_NAME + ".receiveUdp() :: Received something unknown.");
lostFramesPerSecond += 1;
}
}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;
}
}catch(Exception e){
Gdx.app.error(TAG, CLASS_NAME + ".receiveUdp() :: Exception received " + e.getMessage());
lostFramesPerSecond += 1;
return;
}
}
public int getFps(){
return fps;
}
public int getLostFrames(){
return lostFrames;
}
@Override
public void run(){
try{
client = server.accept();
if(netListener != null)
int frames = 0;
lostFrames = 0;
then = System.currentTimeMillis();
while(!done){
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);
toaster.showShortToast("Client connected to VideoStreamingThread");
}
receiveUdp();
frames++;
now = System.currentTimeMillis();
delta = now - then;
if(delta >= 1000){
fps = frames;
frames = 0;
lostFrames = lostFramesPerSecond;
lostFramesPerSecond = 0;
then = now;
delta = 0;
}
}
try{
client.close();
}catch(IOException io){
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error accepting client: " + io.getMessage(), io);
Gdx.app.error(TAG, CLASS_NAME + ".run() :: Error closing client socket.", io);
}
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

@@ -0,0 +1,84 @@
/*
* 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.monitors;
import ve.ucv.ciens.ccg.nxtar.utils.Size;
import com.badlogic.gdx.Gdx;
public class VideoFrameMonitor{
private final String TAG = "VIDEO_FRAME_MONITOR";
private final String CLASS_NAME = VideoFrameMonitor.class.getSimpleName();
private byte[] frameA;
private byte[] frameB;
private Object frameMonitor;
private Size frameDimensions;
private VideoFrameMonitor(){
frameA = null;
frameB = null;
frameMonitor = new Object();
frameDimensions = new Size();
}
private static class SingletonHolder{
public static final VideoFrameMonitor INSTANCE = new VideoFrameMonitor();
}
public static VideoFrameMonitor getInstance(){
return SingletonHolder.INSTANCE;
}
public void setFrameDimensions(int width, int height){
try{
frameDimensions.setWidth(width);
frameDimensions.setHeight(height);
}catch(IllegalArgumentException ia){
Gdx.app.debug(TAG, CLASS_NAME + ".setFrameDimensions() :: Bad argument to Size: " + ia.getMessage());
frameDimensions.setWidth(0);
frameDimensions.setHeight(0);
}
}
public Size getFrameDimensions(){
return frameDimensions;
}
public void setNewFrame(byte[] frame){
byte[] temp;
Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Loading new frame in frameA.");
frameA = frame;
temp = frameA;
synchronized(frameMonitor){
Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping frameA and frameB.");
frameA = frameB;
frameB = temp;
Gdx.app.debug(TAG, CLASS_NAME + ".setNewFrame() :: Swapping done.");
}
}
public byte[] getCurrentFrame(){
byte[] frame;
synchronized(frameMonitor){
//Gdx.app.debug(TAG, CLASS_NAME + ".getCurrentFrame() :: Fetching frameB.");
frame = frameB;
}
return frame;
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.math.Vector3;
public abstract class BaseState implements Screen, ControllerListener, InputProcessor {
protected NxtARCore core;
protected boolean stateActive;
/* STATE METHODS */
public abstract void onStateSet();
public abstract void onStateUnset();
/* SCREEN METHODS*/
@Override
public abstract void render(float delta);
@Override
public abstract void resize(int width, int height);
@Override
public abstract void show();
@Override
public abstract void hide();
@Override
public abstract void pause();
@Override
public abstract void resume();
@Override
public abstract void dispose();
/* INPUT PROCESSOR METHODS. */
@Override
public abstract boolean keyDown(int keycode);
@Override
public abstract boolean keyUp(int keycode);
@Override
public abstract boolean keyTyped(char character);
@Override
public abstract boolean touchDown(int screenX, int screenY, int pointer, int button);
@Override
public abstract boolean touchUp(int screenX, int screenY, int pointer, int button);
@Override
public abstract boolean touchDragged(int screenX, int screenY, int pointer);
@Override
public abstract boolean mouseMoved(int screenX, int screenY);
@Override
public abstract boolean scrolled(int amount);
/* CONTROLLER LISTENER METHODS. */
@Override
public abstract void connected(Controller controller);
@Override
public abstract void disconnected(Controller controller);
@Override
public abstract boolean buttonDown(Controller controller, int buttonCode);
@Override
public abstract boolean buttonUp(Controller controller, int buttonCode);
@Override
public abstract boolean axisMoved(Controller controller, int axisCode, float value);
@Override
public abstract boolean povMoved(Controller controller, int povCode, PovDirection value);
@Override
public abstract boolean xSliderMoved(Controller controller, int sliderCode, boolean value);
@Override
public abstract boolean ySliderMoved(Controller controller, int sliderCode, boolean value);
@Override
public abstract boolean accelerometerMoved(Controller controller, int accelerometerCode, Vector3 value);
}

View File

@@ -0,0 +1,825 @@
/*
* 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.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.interfaces.CVProcessor.CVData;
import ve.ucv.ciens.ccg.nxtar.network.monitors.MotorEventQueue;
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.PovDirection;
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.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.glutils.ShaderProgram;
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 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;
private OrthographicCamera pixelPerfectCamera;
// 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;
// Button touch helper fields.
private Vector3 win2world;
private Vector2 touchPointWorldCoords;
private boolean[] motorButtonsTouched;
private int[] motorButtonsPointers;
private boolean[] motorGamepadButtonPressed;
private boolean[] axisStopSent;
// 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.
pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera = 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[6];
motorButtonsTouched[0] = false;
motorButtonsTouched[1] = false;
motorButtonsTouched[2] = false;
motorButtonsTouched[3] = false;
motorButtonsTouched[4] = false;
motorButtonsTouched[5] = false;
motorButtonsPointers = new int[6];
motorButtonsPointers[0] = -1;
motorButtonsPointers[1] = -1;
motorButtonsPointers[2] = -1;
motorButtonsPointers[3] = -1;
motorButtonsPointers[4] = -1;
motorButtonsPointers[5] = -1;
motorGamepadButtonPressed = new boolean[6];
motorGamepadButtonPressed[0] = false;
motorGamepadButtonPressed[1] = false;
motorGamepadButtonPressed[2] = false;
motorGamepadButtonPressed[3] = false;
motorGamepadButtonPressed[4] = false;
motorGamepadButtonPressed[5] = false;
axisStopSent = new boolean[4];
axisStopSent[0] = true;
axisStopSent[1] = true;
axisStopSent[2] = true;
axisStopSent[3] = true;
backgroundTexture = new Texture(Gdx.files.internal("data/gfx/textures/tile_aqua.png"));
backgroundTexture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
backgroundTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
background = new Sprite(backgroundTexture);
background.setSize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
background.setPosition(-(Gdx.graphics.getWidth() / 2), -(Gdx.graphics.getHeight() / 2));
backgroundShader = new ShaderProgram(Gdx.files.internal(SHADER_PATH + ".vert"), Gdx.files.internal(SHADER_PATH + ".frag"));
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;
}
@Override
public void render(float delta){
int fW, fH;
byte[] frame;
byte[] prevFrame = null;
Size dimensions = null;
CVData data;
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
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();
frame = frameMonitor.getCurrentFrame();
fW = frameMonitor.getFrameDimensions().getWidth();
fH = frameMonitor.getFrameDimensions().getHeight();
data = core.cvProc.processFrame(frame, fW, fH);
if(data != null){
for(int i = 0; i < data.markerCodes.length; i++){
Gdx.app.log(TAG, CLASS_NAME + String.format(".render(): Marker code[%d] = %d", i, data.markerCodes[i]));
}
}
if(data != null && data.outFrame != null && !Arrays.equals(frame, prevFrame)){
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();
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);
}
if(!Ouya.runningOnOuya){
core.batch.setProjectionMatrix(camera.combined);
}else{
core.batch.setProjectionMatrix(pixelPerfectCamera.combined);
}
core.batch.begin();{
renderableVideoFrame.draw(core.batch);
}core.batch.end();
videoFrameTexture.dispose();
}
core.batch.setProjectionMatrix(pixelPerfectCamera.combined);
core.batch.begin();{
if(!Ouya.runningOnOuya){
motorA.draw(core.batch);
motorB.draw(core.batch);
motorC.draw(core.batch);
motorD.draw(core.batch);
headA.draw(core.batch);
headB.draw(core.batch);
}
}core.batch.end();
prevFrame = frame;
}
@Override
public void resize(int width, int height){
// TODO Auto-generated method stub
}
@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();
if(buttonTexture != null)
buttonTexture.dispose();
if(buttonTexture2 != null)
buttonTexture2.dispose();
backgroundTexture.dispose();
if(backgroundShader != null) backgroundShader.dispose();
}
/*;;;;;;;;;;;;;;;;;;
; HELPER METHODS ;
;;;;;;;;;;;;;;;;;;*/
@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);
}
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));
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BEGIN 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);
camera.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);
}
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button){
MotorEvent event;
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
camera.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);
}
}
}
return true;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer){
MotorEvent event;
if(!Ouya.runningOnOuya){
win2world.set(screenX, screenY, 0.0f);
camera.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 + ".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(pointer == motorButtonsPointers[5] && 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);
}
}
}
return true;
}
@Override
public boolean keyDown(int keycode){
if(keycode == Input.Keys.BACK){
// TODO: Go to pause state.
core.nextState = game_states_t.MAIN_MENU;
return true;
}
return false;
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BEGIN CONTROLLER LISTENER METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public boolean buttonDown(Controller controller, int buttonCode){
MotorEvent event;
if(stateActive /*&& Ouya.runningOnOuya*/){
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);
}
}
return true;
}else{
return false;
}
}
@Override
public boolean buttonUp(Controller controller, int buttonCode){
MotorEvent event;
if(stateActive /*&& Ouya.runningOnOuya*/){
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);
}
}
return true;
}else{
return false;
}
}
@Override
public boolean axisMoved(Controller controller, int axisCode, float value){
return false;
}
@Override
public void connected(Controller controller){ }
@Override
public void disconnected(Controller controller){ }
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;
; UNUSED LISTENER METHODS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public boolean mouseMoved(int screenX, int screenY){
return false;
}
@Override
public boolean keyUp(int keycode){
return false;
}
@Override
public boolean keyTyped(char character){
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;
}
@Override
public boolean scrolled(int amount){
return false;
}
}

View File

@@ -0,0 +1,351 @@
/*
* 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.controllers.Controller;
import com.badlogic.gdx.controllers.PovDirection;
import com.badlogic.gdx.controllers.mappings.Ouya;
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.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";
// Helper fields.
protected boolean clientConnected;
private float u_scaling[];
protected OrthographicCamera pixelPerfectCamera;
// Buttons and other gui components.
protected TextButton startButton;
protected Rectangle startButtonBBox;
protected Sprite clientConnectedLedOn;
protected Sprite clientConnectedLedOff;
protected Sprite background;
// Graphic data for the start button.
private Texture startButtonEnabledTexture;
private Texture startButtonDisabledTexture;
private Texture startButtonPressedTexture;
private NinePatch startButtonEnabled9p;
private NinePatch startButtonDisabled9p;
private NinePatch startButtonPressed9p;
private BitmapFont font;
// Other graphics.
private Texture clientConnectedLedOffTexture;
private Texture clientConnectedLedOnTexture;
private Texture backgroundTexture;
private ShaderProgram backgroundShader;
// Button touch helper fields.
private Vector3 win2world;
protected Vector2 touchPointWorldCoords;
protected boolean startButtonTouched;
protected int startButtonTouchPointer;
public MainMenuStateBase(){
TextureRegion region;
this.pixelPerfectCamera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
// Create the start button background.
startButtonEnabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Yellow.png"));
startButtonEnabled9p = new NinePatch(new TextureRegion(startButtonEnabledTexture, 0, 0, startButtonEnabledTexture.getWidth(), startButtonEnabledTexture.getHeight()), 49, 49, 45, 45);
startButtonDisabledTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Cyan.png"));
startButtonDisabled9p = new NinePatch(new TextureRegion(startButtonDisabledTexture, 0, 0, startButtonDisabledTexture.getWidth(), startButtonDisabledTexture.getHeight()), 49, 49, 45, 45);
startButtonPressedTexture = new Texture(Gdx.files.internal("data/gfx/gui/Anonymous_Pill_Button_Blue.png"));
startButtonPressed9p = new NinePatch(new TextureRegion(startButtonPressedTexture, 0, 0, startButtonPressedTexture.getWidth(), startButtonPressedTexture.getHeight()), 49, 49, 45, 45);
// Create the start button font.
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("data/fonts/d-puntillas-B-to-tiptoe.ttf"));
font = generator.generateFont(Ouya.runningOnOuya ? 60 : 40, ProjectConstants.FONT_CHARS, false);
generator.dispose();
// Create the start button itself.
TextButtonStyle tbs = new TextButtonStyle();
tbs.font = font;
tbs.up = new NinePatchDrawable(startButtonEnabled9p);
tbs.checked = new NinePatchDrawable(startButtonPressed9p);
tbs.disabled = new NinePatchDrawable(startButtonDisabled9p);
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 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);
// 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"), Gdx.files.internal(SHADER_PATH + ".frag"));
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;
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(){
startButtonEnabledTexture.dispose();
startButtonDisabledTexture.dispose();
startButtonPressedTexture.dispose();
clientConnectedLedOnTexture.dispose();
clientConnectedLedOffTexture.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);
}
/*;;;;;;;;;;;;;;;;;;
; HELPER METHODS ;
;;;;;;;;;;;;;;;;;;*/
protected void unprojectTouch(int screenX, int screenY){
win2world.set(screenX, screenY, 0.0f);
pixelPerfectCamera.unproject(win2world);
touchPointWorldCoords.set(win2world.x, win2world.y);
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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)){
startButton.setChecked(true);
startButtonTouched = true;
startButtonTouchPointer = pointer;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button pressed.");
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button){
unprojectTouch(screenX, screenY);
Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp(%d, %d, %d, %d)", screenX, screenY, pointer, button));
Gdx.app.log(TAG, CLASS_NAME + String.format(".touchUp() :: Unprojected touch point: (%f, %f)", touchPointWorldCoords.x, touchPointWorldCoords.y));
if(!startButton.isDisabled() && startButtonBBox.contains(touchPointWorldCoords)){
startButton.setChecked(false);
startButtonTouched = false;
startButtonTouchPointer = -1;
core.nextState = game_states_t.IN_GAME;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button released.");
}
return true;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer){
unprojectTouch(screenX, screenY);
if(!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.");
}
return true;
}
@Override
public boolean keyDown(int keycode){
if(keycode == Input.Keys.BACK){
// Ignore.
return true;
}
return false;
}
/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; INPUT LISTENER METHOD STUBS ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
@Override
public boolean keyUp(int keycode){
return false;
}
@Override
public boolean keyTyped(char character){
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY){
return false;
}
@Override
public boolean scrolled(int amount){
return false;
}
@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,125 @@
/*
* 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.GL10;
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;
public OuyaMainMenuState(final NxtARCore core){
this.core = core;
startButton.setPosition(-(startButton.getWidth() / 2), -(startButton.getHeight() / 2));
startButtonBBox.setPosition(startButton.getX(), startButton.getY());
float ledYPos = (-(Gdx.graphics.getHeight() / 2) * 0.5f) + (startButton.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);
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);
ouyaOButton.setPosition(startButton.getX() - ouyaOButton.getWidth() - 20, startButton.getY() + (ouyaOButton.getHeight() / 2));
oButtonPressed = false;
}
@Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.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);
}
startButton.draw(core.batch, 1.0f);
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) {
if(stateActive){
if(buttonCode == Ouya.BUTTON_O){
if(!clientConnected){
core.toast("Can't start the game. No client is connected.", true);
}else{
oButtonPressed = true;
startButton.setChecked(true);
}
}
return true;
}else{
return false;
}
}
@Override
public boolean buttonUp(Controller controller, int buttonCode) {
if(stateActive){
if(buttonCode == Ouya.BUTTON_O){
if(oButtonPressed){
oButtonPressed = false;
startButton.setChecked(false);
core.nextState = game_states_t.IN_GAME;
Gdx.app.log(TAG, CLASS_NAME + ".touchDown() :: Start button released.");
}
}
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,56 @@
/*
* 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.GL10;
public class TabletMainMenuState extends MainMenuStateBase{
public TabletMainMenuState(final NxtARCore core){
this.core = core;
startButton.setPosition(-(startButton.getWidth() / 2), -(startButton.getHeight() / 2));
startButtonBBox.setPosition(startButton.getX(), startButton.getY());
float ledYPos = (-(Gdx.graphics.getHeight() / 2) * 0.5f) + (startButton.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);
}
@Override
public void render(float delta){
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.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);
}
startButton.draw(core.batch, 1.0f);
}core.batch.end();
}
}

View File

@@ -1,12 +1,43 @@
/*
* 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.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;
import com.badlogic.gdx.controllers.mappings.Ouya;
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 String FONT_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static{
OVERSCAN = Ouya.runningOnOuya ? 0.9f : 1.0f;
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2013 Miguel Angel Astor Romero
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ve.ucv.ciens.ccg.nxtar.utils;
public class Size {
private int width;
private int height;
public Size(){
width = 0;
height = 0;
}
public Size(int width, int height){
this.width = (width >= 0) ? width : -1 * width;
this.height = (height >= 0) ? height : -1 * height;
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
public void setWidth(int width) throws IllegalArgumentException{
if(width < 0)
throw new IllegalArgumentException("Width must not be less than cero.");
this.width = width;
}
public void setHeight(int height) throws IllegalArgumentException{
if(height < 0)
throw new IllegalArgumentException("Height must not be less than cero.");
this.height = height;
}
}