|
@@ -0,0 +1,535 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2009-2021 jMonkeyEngine
|
|
|
+ * All rights reserved.
|
|
|
+ *
|
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
|
+ * modification, are permitted provided that the following conditions are
|
|
|
+ * met:
|
|
|
+ *
|
|
|
+ * * Redistributions of source code must retain the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
|
+ *
|
|
|
+ * * Redistributions in binary form must reproduce the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
|
+ *
|
|
|
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
|
+ * may be used to endorse or promote products derived from this software
|
|
|
+ * without specific prior written permission.
|
|
|
+ *
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ */
|
|
|
+package jme3test.games;
|
|
|
+
|
|
|
+import com.jme3.app.Application;
|
|
|
+import com.jme3.app.SimpleApplication;
|
|
|
+import com.jme3.app.state.AbstractAppState;
|
|
|
+import com.jme3.app.state.AppStateManager;
|
|
|
+import com.jme3.bullet.BulletAppState;
|
|
|
+import com.jme3.bullet.collision.shapes.CollisionShape;
|
|
|
+import com.jme3.bullet.collision.shapes.MeshCollisionShape;
|
|
|
+import com.jme3.bullet.collision.shapes.SphereCollisionShape;
|
|
|
+import com.jme3.bullet.control.RigidBodyControl;
|
|
|
+import com.jme3.bullet.debug.DebugTools;
|
|
|
+import com.jme3.font.BitmapText;
|
|
|
+import com.jme3.input.KeyInput;
|
|
|
+import com.jme3.input.controls.AnalogListener;
|
|
|
+import com.jme3.input.controls.KeyTrigger;
|
|
|
+import com.jme3.material.Material;
|
|
|
+import com.jme3.math.ColorRGBA;
|
|
|
+import com.jme3.math.FastMath;
|
|
|
+import com.jme3.math.Ray;
|
|
|
+import com.jme3.math.Vector3f;
|
|
|
+import com.jme3.post.FilterPostProcessor;
|
|
|
+import com.jme3.post.filters.FogFilter;
|
|
|
+import com.jme3.scene.Geometry;
|
|
|
+import com.jme3.scene.Mesh;
|
|
|
+import com.jme3.scene.Node;
|
|
|
+import com.jme3.scene.Spatial;
|
|
|
+import com.jme3.scene.shape.Sphere;
|
|
|
+import java.util.Random;
|
|
|
+import java.util.logging.Level;
|
|
|
+import java.util.logging.Logger;
|
|
|
+
|
|
|
+/**
|
|
|
+ * WorldOfInception - Find the galaxy center ;)
|
|
|
+ *
|
|
|
+ * @author normenhansen
|
|
|
+ */
|
|
|
+public class WorldOfInception extends SimpleApplication implements AnalogListener {
|
|
|
+
|
|
|
+ //Assumptions: POI radius in world == 1, only one player, vector3f hash describes enough worlds
|
|
|
+ private static final Logger logger = Logger.getLogger(WorldOfInception.class.getName());
|
|
|
+ private static final Random random = new Random(System.currentTimeMillis());
|
|
|
+ private static final float scaleDist = 10;
|
|
|
+ private static final float poiRadius = 100;
|
|
|
+ private static final int poiCount = 30;
|
|
|
+ private static Material poiMaterial;
|
|
|
+ private static Mesh poiMesh;
|
|
|
+ private static Material ballMaterial;
|
|
|
+ private static Mesh ballMesh;
|
|
|
+ private static CollisionShape poiHorizonCollisionShape;
|
|
|
+ private static CollisionShape poiCollisionShape;
|
|
|
+ private static CollisionShape ballCollisionShape;
|
|
|
+ private InceptionLevel currentLevel;
|
|
|
+ private final Vector3f walkDirection = new Vector3f();
|
|
|
+ private static DebugTools debugTools;
|
|
|
+
|
|
|
+ public WorldOfInception() {
|
|
|
+ //base level vector position hash == seed
|
|
|
+ super(new InceptionLevel(null, Vector3f.ZERO));
|
|
|
+ currentLevel = super.getStateManager().getState(InceptionLevel.class);
|
|
|
+ currentLevel.takeOverParent();
|
|
|
+ currentLevel.getRootNode().setLocalScale(Vector3f.UNIT_XYZ);
|
|
|
+ currentLevel.getRootNode().setLocalTranslation(Vector3f.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) {
|
|
|
+ WorldOfInception app = new WorldOfInception();
|
|
|
+ app.start();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void simpleInitApp() {
|
|
|
+ //set far frustum only so we see the outer world longer
|
|
|
+ cam.setFrustumFar(10000);
|
|
|
+ cam.setLocation(Vector3f.ZERO);
|
|
|
+ debugTools = new DebugTools(assetManager);
|
|
|
+ rootNode.attachChild(debugTools.debugNode);
|
|
|
+ poiMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
|
|
+ poiMaterial.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
|
|
+ poiMesh = new Sphere(16, 16, 1f);
|
|
|
+
|
|
|
+ ballMaterial = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
|
|
|
+ ballMaterial.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg"));
|
|
|
+ ballMaterial.setColor("Color", ColorRGBA.Red);
|
|
|
+ ballMesh = new Sphere(16, 16, 1.0f);
|
|
|
+
|
|
|
+ poiHorizonCollisionShape = new MeshCollisionShape(new Sphere(128, 128, poiRadius));
|
|
|
+ poiCollisionShape = new SphereCollisionShape(1f);
|
|
|
+ ballCollisionShape = new SphereCollisionShape(1f);
|
|
|
+ setupKeys();
|
|
|
+ setupDisplay();
|
|
|
+ setupFog();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setupKeys() {
|
|
|
+ inputManager.addMapping("StrafeLeft", new KeyTrigger(KeyInput.KEY_A));
|
|
|
+ inputManager.addMapping("StrafeRight", new KeyTrigger(KeyInput.KEY_D));
|
|
|
+ inputManager.addMapping("Forward", new KeyTrigger(KeyInput.KEY_W));
|
|
|
+ inputManager.addMapping("Back", new KeyTrigger(KeyInput.KEY_S));
|
|
|
+ inputManager.addMapping("StrafeUp", new KeyTrigger(KeyInput.KEY_Q));
|
|
|
+ inputManager.addMapping("StrafeDown", new KeyTrigger(KeyInput.KEY_Z), new KeyTrigger(KeyInput.KEY_Y));
|
|
|
+ inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
|
|
|
+ inputManager.addMapping("Return", new KeyTrigger(KeyInput.KEY_RETURN));
|
|
|
+ inputManager.addMapping("Esc", new KeyTrigger(KeyInput.KEY_ESCAPE));
|
|
|
+ inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_UP));
|
|
|
+ inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_DOWN));
|
|
|
+ inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_LEFT));
|
|
|
+ inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_RIGHT));
|
|
|
+ inputManager.addListener(this, "StrafeLeft", "StrafeRight", "Forward", "Back", "StrafeUp", "StrafeDown", "Space", "Reset", "Esc", "Up", "Down", "Left", "Right");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setupDisplay() {
|
|
|
+ if (fpsText == null) {
|
|
|
+ fpsText = new BitmapText(guiFont);
|
|
|
+ }
|
|
|
+ fpsText.setLocalScale(0.7f, 0.7f, 0.7f);
|
|
|
+ fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0);
|
|
|
+ fpsText.setText("");
|
|
|
+ fpsText.setCullHint(Spatial.CullHint.Never);
|
|
|
+ guiNode.attachChild(fpsText);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setupFog() {
|
|
|
+ // use fog to give more sense of depth
|
|
|
+ FilterPostProcessor fpp;
|
|
|
+ FogFilter fog;
|
|
|
+ fpp=new FilterPostProcessor(assetManager);
|
|
|
+ fog=new FogFilter();
|
|
|
+ fog.setFogColor(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f));
|
|
|
+ fog.setFogDistance(poiRadius);
|
|
|
+ fog.setFogDensity(2.0f);
|
|
|
+ fpp.addFilter(fog);
|
|
|
+ viewPort.addProcessor(fpp);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onAnalog(String name, float value, float tpf) {
|
|
|
+ Vector3f left = rootNode.getLocalRotation().mult(Vector3f.UNIT_X.negate());
|
|
|
+ Vector3f forward = rootNode.getLocalRotation().mult(Vector3f.UNIT_Z.negate());
|
|
|
+ Vector3f up = rootNode.getLocalRotation().mult(Vector3f.UNIT_Y);
|
|
|
+ //TODO: properly scale input based on current scaling level
|
|
|
+ tpf = tpf * (10 - (9.0f * currentLevel.getCurrentScaleAmount()));
|
|
|
+ if (name.equals("StrafeLeft") && value > 0) {
|
|
|
+ walkDirection.addLocal(left.mult(tpf));
|
|
|
+ } else if (name.equals("StrafeRight") && value > 0) {
|
|
|
+ walkDirection.addLocal(left.negate().multLocal(tpf));
|
|
|
+ } else if (name.equals("Forward") && value > 0) {
|
|
|
+ walkDirection.addLocal(forward.mult(tpf));
|
|
|
+ } else if (name.equals("Back") && value > 0) {
|
|
|
+ walkDirection.addLocal(forward.negate().multLocal(tpf));
|
|
|
+ } else if (name.equals("StrafeUp") && value > 0) {
|
|
|
+ walkDirection.addLocal(up.mult(tpf));
|
|
|
+ } else if (name.equals("StrafeDown") && value > 0) {
|
|
|
+ walkDirection.addLocal(up.negate().multLocal(tpf));
|
|
|
+ } else if (name.equals("Up") && value > 0) {
|
|
|
+ //TODO: rotate rootNode, needs to be global
|
|
|
+ } else if (name.equals("Down") && value > 0) {
|
|
|
+ } else if (name.equals("Left") && value > 0) {
|
|
|
+ } else if (name.equals("Right") && value > 0) {
|
|
|
+ } else if (name.equals("Esc")) {
|
|
|
+ stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void simpleUpdate(float tpf) {
|
|
|
+ currentLevel = currentLevel.getCurrentLevel();
|
|
|
+ currentLevel.move(walkDirection);
|
|
|
+ fpsText.setText("Location: " + currentLevel.getCoordinates());
|
|
|
+ walkDirection.set(Vector3f.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static class InceptionLevel extends AbstractAppState {
|
|
|
+
|
|
|
+ private final InceptionLevel parent;
|
|
|
+ private final Vector3f inParentPosition;
|
|
|
+ private SimpleApplication application;
|
|
|
+ private BulletAppState physicsState;
|
|
|
+ private Node rootNode;
|
|
|
+ private Vector3f playerPos;
|
|
|
+ private InceptionLevel currentActiveChild;
|
|
|
+ private InceptionLevel currentReturnLevel;
|
|
|
+ private float curScaleAmount = 0;
|
|
|
+
|
|
|
+ public InceptionLevel(InceptionLevel parent, Vector3f inParentPosition) {
|
|
|
+ this.parent = parent;
|
|
|
+ this.inParentPosition = inParentPosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void update(float tpf) {
|
|
|
+ super.update(tpf);
|
|
|
+ if (currentReturnLevel != this) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ debugTools.setYellowArrow(new Vector3f(0, 0, -2), playerPos.divide(poiRadius));
|
|
|
+ float curLocalDist = getPlayerPosition().length();
|
|
|
+ // If we are outside the range of one point of interest, move out to
|
|
|
+ // the next upper level
|
|
|
+ if (curLocalDist > poiRadius + FastMath.ZERO_TOLERANCE) { //DAFUQ normalize?
|
|
|
+ if (parent == null) {
|
|
|
+ //TODO: could add new nodes coming in instead for literally endless space
|
|
|
+ logger.log(Level.INFO, "Hit event horizon");
|
|
|
+ currentReturnLevel = this;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //give to parent
|
|
|
+ logger.log(Level.INFO, "give to parent");
|
|
|
+ parent.takeOverChild(inParentPosition.add(playerPos.normalize()));
|
|
|
+ application.getStateManager().attach(parent);
|
|
|
+ currentReturnLevel = parent;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ AppStateManager stateManager = application.getStateManager();
|
|
|
+ // We create child positions based on the parent position hash, so we
|
|
|
+ // should in practice get the same galaxy w/o too many doubles
|
|
|
+ // with each run with the same root vector.
|
|
|
+ Vector3f[] vectors = getPositions(poiCount, inParentPosition.hashCode());
|
|
|
+ for (int i = 0; i < vectors.length; i++) {
|
|
|
+ Vector3f vector3f = vectors[i];
|
|
|
+ //negative rootNode location is our actual player position
|
|
|
+ Vector3f distVect = vector3f.subtract(playerPos);
|
|
|
+ float distance = distVect.length();
|
|
|
+ if (distance <= 1) {
|
|
|
+ checkActiveChild(vector3f);
|
|
|
+ float percent = 0;
|
|
|
+ curScaleAmount = 0;
|
|
|
+ this.scaleAsParent(percent, playerPos, distVect);
|
|
|
+ currentActiveChild.scaleAsChild(percent, distVect);
|
|
|
+ logger.log(Level.INFO, "Give over to child {0}", currentActiveChild);
|
|
|
+ currentActiveChild.takeOverParent();
|
|
|
+ stateManager.detach(this);
|
|
|
+ currentReturnLevel = currentActiveChild;
|
|
|
+ return;
|
|
|
+ } else if (distance <= 1 + scaleDist) {
|
|
|
+ debugTools.setRedArrow(Vector3f.ZERO, distVect);
|
|
|
+ checkActiveChild(vector3f);
|
|
|
+ //TODO: scale percent nicer for less of an "explosion" effect
|
|
|
+ float percent = 1 - mapValue(distance - 1, 0, scaleDist, 0, 1);
|
|
|
+ curScaleAmount = percent;
|
|
|
+ rootNode.getChild(i).setCullHint(Spatial.CullHint.Always);
|
|
|
+ this.scaleAsParent(percent, playerPos, distVect);
|
|
|
+ currentActiveChild.scaleAsChild(percent, distVect);
|
|
|
+ currentReturnLevel = this;
|
|
|
+ return;
|
|
|
+ } else if (currentActiveChild != null && currentActiveChild.getPositionInParent().equals(vector3f)) {
|
|
|
+ //TODO: doing this here causes problems when close to multiple POIs
|
|
|
+ rootNode.getChild(i).setCullHint(Spatial.CullHint.Inherit);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ checkActiveChild(null);
|
|
|
+ curScaleAmount = 0;
|
|
|
+ rootNode.setLocalScale(1);
|
|
|
+ rootNode.setLocalTranslation(playerPos.negate());
|
|
|
+ debugTools.setRedArrow(Vector3f.ZERO, Vector3f.ZERO);
|
|
|
+ debugTools.setBlueArrow(Vector3f.ZERO, Vector3f.ZERO);
|
|
|
+ debugTools.setGreenArrow(Vector3f.ZERO, Vector3f.ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void checkActiveChild(Vector3f vector3f) {
|
|
|
+ AppStateManager stateManager = application.getStateManager();
|
|
|
+ if(vector3f == null){
|
|
|
+ if(currentActiveChild != null){
|
|
|
+ logger.log(Level.INFO, "Detach child {0}", currentActiveChild);
|
|
|
+ stateManager.detach(currentActiveChild);
|
|
|
+ currentActiveChild = null;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (currentActiveChild == null) {
|
|
|
+ currentActiveChild = new InceptionLevel(this, vector3f);
|
|
|
+ stateManager.attach(currentActiveChild);
|
|
|
+ logger.log(Level.INFO, "Attach child {0}", currentActiveChild);
|
|
|
+ } else if (!currentActiveChild.getPositionInParent().equals(vector3f)) {
|
|
|
+ logger.log(Level.INFO, "Switching from child {0}", currentActiveChild);
|
|
|
+ stateManager.detach(currentActiveChild);
|
|
|
+ currentActiveChild = new InceptionLevel(this, vector3f);
|
|
|
+ stateManager.attach(currentActiveChild);
|
|
|
+ logger.log(Level.INFO, "Attach child {0}", currentActiveChild);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void scaleAsChild(float percent, Vector3f dist) {
|
|
|
+ float childScale = mapValue(percent, 1.0f / poiRadius, 1);
|
|
|
+ Vector3f distToHorizon = dist.normalize();
|
|
|
+ Vector3f scaledDistToHorizon = distToHorizon.mult(childScale * poiRadius);
|
|
|
+ Vector3f rootOff = dist.add(scaledDistToHorizon);
|
|
|
+ debugTools.setBlueArrow(Vector3f.ZERO, rootOff);
|
|
|
+ getRootNode().setLocalScale(childScale);
|
|
|
+ getRootNode().setLocalTranslation(rootOff);
|
|
|
+ //prepare player position already
|
|
|
+ Vector3f playerPosition = dist.normalize().mult(-poiRadius);
|
|
|
+ setPlayerPosition(playerPosition);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void scaleAsParent(float percent, Vector3f playerPos, Vector3f dist) {
|
|
|
+ float scale = mapValue(percent, 1.0f, poiRadius);
|
|
|
+ Vector3f distToHorizon = dist.subtract(dist.normalize());
|
|
|
+ Vector3f offLocation = playerPos.add(distToHorizon);
|
|
|
+ Vector3f rootOff = offLocation.mult(scale).negate();
|
|
|
+ rootOff.addLocal(dist);
|
|
|
+ debugTools.setGreenArrow(Vector3f.ZERO, offLocation);
|
|
|
+ getRootNode().setLocalScale(scale);
|
|
|
+ getRootNode().setLocalTranslation(rootOff);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void takeOverParent() {
|
|
|
+ //got playerPos from scaleAsChild before
|
|
|
+ getPlayerPosition().normalizeLocal().multLocal(poiRadius);
|
|
|
+ currentReturnLevel = this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void takeOverChild(Vector3f playerPos) {
|
|
|
+ this.playerPos.set(playerPos);
|
|
|
+ currentReturnLevel = this;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InceptionLevel getLastLevel(Ray pickRay) {
|
|
|
+ // TODO: get a level based on positions getting ever more accurate,
|
|
|
+ // from any given position
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InceptionLevel getLevel(Vector3f... location) {
|
|
|
+ // TODO: get a level based on positions getting ever more accurate,
|
|
|
+ // from any given position
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initData() {
|
|
|
+ getRootNode();
|
|
|
+ physicsState = new BulletAppState();
|
|
|
+ physicsState.startPhysics();
|
|
|
+ physicsState.getPhysicsSpace().setGravity(Vector3f.ZERO);
|
|
|
+ //horizon
|
|
|
+ physicsState.getPhysicsSpace().add(new RigidBodyControl(poiHorizonCollisionShape, 0));
|
|
|
+ int hashCode = inParentPosition.hashCode();
|
|
|
+ Vector3f[] positions = getPositions(poiCount, hashCode);
|
|
|
+ for (int i = 0; i < positions.length; i++) {
|
|
|
+ Vector3f vector3f = positions[i];
|
|
|
+ Geometry poiGeom = new Geometry("poi", poiMesh);
|
|
|
+ poiGeom.setLocalTranslation(vector3f);
|
|
|
+ poiGeom.setMaterial(poiMaterial);
|
|
|
+ RigidBodyControl control = new RigidBodyControl(poiCollisionShape, 0);
|
|
|
+ //!!! Important
|
|
|
+ control.setApplyPhysicsLocal(true);
|
|
|
+ poiGeom.addControl(control);
|
|
|
+ physicsState.getPhysicsSpace().add(poiGeom);
|
|
|
+ rootNode.attachChild(poiGeom);
|
|
|
+
|
|
|
+ }
|
|
|
+ //add balls after so first 10 geoms == locations
|
|
|
+ for (int i = 0; i < positions.length; i++) {
|
|
|
+ Vector3f vector3f = positions[i];
|
|
|
+ Geometry ball = getRandomBall(vector3f);
|
|
|
+ physicsState.getPhysicsSpace().add(ball);
|
|
|
+ rootNode.attachChild(ball);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private Geometry getRandomBall(Vector3f location) {
|
|
|
+ Vector3f localLocation = new Vector3f();
|
|
|
+ localLocation.set(location);
|
|
|
+ localLocation.addLocal(new Vector3f(random.nextFloat() - 0.5f, random.nextFloat() - 0.5f, random.nextFloat() - 0.5f).normalize().mult(3));
|
|
|
+ Geometry poiGeom = new Geometry("ball", ballMesh);
|
|
|
+ poiGeom.setLocalTranslation(localLocation);
|
|
|
+ poiGeom.setMaterial(ballMaterial);
|
|
|
+ RigidBodyControl control = new RigidBodyControl(ballCollisionShape, 1);
|
|
|
+ //!!! Important
|
|
|
+ control.setApplyPhysicsLocal(true);
|
|
|
+ poiGeom.addControl(control);
|
|
|
+ float x = (random.nextFloat() - 0.5f) * 100;
|
|
|
+ float y = (random.nextFloat() - 0.5f) * 100;
|
|
|
+ float z = (random.nextFloat() - 0.5f) * 100;
|
|
|
+ control.setLinearVelocity(new Vector3f(x, y, z));
|
|
|
+ return poiGeom;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void cleanupData() {
|
|
|
+ physicsState.stopPhysics();
|
|
|
+ //TODO: remove all objects?
|
|
|
+ physicsState = null;
|
|
|
+ rootNode = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void initialize(AppStateManager stateManager, Application app) {
|
|
|
+ super.initialize(stateManager, app);
|
|
|
+ //only generate data and attach node when we are actually attached (or picking)
|
|
|
+ initData();
|
|
|
+ application = (SimpleApplication) app;
|
|
|
+ application.getRootNode().attachChild(getRootNode());
|
|
|
+ application.getStateManager().attach(physicsState);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void cleanup() {
|
|
|
+ super.cleanup();
|
|
|
+ //detach everything when we are detached
|
|
|
+ application.getRootNode().detachChild(rootNode);
|
|
|
+ application.getStateManager().detach(physicsState);
|
|
|
+ cleanupData();
|
|
|
+ }
|
|
|
+
|
|
|
+ public Node getRootNode() {
|
|
|
+ if (rootNode == null) {
|
|
|
+ rootNode = new Node("ZoomLevel");
|
|
|
+ if (parent != null) {
|
|
|
+ rootNode.setLocalScale(1.0f / poiRadius);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return rootNode;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Vector3f getPositionInParent() {
|
|
|
+ return inParentPosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Vector3f getPlayerPosition() {
|
|
|
+ if (playerPos == null) {
|
|
|
+ playerPos = new Vector3f();
|
|
|
+ }
|
|
|
+ return playerPos;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setPlayerPosition(Vector3f vec) {
|
|
|
+ if (playerPos == null) {
|
|
|
+ playerPos = new Vector3f();
|
|
|
+ }
|
|
|
+ playerPos.set(vec);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void move(Vector3f dir) {
|
|
|
+ if (playerPos == null) {
|
|
|
+ playerPos = new Vector3f();
|
|
|
+ }
|
|
|
+ playerPos.addLocal(dir);
|
|
|
+ }
|
|
|
+
|
|
|
+ public float getCurrentScaleAmount() {
|
|
|
+ return curScaleAmount;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InceptionLevel getParent() {
|
|
|
+ return parent;
|
|
|
+ }
|
|
|
+
|
|
|
+ public InceptionLevel getCurrentLevel() {
|
|
|
+ return currentReturnLevel;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String getCoordinates() {
|
|
|
+ InceptionLevel cur = this;
|
|
|
+ StringBuilder stringBuilder = new StringBuilder();
|
|
|
+ stringBuilder.insert(0, this.getPlayerPosition());
|
|
|
+ stringBuilder.insert(0, this.getPositionInParent() + " / ");
|
|
|
+ cur = cur.getParent();
|
|
|
+ while (cur != null) {
|
|
|
+ stringBuilder.insert(0, cur.getPositionInParent() + " / ");
|
|
|
+ cur = cur.getParent();
|
|
|
+ }
|
|
|
+ return stringBuilder.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Vector3f[] getPositions(int count, long seed) {
|
|
|
+ Random rnd = new Random(seed);
|
|
|
+ Vector3f[] vectors = new Vector3f[count];
|
|
|
+ for (int i = 0; i < count; i++) {
|
|
|
+ vectors[i] = new Vector3f((rnd.nextFloat() - 0.5f) * poiRadius,
|
|
|
+ (rnd.nextFloat() - 0.5f) * poiRadius,
|
|
|
+ (rnd.nextFloat() - 0.5f) * poiRadius);
|
|
|
+ }
|
|
|
+ return vectors;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Maps a value from 0-1 to a range from min to max.
|
|
|
+ *
|
|
|
+ * @param x
|
|
|
+ * @param min
|
|
|
+ * @param max
|
|
|
+ * @return the mapped value
|
|
|
+ */
|
|
|
+ private static float mapValue(float x, float min, float max) {
|
|
|
+ return mapValue(x, 0, 1, min, max);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Maps a value from inputMin to inputMax to a range from min to max.
|
|
|
+ *
|
|
|
+ * @param x
|
|
|
+ * @param inputMin
|
|
|
+ * @param inputMax
|
|
|
+ * @param min
|
|
|
+ * @param max
|
|
|
+ * @return the mapped value
|
|
|
+ */
|
|
|
+ private static float mapValue(float x, float inputMin, float inputMax, float min, float max) {
|
|
|
+ return (x - inputMin) * (max - min) / (inputMax - inputMin) + min;
|
|
|
+ }
|
|
|
+}
|