├── .gitignore ├── README.md ├── assets ├── art │ ├── daytime-1080-with-logo.png │ ├── daytime-1080.xcf │ ├── daytime-source.png │ ├── daytime-with-logo.png │ ├── daytime.png │ ├── daytime.xcf │ ├── logo.png │ └── logo.xcf ├── textures.png └── textures.xcf ├── brainstorm ├── README.md ├── draw_arry_rendering.md ├── gameplay_overview.md └── modern_opengl_redesign │ ├── README.md │ ├── model.md │ └── view.md ├── src └── nexus │ ├── main │ ├── Controller.java │ ├── Main.java │ ├── Model.java │ └── View.java │ ├── model │ ├── generators │ │ ├── Interpolate.java │ │ ├── Noise1D.java │ │ ├── Noise2D.java │ │ └── Perlin.java │ ├── renderable │ │ ├── Air.java │ │ ├── BlockMask.java │ │ ├── Grid.java │ │ ├── Renderable.java │ │ └── Solid.java │ └── structs │ │ ├── Block.java │ │ ├── Camera.java │ │ ├── Chunk.java │ │ ├── ChunkContainer.java │ │ ├── Color.java │ │ └── Vector3.java │ └── view │ ├── color │ ├── Colorist.java │ ├── Dirt.java │ ├── Grass.java │ ├── Greyscale.java │ ├── Monochrome.java │ ├── Rainbow.java │ └── Sand.java │ ├── gl │ ├── Outlines.java │ ├── Planes.java │ └── Prisms.java │ └── structs │ └── VertexContainer.java └── test ├── README.md ├── model ├── generators │ ├── GeneratorTests.java │ ├── Noise1DTest.java │ └── Noise2DTest.java └── structs │ ├── ChunkContainerTest.java │ ├── StructTests.java │ └── Vector3Test.java └── view └── gl └── PlanesTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings/ 4 | bin/ 5 | 6 | *.mp4 7 | *.mp3 8 | *.ogg 9 | *.wav 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![heavily altered screenshot](http://i.imgur.com/LJ8tKi6.png) 2 | 3 | # Voxel Party 4 | 5 | Voxel Party is an 3D voxel-based MVC game engine built on Java, LWJGL and OpenGL. 6 | 7 | ## Proposed Features 8 | * **Ultra Concurrent Framework:** a Model-View-Controller solution for ultimate performance 9 | * **OpenGL 3 Support:** clean, modern, and quick 10 | * **Infinite, 3D Terrain:** realistic terrain generation on the fly 11 | * **Distinct Biomes:** diverse worlds with epic mountains, flowing rivers, and immersive forests 12 | * **Elegant Physics Engine:** simple and robust 13 | 14 | Check the [issues page](https://github.com/aaasen/voxel-party/issues) 15 | to request new features and bug fixes, or to see those in progress. 16 | The [milestones page](https://github.com/aaasen/voxel-party/issues/milestones) contains larger goals for the project. 17 | 18 | ## Technologies 19 | * **Java:** cross platform and good enough 20 | * **OpenGL 3+:** no legacy OpenGL 21 | * **LWJGL:** the Light Weight Java Game Library for OpenGL bindings 22 | -------------------------------------------------------------------------------- /assets/art/daytime-1080-with-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/daytime-1080-with-logo.png -------------------------------------------------------------------------------- /assets/art/daytime-1080.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/daytime-1080.xcf -------------------------------------------------------------------------------- /assets/art/daytime-source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/daytime-source.png -------------------------------------------------------------------------------- /assets/art/daytime-with-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/daytime-with-logo.png -------------------------------------------------------------------------------- /assets/art/daytime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/daytime.png -------------------------------------------------------------------------------- /assets/art/daytime.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/daytime.xcf -------------------------------------------------------------------------------- /assets/art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/logo.png -------------------------------------------------------------------------------- /assets/art/logo.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/art/logo.xcf -------------------------------------------------------------------------------- /assets/textures.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/textures.png -------------------------------------------------------------------------------- /assets/textures.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aaasen/voxel-party/d3c3fc4dd7b3c6d285d0e31f03347c33c79729a6/assets/textures.xcf -------------------------------------------------------------------------------- /brainstorm/README.md: -------------------------------------------------------------------------------- 1 | # Brainstorming 2 | 3 | Here are all of my planning and brainstorming documents for Voxel Party. 4 | 5 | I often find myself lost and overwhelmed when working on such a large project. These files help me redesign and comprehend what I've done. Also, they're great for getting excited about the future! 6 | -------------------------------------------------------------------------------- /brainstorm/draw_arry_rendering.md: -------------------------------------------------------------------------------- 1 | 2 | # How I screwed up implenting drawArrays 3 | 4 | ## Terms 5 | 6 | ### Immediate Rendering 7 | 8 | In immediate rendering, no data is stored on the GPU. Instead, the data is loaded onto the GPU every single frame. 9 | If you've just started with OpenGL, this is probably what you are using. 10 | 11 | Example: 12 | 13 | ```java 14 | glBegin(GL_TRIANGLES); 15 | 16 | glColor3f(0, 0, 0); 17 | 18 | glVertex3f(0, 0, 0); 19 | glVertex3f(0, 1, 0); 20 | glVertex3f(1, 1, 0); 21 | 22 | glEnd(); 23 | ``` 24 | 25 | ### drawArrays 26 | 27 | Immediate rendering requires data to be loaded onto the GPU every frame, which is inherently expensive. 28 | The alternative is drawArrays, which are chunks of data that are loaded onto the GPU and kept there through multiple frames. 29 | 30 | ## How I screwed up 31 | 32 | I started porting to drawArrays by using my old immediate rendering code. 33 | I didn't really grasp the concept of drawArrays, and how they were much more static. 34 | Now, I'm finally realizing that *a lot* of my rendering code is just wrong and unreadable. 35 | 36 | ## The new concept 37 | 38 | I'm working on a new solution with a much better understanding of what I actually have to do. 39 | 40 | 41 | * `nexus/view/structs` 42 | * `VertexContainer.java` 43 | * `VertexBuffer.java` 44 | 45 | ### `VertexContainer` 46 | 47 | `VertexContainer` is the parent class of any vertex in Voxel Party. It contains data for position, color, shaders, etc. 48 | `VertexContainer` will be extended to `TriangleVertexConatiner`, `QuadVertexConatiner`, and the like. 49 | 50 | Some pseudocode: 51 | 52 | ```java 53 | 54 | class VertexContainer 55 | float[] vertices; 56 | float[] colors; 57 | 58 | 59 | ``` 60 | 61 | ### `VertexBuffer` 62 | 63 | `VertexBuffer` is the rendering container for `VertexConatiner`. 64 | 65 | I'm not sure how this will work in a dynamic context. 66 | 67 | ```java 68 | 69 | class VertexBuffer 70 | FloatBuffer vertices 71 | boolean read = false 72 | 73 | put(VertexContainer) 74 | if (there is not room) 75 | expand the buffer 76 | 77 | write() 78 | vertices.put(VertexConatiner) 79 | read() 80 | 81 | write() 82 | if (read) 83 | vertices.flip() 84 | read = false 85 | 86 | read() 87 | if (!read) 88 | vertices.flip() 89 | read = true 90 | 91 | ``` 92 | -------------------------------------------------------------------------------- /brainstorm/gameplay_overview.md: -------------------------------------------------------------------------------- 1 | # Gameplay [indev] 2 | 3 | At its base, I envision Voxel Party to be a laid back RTS/City Builder set in a procedurally generated world. 4 | 5 | ## RTS Elements 6 | 7 | Voxel Party will mainly be set from an isometric view port, but will have a free camera mode as well. Might was well since it's using 3D OpenGL. This might, however be detrimental to performance. It's something consider, but I'm designing for some time in the future so I try not to be hindered by performance. 8 | 9 | Combat, multiplayer, and progression are all elements that will be taken from the RTS genre. However, since these are rather time consuming features to implement, Voxel Party will begin as a city builder. 10 | 11 | * Age of Empires 12 | 13 | ## City Builder Elements 14 | 15 | Unlike most RTS style games which focus on combat, I'd like Voxel Party to focus on resource gathering and technological progression. Not necessarily grinding, but there's something awesome about building supply chains and machines to automate resource gathering. 16 | 17 | * Sim City 18 | 19 | ## Minecraft Inspirations 20 | 21 | Minecraft is one of my favorite games. I would say my favorite, but I've got a lot of nostalgia for the older versions of Age of Empires and Battlefield. 22 | 23 | ### World 24 | 25 | What I love about Minecraft is the world and atmosphere. I've already experimented with some terrain generation in Voxel Party, and I *love* it. Unlike most high-budget RTS games, I don't have a full team of level designers. I would much rather generate beautiful, infinite worlds than make them by hand. 26 | 27 | ### Voxels 28 | 29 | Voxel Party is also made of voxels, just like Minecraft. Minecraft certainly isn't the first game to use voxels, but it's the first mainstream game to (that I can think of). With voxels, it's possible to make something resembling a world, but they're imprecise enough to distance the game from reality. There isn't that pressure to make photo realistic worlds when you use voxels, simply because you *can't*. They also makes engine development, building, and fully destructible worlds much easier. 30 | 31 | ### Machines 32 | 33 | There are several mods that add very powerful machines to Minecraft. They excavate quarries, process minerals, generate power, and perform all sorts of other tasks. They're just awesome. And, it would be much more awesome in an RTS style game. 34 | 35 | * [Feed The Beast](http://feed-the-beast.com/) 36 | 37 | 38 | -------------------------------------------------------------------------------- /brainstorm/modern_opengl_redesign/README.md: -------------------------------------------------------------------------------- 1 | # Why Redesign? 2 | 3 | I originally created Voxel Party during a weekend of fervent hacking. 4 | I came into it with no knowledge of OpenGL, and tutorials taught me legacy methods. 5 | 6 | Now, I'm modernizing VoxelParty. Updating it to OpenGL 3, and cleaning everything up in the process. 7 | 8 | ## MVC (Model View Controller) 9 | 10 | Voxel Party uses an MVC model, which is explained below: 11 | 12 | > Model–view–controller (MVC) is a software architecture pattern that separates the representation of information from the user's interaction with it. 13 | > The model consists of application data and business rules, and the controller mediates input, converting it to commands for the model or view. 14 | > A view can be any output representation of data, such as a chart or a diagram. 15 | > Multiple views of the same data are possible, such as a pie chart for management and a tabular view for accountants. 16 | > The central ideas behind MVC are code reusability and separation of concerns. 17 | > 18 | > ![mvc explanation](http://upload.wikimedia.org/wikipedia/commons/f/fd/MVC-Process.png) 19 | > 20 | > from Wikipedia, [*Model-view-controller*](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 21 | 22 | I chose MVC for threading and modularity concerns. 23 | However, I've heard that threading can get really ugly in games and isn't worth it. 24 | Oh well! Current computers have at least 2 cores, my desktop has 6, and next gen consoles are rumored to have 8. 25 | There's got to be some point in threading games. 26 | 27 | # Structural Overview 28 | 29 | `Init` 30 | * model/ 31 | * `Model` 32 | * structs/ 33 | * `Block` 34 | * `Chunk` 35 | * `World` 36 | * generators/ 37 | * blocks/ 38 | * view/ 39 | * `View` 40 | * structs/ 41 | * `VertexContainer` 42 | * `VertexBuffer` 43 | * util/ 44 | * `Plane` 45 | * `Shaders` 46 | * controller/ 47 | * `Controller` 48 | 49 | --- 50 | 51 | ## Structural Specifics 52 | I've include an explanation for `Init` below. It contains pseudocode, and a brief description. 53 | 54 | Descriptions for other files can be found in `model.md` and `view.md`. 55 | 56 | ### Init 57 | Init reads a configuration file and launches the engine. In the future, it will do so via a proper graphical launcher. 58 | 59 | ```java 60 | class Init: 61 | Map config = loadConfigFromFile("config.json") 62 | // config = { "screen_width" : 1920, "screen_height" : 1080 } 63 | 64 | // initialize LWJGL 65 | Display.create(); 66 | Keyboard.create(); 67 | Mouse.create(); 68 | 69 | Model model = new Model(...) 70 | Controller controller = new Controller(model, ...) 71 | View view = new View(model, ...) 72 | 73 | model.init() 74 | view.init() 75 | ``` 76 | -------------------------------------------------------------------------------- /brainstorm/modern_opengl_redesign/model.md: -------------------------------------------------------------------------------- 1 | # Model 2 | In an MVC framework, the model contains all data about the game world, players, etc. 3 | 4 | Threading out the Model can create concurrent modification issues, which I try to solve by locking the Model when it is being updated. 5 | Don't know how this will work out, though. 6 | ```java 7 | class Model implements Runnable: 8 | World world 9 | Thread modelThread 10 | boolean locked 11 | 12 | public Model(): 13 | this.modelThread = new Thread(this, "model_thread") 14 | this.modelThread.start() 15 | 16 | this.world = new World() 17 | 18 | public void run(): 19 | while (!stop): 20 | this.tick() 21 | 22 | this.cleanup() 23 | 24 | // updates the Model 25 | public void tick(): 26 | this.locked = true 27 | // update the world 28 | this.locked = false 29 | ``` 30 | --- 31 | 32 | ## Block 33 | The world of Voxel Party is made of Blocks. Every part of the landscape, from dirt and sand to water and leaves, are Blocks. 34 | 35 | Block is meant to be a parent class, and should not be used directly. I've included a possible Block hierarchy below. 36 | 37 | > * Block 38 | * TransparentBlock 39 | * Glass 40 | * Water 41 | * OpaqueBlock 42 | * Dirt 43 | * Sand 44 | 45 | ```java 46 | class Block: 47 | Vector a, b 48 | 49 | public Block(Vector a): 50 | if a is not integer: throw IllegalArgumentException 51 | 52 | this.a = a 53 | this.b = a.add((1, 1, 1)) 54 | ``` 55 | 56 | ### Tests 57 | * should be integers (but in float form: 1.0) 58 | 59 | --- 60 | 61 | ## Chunk 62 | Chunks are containers for blocks, usually 16x16 at the base and 128+ in height. 63 | 64 | ```java 65 | class Chunk: 66 | final static Vector dimensions = (16, 128, 16) 67 | 68 | Vector pos 69 | Block[][][] blocks 70 | Generator generator 71 | 72 | public Chunk(Generator generator): 73 | this.blocks = new Block[dimensions.x][dimensions.y][dimensions.z] 74 | this.generator = generator 75 | 76 | public Block getBlock(Vector pos): 77 | if pos.y is negative or 78 | pos.y is greater than dimensions.y or 79 | pos.x is out of bounds or 80 | pos.z is out of bounds: 81 | throw IllegalArgumentException 82 | 83 | return this.blocks[(int) dimensions.x][(int) dimensions.y][(int) dimensions.z] 84 | 85 | ``` 86 | 87 | ### Tests 88 | * check that out of bounds exceptions work 89 | 90 | --- 91 | 92 | ## World 93 | The World (name pending), is a container for Chunks. Chunks should not usually be accessed directly, rather they should be accessed via the World. 94 | 95 | ```java 96 | class World: 97 | HashMap chunks 98 | 99 | private Long getKey(Vector pos): 100 | return (int) pos.x * BIG_NUMBER + (int) pos.z 101 | 102 | public Chunk getChunk(Vector pos): 103 | Chunk chunk = chunks.get(getKey(pos)) 104 | 105 | if (chunk == null): 106 | // generate it 107 | else: 108 | return chunk 109 | 110 | public Block getBlock(Vector pos): 111 | if pos.y out of bounds: 112 | thrown IllegalArgumentException 113 | 114 | return this.getChunk(pos).getBlock(pos) 115 | 116 | ``` 117 | 118 | ### Tests 119 | * check that getBlock() out of bounds works 120 | * check that getKey() does not produce collisions in a reasonable range 121 | 122 | --- 123 | 124 | ## Generator 125 | Generators fill Chunks with Blocks. 126 | 127 | By making Generator an interface, biomes become much easier. There can be a Forest Generator, a Mountain Generator, an Ocean Generator, etc. 128 | ```java 129 | interface Generator: 130 | public Chunk genChunk(Chunk chunk) 131 | 132 | ``` 133 | 134 | ### Tests 135 | * must fill every single block, no null values 136 | 137 | --- 138 | -------------------------------------------------------------------------------- /brainstorm/modern_opengl_redesign/view.md: -------------------------------------------------------------------------------- 1 | # View 2 | In MVC, the View just draws the Model every frame. 3 | 4 | The View is not threaded, as OpenGL must be used from a single thread. 5 | ```java 6 | class View: 7 | VertexBuffer vertices 8 | 9 | public init(): 10 | // initialize Shaders, etc. 11 | 12 | public run(): 13 | while (!stop): 14 | this.render() 15 | 16 | this.cleanup() 17 | 18 | private render(): 19 | glDrawArrays(vertices, ...) 20 | 21 | // render any 2D overlays 22 | 23 | private cleanup(): 24 | // destroy the VertexBuffer and release GPU memory 25 | ``` 26 | 27 | --- 28 | 29 | ## VertexContainer 30 | VertexContainer is a temporary container for vertex data. 31 | It is loaded into a container, then added to a buffer at which point the container is deleted. 32 | 33 | Not sure if this is even necessary, actually. 34 | 35 | --- 36 | 37 | ## VertexBuffer 38 | The VertexBuffer contains data for many vertices. 39 | Ideally, the entire program would need just one. 40 | 41 | ### Concerns 42 | 43 | > I have no idea how to do this dynamically right now, but it can be done. 44 | Dynamically updating the VertexBuffer might conflict with threading. 45 | OpenGL can only be used from one thread, and updating it would probably necessitate using it from the Model. 46 | 47 | One possible solution would be to implement a message passing system in VertexBuffer. 48 | `VertexBuffer.remove(VertexContainer)`, and similar `add()` methods. 49 | This would also make VertexContainer non-temporary and require more memory. 50 | 51 | --- 52 | 53 | -------------------------------------------------------------------------------- /src/nexus/main/Controller.java: -------------------------------------------------------------------------------- 1 | package nexus.main; 2 | 3 | /** 4 | * The Controller which handles input 5 | * 6 | * Controller runs in a seperate thread from the View to ensure responsiveness 7 | * 8 | * TODO: cap controller tick 9 | * 10 | * @author Lane Aasen 11 | * 12 | */ 13 | 14 | 15 | 16 | import nexus.model.renderable.Air; 17 | import nexus.model.renderable.BlockMask; 18 | import nexus.model.renderable.Solid; 19 | import nexus.model.structs.Block; 20 | import nexus.model.structs.Vector3; 21 | import nexus.view.color.Greyscale; 22 | 23 | import org.lwjgl.LWJGLException; 24 | import org.lwjgl.input.Keyboard; 25 | import org.lwjgl.input.Mouse; 26 | 27 | public class Controller implements Runnable { 28 | public static final float MAX_SELECT_DISTANCE = 10f; 29 | 30 | Thread inputThread; 31 | boolean stop = false; 32 | Model model; 33 | float mouseSensitivity = 0.003f; 34 | 35 | /** 36 | * Creates a Controller 37 | * 38 | * @param model a Model for the Controller to manipulate 39 | * @param view 40 | */ 41 | public Controller(Model model) { 42 | this.inputThread = new Thread(this, "input_thread"); 43 | this.inputThread.start(); 44 | this.model = model; 45 | 46 | try { 47 | Mouse.create(); 48 | Mouse.setGrabbed(true); 49 | Keyboard.create(); 50 | } catch (LWJGLException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | /** 56 | * Starts the Controller 57 | */ 58 | @Override 59 | public void run() { 60 | while (!this.stop) { 61 | processInput(); 62 | 63 | try { 64 | Thread.sleep(15l); 65 | } catch (InterruptedException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | this.cleanUp(); 71 | } 72 | 73 | /** 74 | * Processes input once 75 | */ 76 | public void processInput() { 77 | int dy = 0; 78 | int dx = 0; 79 | 80 | while (Mouse.next()) { 81 | dy += Mouse.getEventDY(); 82 | dx += Mouse.getEventDX(); 83 | } 84 | 85 | this.model.camera.pitch(dy * this.mouseSensitivity); 86 | this.model.camera.yaw(dx * this.mouseSensitivity); 87 | 88 | if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) { 89 | this.stop(); 90 | } 91 | 92 | if (Keyboard.isKeyDown(Keyboard.KEY_W)) { 93 | this.model.camera.forwards(); 94 | } 95 | 96 | if (Keyboard.isKeyDown(Keyboard.KEY_S)) { 97 | this.model.camera.backwards(); 98 | } 99 | 100 | this.model.camera.update(); 101 | 102 | for (float i = 0f; i < MAX_SELECT_DISTANCE; i += 0.1f) { 103 | Vector3 target = model.camera.eye.add(model.camera.unitFocal.scale((float) i)); 104 | 105 | if (model.chunks.inBounds(target) && model.chunks.getBlock(target).visible()) { 106 | if (target != model.chunks.selected) { 107 | model.chunks.getBlock(model.chunks.selected).getMask().setDrawOutline(false); 108 | model.chunks.getBlock(target).getMask().setDrawOutline(true); 109 | model.chunks.selected = target; 110 | } 111 | 112 | if (Mouse.isButtonDown(0)) { 113 | model.chunks.setBlock(new Air(model.chunks.selected, 1.0f)); 114 | } 115 | 116 | if (Mouse.isButtonDown(1)) { 117 | Block newBlock = new Solid(model.chunks.getBlock(model.chunks.selected).a.add(new Vector3(0f, 1f, 0f)), 1.0f, new Greyscale(16.0f, 0.0f)); 118 | model.chunks.setBlock(newBlock); 119 | } 120 | 121 | break; 122 | } 123 | } 124 | } 125 | 126 | /** 127 | * Stops the Controller 128 | */ 129 | public void stop() { 130 | this.stop = true; 131 | 132 | this.model.stop(); 133 | } 134 | 135 | public void cleanUp() { 136 | 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/nexus/main/Main.java: -------------------------------------------------------------------------------- 1 | package nexus.main; 2 | 3 | /** 4 | * Launcher for Nexus 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import nexus.model.structs.Camera; 11 | import nexus.model.structs.Vector3; 12 | 13 | import org.lwjgl.LWJGLException; 14 | import org.lwjgl.input.Keyboard; 15 | import org.lwjgl.input.Mouse; 16 | import org.lwjgl.opengl.ContextAttribs; 17 | import org.lwjgl.opengl.Display; 18 | import org.lwjgl.opengl.DisplayMode; 19 | import org.lwjgl.opengl.PixelFormat; 20 | 21 | public class Main { 22 | public static final int WIDTH = 600; 23 | public static final int HEIGHT = 400; 24 | public static final String WINDOW_TITLE = "Nexus"; 25 | public static final int INIT_RENDER_DISTANCE = 1; 26 | public static final Vector3 INIT_CAMERA_POSITION = new Vector3(0.0f, 16.0f, 0.0f); 27 | public static final float INIT_SENSITIVITY = 0.3f; 28 | public static final boolean CAMERA_INVERT = true; 29 | 30 | public static void main(String[] argv) { 31 | 32 | // set opengl version 33 | PixelFormat pixelFormat = new PixelFormat(); 34 | ContextAttribs contextAtrributes = new ContextAttribs(3, 0); 35 | contextAtrributes.withForwardCompatible(true); 36 | 37 | try { 38 | // initialize lwjgl 39 | Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT)); 40 | Display.setTitle(WINDOW_TITLE); 41 | Display.create(pixelFormat, contextAtrributes); 42 | Keyboard.create(); 43 | Mouse.create(); 44 | 45 | } catch (LWJGLException e) { 46 | e.printStackTrace(); 47 | System.exit(0); 48 | } 49 | 50 | // start the nexus.mvc framework 51 | Model world = new Model(new Camera(INIT_CAMERA_POSITION, 0.0f, 0.0f, INIT_SENSITIVITY, CAMERA_INVERT)); 52 | View view = new View(world, INIT_RENDER_DISTANCE); 53 | @SuppressWarnings("unused") 54 | Controller controller = new Controller(world); 55 | 56 | view.init(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/nexus/main/Model.java: -------------------------------------------------------------------------------- 1 | package nexus.main; 2 | 3 | /** 4 | * The Model which is responsible for holding all of the world's data 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import nexus.model.structs.Camera; 11 | import nexus.model.structs.ChunkContainer; 12 | 13 | public class Model implements Runnable { 14 | public static final long SLEEPTIME = 15l; 15 | 16 | Thread modelThread; 17 | boolean stop = false; 18 | boolean locked; 19 | 20 | public ChunkContainer chunks; 21 | public Camera camera; 22 | 23 | /** 24 | * Constructs an empty Model 25 | */ 26 | public Model(Camera camera) { 27 | this.modelThread = new Thread(this, "model_thread"); 28 | this.modelThread.start(); 29 | 30 | this.chunks = new ChunkContainer(); 31 | this.camera = camera; 32 | this.locked = false; 33 | } 34 | 35 | /** 36 | * Disables writing to the model 37 | * 38 | * Obedient programs do not write to the model when it is locked (for thread safety) 39 | */ 40 | public void lock() { 41 | this.locked = true; 42 | } 43 | 44 | /** 45 | * Enables writing to the model 46 | */ 47 | public void unlock() { 48 | this.locked = false; 49 | } 50 | 51 | /** 52 | * Starts the Model and ticks about 60 times a second 53 | */ 54 | @Override 55 | public void run() { 56 | while (!this.stop) { 57 | tick(); 58 | 59 | try { 60 | Thread.sleep(SLEEPTIME); 61 | } catch (InterruptedException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | this.cleanUp(); 67 | } 68 | 69 | /** 70 | * Updates the Model each frame 71 | */ 72 | public void tick() { 73 | 74 | } 75 | 76 | /** 77 | * Tells the Model to exit 78 | */ 79 | public void stop() { 80 | this.stop = true; 81 | } 82 | 83 | public void cleanUp() { 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/nexus/main/View.java: -------------------------------------------------------------------------------- 1 | package nexus.main; 2 | 3 | /** 4 | * The View which is responsible for rendering a Model to the screen 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; 11 | import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; 12 | import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST; 13 | import static org.lwjgl.opengl.GL11.GL_LEQUAL; 14 | import static org.lwjgl.opengl.GL11.GL_MODELVIEW; 15 | import static org.lwjgl.opengl.GL11.GL_NICEST; 16 | import static org.lwjgl.opengl.GL11.GL_PERSPECTIVE_CORRECTION_HINT; 17 | import static org.lwjgl.opengl.GL11.GL_PROJECTION; 18 | import static org.lwjgl.opengl.GL11.GL_SMOOTH; 19 | import static org.lwjgl.opengl.GL11.glClear; 20 | import static org.lwjgl.opengl.GL11.glClearColor; 21 | import static org.lwjgl.opengl.GL11.glClearDepth; 22 | import static org.lwjgl.opengl.GL11.glDepthFunc; 23 | import static org.lwjgl.opengl.GL11.glEnable; 24 | import static org.lwjgl.opengl.GL11.glHint; 25 | import static org.lwjgl.opengl.GL11.glLoadIdentity; 26 | import static org.lwjgl.opengl.GL11.glMatrixMode; 27 | import static org.lwjgl.opengl.GL11.glShadeModel; 28 | import static org.lwjgl.opengl.GL11.glViewport; 29 | 30 | import java.util.HashSet; 31 | 32 | import nexus.model.structs.Chunk; 33 | import nexus.model.structs.Vector3; 34 | 35 | import org.lwjgl.opengl.Display; 36 | import org.lwjgl.util.glu.GLU; 37 | 38 | public class View { 39 | Model model; 40 | int renderDistance; 41 | 42 | public HashSet toRender; 43 | 44 | /** 45 | * Constructs a View 46 | * 47 | * Camera position is currently implied; it looks down the z axis by default 48 | * 49 | * @param model model, or world to be drawn 50 | */ 51 | public View(Model model, int renderDistance) { 52 | this.model = model; 53 | this.renderDistance = renderDistance; 54 | } 55 | 56 | /** 57 | * Initializes OpenGL 58 | */ 59 | public void init() { 60 | int width = Display.getDisplayMode().getWidth(); 61 | int height = Display.getDisplayMode().getHeight(); 62 | 63 | glViewport(0, 0, width, height); 64 | glMatrixMode(GL_PROJECTION); 65 | glLoadIdentity(); 66 | GLU.gluPerspective(45.0f, ((float) width / (float) height), 0.1f, 256.0f); 67 | glMatrixMode(GL_MODELVIEW); 68 | glLoadIdentity(); 69 | 70 | glShadeModel(GL_SMOOTH); 71 | glClearColor(0.55f, 0.804f, 0.97f, 0.0f); 72 | glClearDepth(1.0f); 73 | glEnable(GL_DEPTH_TEST); 74 | glDepthFunc(GL_LEQUAL); 75 | glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 76 | 77 | this.run(); 78 | } 79 | 80 | /** 81 | * Continuously renders the world 82 | */ 83 | public void run() { 84 | while (!Display.isCloseRequested() && !this.model.stop) { 85 | Display.sync(60); 86 | 87 | this.render(); 88 | } 89 | 90 | this.cleanUp(); 91 | } 92 | 93 | /** 94 | * Renders the world once 95 | */ 96 | public void render() { 97 | // check if the model is currently being written to and skip a frame if it is 98 | if(!model.locked) { 99 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 100 | 101 | // adjust camera angle 102 | glLoadIdentity(); 103 | GLU.gluLookAt(this.model.camera.eye.x, this.model.camera.eye.y, this.model.camera.eye.z, 104 | this.model.camera.focal.x, this.model.camera.focal.y, this.model.camera.focal.z, 105 | 0.0f, 1.0f, 0.0f); 106 | 107 | 108 | // this.model.requestChunk(new Vector3(0, 0, 0)); 109 | 110 | for(int i = -this.renderDistance; i <= this.renderDistance; i++) { 111 | for(int j = -this.renderDistance; j <= this.renderDistance; j++) { 112 | Chunk chunk = this.model.chunks.getChunk(this.model.camera.eye.add(new Vector3(i, 0, j).scale(16f))); 113 | chunk.drawBlocks(); 114 | } 115 | } 116 | 117 | Display.update(); 118 | } 119 | } 120 | 121 | public void cleanUp() { 122 | Display.destroy(); 123 | 124 | System.exit(0); 125 | } 126 | } -------------------------------------------------------------------------------- /src/nexus/model/generators/Interpolate.java: -------------------------------------------------------------------------------- 1 | package nexus.model.generators; 2 | 3 | /** 4 | * A library of static functions for Interpolation 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | public class Interpolate { 11 | 12 | /** 13 | * Given two y values a and b which are 1 apart on the x axis, return the y value of the point that is x units from a on the x axis 14 | * 15 | * @param a y value of left most point 16 | * @param b y value of right most point 17 | * @param x offset of the point from a 18 | * @return 19 | */ 20 | public static float cosine(float a, float b, float x) { 21 | float ft = x * (float) Math.PI; 22 | float f = (1.0f - (float) Math.cos(ft)) * 0.5f; 23 | 24 | return a*(1-f) + b*f; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/nexus/model/generators/Noise1D.java: -------------------------------------------------------------------------------- 1 | package nexus.model.generators; 2 | 3 | public class Noise1D { 4 | 5 | public static final int[][] primes = { {15731, 789221, 1376312589, 107374182} }; 6 | 7 | /** 8 | * Deteministically hashes one number 9 | * 10 | * @param x 11 | * @param primes set of 4 primes to use in generating the noise 12 | * @return deteministic noise; hash of x 13 | */ 14 | public static float noise(int x, int[] primes) { 15 | x = (x<<17) ^ x; 16 | return ((((x * (x * x * primes[0] + primes[1]) + primes[2]) & 0x7fffffff) / (float) primes[3]) - 10.0f) / 10.0f; 17 | } 18 | 19 | 20 | /** 21 | * Creates smooth noise from one numbers based on its neighbors 22 | * 23 | * @param x 24 | * @param primes set of 4 primes to use in generating the noise 25 | * @return deteministic noise 26 | */ 27 | public static float smooth(int x, int[] primes) { 28 | return noise(x, primes) / 2.0f + noise(x - 1, primes) / 4.0f + noise(x + 1, primes) / 4.0f; 29 | } 30 | 31 | /** 32 | * Performs cosine interpolation on smooth noise 33 | * 34 | * @param x 35 | * @param primes 36 | * @return 37 | */ 38 | public static float interpolate(float x, int[] primes) { 39 | int intX = (int) x; 40 | float fractionalX = x - intX; 41 | 42 | float v1 = smooth(intX, primes); 43 | float v2 = smooth(intX + 1, primes); 44 | 45 | return Interpolate.cosine(v1, v2, fractionalX); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/nexus/model/generators/Noise2D.java: -------------------------------------------------------------------------------- 1 | package nexus.model.generators; 2 | 3 | /** 4 | * Utilities for creating deterministic Noise 5 | * 6 | * @author Lane Aasen 7 | * @see [http://freespace.virgin.net/hugo.elias/models/m_perlin.htm](Noise Tutorial) 8 | * 9 | */ 10 | 11 | public class Noise2D { 12 | 13 | /** 14 | * Deteministically hashes two numbers 15 | * 16 | * @param x 17 | * @param y 18 | * @param primes set of 4 primes to use in generating the noise 19 | * @return deteministic noise; hash of x 20 | */ 21 | public static float noise(int x, int y, int[] primes) { 22 | int n = x + y * 57; 23 | n = (n<<13) ^ n; 24 | return Noise1D.noise(n, primes); 25 | } 26 | 27 | /** 28 | * Creates smooth noise from two numbers based on its neighbors 29 | * 30 | * @param x 31 | * @param primes set of 4 primes to use in generating the noise 32 | * @return deteministic noise 33 | */ 34 | public static float smooth(int x, int y, int[] primes) { 35 | float corners = (noise(x - 1, y - 1, primes) + noise(x + 1, y - 1, primes) + noise(x - 1, y + 1, primes) + noise(x + 1, y + 1, primes)) / 16.0f; 36 | float sides = (noise(x - 1, y, primes) + noise(x + 1, y, primes) + noise(x, y - 1, primes) + noise(x, y + 1, primes)) / 8.0f; 37 | float center = noise(x, y, primes) / 4.0f; 38 | return corners + sides + center; 39 | } 40 | 41 | /** 42 | * Performs cosine interpolation on smooth noise 43 | * 44 | * @param x 45 | * @param primes 46 | * @return 47 | */ 48 | public static float interpolate(float x, float y, int[] primes) { 49 | int intX = (int) x; 50 | float fractionalX = x - intX; 51 | 52 | int intY = (int) y; 53 | float fractionalY = y - intY; 54 | 55 | float v1 = smooth(intX, intY, primes); 56 | float v2 = smooth(intX + 1, intY, primes); 57 | float v3 = smooth(intX, intY + 1, primes); 58 | float v4 = smooth(intX + 1, intY + 1, primes); 59 | 60 | float i1 = Interpolate.cosine(v1, v2, fractionalX); 61 | float i2 = Interpolate.cosine(v3, v4, fractionalX); 62 | 63 | return Interpolate.cosine(i1, i2, fractionalY); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/nexus/model/generators/Perlin.java: -------------------------------------------------------------------------------- 1 | package nexus.model.generators; 2 | 3 | /** 4 | * Utilities for generating Perlin noise 5 | * 6 | * @author Lane Aasen 7 | * @see [http://freespace.virgin.net/hugo.elias/models/m_perlin.htm](Noise Tutorial) 8 | * 9 | */ 10 | 11 | public class Perlin { 12 | public static final float P = 0.5f; 13 | public static final int OCTAVES = 1; 14 | 15 | public static float perlin2D(float x, float y) { 16 | float total = 0.0f; 17 | 18 | for (int i = 0; i < OCTAVES; i++) { 19 | int frequency = (int) Math.pow(2, i); 20 | float amplitude = (float) Math.pow(P, i); 21 | 22 | total += Noise2D.interpolate(x * frequency, y * frequency, Noise1D.primes[0]) * amplitude; 23 | } 24 | 25 | return total; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/nexus/model/renderable/Air.java: -------------------------------------------------------------------------------- 1 | package nexus.model.renderable; 2 | 3 | import nexus.model.structs.Block; 4 | import nexus.model.structs.Vector3; 5 | 6 | public class Air extends Block { 7 | 8 | public Air(Vector3 a, float dimension) { 9 | super(a, dimension, null); 10 | } 11 | 12 | @Override 13 | public boolean visible() { 14 | return false; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/nexus/model/renderable/BlockMask.java: -------------------------------------------------------------------------------- 1 | package nexus.model.renderable; 2 | 3 | /** 4 | * BlockMasks define which parts of a Block must be rendered 5 | */ 6 | 7 | import static org.lwjgl.opengl.GL11.glColor3f; 8 | import static org.lwjgl.opengl.GL11.glLineWidth; 9 | 10 | import java.util.Arrays; 11 | 12 | import nexus.model.structs.Block; 13 | import nexus.model.structs.Vector3; 14 | import nexus.view.gl.Outlines; 15 | import nexus.view.gl.Planes; 16 | import nexus.view.structs.VertexContainer; 17 | 18 | public class BlockMask implements Renderable { 19 | public static final float OUTLINE_R = 0.5f; 20 | public static final float OUTLINE_G = 0.5f; 21 | public static final float OUTLINE_B = 0.5f; 22 | public static final float OUTLINE_WIDTH = 4f; 23 | 24 | public static enum index { TOP, BOTTOM, LEFT, RIGHT, NEAR, FAR } 25 | 26 | public Block block; 27 | 28 | public static long face = 0; 29 | 30 | // top, bottom, near, far, left, right 31 | private boolean[] doRender; 32 | public boolean render = false; 33 | public boolean outline = false; 34 | private VertexContainer[] faces; 35 | 36 | public BlockMask(Block block) { 37 | this.block = block; 38 | 39 | this.doRender = new boolean[6]; 40 | Arrays.fill(doRender, false); 41 | 42 | this.faces = new VertexContainer[6]; 43 | } 44 | 45 | /** 46 | * Draws the specified sides of the Mask's Block 47 | */ 48 | @Override 49 | public void draw() { 50 | if (this.render) { 51 | for (int i = 0; i < faces.length; i++) { 52 | if (doRender[i]) { 53 | getFace(i).render(); 54 | } 55 | } 56 | 57 | if (this.outline) { 58 | glColor3f(OUTLINE_R, OUTLINE_G, OUTLINE_B); 59 | glLineWidth(OUTLINE_WIDTH); 60 | Outlines.rectPrism2f(block.a, block.b); 61 | } 62 | } 63 | } 64 | 65 | public VertexContainer getFace(int i) { 66 | if (this.faces[i] == null) { 67 | this.faces[i] = makeFace(i); 68 | } 69 | 70 | return this.faces[i]; 71 | } 72 | 73 | public VertexContainer makeFace(int i) { 74 | if (i == index.TOP.ordinal()) { 75 | return new VertexContainer(Planes.makeQuad2f(new Vector3(block.a.x, block.b.y, block.a.z), block.b)); 76 | } else if (i == index.BOTTOM.ordinal()) { 77 | return new VertexContainer(Planes.makeQuad2f(block.a, new Vector3(block.b.x, block.a.y, block.b.z))); 78 | } else if (i == index.RIGHT.ordinal()) { 79 | return new VertexContainer(Planes.makeQuad2f(new Vector3(block.b.x, block.a.y, block.a.z), block.b)); 80 | } else if (i == index.LEFT.ordinal()) { 81 | return new VertexContainer(Planes.makeQuad2f(block.a, new Vector3(block.a.x, block.b.y, block.b.z))); 82 | } else if (i == index.NEAR.ordinal()) { 83 | return new VertexContainer(Planes.makeQuad2f(block.a, new Vector3(block.b.x, block.b.y, block.a.z))); 84 | } else if (i == index.FAR.ordinal()) { 85 | return new VertexContainer(Planes.makeQuad2f(new Vector3(block.a.x, block.a.y, block.b.z), block.b)); 86 | } else { 87 | throw new IllegalArgumentException(i + " is out of range"); 88 | } 89 | } 90 | 91 | public void setRender(boolean x) { 92 | this.render = x; 93 | } 94 | 95 | public void setDrawOutline(boolean x) { 96 | this.outline = x; 97 | } 98 | 99 | private void setField(int i, boolean x) { 100 | if (x) { 101 | this.render = true; 102 | } 103 | 104 | this.doRender[i] = x; 105 | } 106 | 107 | public void setDrawTop(boolean x) { 108 | this.setField(index.TOP.ordinal(), x); 109 | } 110 | 111 | public void setDrawBottom(boolean x) { 112 | this.setField(index.BOTTOM.ordinal(), x); 113 | } 114 | 115 | public void setDrawLeft(boolean x) { 116 | this.setField(index.LEFT.ordinal(), x); 117 | } 118 | 119 | public void setDrawRight(boolean x) { 120 | this.setField(index.RIGHT.ordinal(), x); 121 | } 122 | 123 | public void setDrawNear(boolean x) { 124 | this.setField(index.NEAR.ordinal(), x); 125 | } 126 | 127 | public void setDrawFar(boolean x) { 128 | this.setField(index.FAR.ordinal(), x); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/nexus/model/renderable/Grid.java: -------------------------------------------------------------------------------- 1 | package nexus.model.renderable; 2 | 3 | /** 4 | * A horizontal Grid for orientation 5 | * 6 | * @author Lane Aasen 7 | */ 8 | 9 | import static org.lwjgl.opengl.GL11.GL_LINES; 10 | import static org.lwjgl.opengl.GL11.glBegin; 11 | import static org.lwjgl.opengl.GL11.glEnd; 12 | import static org.lwjgl.opengl.GL11.glLineWidth; 13 | import static org.lwjgl.opengl.GL11.glVertex3f; 14 | 15 | public class Grid implements Renderable { 16 | float width, spacing, height, thickness; 17 | 18 | /** 19 | * Creates a Grid 20 | * 21 | * @param width the Grid will extend outwards from the origin by this much 22 | * @param spacing units in between each line of the grid 23 | * @param height y coordinate of the Grid 24 | * @param thickness thickness (in pixels) for the grid lines 25 | */ 26 | public Grid(float width, float spacing, float height, float thickness) { 27 | this.width = width; 28 | this.spacing = spacing; 29 | this.height = height; 30 | this.thickness = thickness; 31 | } 32 | 33 | @Override 34 | public void draw() { 35 | glLineWidth(this.thickness); 36 | glBegin(GL_LINES); 37 | 38 | for(float i = -this.width; i <= this.width; i += this.spacing) { 39 | glVertex3f(-this.width, this.height, i); 40 | glVertex3f(this.width, this.height, i); 41 | glVertex3f(i, this.height, -this.width); 42 | glVertex3f(i, this.height, this.width); 43 | } 44 | 45 | glEnd(); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/nexus/model/renderable/Renderable.java: -------------------------------------------------------------------------------- 1 | package nexus.model.renderable; 2 | 3 | /** 4 | * An interface for anything that can be drawn in the Model 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | public interface Renderable { 11 | 12 | /** 13 | * Draws the object itself 14 | */ 15 | public void draw(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/nexus/model/renderable/Solid.java: -------------------------------------------------------------------------------- 1 | package nexus.model.renderable; 2 | 3 | import nexus.model.structs.Block; 4 | import nexus.model.structs.Vector3; 5 | import nexus.view.color.Colorist; 6 | 7 | public class Solid extends Block { 8 | 9 | public Solid(Vector3 a, float dimension, Colorist colorist) { 10 | super(a, dimension, colorist); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/nexus/model/structs/Block.java: -------------------------------------------------------------------------------- 1 | package nexus.model.structs; 2 | 3 | import nexus.model.renderable.BlockMask; 4 | import nexus.view.color.Colorist; 5 | 6 | /** 7 | * A generic rectangular prism 8 | * 9 | * @author Lane Aasen 10 | * 11 | */ 12 | 13 | public class Block { 14 | public Vector3 a; 15 | public Vector3 b; 16 | public float dimension; 17 | public Colorist colorist; 18 | private BlockMask mask; 19 | 20 | /** 21 | * Creates a Block 22 | * 23 | * @param position near bottom right corner of the block 24 | * @param width width on the x axis 25 | * @param height height on the y axis 26 | * @param depth depth on the z axis 27 | */ 28 | public Block(Vector3 a, float dimension, Colorist colorist) { 29 | this.a = a; 30 | this.b = new Vector3(a.x + dimension, a.y + dimension, a.z + dimension); 31 | this.dimension = dimension; 32 | this.colorist = colorist; 33 | } 34 | 35 | public void draw() { 36 | if (this.visible() && this.mask != null) { 37 | mask.draw(); 38 | } 39 | } 40 | 41 | public boolean visible() { 42 | return true; 43 | } 44 | 45 | public BlockMask getMask() { 46 | if (this.mask == null && this.visible()) { 47 | this.mask = new BlockMask(this); 48 | } 49 | 50 | return this.mask; 51 | } 52 | 53 | public boolean isOnGrid() { 54 | return (a.length() % 1 == 0); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/nexus/model/structs/Camera.java: -------------------------------------------------------------------------------- 1 | package nexus.model.structs; 2 | 3 | /** 4 | * A Camera that defines a viewpoint 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | public class Camera { 11 | public Vector3 eye; 12 | public Vector3 focal; 13 | public Vector3 unitFocal; 14 | public float pitch, yaw; 15 | public boolean invert; 16 | public float sensitivity; 17 | 18 | /** 19 | * Creates a Camera 20 | * 21 | * @param eye position of the viewer 22 | * @param pitch rotation around the x axis 23 | * @param yaw rotation around the y axis 24 | */ 25 | public Camera(Vector3 eye, float pitch, float yaw, float sensitivity, boolean invert) { 26 | this.eye = eye; 27 | this.pitch = pitch; 28 | this.yaw = yaw; 29 | this.sensitivity = sensitivity; 30 | this.invert = invert; 31 | this.focal = new Vector3(0.0f, 0.0f, 0.0f); 32 | } 33 | 34 | /** 35 | * Calculates the point on the unit sphere that the Camera is pointing at 36 | * 37 | * @return a point on the unit sphere which the Camera is pointing at 38 | */ 39 | private Vector3 unitLookAt() { 40 | return new Vector3((float) Math.cos(this.yaw), (float) Math.tan((invert ? 1.0f : -1.0f) * this.pitch), (float) Math.sin(this.yaw)); 41 | } 42 | 43 | /** 44 | * Calculates an absolute point that the camera should point at 45 | * 46 | * @see unitLookAt() 47 | * @return an absolute point that the camera should look at 48 | */ 49 | public Vector3 lookAt() { 50 | unitFocal = unitLookAt(); 51 | return new Vector3(this.eye.x + unitFocal.x, this.eye.y + unitFocal.y, this.eye.z + unitFocal.z); 52 | } 53 | 54 | /** 55 | * Updates the Camera's focal point 56 | */ 57 | public void update() { 58 | this.focal = lookAt(); 59 | } 60 | 61 | /** 62 | * Sets for Camera's pitch and limits it to a reasonable range 63 | * 64 | * @param a 65 | */ 66 | public void pitch(float a) { 67 | this.pitch += a; 68 | 69 | if (this.pitch > 1.0f) { 70 | this.pitch = 1.0f; 71 | } else if (this.pitch < -1.0f) { 72 | this.pitch = -1.0f; 73 | } 74 | } 75 | 76 | /** 77 | * Sets the Camera's yaw 78 | * 79 | * Does nothing special; it's only here for consistency with pitch() 80 | * 81 | * @param a 82 | */ 83 | public void yaw(float a) { 84 | this.yaw += a; 85 | } 86 | 87 | /** 88 | * Moves the camera towards where it is pointing 89 | */ 90 | public void forwards() { 91 | this.eye = this.unitLookAt().scale(this.sensitivity).add(this.eye); 92 | } 93 | 94 | /** 95 | * Moves the camera away from where it is pointing 96 | */ 97 | public void backwards() { 98 | this.eye = this.unitLookAt().scale(-this.sensitivity).add(this.eye); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/nexus/model/structs/Chunk.java: -------------------------------------------------------------------------------- 1 | package nexus.model.structs; 2 | 3 | import nexus.model.generators.Perlin; 4 | import nexus.model.renderable.Air; 5 | import nexus.model.renderable.Solid; 6 | import nexus.view.color.Colorist; 7 | import nexus.view.color.Greyscale; 8 | 9 | /** 10 | * 16x16 containers for Terrain 11 | * 12 | * @author Lane Aasen 13 | * 14 | */ 15 | 16 | public class Chunk { 17 | // this value should not need to be changed 18 | public static final int WIDTH = 16; 19 | public static final int HEIGHT = 64; 20 | public static final int BIG_NUMBER = (int) Math.pow(2, 18); 21 | 22 | int x, z; 23 | Vector3 dilation; 24 | public Block[][][] blocks; 25 | boolean mask = false; 26 | Colorist colorist; 27 | ChunkContainer parent; 28 | 29 | /** 30 | * Creates a new Chunk 31 | * 32 | * @param x x position of the Chunk in its container 33 | * @param z z position of the Chunk in its container 34 | * @param dilation (x, y, z) dilation for terrain generatrion 35 | * @param parent the Chunk's parent ChunkContainer 36 | */ 37 | public Chunk(int x, int z, Vector3 dilation, ChunkContainer parent) { 38 | this.x = x; 39 | this.z = z; 40 | this.dilation = dilation; 41 | this.blocks = new Block[WIDTH][WIDTH][HEIGHT]; 42 | this.colorist = new Greyscale(16.0f, 0.0f); 43 | this.parent = parent; 44 | } 45 | 46 | /** 47 | * Fills the Chunk with a heightmap of 2D Perlin noise 48 | */ 49 | public void generate() { 50 | for (int i = 0; i < WIDTH; i++) { 51 | for (int j = 0; j < WIDTH; j++) { 52 | float x = (float) this.x * Chunk.WIDTH + i; 53 | float z = (float) this.z * Chunk.WIDTH + j; 54 | float y = (int) dilation.y * ((Perlin.perlin2D(x * dilation.x + BIG_NUMBER, z * dilation.z + BIG_NUMBER) + 1) / 2) + 1; 55 | 56 | for (int k = 0; k < HEIGHT; k++) { 57 | if (k <= y) { 58 | this.blocks[i][j][k] = new Solid(new Vector3(x, (float) k, z), 1.0f, this.colorist); 59 | } else { 60 | this.blocks[i][j][k] = new Air(new Vector3(x, (float) k, z), 1.0f); 61 | } 62 | } 63 | } 64 | } 65 | } 66 | 67 | /** 68 | * Calculates visible sides of blocks and updates each block's mask accordingly 69 | */ 70 | public void calcVisible() { 71 | this.mask = true; 72 | 73 | for (int x = 0; x < WIDTH; x++) { 74 | for (int z = 0; z < WIDTH; z++) { 75 | for (int y = 0; y < HEIGHT; y++) { 76 | if (blocks[x][z][y].visible()) { 77 | if (x == 0) { 78 | if (!parent.getChunk(this.x - 1, this.z, false).blocks[WIDTH - 1][z][y].visible()) { 79 | blocks[x][z][y].getMask().setRender(true); 80 | blocks[x][z][y].getMask().setDrawLeft(true); 81 | } 82 | if (!blocks[x + 1][z][y].visible()) { 83 | blocks[x][z][y].getMask().setRender(true); 84 | blocks[x][z][y].getMask().setDrawRight(true); 85 | } 86 | } else if (x == WIDTH - 1) { 87 | if (!parent.getChunk(this.x + 1, this.z, false).blocks[0][z][y].visible()) { 88 | blocks[x][z][y].getMask().setRender(true); 89 | blocks[x][z][y].getMask().setDrawRight(true); 90 | } 91 | if (!blocks[x - 1][z][y].visible()) { 92 | blocks[x][z][y].getMask().setRender(true); 93 | blocks[x][z][y].getMask().setDrawLeft(true); 94 | } 95 | } else { 96 | if (!blocks[x + 1][z][y].visible()) { 97 | blocks[x][z][y].getMask().setRender(true); 98 | blocks[x][z][y].getMask().setDrawRight(true); 99 | } 100 | 101 | if (!blocks[x - 1][z][y].visible()) { 102 | blocks[x][z][y].getMask().setRender(true); 103 | blocks[x][z][y].getMask().setDrawLeft(true); 104 | } 105 | } 106 | 107 | 108 | if (z == 0) { 109 | if (!parent.getChunk(this.x, this.z - 1, false).blocks[x][WIDTH - 1][y].visible()) { 110 | blocks[x][z][y].getMask().setRender(true); 111 | blocks[x][z][y].getMask().setDrawNear(true); 112 | } 113 | if (!blocks[x][z + 1][y].visible()) { 114 | blocks[x][z][y].getMask().setRender(true); 115 | blocks[x][z][y].getMask().setDrawFar(true); 116 | } 117 | } else if (z == WIDTH - 1) { 118 | if (!parent.getChunk(this.x, this.z + 1, false).blocks[x][0][y].visible()) { 119 | blocks[x][z][y].getMask().setRender(true); 120 | blocks[x][z][y].getMask().setDrawFar(true); 121 | } 122 | if (!blocks[x][z - 1][y].visible()) { 123 | blocks[x][z][y].getMask().setRender(true); 124 | blocks[x][z][y].getMask().setDrawNear(true); 125 | } 126 | } else { 127 | if (!blocks[x][z + 1][y].visible()) { 128 | blocks[x][z][y].getMask().setRender(true); 129 | blocks[x][z][y].getMask().setDrawFar(true); 130 | } 131 | 132 | if (!blocks[x][z - 1][y].visible()) { 133 | blocks[x][z][y].getMask().setRender(true); 134 | blocks[x][z][y].getMask().setDrawNear(true); 135 | } 136 | } 137 | 138 | if (y == HEIGHT - 1) { 139 | blocks[x][z][y].getMask().setRender(true); 140 | blocks[x][z][y].getMask().setDrawTop(true); 141 | } else if (y != 0) { 142 | if (!blocks[x][z][y + 1].visible()) { 143 | blocks[x][z][y].getMask().setRender(true); 144 | blocks[x][z][y].getMask().setDrawTop(true); 145 | } 146 | 147 | if (!blocks[x][z][y - 1].visible()) { 148 | blocks[x][z][y].getMask().setRender(true); 149 | blocks[x][z][y].getMask().setDrawBottom(true); 150 | } 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | 158 | /** 159 | * Draws each block in the Chunk 160 | */ 161 | public void drawBlocks() { 162 | for (Block[][] a : blocks) { 163 | for (Block[] b : a) { 164 | for (Block block : b) { 165 | block.draw(); 166 | } 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/nexus/model/structs/ChunkContainer.java: -------------------------------------------------------------------------------- 1 | package nexus.model.structs; 2 | 3 | /** 4 | * A Container for Chunks 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static nexus.model.structs.Chunk.HEIGHT; 11 | import static nexus.model.structs.Chunk.WIDTH; 12 | 13 | import java.util.HashMap; 14 | 15 | public class ChunkContainer { 16 | public HashMap chunks; 17 | public Vector3 selected; 18 | 19 | /** 20 | * Creates an empty ChunkContainer 21 | */ 22 | public ChunkContainer() { 23 | this.chunks = new HashMap(); 24 | this.selected = new Vector3(0f, 0f, 0f); 25 | } 26 | 27 | /** 28 | * Returns the Chunk at the given Chunk coordinates 29 | * 30 | * This will generate a new Chunk if there is none at the specified position 31 | * 32 | * @param x 33 | * @param z 34 | * @param mask if the Chunk should be visually culled 35 | * @return 36 | */ 37 | public Chunk getChunk(int x, int z, boolean mask) { 38 | Chunk chunk = this.chunks.get(getKey(x, z)); 39 | 40 | if (chunk == null) { 41 | chunk = new Chunk(x, z, new Vector3(0.2f, 20.0f, 0.2f), this); 42 | chunk.generate(); 43 | 44 | if (mask) { 45 | chunk.calcVisible(); 46 | } 47 | 48 | this.chunks.put(getKey(x, z), chunk); 49 | } else if (mask) { 50 | if (!chunk.mask) { 51 | chunk.calcVisible(); 52 | } 53 | } 54 | 55 | return chunk; 56 | } 57 | 58 | /** 59 | * Returns the Chunk that the given Vector is in 60 | * 61 | * @param position 62 | * @return 63 | */ 64 | public Chunk getChunk(Vector3 pos) { 65 | return getChunk((int) Math.floor(pos.x / WIDTH), (int) Math.floor(pos.z / WIDTH)); 66 | } 67 | 68 | public Chunk getChunk(int x, int y) { 69 | return getChunk(x, y, true); 70 | } 71 | 72 | /** 73 | * @return the Block that the given Vector is in 74 | */ 75 | public Block getBlock(Vector3 pos) { 76 | if (inBounds(pos)) { 77 | return this.getChunk(pos).blocks[posMod((int) Math.floor(pos.x), WIDTH)][posMod((int) Math.floor(pos.z), WIDTH)][(int) Math.floor(pos.y)]; 78 | } else { 79 | throw new IllegalArgumentException(pos + " is out of bounds"); 80 | } 81 | } 82 | 83 | /** 84 | * places a block into the ChunkContainer and updates nearby masks if necessary 85 | * 86 | * @param block 87 | */ 88 | public void setBlock(Block block) { 89 | if (inBounds(block.a) && inBounds(block.b) && block.isOnGrid()) { 90 | this.getChunk(block.a).blocks[posMod((int) block.a.x, WIDTH)][posMod((int) block.a.z, WIDTH)][(int) block.a.y] = block; 91 | 92 | if (!block.visible()) { 93 | updateNearbyMask(block); 94 | } else { 95 | updateMask(block); 96 | } 97 | } else { 98 | throw new IllegalArgumentException(block.a.toString() + " is an illegal block position"); 99 | } 100 | } 101 | 102 | /** 103 | * Updates the rendering masks of nearby blocks 104 | * 105 | * @param block 106 | */ 107 | private void updateNearbyMask(Block block) { 108 | Block below = this.getBlock(block.a.add(new Vector3(0f, -1f, 0f))); 109 | Block above = this.getBlock(block.a.add(new Vector3(0f, 1f, 0f))); 110 | Block left = this.getBlock(block.a.add(new Vector3(-1f, 0f, 0f))); 111 | Block right = this.getBlock(block.a.add(new Vector3(1f, 0f, 0f))); 112 | Block near = this.getBlock(block.a.add(new Vector3(0f, 0f, -1f))); 113 | Block far = this.getBlock(block.a.add(new Vector3(0f, 0f, 1f))); 114 | 115 | if (below.visible()) { 116 | block.getMask().setDrawBottom(true); 117 | } 118 | 119 | if (above.visible()) { 120 | block.getMask().setDrawTop(true); 121 | } 122 | 123 | if (left.visible()) { 124 | block.getMask().setDrawLeft(true); 125 | } 126 | 127 | if (right.visible()) { 128 | block.getMask().setDrawRight(true); 129 | } 130 | 131 | if (near.visible()) { 132 | block.getMask().setDrawNear(true); 133 | } 134 | 135 | if (far.visible()) { 136 | block.getMask().setDrawFar(true); 137 | } 138 | } 139 | 140 | /** 141 | * Updates the mask of the given block based on its neighbors 142 | * 143 | * @param block 144 | */ 145 | private void updateMask(Block block) { 146 | Block below = this.getBlock(block.a.add(new Vector3(0f, -1f, 0f))); 147 | Block above = this.getBlock(block.a.add(new Vector3(0f, 1f, 0f))); 148 | Block left = this.getBlock(block.a.add(new Vector3(-1f, 0f, 0f))); 149 | Block right = this.getBlock(block.a.add(new Vector3(1f, 0f, 0f))); 150 | Block near = this.getBlock(block.a.add(new Vector3(0f, 0f, -1f))); 151 | Block far = this.getBlock(block.a.add(new Vector3(0f, 0f, 1f))); 152 | 153 | if (!below.visible()) { 154 | block.getMask().setDrawBottom(true); 155 | } 156 | 157 | if (!above.visible()) { 158 | block.getMask().setDrawTop(true); 159 | } 160 | 161 | if (!left.visible()) { 162 | block.getMask().setDrawLeft(true); 163 | } 164 | 165 | if (!right.visible()) { 166 | block.getMask().setDrawRight(true); 167 | } 168 | 169 | if (!near.visible()) { 170 | block.getMask().setDrawNear(true); 171 | } 172 | 173 | if (!far.visible()) { 174 | block.getMask().setDrawFar(true); 175 | } 176 | } 177 | 178 | /** 179 | * Generates a unique key for the given Chunk coordinate 180 | */ 181 | public static long getKey(int x, int y) { 182 | return x * (long) Math.pow(2, 31) + y; 183 | } 184 | 185 | private static int posMod(int a, int b) { 186 | int r = a % b; 187 | return r < 0 ? r + b : r; 188 | } 189 | 190 | public boolean inBounds(Vector3 pos) { 191 | return (pos.y <= (HEIGHT - 1) && pos.y >= 0f); 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/nexus/model/structs/Color.java: -------------------------------------------------------------------------------- 1 | package nexus.model.structs; 2 | 3 | /** 4 | * Container for an OpenGL RGBA Color 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.glColor4f; 11 | 12 | public class Color { 13 | float r, g, b, a; 14 | 15 | /** 16 | * Contructs a Color with RBGA 17 | * 18 | * @param r 19 | * @param g 20 | * @param b 21 | * @param a 22 | */ 23 | public Color(float r, float g, float b, float a) { 24 | this.r = r; 25 | this.g = g; 26 | this.b = b; 27 | this.a = a; 28 | } 29 | 30 | /** 31 | * Constructs a Color with RGB and infers 1.0 for A 32 | * 33 | * @param r 34 | * @param g 35 | * @param b 36 | */ 37 | public Color(float r, float g, float b) { 38 | this(r, g, b, 1.0f); 39 | } 40 | 41 | /** 42 | * Sets the OpenGL Color to this Color 43 | */ 44 | public void color() { 45 | glColor4f(this.r, this.g, this.b, this.a); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/nexus/model/structs/Vector3.java: -------------------------------------------------------------------------------- 1 | package nexus.model.structs; 2 | 3 | /** 4 | * A generic 3D point made of floats for OpenGL compatibility 5 | * 6 | * @author Lane Aasen 7 | * @auhtor Dylan Swiggett 8 | * 9 | */ 10 | 11 | public class Vector3 { 12 | public static final int NUM_ELEMENTS = 3; 13 | public float x, y, z; 14 | 15 | /** 16 | * Constructs a Point 17 | * 18 | * @param x 19 | * @param y 20 | * @param z 21 | */ 22 | public Vector3(float x, float y, float z) { 23 | this.x = x; 24 | this.y = y; 25 | this.z = z; 26 | } 27 | 28 | public Vector3 add(Vector3 a) { 29 | return new Vector3(a.x + x, a.y + y, a.z + z); 30 | } 31 | 32 | public Vector3 subtract(Vector3 a) { 33 | return new Vector3(x - a.x, y - a.y, z - a.z); 34 | } 35 | 36 | public float length() { 37 | return (float) Math.sqrt(x * x + y * y + z * z); 38 | } 39 | 40 | public float lengthSquared() { 41 | return x * x + y * y + z * z; 42 | } 43 | 44 | public Vector3 scale(float scalar) { 45 | return new Vector3(x * scalar, y * scalar, z * scalar); 46 | } 47 | 48 | public Vector3 normalize() { 49 | return this.scale(1 / length()); 50 | } 51 | 52 | public float dotProduct(Vector3 vector) { 53 | return x * vector.x + y * vector.y + z * vector.z; 54 | } 55 | 56 | public Vector3 crossProduct(Vector3 vector) { 57 | return new Vector3(y * vector.z - z * vector.y, z * vector.x - x * vector.z, x * vector.y - y * vector.x); 58 | } 59 | 60 | public Vector3 projection(Vector3 vector) { 61 | float length = vector.length(); 62 | return vector.scale(dotProduct(vector) / (length * length)); 63 | } 64 | 65 | public Vector3 perpendicular(Vector3 vector) { 66 | return this.subtract(projection(vector)); 67 | } 68 | 69 | public String toString() { 70 | return "(" + x + ", " + y + ", " + z + ")"; 71 | } 72 | 73 | public boolean equals(Vector3 a) { 74 | return this.x == a.x && this.y == a.y && this.z == a.z; 75 | } 76 | 77 | /** 78 | * Converts a Vector into an array of floats 79 | */ 80 | public float[] toArray() { 81 | return new float[] { x, y, z }; 82 | } 83 | 84 | /** 85 | * Combines multiple Vectors into one array of floats 86 | * 87 | * @param vectors 88 | * @return combined array of floats 89 | */ 90 | public static float[] combine(Vector3[] vectors) { 91 | float[] combined = new float[vectors.length * Vector3.NUM_ELEMENTS]; 92 | 93 | for (int i = 0; i < vectors.length; i++) { 94 | combined[Vector3.NUM_ELEMENTS * i] = vectors[i].x; 95 | combined[Vector3.NUM_ELEMENTS * i + 1] = vectors[i].y; 96 | combined[Vector3.NUM_ELEMENTS * i + 2] = vectors[i].z; 97 | } 98 | 99 | return combined; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/nexus/view/color/Colorist.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | import static org.lwjgl.opengl.GL11.glColor3f; 4 | 5 | /** 6 | * The parent class for all Colorists, which handle dynamically coloring things 7 | * 8 | * @author Lane Aasen 9 | * 10 | */ 11 | 12 | public class Colorist { 13 | float range; 14 | float offset; 15 | 16 | /** 17 | * Creates a Colorist 18 | * 19 | * @param range 20 | * @param offset 21 | */ 22 | public Colorist(float range, float offset) { 23 | this.range = range; 24 | this.offset = offset; 25 | } 26 | 27 | /** 28 | * Adjusts the value to a float from 0.0 to 1.0 based on the Colorist's range 29 | * 30 | * @param value 31 | * @return value mapped into 0.0 to 1.0 32 | */ 33 | public float value(float value) { 34 | return (value - offset) / this.range; 35 | } 36 | 37 | /** 38 | * Selects a color based on the value 39 | * 40 | * @param value 41 | */ 42 | public void color(float value) { 43 | value = this.value(value); 44 | glColor3f(value, value, value); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/nexus/view/color/Dirt.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | /** 4 | * A Dirt Colorist 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.glColor3f; 11 | 12 | public class Dirt extends Colorist { 13 | public Dirt(float range, float offset) { 14 | super(range, offset); 15 | } 16 | 17 | /** 18 | * Selects a color based on the value 19 | * 20 | * @param value 21 | */ 22 | public void color(float value) { 23 | value = this.value(value); 24 | glColor3f(0.5f * value, 0.25f * value, 0.0f * value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/nexus/view/color/Grass.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | /** 4 | * A Dirt Colorist 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.glColor3f; 11 | 12 | public class Grass extends Colorist { 13 | public Grass(float range, float offset) { 14 | super(range, offset); 15 | } 16 | 17 | /** 18 | * Selects a color based on the value 19 | * 20 | * @param value 21 | */ 22 | public void color(float value) { 23 | value = this.value(value); 24 | glColor3f(0.0f, value, 0.0f); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/nexus/view/color/Greyscale.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | /** 4 | * A Greyscale Colorist 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.glColor3f; 11 | 12 | public class Greyscale extends Colorist { 13 | public Greyscale(float range, float offset) { 14 | super(range, offset); 15 | } 16 | 17 | /** 18 | * Selects a color based on the value 19 | * 20 | * @param value 21 | */ 22 | public void color(float value) { 23 | value = this.value(value); 24 | glColor3f(value, value, value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/nexus/view/color/Monochrome.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | import static org.lwjgl.opengl.GL11.glColor3f; 4 | 5 | /** 6 | * A Colorist that uses only on given color 7 | * 8 | * @author Lane Aasen 9 | * 10 | */ 11 | 12 | public class Monochrome extends Colorist { 13 | float red, green, blue; 14 | 15 | public Monochrome(float range, float offset, float red, float green, float blue) { 16 | super(range, offset); 17 | this.red = red; 18 | this.green = green; 19 | this.blue = blue; 20 | } 21 | 22 | public void color(float value) { 23 | glColor3f(this.red, this.green, this.blue); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/nexus/view/color/Rainbow.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | /** 4 | * A Rainbow Colorist 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.glColor3f; 11 | 12 | public class Rainbow extends Colorist { 13 | public Rainbow(float range, float offset) { 14 | super(range, offset); 15 | } 16 | 17 | /** 18 | * Selects a color based on the value 19 | * 20 | * @param value 21 | */ 22 | public void color(float value) { 23 | value = this.value(value); 24 | glColor3f(0.5f * value, 0.25f * value, 0.0f * value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/nexus/view/color/Sand.java: -------------------------------------------------------------------------------- 1 | package nexus.view.color; 2 | 3 | /** 4 | * A Dirt Colorist 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.glColor3f; 11 | 12 | public class Sand extends Colorist { 13 | public Sand(float range, float offset) { 14 | super(range, offset); 15 | } 16 | 17 | /** 18 | * Selects a color based on the value 19 | * 20 | * @param value 21 | */ 22 | public void color(float value) { 23 | value = this.value(value); 24 | glColor3f(value, value, value / 2.0f); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/nexus/view/gl/Outlines.java: -------------------------------------------------------------------------------- 1 | package nexus.view.gl; 2 | 3 | /** 4 | * A collection of static helper methods for drawing Outlines 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import static org.lwjgl.opengl.GL11.GL_LINES; 11 | import static org.lwjgl.opengl.GL11.glBegin; 12 | import static org.lwjgl.opengl.GL11.glEnd; 13 | import static org.lwjgl.opengl.GL11.glVertex3f; 14 | import nexus.model.structs.Vector3; 15 | 16 | public class Outlines { 17 | 18 | /** 19 | * Draws a line through all Points in the array 20 | * 21 | * @param points an array of Points 22 | */ 23 | public static void outline(Vector3[] points) { 24 | for (int i = 0; i < points.length; i++) { 25 | drawLine(points[i], points[(i + 1) % points.length]); 26 | } 27 | } 28 | 29 | /** 30 | * Draws a line through 4 points 31 | * 32 | * TODO: find a better way to do this 33 | * 34 | * @param a 35 | * @param b 36 | * @param c 37 | * @param d 38 | */ 39 | @Deprecated 40 | public static void outline(Vector3 a, Vector3 b, Vector3 c, Vector3 d) { 41 | Vector3[] points = {a, b, c, d}; 42 | Outlines.outline(points); 43 | } 44 | 45 | /** 46 | * Outlines a quad 47 | * 48 | * @param a 49 | * @param b 50 | */ 51 | @Deprecated 52 | public static void outlineQuad2f(Vector3 a, Vector3 b) { 53 | glBegin(GL_LINES); 54 | 55 | glVertex3f(a.x, a.y, a.z); 56 | glVertex3f(a.x, b.y, b.z); 57 | 58 | glVertex3f(b.x, b.y, b.z); 59 | glVertex3f(a.x, b.y, b.z); 60 | 61 | glVertex3f(a.x, a.y, a.z); 62 | glVertex3f(b.x, a.y, a.z); 63 | 64 | glVertex3f(b.x, b.y, b.z); 65 | glVertex3f(b.x, a.y, a.z); 66 | 67 | glEnd(); 68 | } 69 | 70 | /** 71 | * Outlines a (mostly oblique) rectangular prism 72 | * 73 | * @param a 74 | * @param b 75 | */ 76 | @Deprecated 77 | public static void rectPrism2f(Vector3 a, Vector3 b) { 78 | outlineQuad2f(a, new Vector3(b.x, b.y, a.z)); 79 | outlineQuad2f(a, new Vector3(b.x, a.y, b.z)); 80 | outlineQuad2f(b, new Vector3(a.x, a.y, b.z)); 81 | outlineQuad2f(b, new Vector3(a.x, b.y, a.z)); 82 | } 83 | 84 | 85 | /** 86 | * Draws a line from Point a to b 87 | * 88 | * @param a 89 | * @param b 90 | */ 91 | @Deprecated 92 | public static void drawLine(Vector3 a, Vector3 b) { 93 | glBegin(GL_LINES); 94 | 95 | glVertex3f(a.x, a.y, a.z); 96 | glVertex3f(b.x, b.y, b.z); 97 | 98 | glEnd(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/nexus/view/gl/Planes.java: -------------------------------------------------------------------------------- 1 | package nexus.view.gl; 2 | 3 | /** 4 | * A collection of static helper methods for drawing 2D shapes 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import nexus.model.structs.Vector3; 11 | 12 | public class Planes { 13 | /** 14 | * Form a quad between two vectors. Only tested for orthagonal. 15 | * 16 | * @param a lower left corner 17 | * @param b upper right corner 18 | * @return float[] that, when parsed as triangles, will for a quad 19 | */ 20 | public static float[] makeQuad2f(Vector3 a, Vector3 b) { 21 | if (a.x <= b.x && a.y <= b.y && a.z <= b.z) { 22 | if (a.x == b.x) { 23 | return Vector3.combine(new Vector3[] { a, b, new Vector3(a.x, b.y, a.z), b, a, new Vector3(a.x, a.y, b.z)}); 24 | } else if (a.y == b.y) { 25 | return Vector3.combine(new Vector3[] { a, b, new Vector3(a.x, a.y, b.z), b, a, new Vector3(b.x, a.y, a.z)}); 26 | } else if (a.z == b.z) { 27 | return Vector3.combine(new Vector3[] { a, b, new Vector3(a.x, b.y, a.z), b, a, new Vector3(b.x, a.y, a.z)}); 28 | } else { 29 | throw new IllegalArgumentException("vertices must be orthagonal: " + a.toString() + ", " + b.toString()); 30 | } 31 | } else { 32 | throw new IllegalArgumentException("all values of a must be smaller than those of b"); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/nexus/view/gl/Prisms.java: -------------------------------------------------------------------------------- 1 | package nexus.view.gl; 2 | 3 | /** 4 | * A collection of static helper methods for drawing 3D shapes 5 | * 6 | * @author Lane Aasen 7 | * 8 | */ 9 | 10 | import nexus.model.structs.Vector3; 11 | 12 | public class Prisms { 13 | 14 | /** 15 | * Draws a rectangular prism given two Points 16 | * 17 | * @param a near bottom left corner 18 | * @param b far top right corner 19 | * 20 | * TODO: implement 21 | */ 22 | public static float[] makeRectPrism2f(Vector3 a, Vector3 b) { 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/nexus/view/structs/VertexContainer.java: -------------------------------------------------------------------------------- 1 | package nexus.view.structs; 2 | 3 | import static org.lwjgl.opengl.ARBBufferObject.GL_STATIC_DRAW_ARB; 4 | import static org.lwjgl.opengl.ARBBufferObject.glBindBufferARB; 5 | import static org.lwjgl.opengl.ARBBufferObject.glBufferDataARB; 6 | import static org.lwjgl.opengl.ARBBufferObject.glGenBuffersARB; 7 | import static org.lwjgl.opengl.ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB; 8 | import static org.lwjgl.opengl.GL11.GL_COLOR_ARRAY; 9 | import static org.lwjgl.opengl.GL11.GL_FLOAT; 10 | import static org.lwjgl.opengl.GL11.GL_TRIANGLES; 11 | import static org.lwjgl.opengl.GL11.GL_VERTEX_ARRAY; 12 | import static org.lwjgl.opengl.GL11.glColorPointer; 13 | import static org.lwjgl.opengl.GL11.glDisableClientState; 14 | import static org.lwjgl.opengl.GL11.glDrawArrays; 15 | import static org.lwjgl.opengl.GL11.glEnableClientState; 16 | import static org.lwjgl.opengl.GL11.glVertexPointer; 17 | import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; 18 | 19 | import java.nio.FloatBuffer; 20 | import java.nio.IntBuffer; 21 | import java.nio.ShortBuffer; 22 | import java.util.Arrays; 23 | 24 | import org.lwjgl.BufferUtils; 25 | 26 | public class VertexContainer { 27 | public float[] vertices; 28 | public FloatBuffer vertexBuffer; 29 | public int vertexId; 30 | 31 | public short[] indices; 32 | public ShortBuffer indexBuffer; 33 | public int indexId; 34 | 35 | public float[] colors; 36 | public FloatBuffer colorBuffer; 37 | public int colorId; 38 | 39 | public IntBuffer ib; 40 | public int type; 41 | 42 | public VertexContainer(float[] vertices) { 43 | this.vertices = vertices; 44 | this.indices = new short[] { 0, 1, 2, 0, 2, 3, 0, 1, 2, 0, 2, 3 }; 45 | this.colors = new float[vertices.length]; 46 | Arrays.fill(this.colors, 1); 47 | 48 | this.vertexBuffer = BufferUtils.createFloatBuffer(this.vertices.length); 49 | this.vertexBuffer.put(this.vertices); 50 | this.vertexBuffer.flip(); 51 | 52 | this.indexBuffer = BufferUtils.createShortBuffer(this.indices.length); 53 | this.indexBuffer.put(this.indices); 54 | this.indexBuffer.flip(); 55 | 56 | this.colorBuffer = BufferUtils.createFloatBuffer(this.colors.length); 57 | this.colorBuffer.put(this.colors); 58 | this.colorBuffer.flip(); 59 | 60 | this.load(); 61 | } 62 | 63 | private void load() { 64 | this.ib = BufferUtils.createIntBuffer(2); 65 | glGenBuffersARB(ib); 66 | 67 | this.vertexId = ib.get(0); 68 | this.colorId = ib.get(1); 69 | 70 | glBindBufferARB(GL_ARRAY_BUFFER_ARB, this.vertexId); 71 | glBufferDataARB(GL_ARRAY_BUFFER_ARB, this.vertexBuffer, GL_STATIC_DRAW_ARB); 72 | glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); 73 | 74 | glBindBufferARB(GL_ARRAY_BUFFER_ARB, this.indexId); 75 | glBufferDataARB(GL_ARRAY_BUFFER_ARB, this.indexBuffer, GL_STATIC_DRAW_ARB); 76 | glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); 77 | } 78 | 79 | public void render() { 80 | 81 | glEnableClientState(GL_VERTEX_ARRAY); 82 | glBindBufferARB(GL_ARRAY_BUFFER_ARB, this.vertexId); 83 | 84 | glVertexPointer(3, GL_FLOAT, 3 << 2, 0L); 85 | 86 | 87 | // glDisableClientState(GL_VERTEX_ARRAY); 88 | glEnableClientState(GL_COLOR_ARRAY); 89 | 90 | glBindBufferARB(GL_ARRAY_BUFFER_ARB, this.colorId); 91 | glColorPointer(3, GL_FLOAT, 3 << 2, 0L); 92 | 93 | // glDisableClientState(GL_COLOR_ARRAY); 94 | 95 | glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, this.indexId); 96 | 97 | glDrawArrays(GL_TRIANGLES, 0, 6); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Unit Tests 2 | 3 | These tests are by not means complete. Unit tests for data structures are here, but the graphical side of the engine cannot easily be tested by a computer. It's too complex and subjective. 4 | -------------------------------------------------------------------------------- /test/model/generators/GeneratorTests.java: -------------------------------------------------------------------------------- 1 | package model.generators; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | import org.junit.runners.Suite.SuiteClasses; 6 | 7 | @RunWith(Suite.class) 8 | @SuiteClasses({ Noise1DTest.class, Noise2DTest.class }) 9 | public class GeneratorTests { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/model/generators/Noise1DTest.java: -------------------------------------------------------------------------------- 1 | package model.generators; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.Set; 8 | import java.util.TreeSet; 9 | 10 | import nexus.model.generators.Noise1D; 11 | 12 | import org.junit.Test; 13 | 14 | public class Noise1DTest { 15 | public static final float ACCEPTABLE_DISTRIBUTION_PERCENT = 0.01f; 16 | public static final float ACCEPTABLE_COLLISION_PERCENT = 0.05f; 17 | public static final int TEST_ITERATIONS = (int) Math.pow(2, 20); 18 | public final TreeSet noise = new TreeSet(genRandom(TEST_ITERATIONS)); 19 | 20 | @Test 21 | public void noiseCollision() { 22 | System.out.println(noise.size()); 23 | System.out.println(TEST_ITERATIONS); 24 | assertEquals((TEST_ITERATIONS - (float) noise.size()) / TEST_ITERATIONS, 0f, ACCEPTABLE_COLLISION_PERCENT); 25 | } 26 | 27 | @Test 28 | public void noiseDistribution() { 29 | float expectedAverage = (noise.first() + noise.last()) / 2; 30 | double sum = 0; 31 | Iterator iter = noise.iterator(); 32 | 33 | while (iter.hasNext()) { 34 | sum += iter.next(); 35 | } 36 | 37 | float realAverage = (float) (sum / noise.size()); 38 | 39 | assertEquals(expectedAverage, realAverage, ACCEPTABLE_DISTRIBUTION_PERCENT); 40 | } 41 | 42 | @Test 43 | public void noiseLow() { 44 | assertEquals(noise.first() + "", true, noise.first() >= -1); 45 | } 46 | 47 | @Test 48 | public void noiseHigh() { 49 | assertEquals(noise.last() + "", true, noise.last() <= 1); 50 | } 51 | 52 | private Set genRandom(int iterations) { 53 | Set results = new HashSet(); 54 | 55 | for (int i = 0; i < iterations; i++) { 56 | results.add(Noise1D.noise(i, Noise1D.primes[0])); 57 | } 58 | 59 | return results; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/model/generators/Noise2DTest.java: -------------------------------------------------------------------------------- 1 | package model.generators; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.HashSet; 6 | import java.util.Iterator; 7 | import java.util.Set; 8 | import java.util.TreeSet; 9 | 10 | import nexus.model.generators.Noise1D; 11 | import nexus.model.generators.Noise2D; 12 | 13 | import org.junit.Test; 14 | 15 | public class Noise2DTest { 16 | public static final float ACCEPTABLE_DISTRIBUTION_PERCENT = 0.01f; 17 | public static final float ACCEPTABLE_COLLISION_PERCENT = 0.05f; 18 | public static final int TEST_ITERATIONS = (int) Math.pow(2, 11); 19 | public static final int TEST_RUNS = TEST_ITERATIONS * TEST_ITERATIONS; 20 | public final TreeSet noise = new TreeSet(genRandom(TEST_ITERATIONS)); 21 | 22 | @Test 23 | public void noiseCollision() { 24 | assertEquals((float) noise.size() / TEST_RUNS, 0, ACCEPTABLE_COLLISION_PERCENT); 25 | } 26 | 27 | @Test 28 | public void noiseDistribution() { 29 | float expectedAverage = (noise.first() + noise.last()) / 2; 30 | double sum = 0; 31 | Iterator iter = noise.iterator(); 32 | 33 | while (iter.hasNext()) { 34 | sum += iter.next(); 35 | } 36 | 37 | float realAverage = (float) (sum / noise.size()); 38 | 39 | assertEquals(expectedAverage, realAverage, ACCEPTABLE_DISTRIBUTION_PERCENT); 40 | } 41 | 42 | @Test 43 | public void noiseLow() { 44 | assertEquals(noise.first() + "", true, noise.first() >= -1); 45 | } 46 | 47 | @Test 48 | public void noiseHigh() { 49 | assertEquals(noise.last() + "", true, noise.last() <= 1); 50 | } 51 | 52 | private Set genRandom(int iterations) { 53 | Set results = new HashSet(); 54 | 55 | for (int i = 0; i < iterations; i++) { 56 | for (int j = 0; j < iterations; j++) { 57 | results.add(Noise2D.noise(i, j, Noise1D.primes[0])); 58 | } 59 | } 60 | 61 | return results; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test/model/structs/ChunkContainerTest.java: -------------------------------------------------------------------------------- 1 | package model.structs; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | import nexus.main.Model; 9 | import nexus.model.structs.Block; 10 | import nexus.model.structs.Camera; 11 | import nexus.model.structs.Chunk; 12 | import nexus.model.structs.ChunkContainer; 13 | import nexus.model.structs.Vector3; 14 | 15 | import org.junit.BeforeClass; 16 | import org.junit.Test; 17 | 18 | public class ChunkContainerTest { 19 | public static final double EXPECTED_GENERATION_TIME = 30; 20 | public static final double EXPECTED_CULLING_TIME = 20; 21 | public static final int TEST_ITERATIONS = 2; // default is 5, but this can be annoying for dev 22 | public static Model world; 23 | 24 | @BeforeClass 25 | public static void testSetup() { 26 | world = new Model(new Camera(new Vector3(0f, 0f, 0f), 0f, 0f, 0f, false)); 27 | } 28 | 29 | @Test 30 | public void getBlock() { 31 | Block block = world.chunks.getBlock(new Vector3(0.5f, 0.5f, 0.5f)); 32 | 33 | assertEquals(true, new Vector3(0f, 0f, 0f).equals(block.a)); 34 | } 35 | 36 | @Test 37 | public void getBlockNegative() { 38 | Block block = world.chunks.getBlock(new Vector3(-0.5f, 0.5f, -0.5f)); 39 | 40 | assertEquals(true, new Vector3(-1f, 0f, -1f).equals(block.a)); 41 | } 42 | 43 | @Test 44 | public void getBlockOrigin() { 45 | Block block = world.chunks.getBlock(new Vector3(0f, 0f, 0f)); 46 | 47 | assertEquals(true, new Vector3(0f, 0f, 0f).equals(block.a)); 48 | } 49 | 50 | @Test(expected=IllegalArgumentException.class) 51 | public void getBlockHigh() { 52 | world.chunks.getBlock(new Vector3(0f, 100000f, 0f)); 53 | } 54 | 55 | @Test(expected=IllegalArgumentException.class) 56 | public void getBlockLow() { 57 | world.chunks.getBlock(new Vector3(0f, -1000000f, 0f)); 58 | } 59 | 60 | @Test(expected=IllegalArgumentException.class) 61 | public void setBlockHigh() { 62 | world.chunks.setBlock(new Block(new Vector3(0f, 100000f, 0f), 1f, null)); 63 | } 64 | 65 | @Test(expected=IllegalArgumentException.class) 66 | public void setBlockLow() { 67 | world.chunks.setBlock(new Block(new Vector3(0f, -1000000f, 0f), 1f, null)); 68 | } 69 | 70 | @Test(expected=IllegalArgumentException.class) 71 | public void setBlockFloat() { 72 | world.chunks.setBlock(new Block(new Vector3(0f, 0.1f, 0f), 1f, null)); 73 | } 74 | 75 | @Test 76 | public void chunkGenerationSpeed() { 77 | long begin = System.currentTimeMillis(); 78 | 79 | int numChunks = generateChunks(TEST_ITERATIONS, 0, false); 80 | 81 | long end = System.currentTimeMillis(); 82 | long elapsed = end - begin; 83 | 84 | double timePerChunk = (double) elapsed / numChunks; 85 | 86 | assertEquals("took " + timePerChunk + " ms/chunk, expected " + EXPECTED_GENERATION_TIME, true, timePerChunk < EXPECTED_GENERATION_TIME); 87 | } 88 | 89 | @Test 90 | public void chunkCullingSpeed() { 91 | 92 | generateChunks(TEST_ITERATIONS, 100, false); 93 | 94 | long begin = System.currentTimeMillis(); 95 | 96 | int numChunks = generateChunks(TEST_ITERATIONS - 1, 100, true); 97 | 98 | long end = System.currentTimeMillis(); 99 | long elapsed = end - begin; 100 | double timePerChunk = (double) elapsed / numChunks; 101 | 102 | assertEquals("took " + timePerChunk + " ms/chunk, expected " + EXPECTED_CULLING_TIME, true, timePerChunk < EXPECTED_CULLING_TIME); 103 | } 104 | 105 | public int generateChunks(int iterations, int offset, boolean mask) { 106 | for (int i = -iterations; i <= iterations; i++) { 107 | for (int j = -iterations; j <= iterations; j++) { 108 | world.chunks.getChunk(i + offset, j + offset, mask); 109 | } 110 | } 111 | 112 | return (int) Math.pow(iterations * 2 + 1, 2); 113 | } 114 | 115 | @Test 116 | public void getKey() { 117 | int high = 100; 118 | int low = -high; 119 | 120 | Set results = new HashSet(); 121 | int expected = (int) Math.pow(high + Math.abs(low) + 1, 2); 122 | 123 | for (int i = low; i <= high; i++) { 124 | for (int j = low; j <= high; j++) { 125 | results.add(ChunkContainer.getKey(i, j)); 126 | } 127 | } 128 | 129 | assertEquals(expected, results.size()); 130 | } 131 | 132 | @Test 133 | public void inBoundsBelow() { 134 | assertEquals(false, world.chunks.inBounds(new Vector3(0f, -1f, 0f))); 135 | } 136 | 137 | @Test 138 | public void inBoundsLowEdge() { 139 | assertEquals(true, world.chunks.inBounds(new Vector3(0f, 0f, 0f))); 140 | } 141 | 142 | @Test 143 | public void inBoundsInside() { 144 | assertEquals(true, world.chunks.inBounds(new Vector3(0f, 1f, 0f))); 145 | } 146 | 147 | @Test 148 | public void inBoundsAbove() { 149 | assertEquals(false, world.chunks.inBounds(new Vector3(0f, Chunk.HEIGHT, 0f))); 150 | } 151 | 152 | @Test 153 | public void inBoundsHighEdge() { 154 | assertEquals(true, world.chunks.inBounds(new Vector3(0f, Chunk.HEIGHT - 1, 0f))); 155 | } 156 | } -------------------------------------------------------------------------------- /test/model/structs/StructTests.java: -------------------------------------------------------------------------------- 1 | package model.structs; 2 | 3 | import org.junit.runner.RunWith; 4 | import org.junit.runners.Suite; 5 | import org.junit.runners.Suite.SuiteClasses; 6 | 7 | @RunWith(Suite.class) 8 | @SuiteClasses({ ChunkContainerTest.class, Vector3Test.class }) 9 | public class StructTests { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /test/model/structs/Vector3Test.java: -------------------------------------------------------------------------------- 1 | package model.structs; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.fail; 5 | 6 | import java.util.Arrays; 7 | 8 | import nexus.model.structs.Vector3; 9 | 10 | import org.junit.Test; 11 | 12 | public class Vector3Test { 13 | 14 | @Test 15 | public void testEqualsVector3True() { 16 | Vector3 a = new Vector3(0f, 1.001f, 2f); 17 | Vector3 b = new Vector3(0f, 1.001f, 2.0f); 18 | 19 | assertEquals(true, a.equals(b)); 20 | } 21 | 22 | @Test 23 | public void testEqualsVector3False() { 24 | Vector3 a = new Vector3(0f, 1f, 0.0f); 25 | Vector3 b = new Vector3(1f, 1f, 1f); 26 | 27 | assertEquals(false, a.equals(b)); 28 | } 29 | 30 | @Test 31 | public void testAdd() { 32 | Vector3 a = new Vector3(4f, -4f, -4f); 33 | Vector3 b = new Vector3(1f, 3f, -2f); 34 | Vector3 expect = new Vector3(5f, -1f, -6f); 35 | Vector3 result = a.add(b); 36 | 37 | assertEquals(result.toString(), true, result.equals(expect)); 38 | } 39 | 40 | @Test 41 | public void testSubtract() { 42 | Vector3 a = new Vector3(4f, -4f, -4f); 43 | Vector3 b = new Vector3(1f, 3f, -2f); 44 | Vector3 expect = new Vector3(3f, -7f, -2f); 45 | Vector3 result = a.subtract(b); 46 | 47 | assertEquals(result.toString(), true, result.equals(expect)); 48 | } 49 | 50 | @Test 51 | public void testLength() { 52 | Vector3 a = new Vector3(1f, 2f, 2f); 53 | 54 | assertEquals(3f, a.length(), 0); 55 | } 56 | 57 | @Test 58 | public void testLengthSquared() { 59 | Vector3 a = new Vector3(1f, 2f, 2f); 60 | 61 | assertEquals(9f, a.lengthSquared(), 0); 62 | } 63 | 64 | @Test 65 | public void testScale() { 66 | Vector3 a = new Vector3(1f, 2f, -2f); 67 | float scalar = 4f; 68 | Vector3 expect = new Vector3(4f, 8f, -8f); 69 | Vector3 result = a.scale(scalar); 70 | 71 | assertEquals(true, expect.equals(result)); 72 | } 73 | 74 | @Test 75 | public void testNormalize() { 76 | Vector3 a = new Vector3(1f, 2f, 2f); 77 | Vector3 expect = new Vector3(1f/3, 2f/3, 2f/3); 78 | Vector3 result = a.normalize(); 79 | 80 | assertEquals(true, result.equals(expect)); 81 | } 82 | 83 | @Test 84 | public void testDotProduct() { 85 | Vector3 a = new Vector3(1f, 2f, 3f); 86 | Vector3 b = new Vector3(4f, -5f, 6f); 87 | float expect = 12f; 88 | float result = a.dotProduct(b); 89 | 90 | assertEquals(result, expect, 0f); 91 | } 92 | 93 | @Test 94 | public void testCrossProduct() { 95 | Vector3 a = new Vector3(3f, -3f, 1f); 96 | Vector3 b = new Vector3(4f, 9f, 2f); 97 | Vector3 expect = new Vector3(-15f, -2f, 39f); 98 | Vector3 result = a.crossProduct(b); 99 | 100 | assertEquals(true, result.equals(expect)); 101 | } 102 | 103 | @Test 104 | public void testProjection() { 105 | Vector3 a = new Vector3(3f, -3f, 1f); 106 | Vector3 b = new Vector3(4f, 9f, 2f); 107 | Vector3 expect = new Vector3(-0.5148515f, -1.1584159f, -0.25742576f); 108 | Vector3 result = a.projection(b); 109 | 110 | assertEquals(result.toString() + " : " + expect.toString(), true, result.equals(expect)); 111 | } 112 | 113 | @Test 114 | public void testPerpendicular() { 115 | fail("not implemented"); 116 | } 117 | 118 | @Test 119 | public void testToArray() { 120 | Vector3 a = new Vector3(3f, -1f, 0f); 121 | 122 | assertEquals(true, Arrays.equals(a.toArray(), new float[] { 3f, -1f, 0f })); 123 | } 124 | 125 | @Test 126 | public void testCombined() { 127 | Vector3 a = new Vector3(0f, 1f, 2f); 128 | Vector3 b = new Vector3(3f, 4f, 5f); 129 | Vector3 c = new Vector3(6f, 7f, 8f); 130 | 131 | assertEquals(true, Arrays.equals(Vector3.combine(new Vector3[] { a, b, c }), 132 | new float[] { 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f })); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /test/view/gl/PlanesTest.java: -------------------------------------------------------------------------------- 1 | package view.gl; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import java.util.Arrays; 6 | 7 | import nexus.model.structs.Vector3; 8 | import nexus.view.gl.Planes; 9 | 10 | import org.junit.Test; 11 | 12 | public class PlanesTest { 13 | 14 | @Test(expected=IllegalArgumentException.class) 15 | public void makeQuad2fSwappedArguments() { 16 | Vector3 a = new Vector3(0, 0, 0); 17 | Vector3 b = new Vector3(1, 1, 1); 18 | 19 | Planes.makeQuad2f(b, a); 20 | } 21 | 22 | @Test 23 | public void makeQuad2fXAxis() { 24 | Vector3 a = new Vector3(0, 0, 0); 25 | Vector3 b = new Vector3(0, 1, 1); 26 | float[] result = Planes.makeQuad2f(a, b); 27 | float[] expected = new float[] { 0, 0, 0, 0, 1, 1, 0, 1, 0, 28 | 0, 1, 1, 0, 0, 0, 0, 0, 1 }; 29 | 30 | System.out.println("x axis"); 31 | System.out.println(Arrays.toString(expected)); 32 | System.out.println(Arrays.toString(result)); 33 | 34 | assertEquals("got " + Arrays.toString(expected) + "\nexpected " + Arrays.toString(result), true, Arrays.equals(expected, result)); 35 | } 36 | 37 | @Test 38 | public void makeQuad2fZAxis() { 39 | Vector3 a = new Vector3(0, 0, 0); 40 | Vector3 b = new Vector3(1, 1, 0); 41 | float[] result = Planes.makeQuad2f(a, b); 42 | float[] expected = new float[] { 0, 0, 0, 1, 1, 0, 0, 1, 0, 43 | 1, 1, 0, 0, 0, 0, 1, 0, 0 }; 44 | 45 | System.out.println("z axis"); 46 | System.out.println(Arrays.toString(expected)); 47 | System.out.println(Arrays.toString(result)); 48 | 49 | assertEquals("got " + Arrays.toString(expected) + "\nexpected " + Arrays.toString(result), true, Arrays.equals(expected, result)); 50 | } 51 | 52 | @Test 53 | public void makeQuad2fYAxis() { 54 | Vector3 a = new Vector3(0, 0, 0); 55 | Vector3 b = new Vector3(1, 0, 1); 56 | float[] result = Planes.makeQuad2f(a, b); 57 | float[] expected = new float[] { 0, 0, 0, 1, 0, 1, 0, 0, 1, 58 | 1, 0, 1, 0, 0, 0, 1, 0, 0 }; 59 | 60 | System.out.println("y axis"); 61 | System.out.println(Arrays.toString(expected)); 62 | System.out.println(Arrays.toString(result)); 63 | 64 | assertEquals("got " + Arrays.toString(expected) + "\nexpected " + Arrays.toString(result), true, Arrays.equals(expected, result)); 65 | } 66 | 67 | @Test(expected=IllegalArgumentException.class) 68 | public void makeQuad2fNonOrtho() { 69 | Vector3 a = new Vector3(0, 0, 0); 70 | Vector3 b = new Vector3(1, 1, 1); 71 | Planes.makeQuad2f(a, b); 72 | } 73 | } 74 | --------------------------------------------------------------------------------