commit f49f7b61e8359f5c2e82df68866ed5cd9adb8eb0 Author: jsrobson10 Date: Tue Sep 22 14:19:27 2020 +1000 Initial commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..43bae09 --- /dev/null +++ b/.classpath @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba077a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bin diff --git a/.project b/.project new file mode 100644 index 0000000..3db1100 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + AntSim + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/src/antsim/display/DisplayCamera.java b/src/antsim/display/DisplayCamera.java new file mode 100644 index 0000000..0c8dc46 --- /dev/null +++ b/src/antsim/display/DisplayCamera.java @@ -0,0 +1,11 @@ +package antsim.display; + +import gl_engine.vec.Vec2d; + +public class DisplayCamera +{ + public static DisplayCamera CAMERA = new DisplayCamera(); + + public double zoom = 0.05; + public Vec2d pos = new Vec2d(0, 0); +} diff --git a/src/antsim/display/DisplayWindow.java b/src/antsim/display/DisplayWindow.java new file mode 100644 index 0000000..c889976 --- /dev/null +++ b/src/antsim/display/DisplayWindow.java @@ -0,0 +1,150 @@ +package antsim.display; + +import java.nio.IntBuffer; + +import org.lwjgl.BufferUtils; +import org.lwjgl.glfw.GLFW; +import org.lwjgl.opengl.GL33; + +import antsim.entity.Entity; +import antsim.init.Models; +import antsim.model.Model; +import antsim.start.Start; +import antsim.world.World; +import gl_engine.graphics.GraphicsHelpers; +import gl_engine.graphics.GraphicsShader; +import gl_engine.matrix.Matrix4; +import gl_engine.texture.TextureRef3D; +import gl_engine.vec.Vec2d; +import gl_engine.vec.Vec3d; +import mainloop.task.IMainloopTask; + +public class DisplayWindow implements IMainloopTask +{ + public static final DisplayWindow WINDOW = new DisplayWindow(); + + private long window; + private long monitor; + private int width; + private int height; + + public int fps; + public GraphicsShader renderer; + public GraphicsShader background; + + public int glsl_matrix; + public int glsl_projection; + + public int glsl_bg_scale; + public int glsl_bg_translate; + + private boolean hasDoneSetup = false; + + public void init() + { + // Initialize GLFW + if(!GLFW.glfwInit()) { + throw new RuntimeException("Failed to initialize GLFW"); + } + + // Get the monitor size + IntBuffer w = BufferUtils.createIntBuffer(1); + IntBuffer h = BufferUtils.createIntBuffer(1); + monitor = GLFW.glfwGetPrimaryMonitor(); + GLFW.glfwGetMonitorPhysicalSize(monitor, w, h); + width = w.get()*4; + height = h.get()*4; + + //GLFW.glfwWindowHint(GLFW.GLFW_DOUBLEBUFFER, GLFW.GLFW_FALSE); + + // Create the window + window = GraphicsHelpers.initWindow("Ant Sim", width, height, 0); + + renderer = new GraphicsShader("/resources/shader/renderer"); + renderer.use(); + + glsl_matrix = GL33.glGetUniformLocation(renderer.program, "matrix"); + glsl_projection = GL33.glGetUniformLocation(renderer.program, "projection"); + + background = new GraphicsShader("/resources/shader/background"); + background.use(); + + glsl_bg_scale = GL33.glGetUniformLocation(background.program, "scale"); + glsl_bg_translate = GL33.glGetUniformLocation(background.program, "translate"); + + // Make the context current + GLFW.glfwMakeContextCurrent(this.window); + } + + @Override + public boolean MainLoopDelay(long millis) { + return millis > Start.mspf; + } + + @Override + public boolean MainLoopRepeat() { + return true; + } + + @Override + public void MainLoopUpdate() + { + int w[] = {0}; + int h[] = {0}; + GLFW.glfwGetFramebufferSize(this.window, w, h); + width = w[0]; + height = h[0]; + + GL33.glBindFramebuffer(GL33.GL_FRAMEBUFFER, 0); + GL33.glClear(GL33.GL_DEPTH_BUFFER_BIT | GL33.GL_COLOR_BUFFER_BIT); + GL33.glViewport(0, 0, width, height); + + background.use(); + + if(!hasDoneSetup) + { + hasDoneSetup = true; + + int glsl_grass_tex = GL33.glGetUniformLocation(background.program, "tex"); + TextureRef3D grass_tex = Models.GRASS.getTextures()[0]; + + GL33.glUniform4f(glsl_grass_tex, grass_tex.sx, grass_tex.sy, grass_tex.ex, grass_tex.ey); + } + + double camera_zoom = DisplayCamera.CAMERA.zoom; + Vec2d camera_pos = DisplayCamera.CAMERA.pos; + + GL33.glUniform2f(glsl_bg_scale, (float)camera_zoom * (float)height / (float)width, (float)camera_zoom); + GL33.glUniform2f(glsl_bg_translate, (float)camera_pos.x, (float)camera_pos.y); + + Models.GRASS.setModel(Matrix4.identity()); + Models.GRASS.render(); + + renderer.use(); + + + + GL33.glUniformMatrix4fv(glsl_projection, true, Matrix4.multiply( + Matrix4.translate(camera_pos.x, camera_pos.y, 0), + Matrix4.scale(new Vec3d(camera_zoom * (double)height / (double)width, camera_zoom, 1))).getArray()); + + for(Entity e : World.WORLD.entities) + { + Model model = e.getModel(); + model.setModel(Matrix4.multiply(e.getMatrix(), Matrix4.translate(e.pos.x, e.pos.y, 0))); + model.render(); + } + + GLFW.glfwSwapBuffers(window); + + fps += 1; + } + + public void pollEvents() { + GLFW.glfwPollEvents(); + } + + public boolean shouldClose() { + return GLFW.glfwWindowShouldClose(window); + } +} diff --git a/src/antsim/entity/Entity.java b/src/antsim/entity/Entity.java new file mode 100644 index 0000000..d1558cc --- /dev/null +++ b/src/antsim/entity/Entity.java @@ -0,0 +1,126 @@ +package antsim.entity; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import antsim.init.Entities; +import antsim.model.Model; +import antsim.start.Start; +import antsim.start.StartServer; +import antsim.util.ClassBdf; +import antsim.world.World; +import bdf.types.BdfNamedList; +import bdf.types.BdfObject; +import gl_engine.matrix.Matrix4; +import gl_engine.vec.Vec2d; + +public abstract class Entity implements ClassBdf +{ + public Vec2d pos = new Vec2d(0, 0); + public Vec2d velocity = new Vec2d(0, 0); + private boolean isDead; + private boolean dirty; + + public int getID() { + for(int i=0;i ec = Entities.ENTITIES.get(i); + if(ec == this.getClass()) { + return i; + } + } + return -1; + } + + public static Entity loadEntity(BdfObject bdf) + { + try + { + // Load the entity id + BdfNamedList nl = bdf.getNamedList(); + int id = (int)nl.get("id").getAutoInt(); + + // Send back null if the id is out of range + if(id < 0 || id >= Entities.ENTITIES.size()) { + System.out.println("Warning: Invalid ID detected: " + id); + return null; + } + + // Get the class and the constructor + Class ecl = Entities.ENTITIES.get(id); + Constructor econ = ecl.getConstructor(BdfObject.class); + + // Send back the new entity + return econ.newInstance(bdf); + } + + catch( + NoSuchMethodException | + InstantiationException | + IllegalAccessException | + IllegalArgumentException | + InvocationTargetException e) + { + // Send null if there was an issue + e.printStackTrace(); + return null; + } + } + + public abstract Model getModel(); + + public boolean isDirty() { + return dirty; + } + + protected void markDirty() { + dirty = true; + } + + public Matrix4 getMatrix() { + return Matrix4.identity(); + } + + public void push(Vec2d amount) { + velocity = velocity.add(amount); + } + + public void update() + { + pos = pos.add(velocity); + velocity = velocity.multiply(0.8); + } + + @Override + public void loadBDF(BdfObject bdf) + { + BdfNamedList nl = bdf.getNamedList(); + pos = new Vec2d(nl.get("pos")); + velocity = new Vec2d(nl.get("velocity")); + } + + @Override + public void saveBDF(BdfObject bdf) + { + BdfNamedList nl = bdf.getNamedList(); + pos.BdfClassSave(nl.get("pos")); + velocity.BdfClassSave(nl.get("velocity")); + nl.set("id", bdf.newObject().setAutoInt(getID())); + } + + public void kill() + { + isDead = true; + + if(Start.isServer) { + StartServer.markNeedToUpdate(); + } + } + + public boolean isDead() { + return isDead; + } + + public void notDead() { + isDead = false; + } +} diff --git a/src/antsim/entity/EntityAnt.java b/src/antsim/entity/EntityAnt.java new file mode 100644 index 0000000..b627a50 --- /dev/null +++ b/src/antsim/entity/EntityAnt.java @@ -0,0 +1,390 @@ +package antsim.entity; + +import java.util.Random; + +import antsim.init.Entities; +import antsim.init.Models; +import antsim.model.Model; +import antsim.nest.Nest; +import antsim.start.Start; +import antsim.start.StartServer; +import antsim.world.World; +import bdf.types.BdfNamedList; +import bdf.types.BdfObject; +import gl_engine.matrix.Matrix4; +import gl_engine.vec.Vec2d; + +public class EntityAnt extends Entity implements EntityEdible +{ + private static final Random RANDOM = new Random(); + + private Random random; + private long seed; + public Nest nest; + private Vec2d target; + private double angle; + private EntityEdible food_target; + private int hasFood = 0; + private double speed; + private double hunger = 1; + private boolean antDead; + private int pregnant = -2; + private boolean fighting; + + public EntityAnt(Nest nest, Vec2d pos) + { + this.nest = nest; + this.pos = pos; + this.target = pos; + this.speed = Math.random() * 0.1 + 0.9; + + seed = RANDOM.nextLong(); + random = new Random(seed); + } + + public EntityAnt(BdfObject bdf) { + loadBDF(bdf, true); + } + + public EntityAnt(BdfObject bdf, boolean dontLoadNest) { + loadBDF(bdf, dontLoadNest); + } + + @Override + public void saveBDF(BdfObject bdf) { + super.saveBDF(bdf); + + BdfNamedList nl = bdf.getNamedList(); + nl.set("seed", bdf.newObject().setLong(seed)); + nl.set("angle", bdf.newObject().setDouble(angle)); + nl.set("hasFood", bdf.newObject().setAutoInt(hasFood)); + nl.set("hunger", bdf.newObject().setDouble(hunger)); + nl.set("speed", bdf.newObject().setDouble(speed)); + nl.set("pregnant", bdf.newObject().setAutoInt(pregnant)); + nl.set("nest", bdf.newObject().setAutoInt(nest.id)); + nl.set("fighting", bdf.newObject().setBoolean(fighting)); + nl.set("antDead", bdf.newObject().setBoolean(antDead)); + + target.BdfClassSave(nl.get("target")); + } + + @Override + public void loadBDF(BdfObject bdf) { + loadBDF(bdf, true); + } + + public void loadBDF(BdfObject bdf, boolean loadNest) { + super.loadBDF(bdf); + + BdfNamedList nl = bdf.getNamedList(); + seed = nl.get("seed").getLong(); + angle = nl.get("angle").getDouble(); + hasFood = (int)nl.get("hasFood").getAutoInt(); + hunger = nl.get("hunger").getDouble(); + speed = nl.get("speed").getDouble(); + pregnant = (int)nl.get("pregnant").getAutoInt(); + fighting = nl.get("fighting").getBoolean(); + antDead = nl.get("antDead").getBoolean(); + random = new Random(seed); + + target = new Vec2d(nl.get("target")); + + if(loadNest) { + nest = World.WORLD.nests.get((int)nl.get("nest").getAutoInt()); + } + } + + @Override + public Model getModel() + { + switch(hasFood) + { + case 1: return Models.ANT_FOOD; + case 2: return Models.ANT_EAT_ANT; + } + + return Models.ANT; + } + + @Override + public Matrix4 getMatrix() { + return Matrix4.rotate(angle, 0, 0, 1); + } + + @Override + public void update() + { + super.update(); + + if(antDead) { + return; + } + + hunger -= 0.0015; + + if(hunger < 0.125) + { + if(hasFood > 0) + { + hasFood = 0; + hunger += 0.25; + target = pos; + } + } + + if(hunger <= 0) { + antDead = true; + } + + Vec2d toTarget = target.subtract(pos); + double distanceToTarget = toTarget.length(); + + if(pregnant == -1) + { + for(Entity e : World.WORLD.entities) + { + if( + e instanceof EntityAnt && ((EntityAnt) e).nest == nest && + e != this && e.pos.squareDistance(pos) < 0.05) + { + pregnant = 20; + ((EntityAnt)e).pregnant = -2; + distanceToTarget = 0; + break; + } + } + } + + else if(pregnant > 0) { + pregnant -= 1; + } + + else if(pregnant == 0) + { + if(Start.isServer) { + World.WORLD.entities.add(new EntityAnt(nest, pos.copy())); + StartServer.markNeedToUpdate(); + } + + hunger -= 0.25; + pregnant = -2; + } + + if(distanceToTarget < 0.5) + { + for(Entity e : World.WORLD.entities) + { + if(e instanceof EntityNestEntrance && ((EntityNestEntrance) e).nest == nest) + { + EntityNestEntrance entrance = (EntityNestEntrance)e; + + if(entrance.nest == nest && entrance.pos.squareDistance(pos) < 1) + { + hasFood = 0; + nest.addFood(); + nest.ants.add(this); + kill(); + + return; + } + } + + if(fighting && e instanceof EntityAnt) + { + EntityAnt ea = (EntityAnt)e; + + if(ea.fighting && ea.nest != nest && !ea.antDead && ea.hunger < hunger) + { + ea.antDead = true; + hunger -= 0.25; + } + } + } + + if(pregnant == -1) { + pregnant = -2; + } + + if(food_target != null && !food_target.isDead() && food_target.onEaten()) + { + if(food_target instanceof EntityAnt) { + hasFood = 2; + } + + else { + hasFood = 1; + } + } + + food_target = null; + + double targetType = random.nextDouble(); + + if(targetType > 0.6 && hunger > 0.5 && pregnant == -2) + { + EntityAnt closest = null; + double distance = 50; + boolean fight = false; + + for(Entity e : World.WORLD.entities) + { + if(e instanceof EntityAnt && e != this) + { + EntityAnt ea = (EntityAnt)e; + double d = e.pos.squareDistance(pos) * (ea.nest != nest ? 0.25 : 1); + + if(d < distance && !ea.antDead && ea.hunger > 0.25 && ea.pregnant == -2) { + distance = d; + closest = (EntityAnt)e; + fight = ea.nest != nest; + } + } + } + + if(closest == null) { + return; + } + + if(fight) { + fighting = true; + closest.fighting = true; + } + + else { + pregnant = -1; + closest.pregnant = -1; + } + + target = closest.pos.copy(); + closest.target = pos.copy(); + + return; + } + + if(targetType > 0.6 && hasFood == 0) + { + // Get the closest food item + + EntityEdible closest = null; + double distance = 1000; + + for(Entity e : World.WORLD.entities) + { + if(!(e instanceof EntityEdible)) { + continue; + } + + EntityEdible ee = (EntityEdible)e; + + if(!ee.isEdible(this)) { + continue; + } + + double d = e.pos.squareDistance(pos) / ee.getWeight(); + + if(d < distance) + { + distance = d; + closest = ee; + } + } + + if(closest != null) { + target = closest.getPos().copy(); + food_target = closest; + } + + else { + target = pos; + } + } + + else if((targetType > 0.6 && hasFood > 0) || (hasFood == 0 && targetType < 1 - hunger)) + { + // Find the nearest nest + + EntityNestEntrance entrance = null; + double distance = 0; + + for(Entity e : World.WORLD.entities) + { + if(e instanceof EntityNestEntrance && ((EntityNestEntrance) e).nest == nest) + { + double d = e.pos.squareDistance(pos); + + if(d < distance || entrance == null) + { + entrance = (EntityNestEntrance)e; + distance = d; + } + } + } + + if(entrance != null) { + target = entrance.pos.copy(); + } + } + + else { + target = pos.add(new Vec2d(random.nextDouble() * 20 - 10, random.nextDouble() * 20 - 10)); + } + + toTarget = target.subtract(pos); + distanceToTarget = toTarget.length(); + + angle = Math.toDegrees(Math.atan2(toTarget.x, toTarget.y)); + + seed = random.nextLong(); + random = new Random(seed); + } + + push(toTarget.normalize().multiply(0.05 * speed)); + } + + public void exitNest(Vec2d pos) + { + if(Start.isServer) + { + this.pos = pos; + + notDead(); + + target = pos.add(new Vec2d(random.nextDouble() * 20 - 10, random.nextDouble() * 20 - 10)); + + Vec2d toTarget = target.subtract(pos); + + angle = Math.toDegrees(Math.atan2(toTarget.x, toTarget.y)); + + while(hunger < 0.75 && nest.takeFood()) { + hunger += 0.25; + } + + seed = random.nextLong(); + random = new Random(seed); + } + } + + @Override + public boolean isEdible(Entity entity) { + return antDead; + } + + @Override + public Vec2d getPos() { + return pos; + } + + @Override + public double getWeight() { + return 2; + } + + @Override + public boolean onEaten() + { + kill(); + + return true; + } + +} diff --git a/src/antsim/entity/EntityEdible.java b/src/antsim/entity/EntityEdible.java new file mode 100644 index 0000000..ed8f440 --- /dev/null +++ b/src/antsim/entity/EntityEdible.java @@ -0,0 +1,12 @@ +package antsim.entity; + +import gl_engine.vec.Vec2d; + +public interface EntityEdible +{ + public boolean isEdible(Entity entity); + public boolean isDead(); + public Vec2d getPos(); + public double getWeight(); + public boolean onEaten(); +} diff --git a/src/antsim/entity/EntityFood.java b/src/antsim/entity/EntityFood.java new file mode 100644 index 0000000..c428113 --- /dev/null +++ b/src/antsim/entity/EntityFood.java @@ -0,0 +1,66 @@ +package antsim.entity; + +import antsim.init.Models; +import antsim.model.Model; +import antsim.start.Start; +import bdf.types.BdfObject; +import gl_engine.vec.Vec2d; + +public class EntityFood extends Entity implements EntityEdible +{ + private int age; + + public EntityFood(Vec2d pos) { + this.pos = pos; + this.age = 6000; // Stay for a minute + } + + public EntityFood(BdfObject bdf) { + loadBDF(bdf); + } + + @Override + public Model getModel() { + return Models.FOOD; + } + + @Override + public boolean isEdible(Entity entity) { + return true; + } + + @Override + public Vec2d getPos() { + return pos; + } + + @Override + public void update() + { + super.update(); + + if(!Start.isServer) { + return; + } + + age -= 1; + + if(age <= 0) { + kill(); + } + } + + @Override + public double getWeight() { + return 1; + } + + @Override + public boolean onEaten() + { + kill(); + + return true; + } + +} diff --git a/src/antsim/entity/EntityNestEntrance.java b/src/antsim/entity/EntityNestEntrance.java new file mode 100644 index 0000000..bf4f776 --- /dev/null +++ b/src/antsim/entity/EntityNestEntrance.java @@ -0,0 +1,96 @@ +package antsim.entity; + +import java.util.ArrayList; + +import antsim.init.Models; +import antsim.model.Model; +import antsim.nest.Nest; +import antsim.start.Start; +import antsim.start.StartServer; +import antsim.world.World; +import bdf.types.BdfArray; +import bdf.types.BdfNamedList; +import bdf.types.BdfObject; +import gl_engine.vec.Vec2d; + +public class EntityNestEntrance extends Entity implements EntityEdible +{ + private int cooldown; + public Nest nest; + + public EntityNestEntrance(BdfObject bdf) { + loadBDF(bdf); + } + + public EntityNestEntrance(Nest nest, Vec2d pos) { + this.nest = nest; + this.pos = pos; + } + + @Override + public void loadBDF(BdfObject bdf) { + super.loadBDF(bdf); + + BdfNamedList nl = bdf.getNamedList(); + + nest = World.WORLD.nests.get(nl.get("nest").getInteger()); + } + + @Override + public void saveBDF(BdfObject bdf) { + super.saveBDF(bdf); + + BdfNamedList nl = bdf.getNamedList(); + + nl.set("nest", bdf.newObject().setInteger(nest.id)); + } + + @Override + public void update() + { + super.update(); + + if(Start.isServer && nest.ants.size() > 0) + { + cooldown -= 1; + + if(cooldown <= 0) + { + EntityAnt ant = nest.ants.remove(0); + World.WORLD.entities.add(ant); + ant.nest = nest; + ant.exitNest(pos); + + StartServer.markNeedToUpdate(); + + cooldown = 50; + } + } + } + + @Override + public Model getModel() { + return Models.NEST; + } + + @Override + public Vec2d getPos() { + return pos; + } + + @Override + public double getWeight() { + return 4; + } + + @Override + public boolean isEdible(Entity entity) { + return entity instanceof EntityAnt && ((EntityAnt)entity).nest == nest && nest.getFood() > 0; + } + + @Override + public boolean onEaten() { + return nest.takeFood(); + } + +} diff --git a/src/antsim/init/Entities.java b/src/antsim/init/Entities.java new file mode 100644 index 0000000..828551b --- /dev/null +++ b/src/antsim/init/Entities.java @@ -0,0 +1,33 @@ +package antsim.init; + +import java.util.ArrayList; + +import antsim.entity.Entity; +import antsim.entity.EntityAnt; +import antsim.entity.EntityFood; +import antsim.entity.EntityNestEntrance; +import bdf.types.BdfObject; + +public class Entities +{ +public static final ArrayList> ENTITIES = new ArrayList<>(); + + private static void register(Class e) + { + try { + e.getConstructor(BdfObject.class); + } catch (NoSuchMethodException | SecurityException err) { + err.printStackTrace(); + System.exit(1); + } + + ENTITIES.add(e); + } + + public static void init() + { + register(EntityAnt.class); + register(EntityFood.class); + register(EntityNestEntrance.class); + } +} diff --git a/src/antsim/init/Models.java b/src/antsim/init/Models.java new file mode 100644 index 0000000..857744e --- /dev/null +++ b/src/antsim/init/Models.java @@ -0,0 +1,15 @@ +package antsim.init; + +import antsim.model.Model; +import antsim.model.ModelFlat; +import gl_engine.vec.Vec2d; + +public class Models +{ + public static final Model ANT = new ModelFlat(Resources.ATLAS.get("/ant.png")); + public static final Model ANT_FOOD = new ModelFlat(Resources.ATLAS.get("/ant_food.png")); + public static final Model ANT_EAT_ANT = new ModelFlat(Resources.ATLAS.get("/ant_eat_ant.png")); + public static final Model GRASS = new ModelFlat(Resources.ATLAS.get("/grass.png"), new Vec2d(2, 2)); + public static final Model FOOD = new ModelFlat(Resources.ATLAS.get("/food.png")); + public static final Model NEST = new ModelFlat(Resources.ATLAS.get("/nest.png")); +} diff --git a/src/antsim/init/Resources.java b/src/antsim/init/Resources.java new file mode 100644 index 0000000..d9c5385 --- /dev/null +++ b/src/antsim/init/Resources.java @@ -0,0 +1,18 @@ +package antsim.init; + +import gl_engine.texture.TextureAtlas3D; +import gl_engine.texture.TextureRef3D; + +public class Resources +{ + public static TextureAtlas3D ATLAS; + public static TextureRef3D TEX_EMPTY; + + public static void init() + { + ATLAS = TextureAtlas3D.loadAll("/resources/texture/list.txt"); + ATLAS.generate(); + + TEX_EMPTY = ATLAS.get(""); + } +} diff --git a/src/antsim/model/IModel.java b/src/antsim/model/IModel.java new file mode 100644 index 0000000..fa1fda9 --- /dev/null +++ b/src/antsim/model/IModel.java @@ -0,0 +1,22 @@ +package antsim.model; + +import gl_engine.matrix.Matrix4; +import gl_engine.texture.TextureRef3D; + +public interface IModel +{ + public int[] getIndicies(); + public float[] getVerticies(); + public TextureRef3D[] getTextures(); + public TextureRef3D getRandomChunk(int[] tex_id); + public double getWidth(); + public double getHeight(); + public int getIndexSize(); + public int getSize(); + + public void bind(); + public void render(); + public void free(); + public void setModel(Matrix4 model); + public boolean isLoaded(); +} diff --git a/src/antsim/model/Model.java b/src/antsim/model/Model.java new file mode 100644 index 0000000..92e5304 --- /dev/null +++ b/src/antsim/model/Model.java @@ -0,0 +1,160 @@ +package antsim.model; + +import java.util.Random; + +import org.lwjgl.opengl.GL33; + +import antsim.display.DisplayWindow; +import antsim.init.Resources; +import gl_engine.MathHelpers; +import gl_engine.matrix.Matrix4; +import gl_engine.texture.TextureRef3D; + +public abstract class Model implements IModel +{ + private static final Random rand = new Random(); + + public static final float OFFSET = 0.00001f; + public static final int SIZE = 14; + public static IModel bound = null; + + int vao, vbo, ibo; + boolean loaded = false; + + public static void setGLArrayAttributes() + { + // aPos + GL33.glVertexAttribPointer(0, 2, GL33.GL_FLOAT, false, Float.BYTES * SIZE, 0); + GL33.glEnableVertexAttribArray(0); + + // aTex + GL33.glVertexAttribPointer(1, 3, GL33.GL_FLOAT, false, Float.BYTES * SIZE, Float.BYTES * 2); + GL33.glEnableVertexAttribArray(1); + + // aTexY + GL33.glVertexAttribPointer(2, 2, GL33.GL_FLOAT, false, Float.BYTES * SIZE, Float.BYTES * 5); + GL33.glEnableVertexAttribArray(2); + + // aOffset + GL33.glVertexAttribPointer(3, 2, GL33.GL_FLOAT, false, Float.BYTES * SIZE, Float.BYTES * 7); + GL33.glEnableVertexAttribArray(3); + + // aAnimate + GL33.glVertexAttribPointer(4, 2, GL33.GL_FLOAT, false, Float.BYTES * SIZE, Float.BYTES * 9); + GL33.glEnableVertexAttribArray(4); + + // aFlags + GL33.glVertexAttribPointer(5, 3, GL33.GL_FLOAT, false, Float.BYTES * SIZE, Float.BYTES * 11); + GL33.glEnableVertexAttribArray(5); + } + + protected void generate() + { + if(loaded) { + return; + } + + int[] indicies = this.getIndicies(); + float[] verticies = this.getVerticies(); + TextureRef3D[] refs = this.getTextures(); + + if(verticies.length % SIZE != 0 || refs.length * SIZE != verticies.length) { + System.err.println("Invalid model: " + verticies.length + ", " + refs.length + ", " + indicies.length); + System.exit(1); + return; + } + + int size = verticies.length/SIZE; + double k = OFFSET; + + for(int i=0;i ants; + + public Nest(int id) + { + this.id = id; + + population = 10; + food = 0; + + ants = new ArrayList(population); + + for(int i=0;i(ants_bdf.size()); + food = nl.get("food").getInteger(); + population = nl.get("population").getInteger(); + id = nl.get("id").getInteger(); + + for(BdfObject ant_bdf : ants_bdf) { + ants.add(new EntityAnt(ant_bdf, false)); + } + } + + public void saveBDF(BdfObject bdf) + { + BdfNamedList nl = bdf.getNamedList(); + BdfArray ants_bdf = bdf.newArray(ants.size()); + + nl.set("food", bdf.newObject().setInteger(food)); + nl.set("population", bdf.newObject().setInteger(population)); + nl.set("ants", bdf.newObject().setArray(ants_bdf)); + nl.set("id", bdf.newObject().setInteger(id)); + + for(int i=0;i 0) { + food -= 1; + return true; + } + + else { + return false; + } + } + +} diff --git a/src/antsim/start/Start.java b/src/antsim/start/Start.java new file mode 100644 index 0000000..9192ac1 --- /dev/null +++ b/src/antsim/start/Start.java @@ -0,0 +1,92 @@ +package antsim.start; + +import antsim.entity.Entity; +import antsim.entity.EntityAnt; +import antsim.entity.EntityFood; +import antsim.world.World; +import gl_engine.MathHelpers; +import gl_engine.vec.Vec2d; +import mainloop.event.IMainloopEvent; +import mainloop.manager.MainloopManager; +import mainloop.task.IMainloopTask; + +public class Start implements IMainloopEvent, IMainloopTask +{ + public static final Start START = new Start(); + public static final int DEFAULT_PORT = 14972; + + public static boolean isServer; + public static MainloopManager mainloop; + public static int mspf = 1000 / 60; + + int counter = 0; + + @Override + public void onClose() { + System.out.println("Mainloop closed cleanly"); + } + + @Override + public void onEarly() + { + mspf -= 1; + + if(mspf < 1) { + mspf = 1; + } + } + + @Override + public void onLate() + { + mspf += 1; + + if(mspf > 1000) { + mspf = 1000; + } + } + + @Override + public void onStart() { + System.out.println("Mainloop started"); + } + + @Override + public boolean MainLoopDelay(long millis) { + return millis > 10; + } + + @Override + public boolean MainLoopRepeat() { + return true; + } + + @Override + public void MainLoopUpdate() + { + for(int i=0;i 25) { + counter -= 25; + + World.WORLD.entities.add(new EntityFood(MathHelpers.moveTowards2( + Math.sqrt(Math.random()) * 40, Math.random() * MathHelpers.TWO_PI))); + + StartServer.markNeedToUpdate(); + } + } + } +} diff --git a/src/antsim/start/StartClient.java b/src/antsim/start/StartClient.java new file mode 100644 index 0000000..98cc4ed --- /dev/null +++ b/src/antsim/start/StartClient.java @@ -0,0 +1,110 @@ +package antsim.start; + +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.ArrayList; + +import org.lwjgl.glfw.GLFW; + +import antsim.display.DisplayCamera; +import antsim.display.DisplayWindow; +import antsim.entity.Entity; +import antsim.entity.EntityAnt; +import antsim.init.Entities; +import antsim.init.Resources; +import antsim.worker.WorkerComType; +import antsim.worker.WorkerSocket; +import antsim.world.World; +import bdf.types.BdfArray; +import bdf.types.BdfIndent; +import bdf.types.BdfNamedList; +import bdf.types.BdfObject; +import bdf.types.BdfReader; +import gl_engine.MathHelpers; +import gl_engine.vec.Vec2d; +import mainloop.manager.MainloopManager; +import mainloop.task.IMainloopTask; + +public class StartClient implements IMainloopTask +{ + private static WorkerSocket socket; + + public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException + { + MathHelpers.init(); + DisplayWindow.WINDOW.init(); + Resources.init(); + Entities.init(); + + String ip = "localhost"; + int port = Start.DEFAULT_PORT; + + if(args.length > 0) { + ip = args[0]; + } + + if(args.length > 1) { + port = Integer.parseInt(args[1]); + } + + System.out.println("IP: " + ip + ", Port: " + port); + + Start.isServer = false; + + socket = new WorkerSocket(ip, port, reader -> { + onRecieveData(reader); + }); + + socket.start(); + + Start.mainloop = new MainloopManager(Start.START); + Start.mainloop.register(new StartClient()); + Start.mainloop.register(DisplayWindow.WINDOW); + Start.mainloop.register(Start.START); + + Start.mainloop.start(); + + socket.close(); + } + + private static void onRecieveData(BdfReader reader) + { + BdfObject bdf = reader.getObject(); + BdfNamedList nl = bdf.getNamedList(); + + int type = (int)nl.get("type").getAutoInt(); + + switch(type) + { + case WorkerComType.UPDATE: + World.WORLD.loadBDF(bdf); + break; + } + } + + @Override + public boolean MainLoopDelay(long millis) { + return millis > 10; + } + + @Override + public boolean MainLoopRepeat() { + return true; + } + + @Override + public void MainLoopUpdate() + { + if(DisplayWindow.WINDOW.shouldClose()) { + Start.mainloop.stop(); + return; + } + + DisplayWindow.WINDOW.pollEvents(); + + socket.update(); + } + +} diff --git a/src/antsim/start/StartServer.java b/src/antsim/start/StartServer.java new file mode 100644 index 0000000..44c5a21 --- /dev/null +++ b/src/antsim/start/StartServer.java @@ -0,0 +1,97 @@ +package antsim.start; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; + +import antsim.entity.EntityNestEntrance; +import antsim.init.Entities; +import antsim.nest.Nest; +import antsim.worker.WorkerComType; +import antsim.worker.WorkerServer; +import antsim.worker.WorkerSocket; +import antsim.world.World; +import bdf.types.BdfArray; +import bdf.types.BdfNamedList; +import bdf.types.BdfObject; +import bdf.types.BdfReader; +import gl_engine.MathHelpers; +import gl_engine.vec.Vec2d; +import mainloop.manager.MainloopManager; +import mainloop.task.IMainloopTask; + +public class StartServer implements IMainloopTask +{ + private static WorkerServer server; + private static AtomicBoolean needToUpdate = new AtomicBoolean(); + + public static void main(String[] args) throws IOException, InterruptedException + { + MathHelpers.init(); + Entities.init(); + + Start.isServer = true; + + int port = Start.DEFAULT_PORT; + + if(args.length > 0) { + port = Integer.parseInt(args[0]); + } + + server = new WorkerServer(port); + server.start(); + + Start.mainloop = new MainloopManager(Start.START); + Start.mainloop.register(new StartServer()); + Start.mainloop.register(Start.START); + + for(int i=0;i<2;i++) + { + Nest nest = new Nest(i); + Vec2d area_pos = new Vec2d(Math.random() * 50 - 25, Math.random() * 50 - 25); + + for(int j=0;j<50;j++) + { + World.WORLD.nests.add(nest); + World.WORLD.entities.add(new EntityNestEntrance( + nest, area_pos.add(new Vec2d(Math.random() * 20 - 10, Math.random() * 20 - 10)))); + } + } + + Start.mainloop.start(); + + server.close(); + } + + public static void markNeedToUpdate() { + needToUpdate.set(true); + } + + @Override + public boolean MainLoopDelay(long millis) { + return millis > 10; + } + + @Override + public boolean MainLoopRepeat() { + return true; + } + + @Override + public void MainLoopUpdate() + { + server.update(); + + if(needToUpdate.getAndSet(false)) + { + BdfReader reader = new BdfReader(); + BdfObject bdf = reader.getObject(); + BdfNamedList nl = bdf.getNamedList(); + + nl.set("type", bdf.newObject().setAutoInt(WorkerComType.UPDATE)); + + World.WORLD.saveBDF(bdf); + + server.broadcast(reader); + } + } +} diff --git a/src/antsim/util/AStar.java b/src/antsim/util/AStar.java new file mode 100644 index 0000000..e3e65f1 --- /dev/null +++ b/src/antsim/util/AStar.java @@ -0,0 +1,178 @@ +package antsim.util; +import java.util.ArrayList; +import java.util.List; + +import gl_engine.vec.Vec2i; + +class Node +{ + int x, y; + int g, h, s; + Node parent; + + public Node(Node parent, int x, int y, int g, int h, int s) { + this.parent = parent; + this.x = x; + this.y = y; + this.g = g; + this.h = h; + this.s = s; + } +} + +public class AStar +{ + AStarSearch search; + + Vec2i start; + Vec2i goal; + int radius; + + List open; + List closed; + + Node now; + Node found; + + public AStar(Vec2i start, int radius, AStarSearch search) + { + this.search = search; + this.start = start; + this.radius = radius; + } + + public Vec2i[] getPath(Vec2i goal) + { + // Goal out of range + if(this.posOutOfRange(goal.x, goal.y)) { + return null; + } + + // Start is a wall + if(search.getWeight(start.x, start.y) == 100) { + return null; + } + + this.goal = goal; + this.open = new ArrayList(radius*4); + this.closed = new ArrayList(radius*4); + + now = new Node(null, start.x, start.y, 0, 0, 1); + closed.add(now); + + addNeighboursToList(); + + while(open.size() > 0) + { + // Found the goal + if(found != null) + { + Node n = found; + Vec2i[] path = new Vec2i[n.s]; + + for(int i=0;n!=null;i++) { + path[i] = new Vec2i(n.x, n.y); + n = n.parent; + } + + return path; + } + + int nid = getEasiestNode(); + Node n = open.get(nid); + + open.remove(nid); + closed.add(n); + now = n; + + addNeighboursToList(); + } + + return null; + } + + private boolean neighbourInList(List nodes, int x, int y) { + for(Node n : nodes) { + if(n.x == x && n.y == y) { + return true; + } + } + return false; + } + + private boolean posOutOfRange(int x, int y) { + return ( + x > start.x + radius || + x < start.x - radius || + y > start.y + radius || + y < start.y - radius); + } + + private void addNeighboursToList() + { + int neighbours[] = { + 1, 0, + -1, 0, + 0, 1, + 0, -1 + }; + + for(int i=0;i sockets; + ServerSocket ss; + + public WorkerServer(int port) throws IOException + { + ss = new ServerSocket(port); + sockets = new Vector(); + sockets_reading = new AtomicBoolean(false); + } + + public void update() + { + sockets_reading.set(true); + + // Update the sockets + for(int i=0;i + { + if(socket.isAlive()) + { + try { + socket.send(reader); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } +} diff --git a/src/antsim/worker/WorkerSocket.java b/src/antsim/worker/WorkerSocket.java new file mode 100644 index 0000000..7cdb25c --- /dev/null +++ b/src/antsim/worker/WorkerSocket.java @@ -0,0 +1,137 @@ +package antsim.worker; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.concurrent.atomic.AtomicBoolean; + +import bdf.data.BdfDatabase; +import bdf.types.BdfObject; +import bdf.types.BdfReader; +import bdf.types.BdfTypes; +import bdf.util.DataHelpers; + +public class WorkerSocket extends Thread +{ + private Socket socket; + private InputStream in; + private OutputStream out; + private ArrayList queue; + private AtomicBoolean queue_reading; + private WorkerSocketCallback callback; + + public WorkerSocket(String ip, int port, WorkerSocketCallback callback) throws UnknownHostException, IOException + { + this.socket = new Socket(ip, port); + this.in = socket.getInputStream(); + this.out = socket.getOutputStream(); + this.queue = new ArrayList(); + this.queue_reading = new AtomicBoolean(false); + this.callback = callback; + } + + public WorkerSocket(Socket socket, WorkerSocketCallback callback) throws IOException + { + this.socket = socket; + this.in = socket.getInputStream(); + this.out = socket.getOutputStream(); + this.queue = new ArrayList(); + this.queue_reading = new AtomicBoolean(false); + this.callback = callback; + } + + public void update() + { + BdfReader next = this.next(); + + if(next == null) { + return; + } + + callback.onRecieve(next); + } + + public void send(BdfReader reader) throws IOException + { + BdfDatabase db = reader.serialize(); + + out.write(ByteBuffer.allocate(4).putInt(db.size()).array()); + db.writeToStream(out); + } + + public BdfReader next() + { + queue_reading.set(true); + + if(queue.size() == 0) + { + queue_reading.set(false); + + return null; + } + + BdfReader reader = queue.get(0); + + queue.remove(0); + + queue_reading.set(false); + + return reader; + } + + @Override + public void run() + { + try + { + while(!socket.isClosed()) + { + int size; + int buffer_size = 0; + byte[] buffer_size_bytes = new byte[4]; + + in.read(buffer_size_bytes); + buffer_size = ByteBuffer.wrap(buffer_size_bytes).getInt(0); + + BdfDatabase database = new BdfDatabase(buffer_size); + + for(int i=0;i entities = new ArrayList(); + public ArrayList nests = new ArrayList(); + + public void loadBDF(BdfObject bdf) + { + BdfNamedList nl = bdf.getNamedList(); + + BdfArray nests_bdf = nl.get("nests").getArray(); + BdfArray entities_bdf = nl.get("entities").getArray(); + + entities = new ArrayList(entities_bdf.size()); + nests = new ArrayList(nests_bdf.size()); + + for(BdfObject nest_bdf : nests_bdf) { + nests.add(new Nest(nest_bdf)); + } + + for(BdfObject entity_bdf : entities_bdf) { + entities.add(Entity.loadEntity(entity_bdf)); + } + } + + public void saveBDF(BdfObject bdf) + { + BdfNamedList nl = bdf.getNamedList(); + + BdfArray entities_bdf = bdf.newArray(entities.size()); + BdfArray nests_bdf = bdf.newArray(nests.size()); + + for(int i=0;i