package com.bitwaffle.spaceguts.physics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.vecmath.Quat4f; import org.lwjgl.util.vector.Quaternion; import org.lwjgl.util.vector.Vector3f; import com.bitwaffle.spaceguts.entities.Camera; import com.bitwaffle.spaceguts.entities.DynamicEntity; import com.bitwaffle.spaceguts.entities.Entities; import com.bitwaffle.spaceguts.graphics.gui.GUI; import com.bitwaffle.spaceguts.graphics.gui.button.MenuButton; import com.bitwaffle.spaceguts.graphics.gui.menu.picker.Picker; import com.bitwaffle.spaceguts.input.KeyBindings; import com.bitwaffle.spaceguts.input.Keys; import com.bitwaffle.spaceguts.input.MouseManager; import com.bitwaffle.spaceguts.util.QuaternionHelper; import com.bitwaffle.spaceout.entities.dynamic.Diamond; import com.bitwaffle.spaceout.entities.dynamic.Asteroid; import com.bitwaffle.spaceout.entities.dynamic.Planet; import com.bitwaffle.spaceout.resources.Models; import com.bitwaffle.spaceout.resources.Textures; import com.bulletphysics.collision.dispatch.CollisionWorld.ClosestRayResultCallback; import com.bulletphysics.linearmath.Transform; /** * A class to manage interacting with the world ('creative mode') * * @author TranquilMarmot * */ public class Builder { /** Camera object for this builder */ private Camera camera; /** What the builder is looking at */ public DynamicEntity lookingAt; /** whether or not what the builder is looking at has been grabbed */ public boolean leftGrabbed = false, rightGrabbed = false; /** Menu for selecting a model to add */ Picker<Models> picker; /** Buttons for adding models */ MenuButton addButton, cancelButton; boolean modelSelected = false; /** * Builder constructor * * @param camera * Camera that this builder will see from */ public Builder(Camera camera) { this.camera = camera; // create the menu for selecting a model picker = new Picker<Models>(-50, -20, 20, 200, Models.values(), Textures.MENU_PICKER_ACTIVE, Textures.MENU_PICKER_MOUSEOVER, Textures.MENU_PICKER_PRESSED, Textures.MENU_PICKER_SELECTED); // create button to add model to world addButton = new MenuButton("Add", 119, 28, 115, -17); addButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (picker.itemHasBeenSelected()) { addDynamicEntity(picker.getSelectedItem()); closePickerMenu(); } } }); // create button to cancel adding a model cancelButton = new MenuButton("Cancel", 119, 28, 115, 17); cancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { closePickerMenu(); } }); } /** * Updates the builder (called when the camera is in builder mode) */ public void update(float timeStep) { whatsTheCameraLookingAt(); if (lookingAt != null && (leftGrabbed || rightGrabbed)) grabLogic(timeStep); if (Keys.P.isPressed()) addRandomPlanet(); if(Keys.I.isPressed()) addRandomAsteroid(); if(Keys.O.isPressed()){ addRandomDiamond(); addRandomDiamond(); } if (KeyBindings.BUILDER_OPEN_ADD_MENU.pressedOnce()){ if(GUI.menuUp) closePickerMenu(); else openPickerMenu(); } } /** * Opens the menu to select a model */ private void openPickerMenu() { GUI.addGUIObject(picker); GUI.addGUIObject(addButton); GUI.addGUIObject(cancelButton); GUI.menuUp = true; } /** * Closes the menu to select a model */ private void closePickerMenu(){ GUI.removeGUIObject(picker); GUI.removeGUIObject(addButton); GUI.removeGUIObject(cancelButton); GUI.menuUp = false; } /** * Adds a dynamic entity to the world right in front of the camera * @param model Model to use when adding entity */ private void addDynamicEntity(Models model) { float mass = 50.0f; float restitution = 1.0f; Vector3f location = new Vector3f(0.0f, 0.0f, 0.0f); Vector3f downInFront = QuaternionHelper.rotateVectorByQuaternion( new Vector3f(0.0f, 0.0f, 15.0f), camera.rotation); Vector3f.add(camera.location, downInFront, location); Quaternion rotation = camera.rotation; DynamicEntity ent = new DynamicEntity(location, rotation, model, mass, restitution); Entities.addDynamicEntity(ent); ent.rigidBody.activate(); } /** * Figures out what's at the center of the camera. Moves what's being looked * at if the left mouse button is down, rotates what's being looked at if * the right mouse button is down */ private void whatsTheCameraLookingAt() { if (!leftGrabbed && !rightGrabbed) { // check if anything has been grabbed ClosestRayResultCallback cameraRay = camera.rayTestAtCenter(); if (cameraRay.hasHit()) { lookingAt = (DynamicEntity) cameraRay.collisionObject .getUserPointer(); if(lookingAt == camera) lookingAt = null; // grab the entity if the mouse button is down if (!leftGrabbed && MouseManager.button0) { // stop any linear velocity lookingAt.rigidBody .setLinearVelocity(new javax.vecmath.Vector3f(0.0f, 0.0f, 0.0f)); leftGrabbed = true; } if (!rightGrabbed && MouseManager.button1) { // stop any angular velocity lookingAt.rigidBody .setAngularVelocity(new javax.vecmath.Vector3f( 0.0f, 0.0f, 0.0f)); rightGrabbed = true; } } else { // looking at nothing lookingAt = null; } } } /** * Moves or rotates what is grabbed * * @param timeStep * Amount of time passed (gotten from camera) */ private void grabLogic(float timeStep) { // activate the rigid body if it's not active (since we're directly // modifying its world transform and not just it's velocities) if (!lookingAt.rigidBody.isActive()) lookingAt.rigidBody.activate(); // move logic if (leftGrabbed) { if (!MouseManager.button0) { leftGrabbed = false; } else { // how far to move what we're looking at float dx = MouseManager.dx * 10; float dy = -MouseManager.dy * 10; float dz = MouseManager.wheel / 10; // rotate the distance by the camera's rotation Vector3f impulse = QuaternionHelper.rotateVectorByQuaternion( new Vector3f(dx, dy, dz), Entities.camera.rotation); // get the entity's transform Transform trans = new Transform(); lookingAt.rigidBody.getWorldTransform(trans); trans.origin.add(new javax.vecmath.Vector3f(impulse.x, impulse.y, impulse.z)); // set the transform to the new location lookingAt.rigidBody.setWorldTransform(trans); // set the location so that it gets rendered properly // (otherwise, the rendering location wouldn't be update until // the end of the next physics world tick, which would be one // frame too late) lookingAt.location.set(trans.origin.x, trans.origin.y, trans.origin.z); } // check for right grab if (MouseManager.button1) { lookingAt.rigidBody .setAngularVelocity(new javax.vecmath.Vector3f(0.0f, 0.0f, 0.0f)); rightGrabbed = true; } } // rotate logic if (rightGrabbed) { if (!MouseManager.button1) { rightGrabbed = false; } else if (MouseManager.dx != 0 || MouseManager.dy != 0 || MouseManager.wheel != 0) { Transform trans = new Transform(); lookingAt.rigidBody.getWorldTransform(trans); Quat4f rot = new Quat4f(); trans.getRotation(rot); // how much rotation to apply Vector3f amount = new Vector3f(-MouseManager.dy * 10, MouseManager.dx * 10, MouseManager.wheel / 10); // rotate amount by the camera's rotation Vector3f impulse = QuaternionHelper.rotateVectorByQuaternion( amount, Entities.camera.rotation); // rotate entity's rotation by our rotated amount Quaternion newRot = QuaternionHelper.rotate(new Quaternion( rot.x, rot.y, rot.z, rot.w), impulse); // set the rotation to the new rotation trans.setRotation(new Quat4f(newRot.x, newRot.y, newRot.z, newRot.w)); // set the entity's world transform lookingAt.rigidBody.setWorldTransform(trans); // set the rotation so that it gets rendered properly // (otherwise, the rendering rotation wouldn't be update until // the end of the next physics world tick, which would be one // frame too late) lookingAt.rotation.set(newRot.x, newRot.y, newRot.z, newRot.w); } // check for left grab if (MouseManager.button0) { lookingAt.rigidBody .setLinearVelocity(new javax.vecmath.Vector3f(0.0f, 0.0f, 0.0f)); leftGrabbed = true; } } // move the entity with the camera moveEntityWithCamera(timeStep); } /** * Moves the grabbed entity with the camera. Note that this is nearly * identical to the <code>freeMode(float timeStep)</code> method in the * {@link Camera} class. * * @param timeStep * Amount of time passed (gotten from camera) */ private void moveEntityWithCamera(float timeStep) { javax.vecmath.Vector3f linvec = new javax.vecmath.Vector3f(); camera.rigidBody.getLinearVelocity(linvec); lookingAt.rigidBody.setLinearVelocity(linvec); } /** * Adds a random sphere to the world, right in front of the camera */ private void addRandomPlanet() { Random randy = new Random(); float sphereSize = randy.nextInt(200) / 10.0f; Textures sphereTexture; String tex; switch (randy.nextInt(4)) { case 0: sphereTexture = Textures.EARTH; tex = "Earth"; break; case 1: sphereTexture = Textures.MERCURY; tex = "Mercury"; break; case 2: sphereTexture = Textures.VENUS; tex = "Venus"; break; case 3: sphereTexture = Textures.MARS; tex = "Mars"; break; default: sphereTexture = Textures.EARTH; tex = "Earth"; } float sphereX = randy.nextFloat() * 75.0f; float sphereY = randy.nextFloat() * 75.0f; float sphereZ = -100.0f; Vector3f sphereLocation = new Vector3f(sphereX, sphereY, sphereZ); // put the sphere right in front of the camera Vector3f downInFront = QuaternionHelper.rotateVectorByQuaternion( new Vector3f(0.0f, 0.0f, 300.0f), camera.rotation); Vector3f.add(camera.location, downInFront, downInFront); Vector3f.add(sphereLocation, downInFront, sphereLocation); Quaternion sphereRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); // float sphereMass = randy.nextFloat() * 10.0f; float sphereMass = sphereSize * 2.0f; Planet p = new Planet(sphereLocation, sphereRotation, sphereSize, sphereMass, 0.0f, sphereTexture); p.type = tex; Entities.addDynamicEntity(p); } /** * Adds a random sphere to the world, right in front of the camera */ private void addRandomAsteroid() { Random randy = new Random(); float asteroidSize = randy.nextInt(500) / 10.0f; float asteroidX = randy.nextFloat() * 1000.0f; float asteroidY = randy.nextFloat() * 1000.0f; float asteroidZ = -100.0f; Vector3f asteroidLocation = new Vector3f(asteroidX, asteroidY, asteroidZ); // put the sphere right in front of the camera Vector3f downInFront = QuaternionHelper.rotateVectorByQuaternion( new Vector3f(0.0f, 0.0f, 300.0f), camera.rotation); Vector3f.add(camera.location, downInFront, downInFront); Vector3f.add(asteroidLocation, downInFront, asteroidLocation); Quaternion asteroidRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); Asteroid p = new Asteroid(asteroidLocation, asteroidRotation, asteroidSize, null); Entities.addDynamicEntity(p); } /** * Adds a random diamond to the world, right in front of the camera */ private void addRandomDiamond() { Random randy = new Random(); float diamondX = randy.nextFloat() * 10.0f; float diamondY = randy.nextFloat() * 10.0f; float diamondZ = randy.nextFloat() * 100.0f; if(randy.nextBoolean()) diamondX = -diamondX; if(randy.nextBoolean()) diamondY = -diamondY; Vector3f diamondLocation = new Vector3f(); // put the sphere right in front of the camera Vector3f downInFront = QuaternionHelper.rotateVectorByQuaternion( new Vector3f(diamondX, diamondY, diamondZ), camera.rotation); Vector3f.add(camera.location, downInFront, diamondLocation); Quaternion diamondRotation = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); float xRot = randy.nextFloat() * 100.0f; float yRot = randy.nextFloat() * 100.0f; float zRot = randy.nextFloat() * 100.0f; diamondRotation = QuaternionHelper.rotate(diamondRotation, new Vector3f(xRot,yRot, zRot)); Diamond d = new Diamond(diamondLocation, diamondRotation, 0.3f); Entities.addDynamicEntity(d); } }