Explorar o código

basic world saving / loading, tile rendering, camera

Kajetan Johannes Hammerle %!s(int64=5) %!d(string=hai) anos
pai
achega
281c14be80

+ 62 - 0
src/me/hammerle/snuviengine/Main.java

@@ -1,14 +1,76 @@
 package me.hammerle.snuviengine;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import me.hammerle.snuviengine.game.Game;
+import me.hammerle.snuviengine.game.World;
+import me.hammerle.snuviengine.util.WrappedInputStream;
+import me.hammerle.snuviengine.util.WrappedOutputStream;
 
 public class Main 
 {
     public static void main(String[] args)
     {
+        try(FileOutputStream out = new FileOutputStream(new File("./testworld")))
+        {
+            WrappedOutputStream o = new WrappedOutputStream(out);
+           
+            o.writeInt(48);
+            o.writeInt(32);
+            
+            for(int l = 0; l < 24; l++)
+            {
+                for(int i = 0; i < 64; i++)
+                {
+                    o.writeShort(1);
+                }
+                for(int i = 0; i < 64; i++)
+                {
+                    o.writeShort(0);
+                }
+                
+                for(int i = 0; i < 64; i++)
+                {
+                    o.writeShort(14);
+                }
+                for(int i = 0; i < 64; i++)
+                {
+                    o.writeShort(15);
+                }
+            }
+            
+            out.flush();
+        }
+        catch(Exception ex)
+        {
+            ex.printStackTrace();
+        }
+        
         Game game = new Game();
         game.run();
         
+        /*World w = new World();
+        
+        try(FileOutputStream out = new FileOutputStream(new File("./testworld")))
+        {
+            WrappedOutputStream o = new WrappedOutputStream(out);
+           
+            o.writeInt(16);
+            o.writeInt(16);
+            
+            for(int i = 0; i < 256; i++)
+            {
+                o.writeShort(i);
+            }
+            
+            out.flush();
+        }
+        catch(Exception ex)
+        {
+            ex.printStackTrace();
+        }*/
+        
         //for(int i = 0; i < 736; i++)
         //{
         //    System.out.println("public static final TexturedTile OUT_" + i + " = register(" + i + ", new TexturedTile(\"out" + i + ".png\"));");

+ 178 - 0
src/me/hammerle/snuviengine/api/Chunk.java

@@ -0,0 +1,178 @@
+package me.hammerle.snuviengine.api;
+
+import java.io.IOException;
+import java.nio.FloatBuffer;
+import me.hammerle.snuviengine.util.WrappedInputStream;
+import me.hammerle.snuviengine.util.WrappedOutputStream;
+import org.lwjgl.BufferUtils;
+import static org.lwjgl.opengl.GL11.*;
+import static org.lwjgl.opengl.GL15.*;
+import static org.lwjgl.opengl.GL20.*;
+import static org.lwjgl.opengl.GL30.*;
+
+public class Chunk
+{
+    public final static int CHUNK_SIZE = 8;
+    public final static int TILE_SIZE = 16;
+    public final static int LAYERS = 4;
+    public final static int LAST_BACK_LAYER = 1;
+    
+    private final short[][][] data = new short[LAYERS][CHUNK_SIZE][CHUNK_SIZE];
+    private final int chunkX;
+    private final int chunkY;
+    
+    private int vao;
+    private int vbo;
+    
+    private final FloatBuffer buffer = BufferUtils.createFloatBuffer(CHUNK_SIZE * CHUNK_SIZE * 24);
+    
+    private boolean[] dirty = new boolean[LAYERS];
+    
+    public Chunk(int x, int y)
+    {
+        this.chunkX = x;
+        this.chunkY = y;
+        
+        for(int i = 0; i < LAYERS; i++)
+        {
+            dirty[i] = true;
+        }
+        
+        Shader.addTask(() -> 
+        {
+            vao = glGenVertexArrays();
+            vbo = glGenBuffers();
+
+            GLHelper.glBindVertexArray(vao);
+            GLHelper.glBindBuffer(vbo);
+
+            glEnableVertexAttribArray(0);
+            glVertexAttribPointer(0, 2, GL_FLOAT, false, 16, 0);
+
+            glEnableVertexAttribArray(1);  
+            glVertexAttribPointer(1, 2, GL_FLOAT, false, 16, 8);
+            
+            glBufferData(GL_ARRAY_BUFFER, CHUNK_SIZE * CHUNK_SIZE * 96 * LAYERS, GL_STATIC_DRAW);
+        });
+    }
+    
+    public void read(WrappedInputStream in) throws IOException
+    {
+        for(int l = 0; l < LAYERS; l++)
+        {
+            for(int x = 0; x < CHUNK_SIZE; x++)
+            {
+                for(int y = 0; y < CHUNK_SIZE; y++)
+                {
+                    data[l][x][y] = in.readShort();
+                }
+            }
+        }
+    }
+    
+    public void write(WrappedOutputStream out) throws IOException
+    {
+        for(int l = 0; l < LAYERS; l++)
+        {
+            for(int x = 0; x < CHUNK_SIZE; x++)
+            {
+                for(int y = 0; y < CHUNK_SIZE; y++)
+                {
+                    out.writeShort(data[l][x][y]);
+                }
+            }
+        }
+    }
+    
+    private void rebuild(int layer)
+    {
+        for(int x = 0; x < CHUNK_SIZE; x++)
+        {
+            for(int y = 0; y < CHUNK_SIZE; y++)
+            {
+                short tile = data[layer][x][y];
+                float minX = x * TILE_SIZE;
+                float minY = y * TILE_SIZE;
+                float maxX = minX + TILE_SIZE;
+                float maxY = minY + TILE_SIZE;
+                float tMinX = (tile % 8) * 0.125f;
+                float tMinY = (tile / 8) * 0.0108695652f;
+                float tMaxX = tMinX + 0.125f;
+                float tMaxY = tMinY + 0.0108695652f;
+                
+                buffer.put(minX);
+                buffer.put(maxY);        
+                buffer.put(tMinX);
+                buffer.put(tMaxY);
+
+                buffer.put(minX);
+                buffer.put(minY);        
+                buffer.put(tMinX);
+                buffer.put(tMinY);
+
+                buffer.put(maxX);
+                buffer.put(maxY);        
+                buffer.put(tMaxX);
+                buffer.put(tMaxY);
+
+                buffer.put(maxX);
+                buffer.put(maxY);        
+                buffer.put(tMaxX);
+                buffer.put(tMaxY);
+
+                buffer.put(minX);
+                buffer.put(minY);        
+                buffer.put(tMinX);
+                buffer.put(tMinY);
+
+                buffer.put(maxX);
+                buffer.put(minY);        
+                buffer.put(tMaxX);
+                buffer.put(tMinY);
+            }
+        }
+        
+        buffer.flip();
+        GLHelper.glBindVertexArray(vao);
+        GLHelper.glBindBuffer(vbo);
+        glBufferSubData(GL_ARRAY_BUFFER, CHUNK_SIZE * CHUNK_SIZE * 96 * layer, buffer);
+    }
+    
+    public void drawBackground()
+    {
+        for(int i = 0; i <= LAST_BACK_LAYER; i++)
+        {
+            if(dirty[i])
+            {
+                rebuild(i);
+                dirty[i] = false;
+            }
+        }
+        GLHelper.glBindVertexArray(vao);
+        GLHelper.glBindBuffer(vbo);
+        Shader.pushMatrix();
+        Shader.translate(chunkX * CHUNK_SIZE * TILE_SIZE, chunkY * CHUNK_SIZE * TILE_SIZE);
+        Shader.updateMatrix();
+        Shader.popMatrix();
+        glDrawArrays(GL_TRIANGLES, 0, CHUNK_SIZE * CHUNK_SIZE * 6 * (LAST_BACK_LAYER + 1));
+    }
+    
+    public void drawForeground()
+    {
+        for(int i = LAST_BACK_LAYER + 1; i < LAYERS; i++)
+        {
+            if(dirty[i])
+            {
+                rebuild(i);
+                dirty[i] = false;
+            }
+        }
+        GLHelper.glBindVertexArray(vao);
+        GLHelper.glBindBuffer(vbo);
+        Shader.pushMatrix();
+        Shader.translate(chunkX * CHUNK_SIZE * TILE_SIZE, chunkY * CHUNK_SIZE * TILE_SIZE);
+        Shader.updateMatrix();
+        Shader.popMatrix();
+        glDrawArrays(GL_TRIANGLES, CHUNK_SIZE * CHUNK_SIZE * 6 * (LAST_BACK_LAYER + 1), CHUNK_SIZE * CHUNK_SIZE * 6 * (LAYERS - LAST_BACK_LAYER - 1));
+    }
+}

+ 0 - 104
src/me/hammerle/snuviengine/api/Map.java

@@ -1,104 +0,0 @@
-package me.hammerle.snuviengine.api;
-
-import me.hammerle.snuviengine.game.Chunk;
-import java.util.Random;
-
-public class Map 
-{
-    protected final static int PARTION = 16;
-    
-    private int layers;
-    private int width;
-    private int height;
-    private short[][][] tiles;
-    
-    private int pWidth;
-    private int pHeight;
-    private Chunk[][][] chunks = null;
-    
-    public Map()
-    {
-    }
-    
-    public void load(String path)
-    {
-        /*layers = 2;
-        width = 64;
-        height = 64;
-        tiles = new short[layers][width][height];
-        Random r = new Random(100);
-        for(int x = 0; x < width; x++)
-        {
-            for(int y = 0; y < height; y++)
-            {
-                tiles[0][x][y] = 0;
-            }
-        }
-        for(int x = 0; x < width; x++)
-        {
-            for(int y = 0; y < height; y++)
-            {
-                tiles[1][x][y] = (short) (r.nextInt(2));
-            }
-        }
-        
-        if(chunks != null)
-        {
-            for(Chunk[][] a : chunks)
-            {
-                for(Chunk[] b : a)
-                {
-                    for(Chunk c : b)
-                    {
-                        c.delete();
-                    }
-                }
-            }
-        }
-        
-        pWidth = width / PARTION;
-        pHeight = height / PARTION;
-        
-        chunks = new Chunk[layers][pWidth][pHeight];
-        for(int l = 0; l < layers; l++)
-        {
-            for(int x = 0; x < pWidth; x++)
-            {
-                for(int y = 0; y < pHeight; y++)
-                {
-                    chunks[l][x][y] = new Chunk();
-                }
-            }
-        }*/
-    }
-    
-    private float getViewX(float x) 
-    {
-        x -= Shader.getViewWidth() >> 1;
-        if(x < 0)
-        {
-            return 0;
-        }
-        float max = width * Engine.TILE_SIZE * Engine.SCALE - Shader.getViewWidth();
-        if(x > max)
-        {
-            return max;
-        }
-        return x;
-    }
-    
-    private float getViewY(float y) 
-    {
-        y -= Shader.getViewHeight() >> 1;
-        if(y < 0)
-        {
-            return 0;
-        }
-        float max = height * Engine.TILE_SIZE * Engine.SCALE - Shader.getViewHeight();
-        if(y > max)
-        {
-            return max;
-        }
-        return y;
-    }
-}

+ 4 - 4
src/me/hammerle/snuviengine/api/TextureRenderer.java

@@ -73,8 +73,8 @@ public class TextureRenderer
             throw new ShaderException("build called too early");
         }
         buffer.flip();
-        glBindVertexArray(vao);
-        glBindBuffer(GL_ARRAY_BUFFER, vbo);
+        GLHelper.glBindVertexArray(vao);
+        GLHelper.glBindBuffer(vbo);
         glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
         
         buffer.limit(buffer.capacity());
@@ -87,8 +87,8 @@ public class TextureRenderer
         {
             throw new ShaderException("build must be called before drawing");
         }
-        glBindVertexArray(vao);
-        glBindBuffer(GL_ARRAY_BUFFER, vbo);
+        GLHelper.glBindVertexArray(vao);
+        GLHelper.glBindBuffer(vbo);
         glDrawArrays(GL_TRIANGLES, 0, buffer.limit() / 4);
     }
     

+ 0 - 8
src/me/hammerle/snuviengine/game/Chunk.java

@@ -1,8 +0,0 @@
-package me.hammerle.snuviengine.game;
-
-public class Chunk
-{
-    public Chunk()
-    {
-    }
-}

+ 14 - 30
src/me/hammerle/snuviengine/game/Entity.java

@@ -33,8 +33,8 @@ public class Entity
         this.width = width;
         this.height = height;
         
-        vx = Math.random() * 8 + 5;
-        vy = Math.random() * 8 + 5;
+        vx = 1; //Math.random() * 8 + 5;
+        vy = 1; //Math.random() * 8 + 5;
     }
     
     private boolean isColliding(Entity ent)
@@ -89,8 +89,8 @@ public class Entity
                 yPos + STEP + height + ((vy > 0) ? vy : 0));
         if(list.isEmpty())
         {
-            xPos = Math.max(Math.min(vx + xPos, 400), 0);
-            yPos = Math.max(Math.min(vy + yPos, 300), 0);
+            xPos += vx;
+            yPos += vy;
             
             game.entities.update(this);
             return;
@@ -128,21 +128,13 @@ public class Entity
                 }
             }
             
-            if(xPos > 400 || xPos < 0)
+            for(Entity ent : list)
             {
-                lvx = 0.0f;
-                xPos = oldXPos;
-            }
-            else
-            {
-                for(Entity ent : list)
+                if(this.isColliding(ent))
                 {
-                    if(this.isColliding(ent))
-                    {
-                        lvx = 0.0f;
-                        xPos = oldXPos;
-                        break;
-                    }
+                    lvx = 0.0f;
+                    xPos = oldXPos;
+                    break;
                 }
             }
             
@@ -173,21 +165,13 @@ public class Entity
                 }
             }
             
-            if(yPos > 300 || yPos < 0)
-            {
-                lvy = 0.0f;
-                yPos = oldYPos;
-            }
-            else
+            for(Entity ent : list)
             {
-                for(Entity ent : list)
+                if(this.isColliding(ent))
                 {
-                    if(this.isColliding(ent))
-                    {
-                        lvy = 0.0f;
-                        yPos = oldYPos;
-                        break;
-                    }
+                    lvy = 0.0f;
+                    yPos = oldYPos;
+                    break;
                 }
             }
         }

+ 28 - 9
src/me/hammerle/snuviengine/game/Game.java

@@ -1,5 +1,8 @@
 package me.hammerle.snuviengine.game;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.Random;
 import me.hammerle.snuviengine.api.Engine;
 import me.hammerle.snuviengine.api.KeyBinding;
@@ -8,6 +11,7 @@ import me.hammerle.snuviengine.api.Shader;
 import me.hammerle.snuviengine.api.Texture;
 import me.hammerle.snuviengine.api.TextureRenderer;
 import me.hammerle.snuviengine.util.Clock;
+import me.hammerle.snuviengine.util.WrappedInputStream;
 import static org.lwjgl.glfw.GLFW.*;
 
 public class Game extends Engine
@@ -23,23 +27,34 @@ public class Game extends Engine
     private boolean toogleTps = false;
     
     private final TextureRenderer tr = new TextureRenderer();
-    private final Texture t = new Texture("images/out3.png");
     
     public BoxList entities = new BoxList(0.0, 0.0, 400.0, 300.0, 1.0);
     
+    private World w;
+    
     public Game()
     {
-        Random r = new Random(200);
+        /*Random r = new Random(200);
         for(int i = 0; i < 128; i++)
         {
             Entity ent = new Entity(this, 5.0, 5.0);
             ent.setPosition((int) (r.nextDouble() * 400.0), (int) (r.nextDouble() * 300));
             ent.node = entities.add(ent);
-        }
+        }*/
         
-        Hero hero = new Hero(this, 5.0, 5.0);
-        hero.setPosition(300.0, 200);
+        Hero hero = new Hero(this, 25.0, 25.0);
+        hero.setPosition(0.0, 0.0);
         hero.node = entities.add(hero);
+        
+        w = new World(hero);
+        try
+        {
+            w.read(new WrappedInputStream(new FileInputStream("./testworld")));
+        }
+        catch(IOException ex)
+        {
+            ex.printStackTrace();
+        }
     }
     
     @Override
@@ -52,7 +67,8 @@ public class Game extends Engine
     @Override
     public void tick()
     {        
-        CLOCK.start();
+        w.tick();
+        /*CLOCK.start();
         entities.forEach(ent -> ent.tick());
         CLOCK.stop();
         CLOCK.calculate();
@@ -66,7 +82,7 @@ public class Game extends Engine
         {
             toogleTps = !toogleTps;
             setRenderTps(toogleTps);
-        }
+        }*/
     }
     
     private static final Clock CLOCK = new Clock(250);
@@ -74,13 +90,16 @@ public class Game extends Engine
     @Override
     public void renderTick(float lag)
     {
-        Shader.setTextureEnabled(false);
+        w.renderTick(lag);
+        /*Shader.setTextureEnabled(false);
         Shader.setColorEnabled(true);
         Shader.getColorRenderer().drawRectangle(0, 299, 400, 300, 0xFFFFFFFF);
-        entities.forEach(ent -> ent.renderTick(lag));
+        entities.forEach(ent -> ent.renderTick(lag));*/
         
         Shader.setTextureEnabled(true);
         Shader.setColorEnabled(true);
+        Shader.translateTo(0.0f, 0.0f);
+        Shader.updateMatrix();
         float y = 5;
         y = Shader.getFontRenderer().drawString(5, y, true, "FPS " + String.format("%.1f", getFps()));
         y = Shader.getFontRenderer().drawString(5, y, true, "TPS " + String.format("%.1f", getTps()));

+ 132 - 0
src/me/hammerle/snuviengine/game/World.java

@@ -0,0 +1,132 @@
+package me.hammerle.snuviengine.game;
+
+import java.io.IOException;
+import me.hammerle.snuviengine.api.Chunk;
+import me.hammerle.snuviengine.api.Shader;
+import me.hammerle.snuviengine.api.Texture;
+import me.hammerle.snuviengine.util.WrappedInputStream;
+import me.hammerle.snuviengine.util.WrappedOutputStream;
+
+public class World
+{
+    private int width;
+    private int height;
+    private Chunk[][] chunks;
+    
+    private final Texture tiles = new Texture("images/tileset-blackvolution.png");
+    
+    private final Hero h;
+    
+    private float cameraX = 0.0f;
+    private float cameraY = 0.0f;
+    private float lastCameraX = 0.0f;
+    private float lastCameraY = 0.0f;
+    
+    public World(Hero h)
+    {
+        this.h = h;
+    }
+    
+    public void read(WrappedInputStream in) throws IOException
+    {
+        width = in.readInt();
+        height = in.readInt();
+        if((width % Chunk.CHUNK_SIZE) != 0 || (height % Chunk.CHUNK_SIZE) != 0)
+        {
+            throw new IllegalArgumentException("world width and height must be a multiply of " + Chunk.CHUNK_SIZE);
+        }
+        chunks = new Chunk[width / Chunk.CHUNK_SIZE][height / Chunk.CHUNK_SIZE];
+        for(int x = 0; x < chunks.length; x++)
+        {
+            for(int y = 0; y < chunks[x].length; y++)
+            {
+                chunks[x][y] = new Chunk(x, y);
+                chunks[x][y].read(in);
+            }
+        }
+    }
+    
+    public void write(WrappedOutputStream out) throws IOException
+    {
+        out.writeInt(width);
+        out.writeInt(height);
+        for(Chunk[] chunk : chunks)
+        {
+            for(Chunk c : chunk)
+            {
+                c.write(out);
+            }
+        }
+    }
+    
+    public void tick()
+    {
+        h.tick();
+        lastCameraX = cameraX;
+        lastCameraY = cameraY;
+        cameraX = -getViewX((float) h.xPos);
+        cameraY = -getViewY((float) h.yPos);
+    }
+    
+    public void renderTick(float lag)
+    {
+        Shader.translateTo(lastCameraX + (cameraX - lastCameraX) * lag, lastCameraY + (cameraY - lastCameraY) * lag);
+        
+        Shader.setColorEnabled(false);
+        Shader.setTextureEnabled(true);
+        tiles.bindTexture();
+        for(Chunk[] chunk : chunks)
+        {
+            for(Chunk c : chunk)
+            {
+                c.drawBackground();
+            }
+        }
+        
+        Shader.updateMatrix();
+        Shader.setColorEnabled(true);
+        Shader.setTextureEnabled(false);
+        h.renderTick(lag);
+        
+        Shader.setColorEnabled(false);
+        Shader.setTextureEnabled(true);
+        tiles.bindTexture();
+        for(Chunk[] chunk : chunks)
+        {
+            for(Chunk c : chunk)
+            {
+                c.drawForeground();
+            }
+        }
+    }
+    
+    private float getViewX(float x) 
+    {
+        x -= Shader.getViewWidth() >> 1;
+        if(x < 0)
+        {
+            return 0;
+        }
+        float max = width * Chunk.TILE_SIZE - Shader.getViewWidth();
+        if(x > max)
+        {
+            return max;
+        }
+        return x;
+    }
+    
+    private float getViewY(float y) 
+    {
+        y -= Shader.getViewHeight() >> 1;
+        if(y < 0)
+        {
+            return 0;
+        }
+        float max = height * Chunk.TILE_SIZE - Shader.getViewHeight();
+        if(y > max)
+        {
+            return max;
+        }
+        return y;
+    }
+}

+ 60 - 0
src/me/hammerle/snuviengine/util/WrappedInputStream.java

@@ -0,0 +1,60 @@
+package me.hammerle.snuviengine.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class WrappedInputStream
+{
+    private final InputStream in;
+    
+    public WrappedInputStream(InputStream in)
+    {
+        this.in = in;
+    }
+    
+    private int read() throws IOException
+    {
+        int i = in.read();
+        if(i == -1)
+        {
+            throw new IOException("end of stream");
+        }
+        return i;
+    }
+    
+    public byte readByte() throws IOException
+    {
+        return (byte) read();
+    }
+    
+    public int readUnsignedByte() throws IOException
+    {
+        return read();
+    }
+    
+    public short readShort() throws IOException
+    {
+        return (short) (read() | (read() << 8));
+    }
+    
+    public int readInt() throws IOException
+    {
+        return read() | (read() << 8) | (read() << 16) | (read() << 24);
+    }
+    
+    public long readLong() throws IOException
+    {
+        return ((long) read()) | (((long) read()) << 8) | (((long) read()) << 16) | (((long) read()) << 24) | 
+                (((long) read()) << 32) | (((long) read()) << 40) | (((long) read()) << 48) | (((long) read()) << 56);
+    }
+    
+    public String readString() throws IOException
+    {
+        byte[] b = new byte[readInt()];
+        for(int i = 0; i < b.length; i++)
+        {
+            b[i] = (byte) read();
+        }
+        return new String(b);
+    }
+}

+ 57 - 0
src/me/hammerle/snuviengine/util/WrappedOutputStream.java

@@ -0,0 +1,57 @@
+package me.hammerle.snuviengine.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class WrappedOutputStream
+{
+    private final OutputStream out;
+    
+    public WrappedOutputStream(OutputStream out)
+    {
+        this.out = out;
+    }
+    
+    public void writeByte(int b) throws IOException
+    {
+        out.write(b);
+    }
+    
+    public void writeUnsignedByte(int b) throws IOException
+    {
+        out.write(b);
+    }
+    
+    public void writeShort(int s) throws IOException
+    {
+        out.write(s & 0xFF);
+        out.write((s >> 8) & 0xFF);
+    }
+    
+    public void writeInt(int i) throws IOException
+    {
+        out.write(i & 0xFF);
+        out.write((i >> 8) & 0xFF);
+        out.write((i >> 16) & 0xFF);
+        out.write((i >> 24) & 0xFF);
+    }
+    
+    public void writeLong(long l) throws IOException
+    {
+        out.write((int) (l & 0xFF));
+        out.write((int) ((l >> 8) & 0xFF));
+        out.write((int) ((l >> 16) & 0xFF));
+        out.write((int) ((l >> 24) & 0xFF));
+        out.write((int) ((l >> 32) & 0xFF));
+        out.write((int) ((l >> 40) & 0xFF));
+        out.write((int) ((l >> 48) & 0xFF));
+        out.write((int) ((l >> 56) & 0xFF));
+    }
+    
+    public void writeString(String s) throws IOException
+    {
+        byte[] b = s.getBytes();
+        writeInt(b.length);
+        out.write(b);
+    }
+}

BIN=BIN
testworld