├── settings.gradle ├── src └── main │ ├── resources │ ├── char.png │ └── terrain.png │ └── java │ └── com │ └── mojang │ └── minecraft │ ├── HitResult.java │ ├── level │ ├── LevelListener.java │ ├── tile │ │ ├── GrassTile.java │ │ ├── Bush.java │ │ └── Tile.java │ ├── DirtyChunkSorter.java │ ├── PerlinNoiseFilter.java │ ├── Tessellator.java │ ├── Chunk.java │ ├── LevelRenderer.java │ ├── Level.java │ └── Frustum.java │ ├── character │ ├── Vec3.java │ ├── Polygon.java │ ├── Vertex.java │ ├── ZombieModel.java │ ├── Zombie.java │ └── Cube.java │ ├── Player.java │ ├── Timer.java │ ├── MinecraftApplet.java │ ├── particle │ ├── ParticleEngine.java │ └── Particle.java │ ├── Textures.java │ ├── Entity.java │ ├── phys │ └── AABB.java │ └── Minecraft.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── metadata.json ├── .idea └── runConfigurations │ └── Run_Client.xml ├── README.md ├── gradlew.bat ├── .gitignore └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'minecraft' -------------------------------------------------------------------------------- /src/main/resources/char.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodeofnotch/mc-161807/HEAD/src/main/resources/char.png -------------------------------------------------------------------------------- /src/main/resources/terrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodeofnotch/mc-161807/HEAD/src/main/resources/terrain.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thecodeofnotch/mc-161807/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "timestampRelease": 1242490020000, 3 | "commitMessage": "rename RubyDung to Minecraft, add applet to run the game" 4 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/HitResult.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | public class HitResult { 4 | 5 | public int type; 6 | 7 | public int x; 8 | public int y; 9 | public int z; 10 | 11 | public int face; 12 | 13 | /** 14 | * Target tile over mouse 15 | * 16 | * @param type Type of result 17 | * @param x Tile position x 18 | * @param y Tile position y 19 | * @param z Tile position z 20 | * @param face Face id of the tile 21 | */ 22 | public HitResult(int type, int x, int y, int z, int face) { 23 | this.type = type; 24 | this.x = x; 25 | this.y = y; 26 | this.z = z; 27 | this.face = face; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/LevelListener.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | public interface LevelListener { 4 | /** 5 | * Gets called when a tile changed it's light level 6 | * 7 | * @param x Tile position x 8 | * @param z Tile position z 9 | * @param minY Minimum tile position Y (Start range) 10 | * @param maxY Maximum tile position Y (End range) 11 | */ 12 | void lightColumnChanged(int x, int z, int minY, int maxY); 13 | 14 | /** 15 | * Gets called when a tile changed it's type 16 | * 17 | * @param x Tile position x 18 | * @param y Tile position y 19 | * @param z Tile position z 20 | */ 21 | void tileChanged(int x, int y, int z); 22 | 23 | /** 24 | * Gets called when the entire level changed 25 | */ 26 | void allChanged(); 27 | } -------------------------------------------------------------------------------- /.idea/runConfigurations/Run_Client.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/character/Vec3.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.character; 2 | 3 | public class Vec3 { 4 | 5 | public float x; 6 | public float y; 7 | public float z; 8 | 9 | /** 10 | * Vector object containing three float values 11 | * 12 | * @param x X value 13 | * @param y Y value 14 | * @param z Z value 15 | */ 16 | public Vec3(float x, float y, float z) { 17 | this.x = x; 18 | this.y = y; 19 | this.z = z; 20 | } 21 | 22 | /** 23 | * Create an interpolated vector from the current vector position to the given one 24 | * 25 | * @param vector The end vector 26 | * @param partialTicks Interpolation progress 27 | * @return Interpolated vector between the two positions 28 | */ 29 | public Vec3 interpolateTo(Vec3 vector, float partialTicks) { 30 | float interpolatedX = this.x + (vector.x - this.x) * partialTicks; 31 | float interpolatedY = this.y + (vector.y - this.y) * partialTicks; 32 | float interpolatedZ = this.z + (vector.z - this.z) * partialTicks; 33 | 34 | return new Vec3(interpolatedX, interpolatedY, interpolatedZ); 35 | } 36 | 37 | /** 38 | * Set x, y and z of the vector 39 | * 40 | * @param x X value 41 | * @param y Y value 42 | * @param z Z value 43 | */ 44 | public void set(float x, float y, float z) { 45 | this.x = x; 46 | this.y = y; 47 | this.z = z; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/tile/GrassTile.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level.tile; 2 | 3 | import com.mojang.minecraft.level.Level; 4 | 5 | import java.util.Random; 6 | 7 | public class GrassTile extends Tile { 8 | 9 | /** 10 | * Create a grass tile with the id 11 | * 12 | * @param id The id of the grass tile 13 | */ 14 | protected GrassTile(int id) { 15 | super(id); 16 | 17 | this.textureId = 3; 18 | } 19 | 20 | @Override 21 | protected int getTexture(int face) { 22 | // Texture mapping of the grass tile 23 | return face == 1 ? 0 : face == 0 ? 2 : 3; 24 | } 25 | 26 | @Override 27 | public void onTick(Level level, int x, int y, int z, Random random) { 28 | if (level.isLit(x, y, z)) { 29 | // Make surrounding dirt tiles to grass 30 | for (int i = 0; i < 4; ++i) { 31 | int targetX = x + random.nextInt(3) - 1; 32 | int targetY = y + random.nextInt(5) - 3; 33 | int targetZ = z + random.nextInt(3) - 1; 34 | 35 | // If target is dirt and has sunlight 36 | if (level.getTile(targetX, targetY, targetZ) == Tile.dirt.id && level.isLit(targetX, targetY, targetZ)) { 37 | 38 | // Set to grass 39 | level.setTile(targetX, targetY, targetZ, Tile.grass.id); 40 | } 41 | } 42 | } else { 43 | // Set tile to dirt if there is no sunlight 44 | level.setTile(x, y, z, Tile.dirt.id); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mc-161807 (Pre-Classic) 2 | Development phase: May 16, 2009 (until 18:07 UTC+2) 3 | 4 | ## Unofficial name 5 | While this version is known to exist it is missing from the launcher and has not been archived elsewhere, meaning that it is currently lost.
6 |
7 | The version name is taken from [Notchs IRC message](https://archive.org/download/Minecraft_IRC_Logs_2009/history/files/May-15-to-June-03-2009/2009-05-16.075419-0400EDT.txt.~1~)
8 | `` (12:07:06) notch: minecraft alpha is available. I need someone on windows, someone on mac and someone on linux!`` 9 |
10 |
11 | Minecraft Wiki uses ``mc-161607`` for this version because it's in UTC format. 12 | ``mc-161807`` would be in UTC+2 (Timezone in Sweden) 13 | 14 | ## Changes 15 | - Renamed com.mojang.rubydung to com.mojang.minecraft 16 | - Added Applet to run the game 17 | 18 | ## Accuracy 19 | This version has only been sent to [dock](https://minecraft.gamepedia.com/Hayden_Scott-Baron) and [fartron](https://forums.tigsource.com/index.php?action=profile;u=61) for testing. 20 | There is no existing backup. 21 | We can assume that all necessary changes have been made in this update for the first release. 22 | 23 | ## References 24 | - [Minecraft Wiki - Java_Edition_pre-Classic_mc-161607](https://minecraft.gamepedia.com/Java_Edition_pre-Classic_mc-161607) 25 | - [IRC logs](https://archive.org/download/Minecraft_IRC_Logs_2009/history/files/May-15-to-June-03-2009/2009-05-16.075419-0400EDT.txt.~1~) to recreate the unreleased features: 26 | - ``[12:07:06] notch: minecraft alpha is available. I need someone on windows, someone on mac and someone on linux!`` 27 | 28 | ## Setup 29 | 1. Clone the project 30 | 2. Execute the gradle task ``run`` -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/character/Polygon.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.character; 2 | 3 | import static org.lwjgl.opengl.GL11.*; 4 | 5 | public class Polygon { 6 | 7 | public Vertex[] vertices; 8 | public int vertexCount; 9 | 10 | /** 11 | * Create polygon without UV mappings 12 | * 13 | * @param vertices Vertex array 14 | */ 15 | public Polygon(Vertex[] vertices) { 16 | this.vertices = vertices; 17 | this.vertexCount = vertices.length; 18 | } 19 | 20 | /** 21 | * Bind UV mappings on the vertices 22 | * 23 | * @param vertices Vertex array 24 | * @param minU Minimum U coordinate 25 | * @param minV Minimum V coordinate 26 | * @param maxU Maximum U coordinate 27 | * @param maxV Maximum V coordinate 28 | */ 29 | public Polygon(Vertex[] vertices, int minU, int minV, int maxU, int maxV) { 30 | this(vertices); 31 | 32 | // Map UV on vertices 33 | vertices[0] = vertices[0].remap(maxU, minV); 34 | vertices[1] = vertices[1].remap(minU, minV); 35 | vertices[2] = vertices[2].remap(minU, maxV); 36 | vertices[3] = vertices[3].remap(maxU, maxV); 37 | } 38 | 39 | public void render() { 40 | // Set color of polygon 41 | glColor3f(1.0F, 1.0F, 1.0F); 42 | 43 | // Render all vertices 44 | for (int i = 3; i >= 0; i--) { 45 | Vertex vertex = this.vertices[i]; 46 | 47 | // Bind UV mappings 48 | glTexCoord2f(vertex.u / 64.0F, vertex.v / 32.0F); 49 | 50 | // Render vertex 51 | glVertex3f(vertex.position.x, vertex.position.y, vertex.position.z); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/character/Vertex.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.character; 2 | 3 | public class Vertex { 4 | 5 | public Vec3 position; 6 | 7 | public float u; 8 | public float v; 9 | 10 | /** 11 | * A vertex contains a 3 float vector position and UV coordinates 12 | * 13 | * @param x X position 14 | * @param y Y position 15 | * @param z Z position 16 | * @param u U mapping 17 | * @param v V mapping 18 | */ 19 | public Vertex(float x, float y, float z, float u, float v) { 20 | this(new Vec3(x, y, z), u, v); 21 | } 22 | 23 | /** 24 | * A vertex contains a 3 float vector position and UV coordinates 25 | * 26 | * @param vertex Vertex for the position 27 | * @param u U mapping 28 | * @param v V mapping 29 | */ 30 | public Vertex(Vertex vertex, float u, float v) { 31 | this.position = vertex.position; 32 | this.u = u; 33 | this.v = v; 34 | } 35 | 36 | /** 37 | * A vertex contains a 3 float vector position and UV coordinates 38 | * 39 | * @param position Vector position 40 | * @param u U mapping 41 | * @param v V mapping 42 | */ 43 | public Vertex(Vec3 position, float u, float v) { 44 | this.position = position; 45 | this.u = u; 46 | this.v = v; 47 | } 48 | 49 | /** 50 | * Create a new vertex of the current one with different UV mappings 51 | * 52 | * @param u New U mapping 53 | * @param v New V mapping 54 | * @return New vertex with the vector position of the current one 55 | */ 56 | public Vertex remap(float u, float v) { 57 | return new Vertex(this, u, v); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/Player.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | import com.mojang.minecraft.level.Level; 4 | import org.lwjgl.input.Keyboard; 5 | 6 | public class Player extends Entity { 7 | 8 | /** 9 | * The player that is controlling the camera of the game 10 | * 11 | * @param level Level of the player 12 | */ 13 | public Player(Level level) { 14 | super(level); 15 | 16 | this.heightOffset = 1.62f; 17 | } 18 | 19 | @Override 20 | public void onTick() { 21 | super.onTick(); 22 | 23 | float forward = 0.0F; 24 | float vertical = 0.0F; 25 | 26 | // Reset the position of the player 27 | if (Keyboard.isKeyDown(19)) { // R 28 | resetPosition(); 29 | } 30 | 31 | // Player movement 32 | if (Keyboard.isKeyDown(200) || Keyboard.isKeyDown(17)) { // Up, W 33 | forward--; 34 | } 35 | if (Keyboard.isKeyDown(208) || Keyboard.isKeyDown(31)) { // Down, S 36 | forward++; 37 | } 38 | if (Keyboard.isKeyDown(203) || Keyboard.isKeyDown(30)) { // Left, A 39 | vertical--; 40 | } 41 | if (Keyboard.isKeyDown(205) || Keyboard.isKeyDown(32)) { // Right, D 42 | vertical++; 43 | } 44 | if ((Keyboard.isKeyDown(57) || Keyboard.isKeyDown(219)) && this.onGround) { // Space, Windows Key 45 | this.motionY = 0.5F; 46 | } 47 | 48 | // Add motion to the player using keyboard input 49 | moveRelative(vertical, forward, this.onGround ? 0.1F : 0.02F); 50 | 51 | // Apply gravity motion 52 | this.motionY -= 0.08D; 53 | 54 | // Move the player using the motion 55 | move(this.motionX, this.motionY, this.motionZ); 56 | 57 | // Decrease motion 58 | this.motionX *= 0.91F; 59 | this.motionY *= 0.98F; 60 | this.motionZ *= 0.91F; 61 | 62 | // Decrease motion on ground 63 | if (this.onGround) { 64 | this.motionX *= 0.7F; 65 | this.motionZ *= 0.7F; 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/DirtyChunkSorter.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import com.mojang.minecraft.Player; 4 | 5 | import java.util.Comparator; 6 | 7 | public class DirtyChunkSorter implements Comparator { 8 | 9 | private final long now = System.currentTimeMillis(); 10 | 11 | private final Player player; 12 | private final Frustum frustum; 13 | 14 | /** 15 | * Sort the chunk render order. 16 | * - Chunks that are visible in the camera have a higher priority than chunks behind the camera. 17 | * - Chunks with a higher dirty duration have a higher priority. 18 | * - Chunks closer to the player have a higher priority. 19 | * 20 | * @param player The player for the distance priority. 21 | * @param frustum Frustum for the visible-in-camera priority 22 | */ 23 | public DirtyChunkSorter(Player player, Frustum frustum) { 24 | this.player = player; 25 | this.frustum = frustum; 26 | } 27 | 28 | @Override 29 | public int compare(Chunk chunk1, Chunk chunk2) { 30 | // Matching chunk instance 31 | if (chunk1.equals(chunk2)) 32 | return 0; 33 | 34 | boolean chunk1Visible = this.frustum.isVisible(chunk1.boundingBox); 35 | boolean chunk2Visible = this.frustum.isVisible(chunk2.boundingBox); 36 | 37 | // Return priority if one of the chunks is not visible 38 | if (chunk1Visible && !chunk2Visible) { 39 | return -1; 40 | } 41 | if (chunk2Visible && !chunk1Visible) { 42 | return 1; 43 | } 44 | 45 | // Get the duration since last chunk dirty 46 | int dirtyDuration1 = (int) ((this.now - chunk1.dirtiedTime) / 2000L); 47 | int dirtyDuration2 = (int) ((this.now - chunk2.dirtiedTime) / 2000L); 48 | 49 | // Return priority if one of the chunks has a bigger dirty duration 50 | if (dirtyDuration1 < dirtyDuration2) { 51 | return -1; 52 | } 53 | if (dirtyDuration1 > dirtyDuration2) { 54 | return 1; 55 | } 56 | 57 | // Decide priority using the distance to the player 58 | return (chunk1.distanceToSqr(this.player) < chunk2.distanceToSqr(this.player)) ? -1 : 1; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/character/ZombieModel.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.character; 2 | 3 | public class ZombieModel { 4 | 5 | public Cube head; 6 | public Cube body; 7 | 8 | public Cube rightArm; 9 | public Cube leftArm; 10 | 11 | public Cube rightLeg; 12 | public Cube leftLeg; 13 | 14 | /** 15 | * Create cubes for the zombie model 16 | */ 17 | public ZombieModel() { 18 | // Create head cube 19 | this.head = new Cube(0, 0) 20 | .addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8); 21 | 22 | // Create body cube 23 | this.body = new Cube(16, 16) 24 | .addBox(-4.0F, 0.0F, -2.0F, 8, 12, 4); 25 | 26 | // Right arm cube 27 | this.rightArm = new Cube(40, 16) 28 | .addBox(-3.0F, -2.0F, -2.0F, 4, 12, 4); 29 | this.rightArm.setPosition(-5.0F, 2.0F, 0.0F); 30 | 31 | // Left arm cube 32 | this.leftArm = new Cube(40, 16) 33 | .addBox(-1.0F, -2.0F, -2.0F, 4, 12, 4); 34 | this.leftArm.setPosition(5.0F, 2.0F, 0.0F); 35 | 36 | // Right Legs cube 37 | this.rightLeg = new Cube(0, 16) 38 | .addBox(-2.0F, 0.0F, -2.0F, 4, 12, 4); 39 | this.rightLeg.setPosition(-2.0F, 12.0F, 0.0F); 40 | 41 | // Left leg cube 42 | this.leftLeg = new Cube(0, 16) 43 | .addBox(-2.0F, 0.0F, -2.0F, 4, 12, 4); 44 | this.leftLeg.setPosition(2.0F, 12.0F, 0.0F); 45 | } 46 | 47 | /** 48 | * Render the model 49 | * 50 | * @param time Animation offset 51 | */ 52 | public void render(double time) { 53 | // Set rotation of cubes 54 | this.head.yRotation = (float) Math.sin(time * 0.83); 55 | this.head.xRotation = (float) Math.sin(time) * 0.8F; 56 | this.rightArm.xRotation = (float) Math.sin(time * 0.6662 + Math.PI) * 2.0F; 57 | this.rightArm.zRotation = (float) (Math.sin(time * 0.2312) + 1.0); 58 | this.leftArm.xRotation = (float) Math.sin(time * 0.6662) * 2.0f; 59 | this.leftArm.zRotation = (float) (Math.sin(time * 0.2812) - 1.0); 60 | this.rightLeg.xRotation = (float) Math.sin(time * 0.6662) * 1.4f; 61 | this.leftLeg.xRotation = (float) Math.sin(time * 0.6662 + Math.PI) * 1.4F; 62 | 63 | // Render cubes 64 | this.head.render(); 65 | this.body.render(); 66 | this.rightArm.render(); 67 | this.leftArm.render(); 68 | this.rightLeg.render(); 69 | this.leftLeg.render(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/Timer.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | public class Timer { 4 | 5 | private static final long NS_PER_SECOND = 1000000000L; 6 | private static final long MAX_NS_PER_UPDATE = 1000000000L; 7 | private static final int MAX_TICKS_PER_UPDATE = 100; 8 | 9 | /** 10 | * Amount of ticks per second 11 | */ 12 | private final float ticksPerSecond; 13 | 14 | /** 15 | * Last time updated in nano seconds 16 | */ 17 | private long lastTime = System.nanoTime(); 18 | 19 | /** 20 | * Scale the tick speed 21 | */ 22 | public float timeScale = 1.0F; 23 | 24 | /** 25 | * Framerate of the advanceTime update 26 | */ 27 | public float fps = 0.0F; 28 | 29 | /** 30 | * Passed time since last game update 31 | */ 32 | public float passedTime = 0.0F; 33 | 34 | /** 35 | * The amount of ticks for the current game update. 36 | * It's the passed time as an integer 37 | */ 38 | public int ticks; 39 | 40 | /** 41 | * The overflow of the current tick, caused by casting the passed time to an integer 42 | */ 43 | public float partialTicks; 44 | 45 | /** 46 | * Timer to control the tick speed independently of the framerate 47 | * 48 | * @param ticksPerSecond Amount of ticks per second 49 | */ 50 | public Timer(float ticksPerSecond) { 51 | this.ticksPerSecond = ticksPerSecond; 52 | } 53 | 54 | /** 55 | * This function calculates the amount of ticks required to reach the ticksPerSecond. 56 | * Call this function in the main render loop of the game 57 | */ 58 | public void advanceTime() { 59 | long now = System.nanoTime(); 60 | long passedNs = now - this.lastTime; 61 | 62 | // Store nano time of this update 63 | this.lastTime = now; 64 | 65 | // Maximum and minimum 66 | passedNs = Math.max(0, passedNs); 67 | passedNs = Math.min(MAX_NS_PER_UPDATE, passedNs); 68 | 69 | // Calculate fps 70 | this.fps = (float) (NS_PER_SECOND / passedNs); 71 | 72 | // Calculate passed time and ticks 73 | this.passedTime += passedNs * this.timeScale * this.ticksPerSecond / NS_PER_SECOND; 74 | this.ticks = (int) this.passedTime; 75 | 76 | // Maximum ticks per update 77 | this.ticks = Math.min(MAX_TICKS_PER_UPDATE, this.ticks); 78 | 79 | // Calculate the overflow of the current tick 80 | this.passedTime -= this.ticks; 81 | this.partialTicks = this.passedTime; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/MinecraftApplet.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | import java.applet.Applet; 4 | import java.awt.*; 5 | 6 | /** 7 | * Applet to run the game 8 | */ 9 | public class MinecraftApplet extends Applet { 10 | 11 | /** 12 | * Applet canvas 13 | */ 14 | private Canvas canvas; 15 | 16 | /** 17 | * Game instance 18 | */ 19 | private Minecraft minecraft; 20 | 21 | /** 22 | * Game main thread 23 | */ 24 | private Thread thread; 25 | 26 | @Override 27 | public void init() { 28 | this.canvas = new Canvas() { 29 | @Override 30 | public void addNotify() { 31 | super.addNotify(); 32 | 33 | startGameThread(); 34 | } 35 | 36 | @Override 37 | public void removeNotify() { 38 | stopGameThread(); 39 | 40 | super.removeNotify(); 41 | } 42 | }; 43 | 44 | // Create game instance 45 | this.minecraft = new Minecraft(this.canvas, getWidth(), getHeight(), false); 46 | this.minecraft.appletMode = true; 47 | 48 | // Setup canvas 49 | this.setLayout(new BorderLayout()); 50 | this.add(this.canvas, "Center"); 51 | this.canvas.setFocusable(true); 52 | this.validate(); 53 | } 54 | 55 | /** 56 | * Start game loop in new thread 57 | */ 58 | public void startGameThread() { 59 | if (this.thread == null) { 60 | (this.thread = new Thread(this.minecraft)).start(); 61 | } 62 | } 63 | 64 | @Override 65 | public void start() { 66 | 67 | } 68 | 69 | @Override 70 | public void stop() { 71 | 72 | } 73 | 74 | @Override 75 | public void destroy() { 76 | stopGameThread(); 77 | } 78 | 79 | /** 80 | * Stop the game loop and destroy everything 81 | */ 82 | public void stopGameThread() { 83 | if (this.thread == null) { 84 | return; 85 | } 86 | 87 | // Stop the game loop 88 | this.minecraft.stop(); 89 | 90 | try { 91 | // Wait for 5 seconds 92 | this.thread.join(5000L); 93 | } catch (InterruptedException interruptedException) { 94 | 95 | // Destroy display, mouse and keyboard 96 | try { 97 | this.minecraft.destroy(); 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | this.thread = null; 104 | } 105 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/particle/ParticleEngine.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.particle; 2 | 3 | import com.mojang.minecraft.Player; 4 | import com.mojang.minecraft.Textures; 5 | import com.mojang.minecraft.level.Level; 6 | import com.mojang.minecraft.level.Tessellator; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Iterator; 10 | import java.util.List; 11 | 12 | import static org.lwjgl.opengl.GL11.*; 13 | 14 | public class ParticleEngine { 15 | 16 | protected final Level level; 17 | 18 | private final List particles = new ArrayList<>(); 19 | 20 | public ParticleEngine(Level level) { 21 | this.level = level; 22 | } 23 | 24 | /** 25 | * Add particle to engine 26 | * 27 | * @param particle The particle to add 28 | */ 29 | public void add(Particle particle) { 30 | this.particles.add(particle); 31 | } 32 | 33 | /** 34 | * Tick all particles and remove dead particles 35 | */ 36 | public void onTick() { 37 | // Tick all particles 38 | Iterator iterator = this.particles.iterator(); 39 | while (iterator.hasNext()) { 40 | Particle particle = iterator.next(); 41 | 42 | // Tick this particle 43 | particle.onTick(); 44 | 45 | // Remove particle when removed flag is set 46 | if (particle.removed) { 47 | iterator.remove(); 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * Render all particles 54 | * 55 | * @param player The player 56 | * @param partialTicks Ticks for interpolation 57 | * @param layer Shadow layer 58 | */ 59 | public void render(Player player, Tessellator tessellator, float partialTicks, int layer) { 60 | glEnable(GL_TEXTURE_2D); 61 | 62 | // Bind terrain texture 63 | int id = Textures.loadTexture("/terrain.png", 9728); 64 | glBindTexture(GL_TEXTURE_2D, id); 65 | 66 | // Get camera angel 67 | double cameraX = -Math.cos(Math.toRadians(player.yRotation)); 68 | double cameraY = Math.cos(Math.toRadians(player.xRotation)); 69 | double cameraZ = -Math.sin(Math.toRadians(player.yRotation)); 70 | 71 | // Get additional camera rotation 72 | double cameraXWithY = -cameraZ * Math.sin(Math.toRadians(player.xRotation)); 73 | double cameraZWithY = cameraX * Math.sin(Math.toRadians(player.xRotation)); 74 | 75 | // Start rendering 76 | glColor4f(0.8F, 0.8F, 0.8F, 1.0F); 77 | tessellator.init(); 78 | 79 | // Render all particles in correct layer 80 | for (Particle particle : this.particles) { 81 | if (particle.isLit() ^ layer == 1) { 82 | particle.render(tessellator, partialTicks, (float) cameraX, (float) cameraY, (float) cameraZ, (float) cameraXWithY, (float) cameraZWithY); 83 | } 84 | } 85 | 86 | // Finish rendering 87 | tessellator.flush(); 88 | glDisable(GL_TEXTURE_2D); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/tile/Bush.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level.tile; 2 | 3 | import com.mojang.minecraft.level.Level; 4 | import com.mojang.minecraft.level.Tessellator; 5 | import com.mojang.minecraft.phys.AABB; 6 | 7 | import java.util.Random; 8 | 9 | public class Bush extends Tile { 10 | 11 | /** 12 | * Create a bush tile with given id 13 | * 14 | * @param id Id of the tile 15 | */ 16 | protected Bush(int id) { 17 | super(id); 18 | 19 | // Set texture slot id 20 | this.textureId = 15; 21 | } 22 | 23 | @Override 24 | public void onTick(Level level, int x, int y, int z, Random random) { 25 | int tileIdBelow = level.getTile(x, y - 1, z); 26 | 27 | // Destroy bush if there is no light or no dirt/grass below it 28 | if (!level.isLit(x, y, z) || (tileIdBelow != Tile.dirt.id && tileIdBelow != Tile.grass.id)) { 29 | level.setTile(x, y, z, 0); 30 | } 31 | } 32 | 33 | @Override 34 | public void render(Tessellator tessellator, Level level, int layer, int x, int y, int z) { 35 | // Render in correct layer 36 | if (level.isLit(x, y, z) ^ layer != 1) { 37 | return; 38 | } 39 | 40 | // Texture id 41 | int textureId = this.getTexture(this.textureId); 42 | 43 | // Texture mapping points 44 | float minU = textureId % 16 / 16.0F; 45 | float minV = minU + 999 / 16000.0F; 46 | float maxU = (float) (textureId / 16) / 16.0F; 47 | float maxV = maxU + 999 / 16000.0F; 48 | 49 | // Color 50 | tessellator.color(1.0F, 1.0F, 1.0F); 51 | 52 | // Two sides 53 | for (int i = 0; i < 2; i++) { 54 | 55 | // Rotation 56 | float sin = (float) Math.sin(i * Math.PI / 2 + Math.PI / 4) / 2; 57 | float cos = (float) Math.cos(i * Math.PI / 2 + Math.PI / 4) / 2; 58 | 59 | // Vertex points 60 | float minX = x + 0.5F - sin; 61 | float maxX = x + 0.5F + sin; 62 | float minY = y + 0.0F; 63 | float maxY = y + 1.0F; 64 | float minZ = z + 0.5F - cos; 65 | float maxZ = z + 0.5F + cos; 66 | 67 | // Render bush side 68 | tessellator.vertexUV(minX, maxY, minZ, minV, maxU); 69 | tessellator.vertexUV(maxX, maxY, maxZ, minU, maxU); 70 | tessellator.vertexUV(maxX, minY, maxZ, minU, maxV); 71 | tessellator.vertexUV(minX, minY, minZ, minV, maxV); 72 | tessellator.vertexUV(maxX, maxY, maxZ, minU, maxU); 73 | tessellator.vertexUV(minX, maxY, minZ, minV, maxU); 74 | tessellator.vertexUV(minX, minY, minZ, minV, maxV); 75 | tessellator.vertexUV(maxX, minY, maxZ, minU, maxV); 76 | } 77 | } 78 | 79 | @Override 80 | public AABB getAABB(int x, int y, int z) { 81 | return null; 82 | } 83 | 84 | @Override 85 | public boolean blocksLight() { 86 | return false; 87 | } 88 | 89 | @Override 90 | public boolean isSolid() { 91 | return false; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Java template 3 | # Compiled class file 4 | *.class 5 | 6 | # Log file 7 | *.log 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files # 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 24 | hs_err_pid* 25 | 26 | ### JetBrains template 27 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 28 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 29 | 30 | # User-specific stuff 31 | .idea/**/workspace.xml 32 | .idea/**/tasks.xml 33 | .idea/**/usage.statistics.xml 34 | .idea/**/dictionaries 35 | .idea/**/shelf 36 | .idea/**/misc.xml 37 | .idea/**/discord.xml 38 | .idea/**/encodings.xml 39 | .idea/codeStyles/ 40 | .idea/sonarlint/ 41 | 42 | # Generated files 43 | .idea/**/contentModel.xml 44 | .idea/**/jarRepositories.xml 45 | .idea/**/uiDesigner.xml 46 | .idea/**/inspectionProfiles 47 | .idea/**/.name 48 | .idea/**/vcs.xml 49 | .idea/**/compiler.xml 50 | 51 | # Sensitive or high-churn files 52 | .idea/**/dataSources/ 53 | .idea/**/dataSources.ids 54 | .idea/**/dataSources.local.xml 55 | .idea/**/sqlDataSources.xml 56 | .idea/**/dynamic.xml 57 | .idea/**/dbnavigator.xml 58 | .idea/kotlinScripting.xml 59 | 60 | # Gradle 61 | .idea/**/gradle.xml 62 | .idea/**/libraries 63 | 64 | # Gradle and Maven with auto-import 65 | # When using Gradle or Maven with auto-import, you should exclude module files, 66 | # since they will be recreated, and may cause churn. Uncomment if using 67 | # auto-import. 68 | # .idea/artifacts 69 | # .idea/compiler.xml 70 | .idea/modules.xml 71 | .idea/*.iml 72 | # .idea/modules 73 | # *.iml 74 | # *.ipr 75 | 76 | # CMake 77 | cmake-build-*/ 78 | 79 | # Mongo Explorer plugin 80 | .idea/**/mongoSettings.xml 81 | 82 | # File-based project format 83 | *.iws 84 | 85 | # IntelliJ 86 | out/ 87 | 88 | # mpeltonen/sbt-idea plugin 89 | .idea_modules/ 90 | 91 | # JIRA plugin 92 | atlassian-ide-plugin.xml 93 | 94 | # Cursive Clojure plugin 95 | .idea/replstate.xml 96 | 97 | # Crashlytics plugin (for Android Studio and IntelliJ) 98 | com_crashlytics_export_strings.xml 99 | crashlytics.properties 100 | crashlytics-build.properties 101 | 102 | # Editor-based Rest Client 103 | .idea/httpRequests 104 | 105 | # Android studio 3.1+ serialized cache file 106 | .idea/caches/build_file_checksums.ser 107 | 108 | ### Gradle template 109 | .gradle 110 | /**/build/ 111 | 112 | # Ignore Gradle GUI config 113 | gradle-app.setting 114 | 115 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 116 | !gradle-wrapper.jar 117 | 118 | # Cache of project 119 | .gradletasknamecache 120 | 121 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 122 | # gradle/wrapper/gradle-wrapper.properties 123 | 124 | .idea/.gitignore 125 | .idea/modules/ 126 | src/main/resources/ 127 | src/test/ 128 | **/src/main/generated/* 129 | 130 | .idea/intellij-javadocs-4.0.1.xml 131 | run -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/Textures.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | import org.lwjgl.BufferUtils; 4 | 5 | import javax.imageio.ImageIO; 6 | import java.awt.image.BufferedImage; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.nio.ByteBuffer; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | import static org.lwjgl.opengl.GL11.*; 14 | import static org.lwjgl.util.glu.GLU.gluBuild2DMipmaps; 15 | 16 | public class Textures { 17 | 18 | private static final Map idMap = new HashMap<>(); 19 | 20 | private static int lastId = Integer.MIN_VALUE; 21 | 22 | /** 23 | * Load a texture into OpenGL 24 | * 25 | * @param resourceName Resource path of the image 26 | * @param mode Texture filter mode (GL_NEAREST, GL_LINEAR) 27 | * @return Texture id of OpenGL 28 | */ 29 | public static int loadTexture(String resourceName, int mode) { 30 | if (idMap.containsKey(resourceName)) { 31 | return idMap.get(resourceName); 32 | } 33 | 34 | // Generate a new texture id 35 | int id = glGenTextures(); 36 | 37 | // Store id in map 38 | idMap.put(resourceName, id); 39 | 40 | // Bind this texture id 41 | bind(id); 42 | 43 | // Set texture filter mode 44 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mode); 45 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mode); 46 | 47 | // Read from resources 48 | InputStream inputStream = Textures.class.getResourceAsStream(resourceName); 49 | 50 | try { 51 | // Read to buffered image 52 | BufferedImage bufferedImage = ImageIO.read(inputStream); 53 | 54 | // Get image size 55 | int width = bufferedImage.getWidth(); 56 | int height = bufferedImage.getHeight(); 57 | 58 | // Write image pixels into array 59 | int[] pixels = new int[width * height]; 60 | bufferedImage.getRGB(0, 0, width, height, pixels, 0, width); 61 | 62 | // Flip RGB order of the integers 63 | for (int i = 0; i < pixels.length; i++) { 64 | int alpha = pixels[i] >> 24 & 0xFF; 65 | int red = pixels[i] >> 16 & 0xFF; 66 | int green = pixels[i] >> 8 & 0xFF; 67 | int blue = pixels[i] & 0xFF; 68 | 69 | // ARGB to ABGR 70 | pixels[i] = alpha << 24 | blue << 16 | green << 8 | red; 71 | } 72 | 73 | // Create bytebuffer from pixel array 74 | ByteBuffer byteBuffer = BufferUtils.createByteBuffer(width * height * 4); 75 | byteBuffer.asIntBuffer().put(pixels); 76 | 77 | // Write texture to opengl 78 | gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, byteBuffer); 79 | } catch (IOException exception) { 80 | throw new RuntimeException("Could not load texture " + resourceName, exception); 81 | } 82 | 83 | return id; 84 | } 85 | 86 | /** 87 | * Bind the texture to OpenGL using the id from {@link #loadTexture(String, int)} 88 | * 89 | * @param id Texture id 90 | */ 91 | public static void bind(int id) { 92 | if (id != lastId) { 93 | glBindTexture(GL_TEXTURE_2D, id); 94 | lastId = id; 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/character/Zombie.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.character; 2 | 3 | import com.mojang.minecraft.Entity; 4 | import com.mojang.minecraft.Textures; 5 | import com.mojang.minecraft.level.Level; 6 | 7 | import static org.lwjgl.opengl.GL11.*; 8 | 9 | public class Zombie extends Entity { 10 | 11 | private final ZombieModel model = new ZombieModel(); 12 | 13 | public double rotation = Math.random() * Math.PI * 2; 14 | public double rotationMotionFactor = (Math.random() + 1.0) * 0.01F; 15 | 16 | public float timeOffset = (float) (Math.random() * 1239813.0F); 17 | public float speed = 1.0F; 18 | 19 | /** 20 | * Human model test 21 | * 22 | * @param level Level of the zombie 23 | */ 24 | public Zombie(Level level, double x, double y, double z) { 25 | super(level); 26 | 27 | // Set the position of the entity 28 | setPosition(x, y, z); 29 | } 30 | 31 | 32 | @Override 33 | public void onTick() { 34 | super.onTick(); 35 | 36 | // Kill in void 37 | if (this.y < -100.0F) { 38 | remove(); 39 | } 40 | 41 | // Increase movement direction 42 | this.rotation += this.rotationMotionFactor; 43 | 44 | // Modify direction motion factor 45 | this.rotationMotionFactor *= 0.99D; 46 | this.rotationMotionFactor += (Math.random() - Math.random()) * Math.random() * Math.random() * 0.009999999776482582; 47 | 48 | // Calculate movement input using rotation 49 | float vertical = (float) Math.sin(this.rotation); 50 | float forward = (float) Math.cos(this.rotation); 51 | 52 | // Randomly jump 53 | if (this.onGround && Math.random() < 0.08F) { 54 | this.motionY = 0.5F; 55 | } 56 | 57 | // Apply motion the zombie using the vertical and forward direction 58 | moveRelative(vertical, forward, this.onGround ? 0.1F : 0.02F); 59 | 60 | // Apply gravity 61 | this.motionY -= 0.08F; 62 | 63 | // Move the entity using motion 64 | move(this.motionX, this.motionY, this.motionZ); 65 | 66 | // Decrease motion speed 67 | this.motionX *= 0.91F; 68 | this.motionY *= 0.98F; 69 | this.motionZ *= 0.91F; 70 | 71 | // Decrease motion speed on ground 72 | if (this.onGround) { 73 | this.motionX *= 0.7F; 74 | this.motionZ *= 0.7F; 75 | } 76 | } 77 | 78 | /** 79 | * Render the zombie 80 | * 81 | * @param partialTicks Overflow for interpolation 82 | */ 83 | public void render(float partialTicks) { 84 | // Start rendering 85 | glPushMatrix(); 86 | glEnable(GL_TEXTURE_2D); 87 | 88 | // Bind texture 89 | glBindTexture(GL_TEXTURE_2D, Textures.loadTexture("/char.png", GL_NEAREST)); 90 | 91 | // Zombie animation time 92 | double time = System.nanoTime() / 1000000000D * 10.0 * this.speed + this.timeOffset; 93 | 94 | // Interpolate entity position 95 | double interpolatedX = this.prevX + (this.x - this.prevX) * partialTicks; 96 | double interpolatedY = this.prevY + (this.y - this.prevY) * partialTicks; 97 | double interpolatedZ = this.prevZ + (this.z - this.prevZ) * partialTicks; 98 | 99 | // Translate using interpolated position 100 | glTranslated(interpolatedX, interpolatedY, interpolatedZ); 101 | 102 | // Flip the entity because it's upside down 103 | glScalef(1.0F, -1.0F, 1.0F); 104 | 105 | // Actual size of the entity 106 | float size = 7.0F / 120.0F; 107 | glScalef(size, size, size); 108 | 109 | // Body offset animation 110 | double offsetY = Math.abs(Math.sin(time * 2.0D / 3.0D)) * 5.0 + 23.0D; 111 | glTranslated(0.0F, -offsetY, 0.0F); 112 | 113 | // Rotate the entity 114 | glRotated(Math.toDegrees(this.rotation) + 180, 0.0F, 1.0F, 0.0F); 115 | 116 | // Render the model 117 | this.model.render(time); 118 | 119 | // Stop rendering 120 | glDisable(GL_TEXTURE_2D); 121 | glPopMatrix(); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/PerlinNoiseFilter.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import java.util.Random; 4 | 5 | public class PerlinNoiseFilter { 6 | 7 | private static final int FUZZINESS = 16; 8 | private final int octave; 9 | 10 | /** 11 | * Perlin noise generator 12 | * 13 | * @param octave The strength of the noise 14 | */ 15 | public PerlinNoiseFilter(int octave) { 16 | this.octave = octave; 17 | } 18 | 19 | /** 20 | * Read random noise values with given amount 21 | * 22 | * @param width Noise width 23 | * @param height Noise height 24 | * @return Noise map 25 | */ 26 | public int[] read(int width, int height) { 27 | Random random = new Random(); 28 | 29 | int[] table = new int[width * height]; 30 | 31 | // Generate temporary table array with random values between -128 and +128 and multiplied with the fuzziness 32 | for (int step = width >> this.octave, y = 0; y < height; y += step) { 33 | for (int x = 0; x < width; x += step) { 34 | table[x + y * width] = (random.nextInt(256) - 128) * FUZZINESS; 35 | } 36 | } 37 | 38 | // Mutate values in table 39 | for (int step = width >> this.octave; step > 1; step /= 2) { 40 | int max = 256 * (step << this.octave); 41 | int halfStep = step / 2; 42 | 43 | // First mutation 44 | for (int y = 0; y < height; y += step) { 45 | for (int x = 0; x < width; x += step) { 46 | // Get value at index 47 | int value = table[x % width + y % height * width]; 48 | 49 | // Add step to create index for value 50 | int stepValueX = table[(x + step) % width + y % height * width]; 51 | int stepValueY = table[x % width + (y + step) % height * width]; 52 | int stepValueXY = table[(x + step) % width + (y + step) % height * width]; 53 | 54 | // Combine values for new value 55 | int mutatedValue = (value + stepValueY + stepValueX + stepValueXY) / 4 + random.nextInt(max * 2) - max; 56 | 57 | // Update value in table 58 | table[x + halfStep + (y + halfStep) * width] = mutatedValue; 59 | } 60 | } 61 | 62 | // Second mutation 63 | for (int y = 0; y < height; y += step) { 64 | for (int x = 0; x < width; x += step) { 65 | // Get value at index 66 | int value = table[x + y * width]; 67 | 68 | // Add step to create index for value 69 | int stepValueX = table[(x + step) % width + y * width]; 70 | int stepValueY = table[x + (y + step) % width * width]; 71 | 72 | // Add step and half step to create index for value 73 | int halfStepValueXPos = table[(x + halfStep & width - 1) + (y + halfStep - step & height - 1) * width]; 74 | int halfStepValueYPos = table[(x + halfStep - step & width - 1) + (y + halfStep & height - 1) * width]; 75 | 76 | // Add half step to create index for value 77 | int halfStepValue = table[(x + halfStep) % width + (y + halfStep) % height * width]; 78 | 79 | // Combine values for new value 80 | int mutatedValueX = (value + stepValueX + halfStepValue + halfStepValueXPos) / 4 + random.nextInt(max * 2) - max; 81 | int mutatedValueY = (value + stepValueY + halfStepValue + halfStepValueYPos) / 4 + random.nextInt(max * 2) - max; 82 | 83 | // Update values in table 84 | table[x + halfStep + y * width] = mutatedValueX; 85 | table[x + (y + halfStep) * width] = mutatedValueY; 86 | } 87 | } 88 | } 89 | 90 | // Create result array 91 | int[] result = new int[width * height]; 92 | 93 | // Generate output values 94 | for (int y = 0; y < height; y++) { 95 | for (int x = 0; x < width; x++) { 96 | result[x + y * width] = table[x % width + y % height * width] / 512 + 128; 97 | } 98 | } 99 | 100 | return result; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/Tessellator.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import org.lwjgl.BufferUtils; 4 | 5 | import java.nio.FloatBuffer; 6 | 7 | import static org.lwjgl.opengl.GL11.*; 8 | 9 | public class Tessellator { 10 | 11 | private static final int MAX_VERTICES = 100000; 12 | 13 | private final FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(MAX_VERTICES * 3); 14 | private final FloatBuffer textureCoordinateBuffer = BufferUtils.createFloatBuffer(MAX_VERTICES * 2); 15 | private final FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(MAX_VERTICES * 3); 16 | 17 | private int vertices = 0; 18 | 19 | // Texture 20 | private boolean hasTexture = false; 21 | private float textureU; 22 | private float textureV; 23 | 24 | // Color 25 | private boolean hasColor; 26 | private float red; 27 | private float green; 28 | private float blue; 29 | 30 | /** 31 | * Reset the buffer 32 | */ 33 | public void init() { 34 | clear(); 35 | } 36 | 37 | /** 38 | * Add a vertex point to buffer 39 | * 40 | * @param x Vertex point x 41 | * @param y Vertex point y 42 | * @param z Vertex point z 43 | */ 44 | public void vertex(float x, float y, float z) { 45 | // Vertex 46 | this.vertexBuffer.put(this.vertices * 3, x); 47 | this.vertexBuffer.put(this.vertices * 3 + 1, y); 48 | this.vertexBuffer.put(this.vertices * 3 + 2, z); 49 | 50 | // Texture coordinate 51 | if (this.hasTexture) { 52 | this.textureCoordinateBuffer.put(this.vertices * 2, this.textureU); 53 | this.textureCoordinateBuffer.put(this.vertices * 2 + 1, this.textureV); 54 | } 55 | 56 | // Color coordinate 57 | if (this.hasColor) { 58 | this.colorBuffer.put(this.vertices * 3, this.red); 59 | this.colorBuffer.put(this.vertices * 3 + 1, this.green); 60 | this.colorBuffer.put(this.vertices * 3 + 2, this.blue); 61 | } 62 | 63 | this.vertices++; 64 | 65 | // Flush if there are too many vertices in the buffer 66 | if (this.vertices == MAX_VERTICES) { 67 | flush(); 68 | } 69 | } 70 | 71 | /** 72 | * Add a vertex and set the texture UV mappings 73 | * 74 | * @param x Vertex point x 75 | * @param y Vertex point y 76 | * @param z Vertex point z 77 | * @param textureU Texture U point 78 | * @param textureV Texture V point 79 | */ 80 | public void vertexUV(float x, float y, float z, float textureU, float textureV) { 81 | texture(textureU, textureV); 82 | vertex(x, y, z); 83 | } 84 | 85 | /** 86 | * Set texture UV mappings 87 | * 88 | * @param textureU Texture U point 89 | * @param textureV Texture V point 90 | */ 91 | public void texture(float textureU, float textureV) { 92 | this.hasTexture = true; 93 | this.textureU = textureU; 94 | this.textureV = textureV; 95 | } 96 | 97 | /** 98 | * Set the RGB color 99 | * 100 | * @param red Red (0.0 - 1.0) 101 | * @param green Green (0.0 - 1.0) 102 | * @param blue Blue (0.0 - 1.0) 103 | */ 104 | public void color(float red, float green, float blue) { 105 | this.hasColor = true; 106 | this.red = red; 107 | this.green = green; 108 | this.blue = blue; 109 | } 110 | 111 | /** 112 | * Render the buffer 113 | */ 114 | public void flush() { 115 | this.vertexBuffer.flip(); 116 | this.textureCoordinateBuffer.flip(); 117 | 118 | // Set points 119 | glVertexPointer(3, GL_POINTS, this.vertexBuffer); 120 | if (this.hasTexture) { 121 | glTexCoordPointer(2, GL_POINTS, this.textureCoordinateBuffer); 122 | } 123 | if (this.hasColor) { 124 | glColorPointer(3, GL_POINTS, this.colorBuffer); 125 | } 126 | 127 | // Enable client states 128 | glEnableClientState(GL_VERTEX_ARRAY); 129 | if (this.hasTexture) { 130 | glEnableClientState(GL_TEXTURE_COORD_ARRAY); 131 | } 132 | if (this.hasColor) { 133 | glEnableClientState(GL_COLOR_ARRAY); 134 | } 135 | 136 | // Draw quads 137 | glDrawArrays(GL_QUADS, GL_POINTS, this.vertices); 138 | 139 | // Reset after rendering 140 | glDisableClientState(GL_VERTEX_ARRAY); 141 | if (this.hasTexture) { 142 | glDisableClientState(GL_TEXTURE_COORD_ARRAY); 143 | } 144 | if (this.hasColor) { 145 | glDisableClientState(GL_COLOR_ARRAY); 146 | } 147 | clear(); 148 | } 149 | 150 | /** 151 | * Reset vertex buffer 152 | */ 153 | private void clear() { 154 | this.vertexBuffer.clear(); 155 | this.textureCoordinateBuffer.clear(); 156 | this.vertices = 0; 157 | 158 | this.hasTexture = false; 159 | this.hasColor = false; 160 | } 161 | } -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/particle/Particle.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.particle; 2 | 3 | import com.mojang.minecraft.Entity; 4 | import com.mojang.minecraft.level.Level; 5 | import com.mojang.minecraft.level.Tessellator; 6 | 7 | public class Particle extends Entity { 8 | 9 | public int textureId; 10 | 11 | private final float textureUOffset; 12 | private final float textureVOffset; 13 | 14 | private final float size; 15 | private final int lifetime; 16 | 17 | private int age = 0; 18 | 19 | /** 20 | * Particle entity 21 | * 22 | * @param level The level 23 | * @param x Particle location x 24 | * @param y Particle location y 25 | * @param z Particle location z 26 | * @param motionX Particle motion x 27 | * @param motionY Particle motion y 28 | * @param motionZ Particle motion z 29 | * @param textureId Texture slot id of the particle 30 | */ 31 | public Particle(Level level, double x, double y, double z, double motionX, double motionY, double motionZ, int textureId) { 32 | super(level); 33 | 34 | // Set texture 35 | this.textureId = textureId; 36 | 37 | // Set size of the particle 38 | setSize(0.2F, 0.2F); 39 | this.heightOffset = this.boundingBoxHeight / 2.0F; 40 | 41 | // Set position 42 | setPosition(x, y, z); 43 | 44 | // Set motion and add random values 45 | this.motionX = motionX + (Math.random() * 2.0D - 1.0D) * 0.4D; 46 | this.motionY = motionY + (Math.random() * 2.0D - 1.0D) * 0.4D; 47 | this.motionZ = motionZ + (Math.random() * 2.0D - 1.0D) * 0.4D; 48 | 49 | // Create random speed 50 | double speed = (Math.random() + Math.random() + 1.0D) * 0.15D; 51 | 52 | // Apply speed 53 | double distance = Math.sqrt(this.motionX * this.motionX + this.motionY * this.motionY + this.motionZ * this.motionZ); 54 | this.motionX = this.motionX / distance * speed * 0.7D; 55 | this.motionY = this.motionY / distance * speed; 56 | this.motionZ = this.motionZ / distance * speed * 0.7D; 57 | 58 | // Create random texture offset 59 | this.textureUOffset = (float) Math.random() * 3.0F; 60 | this.textureVOffset = (float) Math.random() * 3.0F; 61 | 62 | this.size = (float) (Math.random() * 0.5D + 0.5D); 63 | this.lifetime = (int) (4.0D / (Math.random() * 0.9D + 0.1D)); 64 | } 65 | 66 | @Override 67 | public void onTick() { 68 | super.onTick(); 69 | 70 | // Kill randomly 71 | if (this.age++ >= this.lifetime) { 72 | remove(); 73 | } 74 | 75 | // Apply gravity 76 | this.motionY -= 0.06D; 77 | 78 | // Move the particle using motion 79 | this.move(this.motionX, this.motionY, this.motionZ); 80 | 81 | // Decrease motion speed 82 | this.motionX *= 0.98D; 83 | this.motionY *= 0.98D; 84 | this.motionZ *= 0.98D; 85 | 86 | // Decrease motion speed on ground 87 | if (this.onGround) { 88 | this.motionX *= 0.7D; 89 | this.motionZ *= 0.7D; 90 | } 91 | } 92 | 93 | /** 94 | * Render particle 95 | * 96 | * @param tessellator Tessellator for rendering 97 | * @param partialTicks Ticks for interpolation 98 | * @param cameraX Camera rotation X 99 | * @param cameraY Camera rotation Y 100 | * @param cameraZ Camera rotation Z 101 | * @param cameraXWithY Additional camera rotation x including the y rotation 102 | * @param cameraZWithY Additional camera rotation z including the y rotation 103 | */ 104 | public void render(Tessellator tessellator, float partialTicks, float cameraX, float cameraY, float cameraZ, float cameraXWithY, float cameraZWithY) { 105 | // UV mapping points 106 | float minU = (this.textureId % 16 + this.textureUOffset / 4.0F) / 16.0F; 107 | float maxU = minU + 999.0F / 64000.0F; 108 | float minV = ((float) (this.textureId / 16) + this.textureVOffset / 4.0F) / 16.0F; 109 | float maxV = minV + 999.0F / 64000.0F; 110 | 111 | // Interpolate position 112 | float x = (float) (this.prevX + (this.x - this.prevX) * partialTicks); 113 | float y = (float) (this.prevY + (this.y - this.prevY) * partialTicks); 114 | float z = (float) (this.prevZ + (this.z - this.prevZ) * partialTicks); 115 | 116 | // Size of the particle 117 | float size = this.size * 0.1F; 118 | 119 | // Render vertices 120 | tessellator.vertexUV(x - cameraX * size - cameraXWithY * size, y - cameraY * size, z - cameraZ * size - cameraZWithY * size, minU, maxV); 121 | tessellator.vertexUV(x - cameraX * size + cameraXWithY * size, y + cameraY * size, z - cameraZ * size + cameraZWithY * size, minU, minV); 122 | tessellator.vertexUV(x + cameraX * size + cameraXWithY * size, y + cameraY * size, z + cameraZ * size + cameraZWithY * size, maxU, minV); 123 | tessellator.vertexUV(x + cameraX * size - cameraXWithY * size, y - cameraY * size, z + cameraZ * size - cameraZWithY * size, maxU, maxV); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/Chunk.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import com.mojang.minecraft.Player; 4 | import com.mojang.minecraft.level.tile.Tile; 5 | import com.mojang.minecraft.phys.AABB; 6 | 7 | import static org.lwjgl.opengl.GL11.*; 8 | 9 | 10 | public class Chunk { 11 | 12 | private static final Tessellator TESSELLATOR = new Tessellator(); 13 | 14 | /** 15 | * Global rebuild statistic 16 | */ 17 | public static int updates; 18 | public long dirtiedTime; 19 | 20 | /** 21 | * Internal rebuild statistic 22 | */ 23 | private static long totalTime; 24 | private static int totalUpdates; 25 | 26 | /** 27 | * The game level 28 | */ 29 | private final Level level; 30 | 31 | /** 32 | * Bounding box values 33 | */ 34 | public AABB boundingBox; 35 | private final int minX, minY, minZ; 36 | private final int maxX, maxY, maxZ; 37 | private final float x, y, z; 38 | 39 | /** 40 | * Rendering states 41 | */ 42 | private final int lists; 43 | private boolean dirty = true; 44 | 45 | /** 46 | * Chunk containing a part of the tiles in a level 47 | * 48 | * @param level The game level 49 | * @param minX Minimal location X 50 | * @param minY Minimal location Y 51 | * @param minZ Minimal location Z 52 | * @param maxX Maximal location X 53 | * @param maxY Maximal location Y 54 | * @param maxZ Maximal location Z 55 | */ 56 | public Chunk(Level level, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { 57 | this.level = level; 58 | 59 | this.minX = minX; 60 | this.minY = minY; 61 | this.minZ = minZ; 62 | this.maxX = maxX; 63 | this.maxY = maxY; 64 | this.maxZ = maxZ; 65 | 66 | // Center of chunk 67 | this.x = (minX + maxX) / 2.0f; 68 | this.y = (minY + maxY) / 2.0f; 69 | this.z = (minZ + maxZ) / 2.0f; 70 | 71 | // Generate lists id 72 | this.lists = glGenLists(2); 73 | 74 | // Create bounding box object of chunk 75 | this.boundingBox = new AABB(minX, minY, minZ, maxX, maxY, maxZ); 76 | } 77 | 78 | /** 79 | * Render all tiles in this chunk 80 | * 81 | * @param layer The layer of the chunk (For shadows) 82 | */ 83 | public void rebuild(int layer) { 84 | // Update global stats 85 | updates++; 86 | 87 | // Mark chunk as no longer dirty 88 | this.dirty = false; 89 | 90 | // Tile render counter 91 | int tiles = 0; 92 | long timeRebuildStart = System.nanoTime(); 93 | 94 | // Setup tile rendering 95 | glNewList(this.lists + layer, GL_COMPILE); 96 | TESSELLATOR.init(); 97 | 98 | // For each tile in this chunk 99 | for (int x = this.minX; x < this.maxX; ++x) { 100 | for (int y = this.minY; y < this.maxY; ++y) { 101 | for (int z = this.minZ; z < this.maxZ; ++z) { 102 | int tileId = this.level.getTile(x, y, z); 103 | 104 | // Is a tile at this location? 105 | if (tileId > 0) { 106 | // Render the tile 107 | Tile.tiles[tileId].render(TESSELLATOR, this.level, layer, x, y, z); 108 | 109 | // Increase tile render counter 110 | tiles++; 111 | } 112 | } 113 | } 114 | } 115 | 116 | // Finish tile rendering 117 | TESSELLATOR.flush(); 118 | glEndList(); 119 | 120 | // Update chunk update counter 121 | if (tiles > 0) { 122 | totalTime += System.nanoTime() - timeRebuildStart; 123 | totalUpdates++; 124 | } 125 | } 126 | 127 | /** 128 | * Rebuild the chunk for all layers 129 | */ 130 | public void rebuild() { 131 | rebuild(0); 132 | rebuild(1); 133 | } 134 | 135 | /** 136 | * Render all tiles in this chunk 137 | * 138 | * @param layer The render layer (Shadow layer) 139 | */ 140 | public void render(int layer) { 141 | // Call lists id to render the chunk 142 | glCallList(this.lists + layer); 143 | } 144 | 145 | /** 146 | * Mark chunk as dirty. The chunk will rebuild in the next frame 147 | */ 148 | public void setDirty() { 149 | if (!this.dirty) { 150 | this.dirtiedTime = System.currentTimeMillis(); 151 | } 152 | 153 | this.dirty = true; 154 | } 155 | 156 | /** 157 | * State of the chunk for rebuild 158 | * 159 | * @return Chunk is dirty 160 | */ 161 | public boolean isDirty() { 162 | return dirty; 163 | } 164 | 165 | /** 166 | * Calculate squared distance to the player 167 | * 168 | * @param player The player for the location 169 | * @return The squared distance from the center of the chunk to the player 170 | */ 171 | public double distanceToSqr(Player player) { 172 | double distanceX = player.x - this.x; 173 | double distanceY = player.y - this.y; 174 | double distanceZ = player.z - this.z; 175 | return distanceX * distanceX + distanceY * distanceY + distanceZ * distanceZ; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/Entity.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | import com.mojang.minecraft.level.Level; 4 | import com.mojang.minecraft.phys.AABB; 5 | 6 | import java.util.List; 7 | 8 | public abstract class Entity { 9 | 10 | private final Level level; 11 | 12 | public double x, y, z; 13 | public double prevX, prevY, prevZ; 14 | public double motionX, motionY, motionZ; 15 | public float xRotation, yRotation; 16 | 17 | public AABB boundingBox; 18 | protected float boundingBoxWidth = 0.6F; 19 | protected float boundingBoxHeight = 1.8F; 20 | 21 | protected boolean onGround; 22 | protected float heightOffset; 23 | 24 | public boolean removed; 25 | 26 | /** 27 | * Entity with physics 28 | * 29 | * @param level Level of the entity 30 | */ 31 | public Entity(Level level) { 32 | this.level = level; 33 | 34 | resetPosition(); 35 | } 36 | 37 | /** 38 | * Set the entity to a specific location 39 | * 40 | * @param x Position x 41 | * @param y Position y 42 | * @param z Position z 43 | */ 44 | public void setPosition(double x, double y, double z) { 45 | // Set the position of the entity 46 | this.x = x; 47 | this.y = y; 48 | this.z = z; 49 | 50 | // Entity size 51 | float width = this.boundingBoxWidth / 2.0F; 52 | float height = this.boundingBoxHeight / 2.0F; 53 | 54 | // Set the position of the bounding box 55 | this.boundingBox = new AABB(x - width, y - height, 56 | z - width, x + width, 57 | y + height, z + width); 58 | } 59 | 60 | /** 61 | * Reset the position of the entity to a random location on the level 62 | */ 63 | protected void resetPosition() { 64 | float x = (float) Math.random() * this.level.width; 65 | float y = (float) (this.level.depth + 3); 66 | float z = (float) Math.random() * this.level.height; 67 | 68 | setPosition(x, y, z); 69 | } 70 | 71 | /** 72 | * Remove the entity in the next tick 73 | */ 74 | public void remove() { 75 | this.removed = true; 76 | } 77 | 78 | /** 79 | * Set the size of the bounding box 80 | * 81 | * @param width Width of the bounding box 82 | * @param height Height of the bounding box 83 | */ 84 | protected void setSize(float width, float height) { 85 | this.boundingBoxWidth = width; 86 | this.boundingBoxHeight = height; 87 | } 88 | 89 | /** 90 | * Turn the head using motion yaw and pitch 91 | * 92 | * @param x Rotate the head using yaw 93 | * @param y Rotate the head using pitch 94 | */ 95 | public void turn(float x, float y) { 96 | this.yRotation += x * 0.15F; 97 | this.xRotation -= y * 0.15F; 98 | 99 | // Pitch limit 100 | this.xRotation = Math.max(-90.0F, this.xRotation); 101 | this.xRotation = Math.min(90.0F, this.xRotation); 102 | } 103 | 104 | /** 105 | * Update the entity 106 | */ 107 | public void onTick() { 108 | // Store previous position 109 | this.prevX = this.x; 110 | this.prevY = this.y; 111 | this.prevZ = this.z; 112 | } 113 | 114 | /** 115 | * Move entity relative in level with collision check 116 | * 117 | * @param x Relative x 118 | * @param y Relative y 119 | * @param z Relative z 120 | */ 121 | public void move(double x, double y, double z) { 122 | double prevX = x; 123 | double prevY = y; 124 | double prevZ = z; 125 | 126 | // Get surrounded tiles 127 | List aABBs = this.level.getCubes(this.boundingBox.expand(x, y, z)); 128 | 129 | // Check for Y collision 130 | for (AABB abb : aABBs) { 131 | y = abb.clipYCollide(this.boundingBox, y); 132 | } 133 | this.boundingBox.move(0.0F, y, 0.0F); 134 | 135 | // Check for X collision 136 | for (AABB aABB : aABBs) { 137 | x = aABB.clipXCollide(this.boundingBox, x); 138 | } 139 | this.boundingBox.move(x, 0.0F, 0.0F); 140 | 141 | // Check for Z collision 142 | for (AABB aABB : aABBs) { 143 | z = aABB.clipZCollide(this.boundingBox, z); 144 | } 145 | this.boundingBox.move(0.0F, 0.0F, z); 146 | 147 | // Update on ground state 148 | this.onGround = prevY != y && prevY < 0.0F; 149 | 150 | // Stop motion on collision 151 | if (prevX != x) this.motionX = 0.0D; 152 | if (prevY != y) this.motionY = 0.0D; 153 | if (prevZ != z) this.motionZ = 0.0D; 154 | 155 | // Move the actual entity position 156 | this.x = (this.boundingBox.minX + this.boundingBox.maxX) / 2.0D; 157 | this.y = this.boundingBox.minY + this.heightOffset; 158 | this.z = (this.boundingBox.minZ + this.boundingBox.maxZ) / 2.0D; 159 | } 160 | 161 | 162 | /** 163 | * Add motion to the entity in the facing direction with given speed 164 | * 165 | * @param x Motion to add on X axis 166 | * @param z Motion to add on Z axis 167 | * @param speed Strength of the added motion 168 | */ 169 | protected void moveRelative(float x, float z, float speed) { 170 | float distance = x * x + z * z; 171 | 172 | // Stop moving if too slow 173 | if (distance < 0.01F) 174 | return; 175 | 176 | // Apply speed to relative movement 177 | distance = speed / (float) Math.sqrt(distance); 178 | x *= distance; 179 | z *= distance; 180 | 181 | // Calculate sin and cos of entity rotation 182 | double sin = Math.sin(Math.toRadians(this.yRotation)); 183 | double cos = Math.cos(Math.toRadians(this.yRotation)); 184 | 185 | // Move the entity in facing direction 186 | this.motionX += x * cos - z * sin; 187 | this.motionZ += z * cos + x * sin; 188 | } 189 | 190 | /** 191 | * Is entity in sun 192 | * 193 | * @return Entity is in sunlight 194 | */ 195 | public boolean isLit() { 196 | return this.level.isLit((int) this.x, (int) this.y, (int) this.z); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/character/Cube.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.character; 2 | 3 | import static org.lwjgl.opengl.GL11.*; 4 | 5 | public class Cube { 6 | 7 | private Polygon[] polygons; 8 | 9 | private int textureOffsetX; 10 | private int textureOffsetY; 11 | 12 | public float x; 13 | public float y; 14 | public float z; 15 | 16 | public float xRotation; 17 | public float yRotation; 18 | public float zRotation; 19 | 20 | /** 21 | * Create cube object 22 | * 23 | * @param textureOffsetX x offset position on the texture 24 | * @param textureOffsetY y offset position on the texture 25 | */ 26 | public Cube(int textureOffsetX, int textureOffsetY) { 27 | this.textureOffsetX = textureOffsetX; 28 | this.textureOffsetY = textureOffsetY; 29 | } 30 | 31 | /** 32 | * Set the texture offset position of the cube 33 | * 34 | * @param textureOffsetX Offset position x 35 | * @param textureOffsetY Offset position y 36 | */ 37 | public void setTextureOffset(int textureOffsetX, int textureOffsetY) { 38 | this.textureOffsetX = textureOffsetX; 39 | this.textureOffsetY = textureOffsetY; 40 | } 41 | 42 | /** 43 | * Create box using offset position and width, height and depth 44 | * 45 | * @param offsetX X offset of the render position 46 | * @param offsetY Y offset of the render position 47 | * @param offsetZ Z offset of the render position 48 | * @param width Cube width 49 | * @param height Cube height 50 | * @param depth Cube depth 51 | */ 52 | public Cube addBox(float offsetX, float offsetY, float offsetZ, int width, int height, int depth) { 53 | this.polygons = new Polygon[6]; 54 | 55 | float x = offsetX + width; 56 | float y = offsetY + height; 57 | float z = offsetZ + depth; 58 | 59 | // Create bottom vertex points of cube 60 | Vertex vertexBottom1 = new Vertex(offsetX, offsetY, offsetZ, 0.0F, 0.0F); 61 | Vertex vertexBottom2 = new Vertex(x, offsetY, offsetZ, 0.0F, 8.0F); 62 | Vertex vertexBottom3 = new Vertex(offsetX, offsetY, z, 0.0F, 0.0F); 63 | Vertex vertexBottom4 = new Vertex(x, offsetY, z, 0.0F, 8.0F); 64 | 65 | // Create top vertex points of cube 66 | Vertex vertexTop1 = new Vertex(x, y, z, 8.0F, 8.0F); 67 | Vertex vertexTop2 = new Vertex(offsetX, y, z, 8.0F, 0.0F); 68 | Vertex vertexTop3 = new Vertex(x, y, offsetZ, 8.0F, 8.0F); 69 | Vertex vertexTop4 = new Vertex(offsetX, y, offsetZ, 8.0F, 0.0F); 70 | 71 | // Create polygons for each cube side 72 | this.polygons[0] = new Polygon( 73 | new Vertex[]{ 74 | vertexBottom4, vertexBottom2, vertexTop3, vertexTop1 75 | }, 76 | this.textureOffsetX + depth + width, 77 | this.textureOffsetY + depth, 78 | this.textureOffsetX + depth + width + depth, 79 | this.textureOffsetY + depth + height 80 | ); 81 | 82 | this.polygons[1] = new Polygon( 83 | new Vertex[]{ 84 | vertexBottom1, vertexBottom3, vertexTop2, vertexTop4 85 | }, 86 | this.textureOffsetX, 87 | this.textureOffsetY + depth, 88 | this.textureOffsetX + depth, 89 | this.textureOffsetY + depth + height 90 | ); 91 | 92 | this.polygons[2] = new Polygon( 93 | new Vertex[]{ 94 | vertexBottom4, vertexBottom3, vertexBottom1, vertexBottom2 95 | }, 96 | this.textureOffsetX + depth, 97 | this.textureOffsetY, 98 | this.textureOffsetX + depth + width, 99 | this.textureOffsetY + depth 100 | ); 101 | 102 | this.polygons[3] = new Polygon( 103 | new Vertex[]{ 104 | vertexTop3, vertexTop4, vertexTop2, vertexTop1 105 | }, 106 | this.textureOffsetX + depth + width, 107 | this.textureOffsetY, 108 | this.textureOffsetX + depth + width + width, 109 | this.textureOffsetY + depth 110 | ); 111 | 112 | this.polygons[4] = new Polygon( 113 | new Vertex[]{ 114 | vertexBottom2, vertexBottom1, vertexTop4, vertexTop3 115 | }, 116 | this.textureOffsetX + depth, 117 | this.textureOffsetY + depth, 118 | this.textureOffsetX + depth + width, 119 | this.textureOffsetY + depth + height 120 | ); 121 | 122 | this.polygons[5] = new Polygon( 123 | new Vertex[]{ 124 | vertexBottom3, vertexBottom4, vertexTop1, vertexTop2 125 | }, 126 | this.textureOffsetX + depth + width + depth, 127 | this.textureOffsetY + depth, 128 | this.textureOffsetX + depth + width + depth + width, 129 | this.textureOffsetY + depth + height 130 | ); 131 | 132 | return this; 133 | } 134 | 135 | /** 136 | * Set the absolute position of the cube 137 | * 138 | * @param x Absolute x position of cube 139 | * @param y Absolute y position of cube 140 | * @param z Absolute z position of cube 141 | */ 142 | public void setPosition(float x, float y, float z) { 143 | this.x = x; 144 | this.y = y; 145 | this.z = z; 146 | } 147 | 148 | /** 149 | * Render the cube 150 | */ 151 | public void render() { 152 | glPushMatrix(); 153 | 154 | // Position of the cube 155 | glTranslatef(this.x, this.y, this.z); 156 | 157 | // Rotation of the cube 158 | glRotated(Math.toDegrees(this.zRotation), 0.0F, 0.0F, 1.0F); 159 | glRotated(Math.toDegrees(this.yRotation), 0.0F, 1.0F, 0.0F); 160 | glRotated(Math.toDegrees(this.xRotation), 1.0F, 0.0F, 0.0F); 161 | 162 | // Start rendering 163 | glBegin(GL_QUADS); 164 | 165 | // Render polygons 166 | for (Polygon polygon : this.polygons) { 167 | polygon.render(); 168 | } 169 | 170 | // Stop rendering 171 | glEnd(); 172 | 173 | glPopMatrix(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/phys/AABB.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.phys; 2 | 3 | public class AABB { 4 | 5 | private final double epsilon = 0.0F; 6 | 7 | public double minX; 8 | public double minY; 9 | public double minZ; 10 | public double maxX; 11 | public double maxY; 12 | public double maxZ; 13 | 14 | /** 15 | * Bounding box 16 | * 17 | * @param minX Minimum x side 18 | * @param minY Minimum y side 19 | * @param minZ Minimum z side 20 | * @param maxX Maximum x side 21 | * @param maxY Maximum y side 22 | * @param maxZ Maximum z side 23 | */ 24 | public AABB(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { 25 | this.minX = minX; 26 | this.minY = minY; 27 | this.minZ = minZ; 28 | this.maxX = maxX; 29 | this.maxY = maxY; 30 | this.maxZ = maxZ; 31 | } 32 | 33 | /** 34 | * Copy the current bounding box object 35 | * 36 | * @return Clone of the bounding box 37 | */ 38 | public AABB clone() { 39 | return new AABB(this.minX, this.minY, this.minZ, this.maxX, this.maxY, this.maxZ); 40 | } 41 | 42 | /** 43 | * Expand the bounding box. Positive and negative numbers controls which side of the box should grow. 44 | * 45 | * @param x Amount to expand the minX or maxX 46 | * @param y Amount to expand the minY or maxY 47 | * @param z Amount to expand the minZ or maxZ 48 | * @return The expanded bounding box 49 | */ 50 | public AABB expand(double x, double y, double z) { 51 | double minX = this.minX; 52 | double minY = this.minY; 53 | double minZ = this.minZ; 54 | double maxX = this.maxX; 55 | double maxY = this.maxY; 56 | double maxZ = this.maxZ; 57 | 58 | // Handle expanding of min/max x 59 | if (x < 0.0F) { 60 | minX += x; 61 | } else { 62 | maxX += x; 63 | } 64 | 65 | // Handle expanding of min/max y 66 | if (y < 0.0F) { 67 | minY += y; 68 | } else { 69 | maxY += y; 70 | } 71 | 72 | // Handle expanding of min/max z 73 | if (z < 0.0F) { 74 | minZ += z; 75 | } else { 76 | maxZ += z; 77 | } 78 | 79 | // Create new bounding box 80 | return new AABB(minX, minY, minZ, maxX, maxY, maxZ); 81 | } 82 | 83 | /** 84 | * Expand the bounding box on both sides. 85 | * The center is always fixed when using grow. 86 | * 87 | * @param x 88 | * @param y 89 | * @param z 90 | * @return 91 | */ 92 | public AABB grow(double x, double y, double z) { 93 | return new AABB(this.minX - x, this.minY - y, 94 | this.minZ - z, this.maxX + x, 95 | this.maxY + y, this.maxZ + z); 96 | } 97 | 98 | /** 99 | * Check for collision on the X axis 100 | * 101 | * @param otherBoundingBox The other bounding box that is colliding with the this one. 102 | * @param x Position on the X axis that is colliding 103 | * @return Returns the corrected x position that collided. 104 | */ 105 | public double clipXCollide(AABB otherBoundingBox, double x) { 106 | // Check if the boxes are colliding on the Y axis 107 | if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) { 108 | return x; 109 | } 110 | 111 | // Check if the boxes are colliding on the Z axis 112 | if (otherBoundingBox.maxZ <= this.minZ || otherBoundingBox.minZ >= this.maxZ) { 113 | return x; 114 | } 115 | 116 | // Check for collision if the X axis of the current box is bigger 117 | if (x > 0.0F && otherBoundingBox.maxX <= this.minX) { 118 | double max = this.minX - otherBoundingBox.maxX - this.epsilon; 119 | if (max < x) { 120 | x = max; 121 | } 122 | } 123 | 124 | // Check for collision if the X axis of the current box is smaller 125 | if (x < 0.0F && otherBoundingBox.minX >= this.maxX) { 126 | double max = this.maxX - otherBoundingBox.minX + this.epsilon; 127 | if (max > x) { 128 | x = max; 129 | } 130 | } 131 | 132 | return x; 133 | } 134 | 135 | /** 136 | * Check for collision on the Y axis 137 | * 138 | * @param otherBoundingBox The other bounding box that is colliding with the this one. 139 | * @param y Position on the X axis that is colliding 140 | * @return Returns the corrected x position that collided. 141 | */ 142 | public double clipYCollide(AABB otherBoundingBox, double y) { 143 | // Check if the boxes are colliding on the X axis 144 | if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) { 145 | return y; 146 | } 147 | 148 | // Check if the boxes are colliding on the Z axis 149 | if (otherBoundingBox.maxZ <= this.minZ || otherBoundingBox.minZ >= this.maxZ) { 150 | return y; 151 | } 152 | 153 | // Check for collision if the Y axis of the current box is bigger 154 | if (y > 0.0F && otherBoundingBox.maxY <= this.minY) { 155 | double max = this.minY - otherBoundingBox.maxY - this.epsilon; 156 | if (max < y) { 157 | y = max; 158 | } 159 | } 160 | 161 | // Check for collision if the Y axis of the current box is bigger 162 | if (y < 0.0F && otherBoundingBox.minY >= this.maxY) { 163 | double max = this.maxY - otherBoundingBox.minY + this.epsilon; 164 | if (max > y) { 165 | y = max; 166 | } 167 | } 168 | 169 | return y; 170 | } 171 | 172 | /** 173 | * Check for collision on the Y axis 174 | * 175 | * @param otherBoundingBox The other bounding box that is colliding with the this one. 176 | * @param z Position on the X axis that is colliding 177 | * @return Returns the corrected x position that collided. 178 | */ 179 | public double clipZCollide(AABB otherBoundingBox, double z) { 180 | // Check if the boxes are colliding on the X axis 181 | if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) { 182 | return z; 183 | } 184 | 185 | // Check if the boxes are colliding on the Y axis 186 | if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) { 187 | return z; 188 | } 189 | 190 | // Check for collision if the Z axis of the current box is bigger 191 | if (z > 0.0F && otherBoundingBox.maxZ <= this.minZ) { 192 | double max = this.minZ - otherBoundingBox.maxZ - this.epsilon; 193 | if (max < z) { 194 | z = max; 195 | } 196 | } 197 | 198 | // Check for collision if the Z axis of the current box is bigger 199 | if (z < 0.0F && otherBoundingBox.minZ >= this.maxZ) { 200 | double max = this.maxZ - otherBoundingBox.minZ + this.epsilon; 201 | if (max > z) { 202 | z = max; 203 | } 204 | } 205 | 206 | return z; 207 | } 208 | 209 | /** 210 | * Check if the two boxes are intersecting/overlapping 211 | * 212 | * @param otherBoundingBox The other bounding box that could intersect 213 | * @return The two boxes are overlapping 214 | */ 215 | public boolean intersects(AABB otherBoundingBox) { 216 | // Check on X axis 217 | if (otherBoundingBox.maxX <= this.minX || otherBoundingBox.minX >= this.maxX) { 218 | return false; 219 | } 220 | 221 | // Check on Y axis 222 | if (otherBoundingBox.maxY <= this.minY || otherBoundingBox.minY >= this.maxY) { 223 | return false; 224 | } 225 | 226 | // Check on Z axis 227 | return (!(otherBoundingBox.maxZ <= this.minZ)) && (!(otherBoundingBox.minZ >= this.maxZ)); 228 | } 229 | 230 | /** 231 | * Move the bounding box relative. 232 | * 233 | * @param x Relative offset x 234 | * @param y Relative offset y 235 | * @param z Relative offset z 236 | */ 237 | public void move(double x, double y, double z) { 238 | this.minX += x; 239 | this.minY += y; 240 | this.minZ += z; 241 | this.maxX += x; 242 | this.maxY += y; 243 | this.maxZ += z; 244 | } 245 | 246 | /** 247 | * Create a new bounding box with the given offset 248 | * 249 | * @param x Relative offset x 250 | * @param y Relative offset x 251 | * @param z Relative offset x 252 | * @return New bounding box with the given offset relative to this bounding box 253 | */ 254 | public AABB offset(double x, double y, double z) { 255 | return new AABB(this.minX + x, this.minY + y, this.minZ + z, this.maxX + x, this.maxY + y, this.maxZ + z); 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/LevelRenderer.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import com.mojang.minecraft.HitResult; 4 | import com.mojang.minecraft.Player; 5 | import com.mojang.minecraft.Textures; 6 | import com.mojang.minecraft.level.tile.Tile; 7 | import com.mojang.minecraft.phys.AABB; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import static org.lwjgl.opengl.GL11.*; 13 | 14 | public class LevelRenderer implements LevelListener { 15 | 16 | private static final int CHUNK_SIZE = 16; 17 | 18 | private final Tessellator tessellator; 19 | private final Level level; 20 | private final Chunk[] chunks; 21 | 22 | private final int chunkAmountX; 23 | private final int chunkAmountY; 24 | private final int chunkAmountZ; 25 | 26 | /** 27 | * Create renderer for level 28 | * 29 | * @param level The rendered level 30 | */ 31 | public LevelRenderer(Level level) { 32 | level.addListener(this); 33 | 34 | this.tessellator = new Tessellator(); 35 | this.level = level; 36 | 37 | // Calculate amount of chunks of level 38 | this.chunkAmountX = level.width / CHUNK_SIZE; 39 | this.chunkAmountY = level.depth / CHUNK_SIZE; 40 | this.chunkAmountZ = level.height / CHUNK_SIZE; 41 | 42 | // Create chunk array 43 | this.chunks = new Chunk[this.chunkAmountX * this.chunkAmountY * this.chunkAmountZ]; 44 | 45 | // Fill level with chunks 46 | for (int x = 0; x < this.chunkAmountX; x++) { 47 | for (int y = 0; y < this.chunkAmountY; y++) { 48 | for (int z = 0; z < this.chunkAmountZ; z++) { 49 | // Calculate min bounds for chunk 50 | int minChunkX = x * CHUNK_SIZE; 51 | int minChunkY = y * CHUNK_SIZE; 52 | int minChunkZ = z * CHUNK_SIZE; 53 | 54 | // Calculate max bounds for chunk 55 | int maxChunkX = (x + 1) * CHUNK_SIZE; 56 | int maxChunkY = (y + 1) * CHUNK_SIZE; 57 | int maxChunkZ = (z + 1) * CHUNK_SIZE; 58 | 59 | // Check for chunk bounds out of level 60 | maxChunkX = Math.min(level.width, maxChunkX); 61 | maxChunkY = Math.min(level.depth, maxChunkY); 62 | maxChunkZ = Math.min(level.height, maxChunkZ); 63 | 64 | // Create chunk based on bounds 65 | Chunk chunk = new Chunk(level, minChunkX, minChunkY, minChunkZ, maxChunkX, maxChunkY, maxChunkZ); 66 | this.chunks[(x + y * this.chunkAmountX) * this.chunkAmountZ + z] = chunk; 67 | } 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * Get all chunks with dirty flag 74 | * 75 | * @return List of dirty chunks 76 | */ 77 | public List getAllDirtyChunks() { 78 | ArrayList dirty = new ArrayList<>(); 79 | for (final Chunk chunk : this.chunks) { 80 | if (chunk.isDirty()) { 81 | dirty.add(chunk); 82 | } 83 | } 84 | return dirty; 85 | } 86 | 87 | /** 88 | * Render all chunks of the level 89 | * 90 | * @param layer The render layer 91 | */ 92 | public void render(int layer) { 93 | glEnable(GL_TEXTURE_2D); 94 | glBindTexture(GL_TEXTURE_2D, Textures.loadTexture("/terrain.png", GL_NEAREST)); 95 | 96 | // Get current camera frustum 97 | Frustum frustum = Frustum.getFrustum(); 98 | 99 | // For all chunks 100 | for (Chunk chunk : this.chunks) { 101 | 102 | // Render if bounding box of chunk is in frustum 103 | if (frustum.isVisible(chunk.boundingBox)) { 104 | 105 | // Render chunk 106 | chunk.render(layer); 107 | } 108 | } 109 | 110 | glDisable(GL_TEXTURE_2D); 111 | } 112 | 113 | /** 114 | * Rebuild all dirty chunks in a sorted order 115 | * 116 | * @param player The player for the sort priority. Chunks closer to the player will get a higher priority. 117 | */ 118 | public void updateDirtyChunks(Player player) { 119 | // Get all dirty chunks 120 | List dirty = getAllDirtyChunks(); 121 | if (!dirty.isEmpty()) { 122 | 123 | // Sort the dirty chunk list 124 | dirty.sort(new DirtyChunkSorter(player, Frustum.getFrustum())); 125 | 126 | // Rebuild max 8 chunks per frame 127 | for (int i = 0; i < 8 && i < dirty.size(); i++) { 128 | dirty.get(i).rebuild(); 129 | } 130 | } 131 | } 132 | 133 | /** 134 | * Mark all chunks inside of the given area as dirty. 135 | * 136 | * @param minX Minimum on X axis 137 | * @param minY Minimum on Y axis 138 | * @param minZ Minimum on Z axis 139 | * @param maxX Maximum on X axis 140 | * @param maxY Maximum on Y axis 141 | * @param maxZ Maximum on Z axis 142 | */ 143 | public void setDirty(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { 144 | // To chunk coordinates 145 | minX /= CHUNK_SIZE; 146 | minY /= CHUNK_SIZE; 147 | minZ /= CHUNK_SIZE; 148 | maxX /= CHUNK_SIZE; 149 | maxY /= CHUNK_SIZE; 150 | maxZ /= CHUNK_SIZE; 151 | 152 | // Minimum limit 153 | minX = Math.max(minX, 0); 154 | minY = Math.max(minY, 0); 155 | minZ = Math.max(minZ, 0); 156 | 157 | // Maximum limit 158 | maxX = Math.min(maxX, this.chunkAmountX - 1); 159 | maxY = Math.min(maxY, this.chunkAmountY - 1); 160 | maxZ = Math.min(maxZ, this.chunkAmountZ - 1); 161 | 162 | // Mark all chunks as dirty 163 | for (int x = minX; x <= maxX; x++) { 164 | for (int y = minY; y <= maxY; y++) { 165 | for (int z = minZ; z <= maxZ; z++) { 166 | // Get chunk at this position 167 | Chunk chunk = this.chunks[(x + y * this.chunkAmountX) * this.chunkAmountZ + z]; 168 | 169 | // Set dirty 170 | chunk.setDirty(); 171 | } 172 | } 173 | } 174 | } 175 | 176 | /** 177 | * Render pick selection face on tile 178 | * 179 | * @param player The player 180 | */ 181 | public void pick(Player player, Frustum frustum) { 182 | float radius = 3.0F; 183 | AABB boundingBox = player.boundingBox.grow(radius, radius, radius); 184 | 185 | int minX = (int) boundingBox.minX; 186 | int maxX = (int) (boundingBox.maxX + 1.0F); 187 | int minY = (int) boundingBox.minY; 188 | int maxY = (int) (boundingBox.maxY + 1.0F); 189 | int minZ = (int) boundingBox.minZ; 190 | int maxZ = (int) (boundingBox.maxZ + 1.0F); 191 | 192 | glInitNames(); 193 | 194 | // Define type 195 | glPushName(0); // Type 0 196 | 197 | // Define name value x 198 | glPushName(0); 199 | 200 | for (int x = minX; x < maxX; x++) { 201 | // Set last pushed value to x 202 | glLoadName(x); 203 | 204 | // Define name value y 205 | glPushName(0); 206 | 207 | for (int y = minY; y < maxY; y++) { 208 | // Set last pushed value to y 209 | glLoadName(y); 210 | 211 | // Define name value z 212 | glPushName(0); 213 | 214 | for (int z = minZ; z < maxZ; z++) { 215 | // Check for solid tile 216 | Tile tile = Tile.tiles[this.level.getTile(x, y, z)]; 217 | if (tile != null && frustum.isVisible(tile.getTileAABB(x, y, z))) { 218 | 219 | // Set last pushed value to x 220 | glLoadName(z); 221 | 222 | // Define name value face id 223 | glPushName(0); 224 | 225 | // Render all faces 226 | for (int face = 0; face < 6; face++) { 227 | 228 | // Set last pushed value to face id 229 | glLoadName(face); 230 | 231 | // Render selection face 232 | this.tessellator.init(); 233 | tile.renderFaceNoTexture(this.tessellator, x, y, z, face); 234 | this.tessellator.flush(); 235 | } 236 | 237 | // Pop face id 238 | glPopName(); 239 | } 240 | } 241 | 242 | // Pop z 243 | glPopName(); 244 | } 245 | 246 | // Pop y 247 | glPopName(); 248 | } 249 | 250 | // Pop x 251 | glPopName(); 252 | 253 | // Pop type 254 | glPopName(); 255 | } 256 | 257 | /** 258 | * Render hit face of the result 259 | * 260 | * @param hitResult The hit result to render 261 | */ 262 | public void renderHit(HitResult hitResult) { 263 | // Setup blending and color 264 | glEnable(GL_BLEND); 265 | glBlendFunc(GL_SRC_ALPHA, GL_CURRENT_BIT); 266 | glColor4f(1.0F, 1.0F, 1.0F, ((float) Math.sin(System.currentTimeMillis() / 100.0D) * 0.2F + 0.4F) * 0.5F); 267 | 268 | // Render face 269 | this.tessellator.init(); 270 | Tile.rock.renderFaceNoTexture(this.tessellator, hitResult.x, hitResult.y, hitResult.z, hitResult.face); 271 | this.tessellator.flush(); 272 | 273 | // Disable blending 274 | glDisable(GL_BLEND); 275 | } 276 | 277 | @Override 278 | public void lightColumnChanged(int x, int z, int minY, int maxY) { 279 | setDirty(x - 1, minY - 1, z - 1, x + 1, maxY + 1, z + 1); 280 | } 281 | 282 | @Override 283 | public void tileChanged(int x, int y, int z) { 284 | setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1); 285 | } 286 | 287 | @Override 288 | public void allChanged() { 289 | setDirty(0, 0, 0, this.level.width, this.level.depth, this.level.height); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/tile/Tile.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level.tile; 2 | 3 | import com.mojang.minecraft.level.Level; 4 | import com.mojang.minecraft.level.Tessellator; 5 | import com.mojang.minecraft.particle.Particle; 6 | import com.mojang.minecraft.particle.ParticleEngine; 7 | import com.mojang.minecraft.phys.AABB; 8 | 9 | import java.util.Random; 10 | 11 | public class Tile { 12 | 13 | // Tile array 14 | public static final Tile[] tiles = new Tile[256]; 15 | 16 | public static Tile rock = new Tile(1, 1); 17 | public static Tile grass = new GrassTile(2); 18 | public static Tile dirt = new Tile(3, 2); 19 | public static Tile stoneBrick = new Tile(4, 16); 20 | public static Tile wood = new Tile(5, 4); 21 | public static Tile bush = new Bush(6); 22 | 23 | public final int id; 24 | protected int textureId; 25 | 26 | /** 27 | * Create tile using id 28 | * 29 | * @param id Id of the tile 30 | */ 31 | public Tile(int id) { 32 | // Store tile in array 33 | Tile.tiles[id] = this; 34 | this.id = id; 35 | } 36 | 37 | /** 38 | * Create tile using id and texture slot id 39 | * 40 | * @param id Id of the tile 41 | * @param textureId Texture slot id 42 | */ 43 | public Tile(int id, int textureId) { 44 | this(id); 45 | 46 | this.textureId = textureId; 47 | } 48 | 49 | /** 50 | * Render a tile at the given position 51 | * 52 | * @param tessellator Tessellator for rendering 53 | * @param level Level to check for surrounding tiles 54 | * @param layer The layer which decides if it's a shadow or not 55 | * @param x Tile position x 56 | * @param y Tile position y 57 | * @param z Tile position z 58 | */ 59 | public void render(Tessellator tessellator, Level level, int layer, int x, int y, int z) { 60 | float shadeX = 0.6f; 61 | float shadeY = 1.0f; 62 | float shadeZ = 0.8f; 63 | 64 | // Render bottom face 65 | if (shouldRenderFace(level, x, y - 1, z, layer)) { 66 | tessellator.color(shadeY, shadeY, shadeY); 67 | renderFace(tessellator, x, y, z, 0); 68 | } 69 | 70 | // Render top face 71 | if (shouldRenderFace(level, x, y + 1, z, layer)) { 72 | tessellator.color(shadeY, shadeY, shadeY); 73 | renderFace(tessellator, x, y, z, 1); 74 | } 75 | 76 | // Render side faces Z 77 | if (shouldRenderFace(level, x, y, z - 1, layer)) { 78 | tessellator.color(shadeZ, shadeZ, shadeZ); 79 | renderFace(tessellator, x, y, z, 2); 80 | } 81 | if (shouldRenderFace(level, x, y, z + 1, layer)) { 82 | tessellator.color(shadeZ, shadeZ, shadeZ); 83 | renderFace(tessellator, x, y, z, 3); 84 | } 85 | 86 | // Render side faces X 87 | if (shouldRenderFace(level, x - 1, y, z, layer)) { 88 | tessellator.color(shadeX, shadeX, shadeX); 89 | renderFace(tessellator, x, y, z, 4); 90 | } 91 | if (shouldRenderFace(level, x + 1, y, z, layer)) { 92 | tessellator.color(shadeX, shadeX, shadeX); 93 | renderFace(tessellator, x, y, z, 5); 94 | } 95 | } 96 | 97 | private boolean shouldRenderFace(Level level, int x, int y, int z, int layer) { 98 | // Don't render face when both conditions are the same (isShadowLayer != isFullBrightness) 99 | return !level.isSolidTile(x, y, z) && (level.isLit(x, y, z) ^ layer == 1); 100 | } 101 | 102 | /** 103 | * Get the texture slot of the given face 104 | * 105 | * @param face Face id 106 | * @return The texture slot id 107 | */ 108 | protected int getTexture(int face) { 109 | return this.textureId; 110 | } 111 | 112 | /** 113 | * Render the single face of a tile 114 | * 115 | * @param tessellator Tessellator for rendering 116 | * @param x Tile position x 117 | * @param y Tile position y 118 | * @param z Tile position z 119 | * @param face Face id (0:Top, 1:Bottom, ...) 120 | */ 121 | public void renderFace(Tessellator tessellator, int x, int y, int z, int face) { 122 | // Get texture slot id of this face 123 | int textureId = getTexture(face); 124 | 125 | // UV mapping points 126 | float minU = textureId % 16 / 16.0F; 127 | float maxU = minU + 16 / 256F; 128 | float minV = (float) (textureId / 16) / 16.0F; 129 | float maxV = minV + 16 / 256F; 130 | 131 | // Vertex points 132 | float minX = x + 0.0f; 133 | float maxX = x + 1.0f; 134 | float minY = y + 0.0f; 135 | float maxY = y + 1.0f; 136 | float minZ = z + 0.0f; 137 | float maxZ = z + 1.0f; 138 | 139 | // Render bottom face 140 | if (face == 0) { 141 | tessellator.vertexUV(minX, minY, maxZ, minU, maxV); 142 | tessellator.vertexUV(minX, minY, minZ, minU, minV); 143 | tessellator.vertexUV(maxX, minY, minZ, maxU, minV); 144 | tessellator.vertexUV(maxX, minY, maxZ, maxU, maxV); 145 | } 146 | 147 | // Render top face 148 | if (face == 1) { 149 | tessellator.vertexUV(maxX, maxY, maxZ, maxU, maxV); 150 | tessellator.vertexUV(maxX, maxY, minZ, maxU, minV); 151 | tessellator.vertexUV(minX, maxY, minZ, minU, minV); 152 | tessellator.vertexUV(minX, maxY, maxZ, minU, maxV); 153 | } 154 | 155 | // Render side faces Z 156 | if (face == 2) { 157 | tessellator.vertexUV(minX, maxY, minZ, maxU, minV); 158 | tessellator.vertexUV(maxX, maxY, minZ, minU, minV); 159 | tessellator.vertexUV(maxX, minY, minZ, minU, maxV); 160 | tessellator.vertexUV(minX, minY, minZ, maxU, maxV); 161 | } 162 | if (face == 3) { 163 | tessellator.vertexUV(minX, maxY, maxZ, minU, minV); 164 | tessellator.vertexUV(minX, minY, maxZ, minU, maxV); 165 | tessellator.vertexUV(maxX, minY, maxZ, maxU, maxV); 166 | tessellator.vertexUV(maxX, maxY, maxZ, maxU, minV); 167 | } 168 | 169 | // Render side faces X 170 | if (face == 4) { 171 | tessellator.vertexUV(minX, maxY, maxZ, maxU, minV); 172 | tessellator.vertexUV(minX, maxY, minZ, minU, minV); 173 | tessellator.vertexUV(minX, minY, minZ, minU, maxV); 174 | tessellator.vertexUV(minX, minY, maxZ, maxU, maxV); 175 | } 176 | if (face == 5) { 177 | tessellator.vertexUV(maxX, minY, maxZ, minU, maxV); 178 | tessellator.vertexUV(maxX, minY, minZ, maxU, maxV); 179 | tessellator.vertexUV(maxX, maxY, minZ, maxU, minV); 180 | tessellator.vertexUV(maxX, maxY, maxZ, minU, minV); 181 | } 182 | } 183 | 184 | /** 185 | * Render the single face of a tile without a texture 186 | * 187 | * @param tessellator Tessellator for rendering 188 | * @param x Tile position x 189 | * @param y Tile position y 190 | * @param z Tile position z 191 | * @param face Face id (0:Top, 1:Bottom, ...) 192 | */ 193 | public void renderFaceNoTexture(Tessellator tessellator, int x, int y, int z, int face) { 194 | float minX = x + 0.0f; 195 | float maxX = x + 1.0f; 196 | float minY = y + 0.0f; 197 | float maxY = y + 1.0f; 198 | float minZ = z + 0.0f; 199 | float maxZ = z + 1.0f; 200 | 201 | // Render face 202 | if (face == 0) { 203 | tessellator.vertex(minX, minY, maxZ); 204 | tessellator.vertex(minX, minY, minZ); 205 | tessellator.vertex(maxX, minY, minZ); 206 | tessellator.vertex(maxX, minY, maxZ); 207 | } 208 | if (face == 1) { 209 | tessellator.vertex(maxX, maxY, maxZ); 210 | tessellator.vertex(maxX, maxY, minZ); 211 | tessellator.vertex(minX, maxY, minZ); 212 | tessellator.vertex(minX, maxY, maxZ); 213 | } 214 | if (face == 2) { 215 | tessellator.vertex(minX, maxY, minZ); 216 | tessellator.vertex(maxX, maxY, minZ); 217 | tessellator.vertex(maxX, minY, minZ); 218 | tessellator.vertex(minX, minY, minZ); 219 | } 220 | if (face == 3) { 221 | tessellator.vertex(minX, maxY, maxZ); 222 | tessellator.vertex(minX, minY, maxZ); 223 | tessellator.vertex(maxX, minY, maxZ); 224 | tessellator.vertex(maxX, maxY, maxZ); 225 | } 226 | if (face == 4) { 227 | tessellator.vertex(minX, maxY, maxZ); 228 | tessellator.vertex(minX, maxY, minZ); 229 | tessellator.vertex(minX, minY, minZ); 230 | tessellator.vertex(minX, minY, maxZ); 231 | } 232 | if (face == 5) { 233 | tessellator.vertex(maxX, minY, maxZ); 234 | tessellator.vertex(maxX, minY, minZ); 235 | tessellator.vertex(maxX, maxY, minZ); 236 | tessellator.vertex(maxX, maxY, maxZ); 237 | } 238 | } 239 | 240 | /** 241 | * Ticked randomly 242 | * 243 | * @param level Level instance 244 | * @param x Position x of the tile 245 | * @param y Position y of the tile 246 | * @param z Position z of the tile 247 | * @param random Random instance 248 | */ 249 | public void onTick(Level level, int x, int y, int z, Random random) { 250 | // No implementation 251 | } 252 | 253 | /** 254 | * Called when a tile gets destroyed by the player 255 | * 256 | * @param level The current level 257 | * @param x Tile x location 258 | * @param y Tile y location 259 | * @param z Tile z location 260 | * @param particleEngine ParticleEngine to create the particles 261 | */ 262 | public void onDestroy(Level level, int x, int y, int z, ParticleEngine particleEngine) { 263 | int spread = 4; 264 | 265 | // Spread particles in a cube 266 | for (int offsetX = 0; offsetX < spread; offsetX++) { 267 | for (int offsetY = 0; offsetY < spread; offsetY++) { 268 | for (int offsetZ = 0; offsetZ < spread; offsetZ++) { 269 | 270 | float targetX = x + (offsetX + 0.5F) / spread; 271 | float targetY = y + (offsetY + 0.5F) / spread; 272 | float targetZ = z + (offsetZ + 0.5F) / spread; 273 | 274 | float motionX = targetX - x - 0.5F; 275 | float motionY = targetY - y - 0.5F; 276 | float motionZ = targetZ - z - 0.5F; 277 | 278 | // Add particle to the engine 279 | Particle particle = new Particle(level, targetX, targetY, targetZ, motionX, motionY, motionZ, this.textureId); 280 | particleEngine.add(particle); 281 | } 282 | } 283 | } 284 | } 285 | 286 | /** 287 | * Get bounding box of the entire cube 288 | * 289 | * @param x Tile position x 290 | * @param y Tile position y 291 | * @param z Tile position z 292 | * @return Bounding box of the entire cube 293 | */ 294 | public AABB getAABB(int x, int y, int z) { 295 | return new AABB(x, y, z, x + 1, y + 1, z + 1); 296 | } 297 | 298 | /** 299 | * Get bounding box of the actual model 300 | * 301 | * @param x Tile position x 302 | * @param y Tile position y 303 | * @param z Tile position z 304 | * @return Actual bounding box of the tile 305 | */ 306 | public AABB getTileAABB(int x, int y, int z) { 307 | return new AABB(x, y, z, x + 1, y + 1, z + 1); 308 | } 309 | 310 | /** 311 | * Tile is blocking light of the sun 312 | * 313 | * @return Returns true if the tile can block the light of the sun 314 | */ 315 | public boolean blocksLight() { 316 | return true; 317 | } 318 | 319 | /** 320 | * Tile is solid 321 | * 322 | * @return Returns true if the tile is a solid type 323 | */ 324 | public boolean isSolid() { 325 | return true; 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/Level.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import com.mojang.minecraft.level.tile.Tile; 4 | import com.mojang.minecraft.phys.AABB; 5 | 6 | import java.io.DataInputStream; 7 | import java.io.DataOutputStream; 8 | import java.io.FileInputStream; 9 | import java.io.FileOutputStream; 10 | import java.util.ArrayList; 11 | import java.util.Random; 12 | import java.util.zip.GZIPInputStream; 13 | import java.util.zip.GZIPOutputStream; 14 | 15 | public class Level { 16 | 17 | public final int width; 18 | public final int height; 19 | public final int depth; 20 | 21 | private final byte[] blocks; 22 | private final int[] lightDepths; 23 | 24 | private final ArrayList levelListeners = new ArrayList<>(); 25 | private final Random random = new Random(); 26 | 27 | /** 28 | * Three dimensional level containing all tiles 29 | * 30 | * @param width Level width 31 | * @param height Level height 32 | * @param depth Level depth 33 | */ 34 | public Level(int width, int height, int depth) { 35 | this.width = width; 36 | this.height = height; 37 | this.depth = depth; 38 | 39 | this.blocks = new byte[width * height * depth]; 40 | this.lightDepths = new int[width * height]; 41 | 42 | // Load level if it exists 43 | boolean mapLoaded = load(); 44 | 45 | // Generate a new level if file doesn't exists 46 | if (!mapLoaded) { 47 | generateMap(); 48 | } 49 | 50 | // Calculate light depth of the entire level 51 | calcLightDepths(0, 0, width, height); 52 | } 53 | 54 | /** 55 | * Generate a new level 56 | */ 57 | private void generateMap() { 58 | int[] firstHeightMap = new PerlinNoiseFilter(0).read(this.width, this.height); 59 | int[] secondHeightMap = new PerlinNoiseFilter(0).read(this.width, this.height); 60 | int[] cliffMap = new PerlinNoiseFilter(1).read(this.width, this.height); 61 | int[] rockMap = new PerlinNoiseFilter(1).read(this.width, this.height); 62 | 63 | // Generate tiles 64 | for (int x = 0; x < this.width; ++x) { 65 | for (int y = 0; y < this.depth; ++y) { 66 | for (int z = 0; z < this.height; ++z) { 67 | // Extract values from height map 68 | int firstHeightValue = firstHeightMap[x + z * this.width]; 69 | int secondHeightValue = secondHeightMap[x + z * this.width]; 70 | 71 | // Change the height map 72 | if (cliffMap[x + z * this.width] < 128) { 73 | secondHeightValue = firstHeightValue; 74 | } 75 | 76 | // Get max level height at this position 77 | int maxLevelHeight = Math.max(secondHeightValue, firstHeightValue) / 8 + this.depth / 3; 78 | 79 | // Get end of rock layer 80 | int maxRockHeight = rockMap[x + z * this.width] / 8 + this.depth / 3; 81 | 82 | // Keep it below the max height of the level 83 | if (maxRockHeight > maxLevelHeight - 2) { 84 | maxRockHeight = maxLevelHeight - 2; 85 | } 86 | 87 | // Get block array index 88 | int index = (y * this.height + z) * this.width + x; 89 | 90 | int id = 0; 91 | 92 | // Grass layer 93 | if (y == maxLevelHeight) { 94 | id = Tile.grass.id; 95 | } 96 | 97 | // Dirt layer 98 | if (y < maxLevelHeight) { 99 | id = Tile.dirt.id; 100 | } 101 | 102 | // Rock layer 103 | if (y <= maxRockHeight) { 104 | id = Tile.rock.id; 105 | } 106 | 107 | // Set the tile id 108 | this.blocks[index] = (byte) id; 109 | } 110 | } 111 | } 112 | } 113 | 114 | /** 115 | * Load blocks from level.dat 116 | * 117 | * @return successfully loaded 118 | */ 119 | public boolean load() { 120 | try { 121 | DataInputStream dis = new DataInputStream(new GZIPInputStream(new FileInputStream("level.dat"))); 122 | dis.readFully(this.blocks); 123 | calcLightDepths(0, 0, this.width, this.height); 124 | dis.close(); 125 | 126 | // Notify all tiles changed 127 | for (LevelListener levelListener : this.levelListeners) { 128 | levelListener.allChanged(); 129 | } 130 | 131 | return true; 132 | } catch (Exception e) { 133 | e.printStackTrace(); 134 | 135 | return false; 136 | } 137 | } 138 | 139 | /** 140 | * Store blocks in level.dat 141 | */ 142 | public void save() { 143 | try { 144 | DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(new FileOutputStream("level.dat"))); 145 | dos.write(this.blocks); 146 | dos.close(); 147 | } catch (Exception e) { 148 | e.printStackTrace(); 149 | } 150 | } 151 | 152 | /** 153 | * Calculate light depth of given area 154 | * 155 | * @param minX Minimum on X axis 156 | * @param minZ Minimum on Z axis 157 | * @param maxX Maximum on X axis 158 | * @param maxZ Maximum on Z axis 159 | */ 160 | private void calcLightDepths(int minX, int minZ, int maxX, int maxZ) { 161 | // For each x/z position in level 162 | for (int x = minX; x < minX + maxX; x++) { 163 | for (int z = minZ; z < minZ + maxZ; z++) { 164 | 165 | // Get previous light depth value 166 | int prevDepth = this.lightDepths[x + z * this.width]; 167 | 168 | // Calculate new light depth 169 | int depth = this.depth - 1; 170 | while (depth > 0 && !isLightBlocker(x, depth, z)) { 171 | depth--; 172 | } 173 | 174 | // Set new light depth 175 | this.lightDepths[x + z * this.width] = depth; 176 | 177 | // On light depth change 178 | if (prevDepth != depth) { 179 | // Get changed range 180 | int minTileChangeY = Math.min(prevDepth, depth); 181 | int maxTileChangeY = Math.max(prevDepth, depth); 182 | 183 | // Notify tile column changed 184 | for (LevelListener levelListener : this.levelListeners) { 185 | levelListener.lightColumnChanged(x, z, minTileChangeY, maxTileChangeY); 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | /** 193 | * Return true if a tile is available at the given location 194 | * 195 | * @param x Level position x 196 | * @param y Level position y 197 | * @param z Level position z 198 | * @return Tile available 199 | */ 200 | public boolean isTile(int x, int y, int z) { 201 | // Is location out of the level? 202 | if (x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height) { 203 | return false; 204 | } 205 | 206 | // Calculate index from x, y and z 207 | int index = (y * this.height + z) * this.width + x; 208 | 209 | // Return true if there is a tile at this location 210 | return this.blocks[index] != 0; 211 | } 212 | 213 | /** 214 | * Return the id of the tile at the given location 215 | * 216 | * @param x Level position x 217 | * @param y Level position y 218 | * @param z Level position z 219 | * @return Tile id at this location 220 | */ 221 | public int getTile(int x, int y, int z) { 222 | // Is location out of the level? 223 | if (x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height) { 224 | return 0; 225 | } 226 | 227 | // Calculate index from x, y and z 228 | int index = (y * this.height + z) * this.width + x; 229 | 230 | // Return tile id 231 | return this.blocks[index]; 232 | } 233 | 234 | /** 235 | * Returns true if tile is solid and not transparent 236 | * 237 | * @param x Tile position x 238 | * @param y Tile position y 239 | * @param z Tile position z 240 | * @return Tile is solid 241 | */ 242 | public boolean isSolidTile(int x, int y, int z) { 243 | Tile tile = Tile.tiles[this.getTile(x, y, z)]; 244 | return tile != null && tile.isSolid(); 245 | } 246 | 247 | /** 248 | * Returns true if the tile is blocking the light 249 | * 250 | * @param x Tile position x 251 | * @param y Tile position y 252 | * @param z Tile position z 253 | * @return Tile blocks the light 254 | */ 255 | public boolean isLightBlocker(final int x, final int y, final int z) { 256 | Tile tile = Tile.tiles[this.getTile(x, y, z)]; 257 | return tile != null && tile.blocksLight(); 258 | } 259 | 260 | /** 261 | * Get bounding box of all tiles surrounded by the given bounding box 262 | * 263 | * @param boundingBox Target bounding box located in the level 264 | * @return List of bounding boxes representing the tiles around the given bounding box 265 | */ 266 | public ArrayList getCubes(AABB boundingBox) { 267 | ArrayList boundingBoxList = new ArrayList<>(); 268 | 269 | int minX = (int) (Math.floor(boundingBox.minX) - 1); 270 | int maxX = (int) (Math.ceil(boundingBox.maxX) + 1); 271 | int minY = (int) (Math.floor(boundingBox.minY) - 1); 272 | int maxY = (int) (Math.ceil(boundingBox.maxY) + 1); 273 | int minZ = (int) (Math.floor(boundingBox.minZ) - 1); 274 | int maxZ = (int) (Math.ceil(boundingBox.maxZ) + 1); 275 | 276 | // Minimum level position 277 | minX = Math.max(0, minX); 278 | minY = Math.max(0, minY); 279 | minZ = Math.max(0, minZ); 280 | 281 | // Maximum level position 282 | maxX = Math.min(this.width, maxX); 283 | maxY = Math.min(this.depth, maxY); 284 | maxZ = Math.min(this.height, maxZ); 285 | 286 | // Include all surrounding tiles 287 | for (int x = minX; x < maxX; x++) { 288 | for (int y = minY; y < maxY; y++) { 289 | for (int z = minZ; z < maxZ; z++) { 290 | 291 | // Get tile this location 292 | Tile tile = Tile.tiles[this.getTile(x, y, z)]; 293 | if (tile != null) { 294 | 295 | // Get bounding box of the the tile 296 | AABB aabb = tile.getAABB(x, y, z); 297 | if (aabb != null) { 298 | boundingBoxList.add(aabb); 299 | } 300 | } 301 | } 302 | } 303 | } 304 | return boundingBoxList; 305 | } 306 | 307 | 308 | /** 309 | * Set tile at position 310 | * 311 | * @param x Tile position x 312 | * @param y Tile position y 313 | * @param z Tile position z 314 | * @param id Type of tile 315 | * @return Tile changed 316 | */ 317 | public boolean setTile(int x, int y, int z, int id) { 318 | // Check if position is out of level 319 | if (x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height) { 320 | return false; 321 | } 322 | 323 | // Get index of this position 324 | int index = (y * this.height + z) * this.width + x; 325 | 326 | // Check if type changed 327 | if (id == this.blocks[index]) 328 | return false; 329 | 330 | // Set tile 331 | this.blocks[index] = (byte) id; 332 | 333 | // Update lightning 334 | this.calcLightDepths(x, z, 1, 1); 335 | 336 | // Notify tile changed 337 | for (LevelListener levelListener : this.levelListeners) { 338 | levelListener.tileChanged(x, y, z); 339 | } 340 | 341 | return true; 342 | } 343 | 344 | /** 345 | * Register a level listener 346 | * 347 | * @param levelListener Listener interface 348 | */ 349 | public void addListener(LevelListener levelListener) { 350 | this.levelListeners.add(levelListener); 351 | } 352 | 353 | /** 354 | * Check if the given tile position is in the sun 355 | * 356 | * @param x Tile position x 357 | * @param y Tile position y 358 | * @param z Tile position z 359 | * @return Tile is in the sun 360 | */ 361 | public boolean isLit(int x, int y, int z) { 362 | return x < 0 || y < 0 || z < 0 || x >= this.width || y >= this.depth || z >= this.height || y >= this.lightDepths[x + z * this.width]; 363 | } 364 | 365 | /** 366 | * Tick a random tile in the level 367 | */ 368 | public void onTick() { 369 | // Amount of tiles in this level 370 | int totalTiles = this.width * this.height * this.depth; 371 | 372 | // Amount of tiles to process for this tick 373 | int ticks = totalTiles / 400; 374 | 375 | // Tick multiple tiles in one game tick 376 | for (int i = 0; i < ticks; ++i) { 377 | // Get random position of the tile 378 | int x = this.random.nextInt(this.width); 379 | int y = this.random.nextInt(this.depth); 380 | int z = this.random.nextInt(this.height); 381 | 382 | // Get tile type 383 | Tile tile = Tile.tiles[this.getTile(x, y, z)]; 384 | if (tile != null) { 385 | // Tick tile 386 | tile.onTick(this, x, y, z, this.random); 387 | } 388 | } 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/level/Frustum.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft.level; 2 | 3 | import com.mojang.minecraft.phys.AABB; 4 | import org.lwjgl.BufferUtils; 5 | 6 | import java.nio.FloatBuffer; 7 | 8 | import static org.lwjgl.opengl.GL11.*; 9 | 10 | /* 11 | * Author: Ben Humphrey (DigiBen) 12 | * E-mail: digiben@gametutorials.com 13 | */ 14 | public class Frustum { 15 | 16 | // We create an enum of the sides so we don't have to call each side 0 or 1. 17 | // This way it makes it more understandable and readable when dealing with frustum sides. 18 | public static final int RIGHT = 0; // The RIGHT side of the frustum 19 | public static final int LEFT = 1; // The LEFT side of the frustum 20 | public static final int BOTTOM = 2; // The BOTTOM side of the frustum 21 | public static final int TOP = 3; // The TOP side of the frustum 22 | public static final int BACK = 4; // The BACK side of the frustum 23 | public static final int FRONT = 5; // The FRONT side of the frustum 24 | 25 | // Like above, instead of saying a number for the ABC and D of the plane, we 26 | // want to be more descriptive. 27 | public static final int A = 0; // The X value of the plane's normal 28 | public static final int B = 1; // The Y value of the plane's normal 29 | public static final int C = 2; // The Z value of the plane's normal 30 | public static final int D = 3; // The distance the plane is from the origin 31 | 32 | private static Frustum frustum = new Frustum(); 33 | 34 | // This holds the A B C and D values for each side of our frustum. 35 | float[][] m_Frustum = new float[6][4]; 36 | 37 | /** 38 | * FloatBuffer to get ModelView matrix. 39 | **/ 40 | FloatBuffer modl_b; 41 | 42 | /** 43 | * FloatBuffer to get Projection matrix. 44 | **/ 45 | FloatBuffer proj_b; 46 | 47 | 48 | /** 49 | * Frustum constructor. 50 | */ 51 | public Frustum() { 52 | modl_b = BufferUtils.createFloatBuffer(16); 53 | proj_b = BufferUtils.createFloatBuffer(16); 54 | } 55 | 56 | /** 57 | * Calculate the frustum and return it 58 | * 59 | * @return Current frustum 60 | */ 61 | public static Frustum getFrustum() { 62 | Frustum.frustum.calculateFrustum(); 63 | return Frustum.frustum; 64 | } 65 | 66 | ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 67 | ///// 68 | ///// This normalizes a plane (A side) from a given frustum. 69 | ///// 70 | ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 71 | public void normalizePlane(float[][] frustum, int side) { 72 | // Here we calculate the magnitude of the normal to the plane (point A B C) 73 | // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). 74 | // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) 75 | float magnitude = (float) Math.sqrt(frustum[side][A] * frustum[side][A] + 76 | frustum[side][B] * frustum[side][B] + frustum[side][C] * frustum[side][C]); 77 | 78 | // Then we divide the plane's values by it's magnitude. 79 | // This makes it easier to work with. 80 | frustum[side][A] /= magnitude; 81 | frustum[side][B] /= magnitude; 82 | frustum[side][C] /= magnitude; 83 | frustum[side][D] /= magnitude; 84 | } 85 | 86 | 87 | ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 88 | ///// 89 | ///// This extracts our frustum from the projection and modelview matrix. 90 | ///// 91 | ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 92 | public void calculateFrustum() { 93 | float[] proj = new float[16]; // This will hold our projection matrix 94 | float[] modl = new float[16]; // This will hold our modelview matrix 95 | float[] clip = new float[16]; // This will hold the clipping planes 96 | 97 | 98 | // glGetFloat() is used to extract information about our OpenGL world. 99 | // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. 100 | // It then stores the matrix into an array of [16]. 101 | proj_b.rewind(); 102 | glGetFloat(GL_PROJECTION_MATRIX, proj_b); 103 | proj_b.rewind(); 104 | proj_b.get(proj); 105 | 106 | // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. 107 | // This also stores it in an array of [16]. 108 | modl_b.rewind(); 109 | glGetFloat(GL_MODELVIEW_MATRIX, modl_b); 110 | modl_b.rewind(); 111 | modl_b.get(modl); 112 | 113 | // Now that we have our modelview and projection matrix, if we combine these 2 matrices, 114 | // it will give us our clipping planes. To combine 2 matrices, we multiply them. 115 | 116 | clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + modl[3] * proj[12]; 117 | clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + modl[3] * proj[13]; 118 | clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + modl[3] * proj[14]; 119 | clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + modl[3] * proj[15]; 120 | 121 | clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + modl[7] * proj[12]; 122 | clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + modl[7] * proj[13]; 123 | clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + modl[7] * proj[14]; 124 | clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + modl[7] * proj[15]; 125 | 126 | clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + modl[11] * proj[12]; 127 | clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + modl[11] * proj[13]; 128 | clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + modl[11] * proj[14]; 129 | clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + modl[11] * proj[15]; 130 | 131 | clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + modl[15] * proj[12]; 132 | clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + modl[15] * proj[13]; 133 | clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + modl[15] * proj[14]; 134 | clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + modl[15] * proj[15]; 135 | 136 | // Now we actually want to get the sides of the frustum. To do this we take 137 | // the clipping planes we received above and extract the sides from them. 138 | 139 | // This will extract the RIGHT side of the frustum 140 | m_Frustum[RIGHT][A] = clip[3] - clip[0]; 141 | m_Frustum[RIGHT][B] = clip[7] - clip[4]; 142 | m_Frustum[RIGHT][C] = clip[11] - clip[8]; 143 | m_Frustum[RIGHT][D] = clip[15] - clip[12]; 144 | 145 | // Now that we have a normal (A,B,C) and a distance (D) to the plane, 146 | // we want to normalize that normal and distance. 147 | 148 | // Normalize the RIGHT side 149 | normalizePlane(m_Frustum, RIGHT); 150 | 151 | // This will extract the LEFT side of the frustum 152 | m_Frustum[LEFT][A] = clip[3] + clip[0]; 153 | m_Frustum[LEFT][B] = clip[7] + clip[4]; 154 | m_Frustum[LEFT][C] = clip[11] + clip[8]; 155 | m_Frustum[LEFT][D] = clip[15] + clip[12]; 156 | 157 | // Normalize the LEFT side 158 | normalizePlane(m_Frustum, LEFT); 159 | 160 | // This will extract the BOTTOM side of the frustum 161 | m_Frustum[BOTTOM][A] = clip[3] + clip[1]; 162 | m_Frustum[BOTTOM][B] = clip[7] + clip[5]; 163 | m_Frustum[BOTTOM][C] = clip[11] + clip[9]; 164 | m_Frustum[BOTTOM][D] = clip[15] + clip[13]; 165 | 166 | // Normalize the BOTTOM side 167 | normalizePlane(m_Frustum, BOTTOM); 168 | 169 | // This will extract the TOP side of the frustum 170 | m_Frustum[TOP][A] = clip[3] - clip[1]; 171 | m_Frustum[TOP][B] = clip[7] - clip[5]; 172 | m_Frustum[TOP][C] = clip[11] - clip[9]; 173 | m_Frustum[TOP][D] = clip[15] - clip[13]; 174 | 175 | // Normalize the TOP side 176 | normalizePlane(m_Frustum, TOP); 177 | 178 | // This will extract the BACK side of the frustum 179 | m_Frustum[BACK][A] = clip[3] - clip[2]; 180 | m_Frustum[BACK][B] = clip[7] - clip[6]; 181 | m_Frustum[BACK][C] = clip[11] - clip[10]; 182 | m_Frustum[BACK][D] = clip[15] - clip[14]; 183 | 184 | // Normalize the BACK side 185 | normalizePlane(m_Frustum, BACK); 186 | 187 | // This will extract the FRONT side of the frustum 188 | m_Frustum[FRONT][A] = clip[3] + clip[2]; 189 | m_Frustum[FRONT][B] = clip[7] + clip[6]; 190 | m_Frustum[FRONT][C] = clip[11] + clip[10]; 191 | m_Frustum[FRONT][D] = clip[15] + clip[14]; 192 | 193 | // Normalize the FRONT side 194 | normalizePlane(m_Frustum, FRONT); 195 | } 196 | 197 | // The code below will allow us to make checks within the frustum. For example, 198 | // if we want to see if a point, a sphere, or a cube lies inside of the frustum. 199 | // Because all of our planes point INWARDS (The normals are all pointing inside the frustum) 200 | // we then can assume that if a point is in FRONT of all of the planes, it's inside. 201 | 202 | ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 203 | ///// 204 | ///// This determines if a point is inside of the frustum 205 | ///// 206 | ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 207 | public boolean pointInFrustum(float x, float y, float z) { 208 | // Go through all the sides of the frustum 209 | for (int i = 0; i < 6; i++) { 210 | // Calculate the plane equation and check if the point is behind a side of the frustum 211 | if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) { 212 | // The point was behind a side, so it ISN'T in the frustum 213 | return false; 214 | } 215 | } 216 | 217 | // The point was inside of the frustum (In front of ALL the sides of the frustum) 218 | return true; 219 | } 220 | 221 | 222 | ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 223 | ///// 224 | ///// This determines if a sphere is inside of our frustum by it's center and radius. 225 | ///// 226 | ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 227 | public boolean sphereInFrustum(float x, float y, float z, float radius) { 228 | // Go through all the sides of the frustum 229 | for (int i = 0; i < 6; i++) { 230 | // If the center of the sphere is farther away from the plane than the radius 231 | if (m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius) { 232 | // The distance was greater than the radius so the sphere is outside of the frustum 233 | return false; 234 | } 235 | } 236 | 237 | // The sphere was inside of the frustum! 238 | return true; 239 | } 240 | 241 | ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 242 | ///// 243 | ///// This determines if a cube is in or around our frustum by it's center and 1/2 it's length 244 | ///// 245 | ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* 246 | public boolean cubeInFrustum(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { 247 | // This test is a bit more work, but not too much more complicated. 248 | // Basically, what is going on is, that we are given the center of the cube, 249 | // and half the length. Think of it like a radius. Then we checking each point 250 | // in the cube and seeing if it is inside the frustum. If a point is found in front 251 | // of a side, then we skip to the next side. If we get to a plane that does NOT have 252 | // a point in front of it, then it will return false. 253 | 254 | // *Note* - This will sometimes say that a cube is inside the frustum when it isn't. 255 | // This happens when all the corners of the bounding box are not behind any one plane. 256 | // This is rare and shouldn't effect the overall rendering speed. 257 | 258 | for (int i = 0; i < 6; i++) { 259 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * minY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0) 260 | continue; 261 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * minY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0) 262 | continue; 263 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0) 264 | continue; 265 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * minZ + m_Frustum[i][D] > 0) 266 | continue; 267 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * minY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0) 268 | continue; 269 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * minY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0) 270 | continue; 271 | if (m_Frustum[i][A] * minX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0) 272 | continue; 273 | if (m_Frustum[i][A] * maxX + m_Frustum[i][B] * maxY + m_Frustum[i][C] * maxZ + m_Frustum[i][D] > 0) 274 | continue; 275 | 276 | // If we get here, it isn't in the frustum 277 | return false; 278 | } 279 | 280 | return true; 281 | } 282 | 283 | public boolean isVisible(AABB aabb) { 284 | return cubeInFrustum((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, 285 | (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ); 286 | } 287 | } -------------------------------------------------------------------------------- /src/main/java/com/mojang/minecraft/Minecraft.java: -------------------------------------------------------------------------------- 1 | package com.mojang.minecraft; 2 | 3 | import com.mojang.minecraft.character.Zombie; 4 | import com.mojang.minecraft.level.*; 5 | import com.mojang.minecraft.level.tile.Tile; 6 | import com.mojang.minecraft.particle.ParticleEngine; 7 | import org.lwjgl.BufferUtils; 8 | import org.lwjgl.LWJGLException; 9 | import org.lwjgl.input.Keyboard; 10 | import org.lwjgl.input.Mouse; 11 | import org.lwjgl.opengl.Display; 12 | import org.lwjgl.opengl.DisplayMode; 13 | 14 | import javax.swing.*; 15 | import java.awt.*; 16 | import java.nio.FloatBuffer; 17 | import java.nio.IntBuffer; 18 | import java.util.ArrayList; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | 22 | import static org.lwjgl.opengl.GL11.*; 23 | import static org.lwjgl.util.glu.GLU.gluPerspective; 24 | import static org.lwjgl.util.glu.GLU.gluPickMatrix; 25 | 26 | public class Minecraft implements Runnable { 27 | 28 | private final Timer timer = new Timer(20); 29 | 30 | private Level level; 31 | private LevelRenderer levelRenderer; 32 | private Player player; 33 | 34 | private final List zombies = new ArrayList<>(); 35 | private ParticleEngine particleEngine; 36 | 37 | /** 38 | * Fog 39 | */ 40 | private final FloatBuffer fogColorDaylight = BufferUtils.createFloatBuffer(4); 41 | private final FloatBuffer fogColorShadow = BufferUtils.createFloatBuffer(4); 42 | private final FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(16); 43 | 44 | /** 45 | * Tile picking 46 | */ 47 | private final IntBuffer viewportBuffer = BufferUtils.createIntBuffer(16); 48 | private final IntBuffer selectBuffer = BufferUtils.createIntBuffer(2000); 49 | private HitResult hitResult; 50 | 51 | /** 52 | * HUD rendering 53 | */ 54 | private final Tessellator tessellator = new Tessellator(); 55 | 56 | /** 57 | * Selected tile in hand 58 | */ 59 | private int selectedTileId = 1; 60 | 61 | /** 62 | * Canvas 63 | */ 64 | private final Canvas parent; 65 | private int width; 66 | private int height; 67 | private final boolean fullscreen; 68 | public boolean appletMode; 69 | 70 | /** 71 | * Game state 72 | */ 73 | public volatile boolean running; 74 | 75 | /** 76 | * Create Minecraft instance and render it on a canvas 77 | * 78 | * @param parent The canvas as render target 79 | * @param width Canvas width 80 | * @param height Canvas height 81 | * @param fullscreen Is in fullscreen 82 | */ 83 | public Minecraft(Canvas parent, int width, int height, boolean fullscreen) { 84 | this.parent = parent; 85 | this.width = width; 86 | this.height = height; 87 | this.fullscreen = fullscreen; 88 | } 89 | 90 | /** 91 | * Initialize the game. 92 | * Setup display, keyboard, mouse, rendering and camera 93 | * 94 | * @throws LWJGLException Game could not be initialized 95 | */ 96 | public void init() throws LWJGLException { 97 | // Write fog color for daylight 98 | this.fogColorDaylight.put(new float[]{ 99 | 254 / 255.0F, 100 | 251 / 255.0F, 101 | 250 / 255.0F, 102 | 255 / 255.0F 103 | }).flip(); 104 | 105 | // Write fog color for shadow 106 | this.fogColorShadow.put(new float[]{ 107 | 14 / 255.0F, 108 | 11 / 255.0F, 109 | 10 / 255.0F, 110 | 255 / 255.0F 111 | }).flip(); 112 | 113 | if (this.parent == null) { 114 | if (this.fullscreen) { 115 | // Set in fullscreen 116 | Display.setFullscreen(true); 117 | 118 | // Set monitor size 119 | this.width = Display.getDisplayMode().getWidth(); 120 | this.height = Display.getDisplayMode().getHeight(); 121 | } else { 122 | // Set defined window size 123 | Display.setDisplayMode(new DisplayMode(this.width, this.height)); 124 | } 125 | } else { 126 | // Set canvas parent 127 | Display.setParent(this.parent); 128 | } 129 | 130 | // Setup I/O 131 | Display.create(); 132 | Keyboard.create(); 133 | Mouse.create(); 134 | 135 | // Setup texture and color 136 | glEnable(GL_TEXTURE_2D); 137 | glShadeModel(GL_SMOOTH); 138 | glClearColor(0.5F, 0.8F, 1.0F, 0.0F); 139 | glClearDepth(1.0); 140 | 141 | // Setup depth 142 | glEnable(GL_DEPTH_TEST); 143 | glEnable(GL_CULL_FACE); 144 | glDepthFunc(GL_LEQUAL); 145 | 146 | // Setup alpha 147 | glEnable(GL_ALPHA_TEST); 148 | glAlphaFunc(GL_GREATER, 0.5F); 149 | 150 | // Setup camera 151 | glMatrixMode(GL_PROJECTION); 152 | glLoadIdentity(); 153 | glMatrixMode(GL_MODELVIEW); 154 | 155 | // Create level and player (Has to be in main thread) 156 | this.level = new Level(256, 256, 64); 157 | this.levelRenderer = new LevelRenderer(this.level); 158 | this.player = new Player(this.level); 159 | this.particleEngine = new ParticleEngine(this.level); 160 | 161 | // Grab mouse cursor 162 | Mouse.setGrabbed(true); 163 | 164 | // Spawn some zombies 165 | for (int i = 0; i < 10; ++i) { 166 | Zombie zombie = new Zombie(this.level, 128.0F, 0.0F, 129.0F); 167 | zombie.resetPosition(); 168 | this.zombies.add(zombie); 169 | } 170 | } 171 | 172 | /** 173 | * Destroy mouse, keyboard and display 174 | */ 175 | public void destroy() { 176 | this.level.save(); 177 | 178 | Mouse.destroy(); 179 | Keyboard.destroy(); 180 | Display.destroy(); 181 | } 182 | 183 | /** 184 | * Main game thread 185 | * Responsible for the game loop 186 | */ 187 | @Override 188 | public void run() { 189 | // Game is running 190 | this.running = true; 191 | 192 | try { 193 | // Initialize the game 194 | init(); 195 | } catch (Exception e) { 196 | // Show error message dialog and stop the game 197 | JOptionPane.showMessageDialog(null, e, "Failed to start Minecraft", JOptionPane.ERROR_MESSAGE); 198 | System.exit(0); 199 | } 200 | 201 | // To keep track of framerate 202 | int frames = 0; 203 | long lastTime = System.currentTimeMillis(); 204 | 205 | try { 206 | // Start the game loop 207 | while (this.running) { 208 | // On close window 209 | if (this.parent == null && Display.isCloseRequested()) { 210 | this.stop(); 211 | } 212 | 213 | // Update the timer 214 | this.timer.advanceTime(); 215 | 216 | // Call the tick to reach updates 20 per seconds 217 | for (int i = 0; i < this.timer.ticks; ++i) { 218 | onTick(); 219 | } 220 | 221 | // Render the game 222 | render(this.timer.partialTicks); 223 | 224 | // Increase rendered frame 225 | frames++; 226 | 227 | // Loop if a second passed 228 | while (System.currentTimeMillis() >= lastTime + 1000L) { 229 | // Print amount of frames 230 | System.out.println(frames + " fps, " + Chunk.updates); 231 | 232 | // Reset global rebuild stats 233 | Chunk.updates = 0; 234 | 235 | // Increase last time printed and reset frame counter 236 | lastTime += 1000L; 237 | frames = 0; 238 | } 239 | } 240 | } catch (Exception e) { 241 | e.printStackTrace(); 242 | } finally { 243 | // Destroy I/O and save game 244 | destroy(); 245 | } 246 | } 247 | 248 | /** 249 | * Stop the game 250 | */ 251 | public void stop() { 252 | this.running = false; 253 | } 254 | 255 | /** 256 | * Game tick, called exactly 20 times per second 257 | */ 258 | private void onTick() { 259 | // Listen for keyboard inputs 260 | while (Keyboard.next()) { 261 | if (Keyboard.getEventKeyState()) { 262 | 263 | // Exit the game 264 | if (Keyboard.getEventKey() == 1) { // Escape 265 | stop(); 266 | } 267 | 268 | // Save the level 269 | if (Keyboard.getEventKey() == 28) { // Enter 270 | this.level.save(); 271 | } 272 | 273 | // Tile selection 274 | if (Keyboard.getEventKey() == 2) { // 1 275 | this.selectedTileId = Tile.rock.id; 276 | } 277 | if (Keyboard.getEventKey() == 3) { // 2 278 | this.selectedTileId = Tile.dirt.id; 279 | } 280 | if (Keyboard.getEventKey() == 4) { // 3 281 | this.selectedTileId = Tile.stoneBrick.id; 282 | } 283 | if (Keyboard.getEventKey() == 5) { // 4 284 | this.selectedTileId = Tile.wood.id; 285 | } 286 | if (Keyboard.getEventKey() == 7) { // 6 287 | this.selectedTileId = Tile.bush.id; 288 | } 289 | 290 | // Spawn zombie 291 | if (Keyboard.getEventKey() == 34) { // G 292 | this.zombies.add(new Zombie(this.level, this.player.x, this.player.y, this.player.z)); 293 | } 294 | } 295 | } 296 | 297 | // Tick random tile in level 298 | this.level.onTick(); 299 | 300 | // Tick particles 301 | this.particleEngine.onTick(); 302 | 303 | // Tick zombies 304 | Iterator iterator = this.zombies.iterator(); 305 | while (iterator.hasNext()) { 306 | Zombie zombie = iterator.next(); 307 | 308 | // Tick zombie 309 | zombie.onTick(); 310 | 311 | // Remove zombie 312 | if (zombie.removed) { 313 | iterator.remove(); 314 | } 315 | } 316 | 317 | // Tick player 318 | this.player.onTick(); 319 | } 320 | 321 | /** 322 | * Move and rotate the camera to players location and rotation 323 | * 324 | * @param partialTicks Overflow ticks to interpolate 325 | */ 326 | private void moveCameraToPlayer(float partialTicks) { 327 | Entity player = this.player; 328 | 329 | // Eye height 330 | glTranslatef(0.0f, 0.0f, -0.3f); 331 | 332 | // Rotate camera 333 | glRotatef(player.xRotation, 1.0f, 0.0f, 0.0f); 334 | glRotatef(player.yRotation, 0.0f, 1.0f, 0.0f); 335 | 336 | // Smooth movement 337 | double x = this.player.prevX + (this.player.x - this.player.prevX) * partialTicks; 338 | double y = this.player.prevY + (this.player.y - this.player.prevY) * partialTicks; 339 | double z = this.player.prevZ + (this.player.z - this.player.prevZ) * partialTicks; 340 | 341 | // Move camera to players location 342 | glTranslated(-x, -y, -z); 343 | } 344 | 345 | 346 | /** 347 | * Setup the normal player camera 348 | * 349 | * @param partialTicks Overflow ticks to interpolate 350 | */ 351 | private void setupCamera(float partialTicks) { 352 | glMatrixMode(GL_PROJECTION); 353 | glLoadIdentity(); 354 | 355 | // Set camera perspective 356 | gluPerspective(70, width / (float) height, 0.05F, 1000F); 357 | 358 | glMatrixMode(GL_MODELVIEW); 359 | glLoadIdentity(); 360 | 361 | // Move camera to middle of level 362 | moveCameraToPlayer(partialTicks); 363 | } 364 | 365 | /** 366 | * Setup tile picking camera 367 | * 368 | * @param partialTicks Overflow ticks to calculate smooth a movement 369 | * @param x Screen position x 370 | * @param y Screen position y 371 | */ 372 | private void setupPickCamera(float partialTicks, int x, int y) { 373 | glMatrixMode(GL_PROJECTION); 374 | glLoadIdentity(); 375 | 376 | // Reset buffer 377 | this.viewportBuffer.clear(); 378 | 379 | // Get viewport value 380 | glGetInteger(GL_VIEWPORT, this.viewportBuffer); 381 | 382 | // Flip 383 | this.viewportBuffer.flip(); 384 | this.viewportBuffer.limit(16); 385 | 386 | // Set matrix and camera perspective 387 | gluPickMatrix(x, y, 5.0F, 5.0F, this.viewportBuffer); 388 | gluPerspective(70.0F, this.width / (float) this.height, 0.05F, 1000.0F); 389 | 390 | glMatrixMode(GL_MODELVIEW); 391 | glLoadIdentity(); 392 | 393 | // Move camera to middle of level 394 | moveCameraToPlayer(partialTicks); 395 | } 396 | 397 | /** 398 | * @param partialTicks Overflow ticks to interpolate 399 | */ 400 | private void pick(float partialTicks) { 401 | // Reset select buffer 402 | this.selectBuffer.clear(); 403 | 404 | glSelectBuffer(this.selectBuffer); 405 | glRenderMode(GL_SELECT); 406 | 407 | // Setup pick camera 408 | this.setupPickCamera(partialTicks, this.width / 2, this.height / 2); 409 | 410 | // Render all possible pick selection faces to the target 411 | this.levelRenderer.pick(this.player, Frustum.getFrustum()); 412 | 413 | // Flip buffer 414 | this.selectBuffer.flip(); 415 | this.selectBuffer.limit(this.selectBuffer.capacity()); 416 | 417 | long closest = 0L; 418 | int[] names = new int[10]; 419 | int hitNameCount = 0; 420 | 421 | // Get amount of hits 422 | int hits = glRenderMode(GL_RENDER); 423 | for (int hitIndex = 0; hitIndex < hits; hitIndex++) { 424 | 425 | // Get name count 426 | int nameCount = this.selectBuffer.get(); 427 | long minZ = this.selectBuffer.get(); 428 | this.selectBuffer.get(); 429 | 430 | // Check if the hit is closer to the camera 431 | if (minZ < closest || hitIndex == 0) { 432 | closest = minZ; 433 | hitNameCount = nameCount; 434 | 435 | // Fill names 436 | for (int nameIndex = 0; nameIndex < nameCount; nameIndex++) { 437 | names[nameIndex] = this.selectBuffer.get(); 438 | } 439 | } else { 440 | // Skip names 441 | for (int nameIndex = 0; nameIndex < nameCount; ++nameIndex) { 442 | this.selectBuffer.get(); 443 | } 444 | } 445 | } 446 | 447 | // Update hit result 448 | if (hitNameCount > 0) { 449 | this.hitResult = new HitResult(names[0], names[1], names[2], names[3], names[4]); 450 | } else { 451 | this.hitResult = null; 452 | } 453 | } 454 | 455 | 456 | /** 457 | * Rendering the game 458 | * 459 | * @param partialTicks Overflow ticks to interpolate 460 | */ 461 | private void render(float partialTicks) { 462 | // Get mouse motion 463 | float motionX = Mouse.getDX(); 464 | float motionY = Mouse.getDY(); 465 | 466 | // Rotate the camera using the mouse motion input 467 | this.player.turn(motionX, motionY); 468 | 469 | // Pick tile 470 | pick(partialTicks); 471 | 472 | // Listen for mouse inputs 473 | while (Mouse.next()) { 474 | // Right click 475 | if (Mouse.getEventButton() == 1 && Mouse.getEventButtonState() && this.hitResult != null) { 476 | Tile previousTile = Tile.tiles[this.level.getTile(this.hitResult.x, this.hitResult.y, this.hitResult.z)]; 477 | 478 | // Destroy the tile 479 | boolean tileChanged = this.level.setTile(this.hitResult.x, this.hitResult.y, this.hitResult.z, 0); 480 | 481 | // Create particles for this tile 482 | if (previousTile != null && tileChanged) { 483 | previousTile.onDestroy(this.level, this.hitResult.x, this.hitResult.y, this.hitResult.z, this.particleEngine); 484 | } 485 | } 486 | 487 | // Left click 488 | if (Mouse.getEventButton() == 0 && Mouse.getEventButtonState() && this.hitResult != null) { 489 | // Get target tile position 490 | int x = this.hitResult.x; 491 | int y = this.hitResult.y; 492 | int z = this.hitResult.z; 493 | 494 | // Get position of the tile using face direction 495 | if (this.hitResult.face == 0) y--; 496 | if (this.hitResult.face == 1) y++; 497 | if (this.hitResult.face == 2) z--; 498 | if (this.hitResult.face == 3) z++; 499 | if (this.hitResult.face == 4) x--; 500 | if (this.hitResult.face == 5) x++; 501 | 502 | // Set the tile 503 | this.level.setTile(x, y, z, this.selectedTileId); 504 | } 505 | } 506 | 507 | // Clear color and depth buffer and reset the camera 508 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 509 | 510 | // Setup normal player camera 511 | setupCamera(partialTicks); 512 | glEnable(GL_CULL_FACE); 513 | 514 | // Get current frustum 515 | Frustum frustum = Frustum.getFrustum(); 516 | 517 | // Update dirty chunks 518 | this.levelRenderer.updateDirtyChunks(this.player); 519 | 520 | // Setup daylight fog 521 | setupFog(0); 522 | glEnable(GL_FOG); 523 | 524 | // Render bright tiles 525 | this.levelRenderer.render(0); 526 | 527 | // Render zombies in sunlight 528 | for (Zombie zombie : this.zombies) { 529 | if (zombie.isLit() && frustum.isVisible(zombie.boundingBox)) { 530 | zombie.render(partialTicks); 531 | } 532 | } 533 | 534 | // Render particles in sunlight 535 | this.particleEngine.render(this.player, this.tessellator, partialTicks, 0); 536 | 537 | // Setup shadow fog 538 | setupFog(1); 539 | 540 | // Render dark tiles in shadow 541 | this.levelRenderer.render(1); 542 | 543 | // Render zombies in shadow 544 | for (Zombie zombie : this.zombies) { 545 | if (!zombie.isLit() && frustum.isVisible(zombie.boundingBox)) { 546 | zombie.render(partialTicks); 547 | } 548 | } 549 | 550 | // Render particles in shadow 551 | this.particleEngine.render(this.player, this.tessellator, partialTicks, 1); 552 | 553 | // Finish rendering 554 | glDisable(GL_LIGHTING); 555 | glDisable(GL_TEXTURE_2D); 556 | glDisable(GL_FOG); 557 | 558 | // Render the actual hit 559 | if (this.hitResult != null) { 560 | glDisable(GL_ALPHA_TEST); 561 | this.levelRenderer.renderHit(this.hitResult); 562 | glEnable(GL_ALPHA_TEST); 563 | } 564 | 565 | // Draw player HUD 566 | drawGui(partialTicks); 567 | 568 | // Update the display 569 | Display.update(); 570 | } 571 | 572 | /** 573 | * Draw HUD 574 | * 575 | * @param partialTicks Overflow ticks to interpolate 576 | */ 577 | private void drawGui(float partialTicks) { 578 | // Clear depth 579 | glClear(GL_DEPTH_BUFFER_BIT); 580 | 581 | // Setup HUD camera 582 | glMatrixMode(GL_PROJECTION); 583 | glLoadIdentity(); 584 | 585 | int screenWidth = this.width * 240 / this.height; 586 | int screenHeight = this.height * 240 / this.height; 587 | 588 | // Set camera perspective 589 | glOrtho(0.0, screenWidth, screenHeight, 0.0, 100.0F, 300.0F); 590 | 591 | glMatrixMode(GL_MODELVIEW); 592 | glLoadIdentity(); 593 | 594 | // Move camera to Z level -200 595 | glTranslatef(0.0f, 0.0f, -200.0f); 596 | 597 | // Start tile display 598 | glPushMatrix(); 599 | 600 | // Transform tile position to the top right corner 601 | glTranslated(screenWidth - 16, 16.0F, 0.0F); 602 | glScalef(16.0F, 16.0F, 16.0F); 603 | glRotatef(30.0F, 1.0F, 0.0F, 0.0F); 604 | glRotatef(45.0F, 0.0F, 1.0F, 0.0F); 605 | glTranslatef(-1.5F, 0.5F, -0.5F); 606 | glScalef(-1.0F, -1.0F, 1.0F); 607 | 608 | // Setup tile rendering 609 | int id = Textures.loadTexture("/terrain.png", 9728); 610 | glBindTexture(GL_TEXTURE_2D, id); 611 | glEnable(GL_TEXTURE_2D); 612 | 613 | // Render selected tile in hand 614 | this.tessellator.init(); 615 | Tile.tiles[this.selectedTileId].render(this.tessellator, this.level, 0, -2, 0, 0); 616 | this.tessellator.flush(); 617 | 618 | // Finish tile rendering 619 | glDisable(GL_TEXTURE_2D); 620 | glPopMatrix(); 621 | 622 | // Cross hair position 623 | int x = screenWidth / 2; 624 | int y = screenHeight / 2; 625 | 626 | // Cross hair color 627 | glColor4f(1.0F, 1.0F, 1.0F, 1.0F); 628 | 629 | // Render cross hair 630 | this.tessellator.init(); 631 | this.tessellator.vertex((float) (x + 1), (float) (y - 4), 0.0F); 632 | this.tessellator.vertex((float) (x - 0), (float) (y - 4), 0.0F); 633 | this.tessellator.vertex((float) (x - 0), (float) (y + 5), 0.0F); 634 | this.tessellator.vertex((float) (x + 1), (float) (y + 5), 0.0F); 635 | this.tessellator.vertex((float) (x + 5), (float) (y - 0), 0.0F); 636 | this.tessellator.vertex((float) (x - 4), (float) (y - 0), 0.0F); 637 | this.tessellator.vertex((float) (x - 4), (float) (y + 1), 0.0F); 638 | this.tessellator.vertex((float) (x + 5), (float) (y + 1), 0.0F); 639 | this.tessellator.flush(); 640 | } 641 | 642 | /** 643 | * Setup fog with type 644 | * 645 | * @param fogType Type of the fog. (0: daylight, 1: shadow) 646 | */ 647 | private void setupFog(int fogType) { 648 | // Daylight fog 649 | if (fogType == 0) { 650 | // Fog distance 651 | glFogi(GL_FOG_MODE, GL_VIEWPORT_BIT); 652 | glFogf(GL_FOG_DENSITY, 0.001F); 653 | 654 | // Set fog color 655 | glFog(GL_FOG_COLOR, this.fogColorDaylight); 656 | 657 | glDisable(GL_LIGHTING); 658 | } 659 | 660 | // Shadow fog 661 | if (fogType == 1) { 662 | // Fog distance 663 | glFogi(GL_FOG_MODE, GL_VIEWPORT_BIT); 664 | glFogf(GL_FOG_DENSITY, 0.06F); 665 | 666 | // Set fog color 667 | glFog(GL_FOG_COLOR, this.fogColorShadow); 668 | 669 | glEnable(GL_LIGHTING); 670 | glEnable(GL_COLOR_MATERIAL); 671 | 672 | float brightness = 0.6F; 673 | glLightModel(GL_LIGHT_MODEL_AMBIENT, this.getBuffer(brightness, brightness, brightness, 1.0F)); 674 | } 675 | } 676 | 677 | /** 678 | * Fill float buffer with color values and return it 679 | * 680 | * @param red Red value 681 | * @param green Green value 682 | * @param blue Blue value 683 | * @param alpha Alpha value 684 | * @return Float buffer filled in RGBA order 685 | */ 686 | private FloatBuffer getBuffer(float red, float green, float blue, float alpha) { 687 | this.colorBuffer.clear(); 688 | this.colorBuffer.put(red).put(green).put(blue).put(alpha); 689 | this.colorBuffer.flip(); 690 | return this.colorBuffer; 691 | } 692 | 693 | /** 694 | * Entry point of the game 695 | * 696 | * @param args Program arguments (unused) 697 | */ 698 | public static void main(String[] args) { 699 | boolean fullScreen = false; 700 | 701 | // Find fullscreen argument 702 | for (String arg : args) { 703 | if (arg.equalsIgnoreCase("-fullscreen")) { 704 | fullScreen = true; 705 | break; 706 | } 707 | } 708 | 709 | // Launch 710 | new Thread(new Minecraft(null, 1024, 768, fullScreen)).start(); 711 | } 712 | 713 | } 714 | --------------------------------------------------------------------------------