Created
September 21, 2013 21:21
-
-
Save xoppa/6654328 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.badlogic.gdx.tests.g3d; | |
import com.badlogic.gdx.Gdx; | |
import com.badlogic.gdx.InputMultiplexer; | |
import com.badlogic.gdx.assets.AssetManager; | |
import com.badlogic.gdx.graphics.Camera; | |
import com.badlogic.gdx.graphics.GL10; | |
import com.badlogic.gdx.graphics.PerspectiveCamera; | |
import com.badlogic.gdx.graphics.g3d.Model; | |
import com.badlogic.gdx.graphics.g3d.ModelBatch; | |
import com.badlogic.gdx.graphics.g3d.ModelInstance; | |
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight; | |
import com.badlogic.gdx.graphics.g3d.environment.Environment; | |
import com.badlogic.gdx.graphics.g3d.utils.CameraInputController; | |
import com.badlogic.gdx.math.Matrix4; | |
import com.badlogic.gdx.math.Quaternion; | |
import com.badlogic.gdx.math.Vector3; | |
import com.badlogic.gdx.math.collision.BoundingBox; | |
import com.badlogic.gdx.math.collision.Ray; | |
import com.badlogic.gdx.tests.utils.GdxTest; | |
import com.badlogic.gdx.utils.Array; | |
public class Basic3DTest extends GdxTest /* GdxTest = extends InputAdapter implements ApplicationListener */ { | |
public PerspectiveCamera cam; | |
public CameraInputController camController; | |
public ModelBatch modelBatch; | |
public AssetManager assets; | |
public Array<ModelInstance> instances = new Array<ModelInstance>(); | |
public Environment lights; | |
public boolean loading; | |
public int currentDice = -1; | |
public float diceDistance; | |
public float diceRadiusSquared; | |
public float diceRadius; | |
public Vector3 previousTouchPoint = new Vector3(); | |
public Vector3 currentTouchPoint = new Vector3(); | |
public Vector3 touchPos = new Vector3(); | |
public Vector3 xAxis = new Vector3(); | |
public Vector3 yAxis = new Vector3(); | |
public Vector3 zAxis = new Vector3(); | |
public Array<ModelInstance> dices = new Array<ModelInstance>(); | |
@Override | |
public void create () { | |
modelBatch = new ModelBatch(); | |
lights = new Environment(); | |
lights.ambientLight.set(0.4f, 0.4f, 0.4f, 1f); | |
lights.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f)); | |
cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); | |
cam.position.set(0f, 5f, 8f); | |
cam.lookAt(0,0,0); | |
cam.near = 0.1f; | |
cam.far = 300f; | |
cam.normalizeUp(); | |
cam.update(); | |
camController = new CameraInputController(cam); | |
Gdx.input.setInputProcessor(new InputMultiplexer(this, camController)); | |
assets = new AssetManager(); | |
assets.load("data/g3d/test/dieYellow.g3db", Model.class); | |
loading = true; | |
} | |
private void doneLoading() { | |
Model model = assets.get("data/g3d/test/dieYellow.g3db", Model.class); | |
BoundingBox bounds = new BoundingBox(); | |
model.getNode("Die").calculateBoundingBox(bounds); | |
diceRadius = .5f * bounds.getDimensions().len(); | |
diceRadiusSquared = diceRadius * diceRadius; | |
for (float x = -5f; x <= 7f; x += 5f) { | |
ModelInstance aDie = new ModelInstance(model, "Die"); | |
aDie.transform.setToTranslation(x, 0, 0); | |
instances.add(aDie); | |
dices.add(aDie); | |
} | |
loading = false; | |
} | |
@Override | |
public void render () { | |
if (loading && assets.update()) | |
doneLoading(); | |
camController.update(); | |
Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); | |
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); | |
modelBatch.begin(cam); | |
modelBatch.render(dices, lights); | |
modelBatch.end(); | |
} | |
@Override | |
public void dispose () { | |
modelBatch.dispose(); | |
dices.clear(); | |
assets.dispose(); | |
} | |
@Override | |
public void resize(int width, int height) { | |
cam.viewportHeight = width; | |
cam.viewportHeight = height; | |
cam.update(); | |
} | |
@Override public void pause() { } | |
@Override public void resume() { } | |
@Override | |
public boolean touchDragged (int screenX, int screenY, int pointer) { | |
if (currentDice < 0) | |
return false; | |
Ray ray = cam.getPickRay(screenX, screenY); | |
currentTouchPoint.set(ray.direction).scl(diceDistance).add(ray.origin); | |
float degreesY = 90 * (currentTouchPoint.x - previousTouchPoint.x) / diceRadius; | |
float degreesX = 90 * (currentTouchPoint.y - previousTouchPoint.y) / diceRadius; | |
previousTouchPoint.set(currentTouchPoint); | |
rotateDice(dices.get(currentDice).transform, xAxis, degreesX, yAxis, degreesY); | |
return true; | |
} | |
@Override | |
public boolean touchDown (int screenX, int screenY, int pointer, int button) { | |
Ray ray = cam.getPickRay(screenX, screenY); | |
currentDice = getTouchedInstance(touchPos, dices, diceRadiusSquared, ray); | |
if (currentDice < 0) | |
return false; | |
diceDistance = cam.position.dst(touchPos); | |
previousTouchPoint.set(ray.direction).scl(diceDistance).add(ray.origin); | |
// Calculate the axes with respect of the camera, doesn't consider angle>180 (use dot()<0 for that) | |
// (e.g. rotation might be reverse when looking from behind) | |
zAxis.set(cam.direction).scl(-1); | |
xAxis.set(zAxis).crs(cam.up); | |
yAxis.set(xAxis).crs(zAxis); | |
return true; | |
} | |
private static final Vector3 previousPosition = new Vector3(); | |
private static final Quaternion previousRotation = new Quaternion(); | |
/** | |
* Rotates a transformation matrix around the specified X and Y axis with the specified degrees for each axis. | |
*/ | |
public static void rotateDice(Matrix4 transform, Vector3 xAxis, float degreesX, Vector3 yAxis, float degreesY) { | |
transform.getTranslation(previousPosition); | |
transform.getRotation(previousRotation).nor(); | |
transform.idt(); | |
transform.rotate(xAxis, degreesX); | |
transform.rotate(yAxis, degreesY); | |
transform.rotate(previousRotation); | |
transform.trn(previousPosition); | |
} | |
private final static Vector3 tmpV1 = new Vector3(); | |
/** | |
* Checks which instance is touched given the ray (see {@link Camera#getPickRay(float, float)}). Assumes all instances | |
* have the same bounding sphere. | |
* @param out Receives the location in world space closest to the instance | |
* @param instances The array of instances to check | |
* @param squaredRadius The squared radius of the bounding sphere for each instance | |
* @param ray The pick ray (see {@link Camera#getPickRay(float, float)} | |
* @return The index of the instance within the array that is touched, or negative if none. | |
*/ | |
public static int getTouchedInstance(Vector3 out, Array<ModelInstance> instances, float squaredRadius, Ray ray) { | |
int result = -1; | |
float dist = -1; | |
for (int i = instances.size - 1; i >= 0; --i) { | |
instances.get(i).transform.getTranslation(tmpV1); | |
final float d = raySphereIntersection(tmpV1, squaredRadius, ray.origin, ray.direction); | |
if (d >= 0f && (dist < 0 || d < dist)) { | |
out.set(tmpV1); | |
result = i; | |
dist = d; | |
} | |
} | |
if (result >= 0) | |
out.add(ray.origin); | |
return result; | |
} | |
/** | |
* Checks if the given ray intersect the given sphere. And if so, set the sphereCenter to the point on the ray closest to | |
* the center of the sphere and returns the distance of the sphere to the origin of the ray. If the sphere is behind | |
* the origin of the ray or if the ray doesn't intersects the sphere, it will return negative. | |
* The sphereCenter parameter will always be altered, regardless intersection. | |
* @param sphereCenter The center of the sphere, will receive the location of point closest to the center of sphere | |
* relative to the ray origin. | |
* @param sphereRadiusSquared The squared radius of the sphere | |
* @param rayOrigin The origin of the ray (see {@link Ray#origin}) | |
* @param rayDirection The direction of the ray, must be normalized (see {@link Ray#direction}) | |
* @return On success, the squared distance from the origin to the closest point to the center of the sphere, | |
* or negative on no intersection. | |
*/ | |
public static float raySphereIntersection(Vector3 sphereCenter, float sphereRadiusSquared, Vector3 rayOrigin, Vector3 rayDirection) { | |
sphereCenter.sub(rayOrigin); | |
float sx = sphereCenter.x, sy = sphereCenter.y, sz = sphereCenter.z; | |
final float len = rayDirection.dot(sphereCenter); | |
if (len <= 0.f) | |
return -1f; | |
sphereCenter.set(rayDirection).scl(len); | |
if (sphereCenter.dst2(sx, sy, sz) > sphereRadiusSquared) | |
return -1f; | |
return sphereCenter.len2(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment