Merge :game into :engine module

This commit is contained in:
2021-03-03 12:49:33 +01:00
parent 6d24d2600e
commit 6d5c9388f7
103 changed files with 406 additions and 457 deletions

View File

@@ -7,11 +7,15 @@
*/
plugins {
id 'java-library'
id 'java'
id 'application'
id 'org.springframework.boot' version "$springBootVersion"
id 'io.spring.dependency-management' version "$springDependencyManagementVersion"
}
group 'com.bartlomiejpluta.base'
version 'unspecified'
import org.gradle.internal.os.OperatingSystem
switch (OperatingSystem.current()) {
@@ -42,18 +46,20 @@ repositories {
}
bootJar {
enabled = false
}
jar {
enabled = true
}
jar {
enabled = false
}
dependencies {
api platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
implementation project(":proto")
implementation project(":api")
// LWJGL
api "org.lwjgl:lwjgl-glfw"
implementation platform("org.lwjgl:lwjgl-bom:$lwjglVersion")
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-bgfx"
@@ -75,10 +81,15 @@ dependencies {
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
// Spring
api 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// This dependency is used by the application.
api "org.joml:joml:${jomlVersion}"
implementation "org.joml:joml:${jomlVersion}"
}
application {
// Define the main class for the application.
mainClass = 'com.bartlomiejpluta.base.engine.App'
}

View File

@@ -1,5 +0,0 @@
package com.bartlomiejpluta.base.core.engine;
public interface GameEngine {
void start();
}

View File

@@ -1,9 +0,0 @@
package com.bartlomiejpluta.base.core.gl.render;
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.camera.Camera;
public interface Renderable {
void render(Window window, Camera camera, ShaderManager shaderManager);
}

View File

@@ -1,10 +0,0 @@
package com.bartlomiejpluta.base.core.gl.render;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.camera.Camera;
public interface Renderer extends Cleanable {
void init();
void render(Window window, Camera camera, Renderable renderable);
}

View File

@@ -1,7 +0,0 @@
package com.bartlomiejpluta.base.core.profiling.fps;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.core.logic.Updatable;
public interface FPSMonitor extends Updatable, Cleanable {
}

View File

@@ -1,8 +0,0 @@
package com.bartlomiejpluta.base.core.util.mesh;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh;
public interface MeshManager extends Cleanable {
Mesh createQuad(float width, float height, float originX, float originY);
}

View File

@@ -0,0 +1,30 @@
/*
* This Java source file was generated by the Gradle 'init' task.
*/
package com.bartlomiejpluta.base.engine;
import com.bartlomiejpluta.base.engine.core.engine.GameEngine;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication(scanBasePackages = "com.bartlomiejpluta.base")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class App implements ApplicationRunner {
private final GameEngine gameEngine;
@Override
public void run(ApplicationArguments args) {
log.info("Starting game engine");
gameEngine.start();
}
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

View File

@@ -0,0 +1,16 @@
package com.bartlomiejpluta.base.engine.common.asset;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public abstract class Asset {
@NonNull
protected final String uid;
@NonNull
protected final String source;
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.engine.common.manager;
public interface AssetManager<A, T> {
void registerAsset(A asset);
T loadObject(String uid);
}

View File

@@ -0,0 +1,17 @@
package com.bartlomiejpluta.base.engine.common.serial;
import com.bartlomiejpluta.base.engine.error.AppException;
import java.io.InputStream;
public abstract class Deserializer<T> {
protected abstract T parse(InputStream input) throws Exception;
public T deserialize(InputStream input) {
try {
return parse(input);
} catch (Exception e) {
throw new AppException(e);
}
}
}

View File

@@ -1,11 +1,11 @@
package com.bartlomiejpluta.base.core.engine;
package com.bartlomiejpluta.base.engine.core.engine;
import com.bartlomiejpluta.base.core.gc.OffHeapGarbageCollector;
import com.bartlomiejpluta.base.core.logic.GameLogic;
import com.bartlomiejpluta.base.core.thread.ThreadManager;
import com.bartlomiejpluta.base.core.time.ChronoMeter;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.ui.WindowManager;
import com.bartlomiejpluta.base.engine.gc.OffHeapGarbageCollector;
import com.bartlomiejpluta.base.engine.logic.GameLogic;
import com.bartlomiejpluta.base.engine.thread.ThreadManager;
import com.bartlomiejpluta.base.engine.time.ChronoMeter;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.ui.WindowManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

View File

@@ -0,0 +1,5 @@
package com.bartlomiejpluta.base.engine.core.engine;
public interface GameEngine {
void start();
}

View File

@@ -1,11 +1,11 @@
package com.bartlomiejpluta.base.core.gl.object.material;
package com.bartlomiejpluta.base.engine.core.gl.object.material;
import com.bartlomiejpluta.base.core.gl.object.texture.Texture;
import com.bartlomiejpluta.base.core.gl.render.Renderable;
import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.camera.Camera;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderable;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import lombok.Getter;
import org.joml.Vector2f;
import org.joml.Vector4f;

View File

@@ -1,10 +1,10 @@
package com.bartlomiejpluta.base.core.gl.object.mesh;
package com.bartlomiejpluta.base.engine.core.gl.object.mesh;
import com.bartlomiejpluta.base.core.gc.Disposable;
import com.bartlomiejpluta.base.core.gl.render.Renderable;
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.camera.Camera;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderable;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.gc.Disposable;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import org.lwjgl.opengl.GL15;
import org.lwjgl.system.MemoryStack;

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.gl.object.texture;
package com.bartlomiejpluta.base.engine.core.gl.object.texture;
import com.bartlomiejpluta.base.core.util.res.ResourcesManager;
import com.bartlomiejpluta.base.engine.util.res.ResourcesManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.core.gl.object.texture;
package com.bartlomiejpluta.base.engine.core.gl.object.texture;
import com.bartlomiejpluta.base.core.error.AppException;
import com.bartlomiejpluta.base.core.gc.Disposable;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.gc.Disposable;
import lombok.Getter;
import org.joml.Vector2f;
import org.lwjgl.system.MemoryStack;

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.gl.object.texture;
package com.bartlomiejpluta.base.engine.core.gl.object.texture;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
public interface TextureManager extends Cleanable {
Texture loadTexture(String textureFileName);

View File

@@ -1,9 +1,9 @@
package com.bartlomiejpluta.base.core.gl.render;
package com.bartlomiejpluta.base.engine.core.gl.render;
import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.camera.Camera;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -0,0 +1,9 @@
package com.bartlomiejpluta.base.engine.core.gl.render;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
public interface Renderable {
void render(Window window, Camera camera, ShaderManager shaderManager);
}

View File

@@ -0,0 +1,10 @@
package com.bartlomiejpluta.base.engine.core.gl.render;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
public interface Renderer extends Cleanable {
void init();
void render(Window window, Camera camera, Renderable renderable);
}

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.gl.shader.constant;
package com.bartlomiejpluta.base.engine.core.gl.shader.constant;
public interface UniformName {
String UNI_VIEW_MODEL_MATRIX = "viewModelMatrix";

View File

@@ -1,8 +1,8 @@
package com.bartlomiejpluta.base.core.gl.shader.manager;
package com.bartlomiejpluta.base.engine.core.gl.shader.manager;
import com.bartlomiejpluta.base.core.gl.shader.program.ShaderProgram;
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
import com.bartlomiejpluta.base.core.util.res.ResourcesManager;
import com.bartlomiejpluta.base.engine.core.gl.shader.program.ShaderProgram;
import com.bartlomiejpluta.base.engine.core.gl.shader.uniform.Uniform;
import com.bartlomiejpluta.base.engine.util.res.ResourcesManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.joml.*;

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.core.gl.shader.manager;
package com.bartlomiejpluta.base.engine.core.gl.shader.manager;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
import com.bartlomiejpluta.base.engine.core.gl.shader.uniform.Uniform;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import org.joml.*;
public interface ShaderManager extends Cleanable {

View File

@@ -1,8 +1,8 @@
package com.bartlomiejpluta.base.core.gl.shader.program;
package com.bartlomiejpluta.base.engine.core.gl.shader.program;
import com.bartlomiejpluta.base.core.error.AppException;
import com.bartlomiejpluta.base.core.gc.Disposable;
import com.bartlomiejpluta.base.core.gl.shader.uniform.Uniform;
import com.bartlomiejpluta.base.engine.core.gl.shader.uniform.Uniform;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.gc.Disposable;
import lombok.extern.slf4j.Slf4j;
import org.joml.*;
import org.lwjgl.system.MemoryStack;

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.gl.shader.uniform;
package com.bartlomiejpluta.base.engine.core.gl.shader.uniform;
import com.bartlomiejpluta.base.core.gl.shader.program.ShaderProgram;
import com.bartlomiejpluta.base.engine.core.gl.shader.program.ShaderProgram;
public interface Uniform {
void createUniform(ShaderProgram shaderProgram, String uniformName);

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.error;
package com.bartlomiejpluta.base.engine.error;
public class AppException extends RuntimeException {
public AppException() {

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.gc;
package com.bartlomiejpluta.base.engine.gc;
public interface Cleanable {
void cleanUp();

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.gc;
package com.bartlomiejpluta.base.engine.gc;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.gc;
package com.bartlomiejpluta.base.engine.gc;
public interface Disposable {
void dispose();

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.gc;
package com.bartlomiejpluta.base.engine.gc;
public interface OffHeapGarbageCollector {
void cleanUp();

View File

@@ -0,0 +1,144 @@
package com.bartlomiejpluta.base.engine.input;
import com.bartlomiejpluta.base.api.input.Key;
import com.bartlomiejpluta.base.api.input.Keyboard;
import com.bartlomiejpluta.base.engine.ui.Window;
import lombok.RequiredArgsConstructor;
import static org.lwjgl.glfw.GLFW.*;
@RequiredArgsConstructor
public class GLFWKeyboard implements Keyboard {
private final Window window;
@Override
public boolean isKeyPressed(Key key) {
return window.isKeyPressed(glfwCode(key));
}
private static int glfwCode(Key key) {
return switch (key) {
case KEY_SPACE -> GLFW_KEY_SPACE;
case KEY_APOSTROPHE -> GLFW_KEY_APOSTROPHE;
case KEY_COMMA -> GLFW_KEY_COMMA;
case KEY_MINUS -> GLFW_KEY_MINUS;
case KEY_PERIOD -> GLFW_KEY_PERIOD;
case KEY_SLASH -> GLFW_KEY_SLASH;
case KEY_0 -> GLFW_KEY_0;
case KEY_1 -> GLFW_KEY_1;
case KEY_2 -> GLFW_KEY_2;
case KEY_3 -> GLFW_KEY_3;
case KEY_4 -> GLFW_KEY_4;
case KEY_5 -> GLFW_KEY_5;
case KEY_6 -> GLFW_KEY_6;
case KEY_7 -> GLFW_KEY_7;
case KEY_8 -> GLFW_KEY_8;
case KEY_9 -> GLFW_KEY_9;
case KEY_SEMICOLON -> GLFW_KEY_SEMICOLON;
case KEY_EQUAL -> GLFW_KEY_EQUAL;
case KEY_A -> GLFW_KEY_A;
case KEY_B -> GLFW_KEY_B;
case KEY_C -> GLFW_KEY_C;
case KEY_D -> GLFW_KEY_D;
case KEY_E -> GLFW_KEY_E;
case KEY_F -> GLFW_KEY_F;
case KEY_G -> GLFW_KEY_G;
case KEY_H -> GLFW_KEY_H;
case KEY_I -> GLFW_KEY_I;
case KEY_J -> GLFW_KEY_J;
case KEY_K -> GLFW_KEY_K;
case KEY_L -> GLFW_KEY_L;
case KEY_M -> GLFW_KEY_M;
case KEY_N -> GLFW_KEY_N;
case KEY_O -> GLFW_KEY_O;
case KEY_P -> GLFW_KEY_P;
case KEY_Q -> GLFW_KEY_Q;
case KEY_R -> GLFW_KEY_R;
case KEY_S -> GLFW_KEY_S;
case KEY_T -> GLFW_KEY_T;
case KEY_U -> GLFW_KEY_U;
case KEY_V -> GLFW_KEY_V;
case KEY_W -> GLFW_KEY_W;
case KEY_X -> GLFW_KEY_X;
case KEY_Y -> GLFW_KEY_Y;
case KEY_Z -> GLFW_KEY_Z;
case KEY_LEFT_BRACKET -> GLFW_KEY_LEFT_BRACKET;
case KEY_BACKSLASH -> GLFW_KEY_BACKSLASH;
case KEY_RIGHT_BRACKET -> GLFW_KEY_RIGHT_BRACKET;
case KEY_GRAVE_ACCENT -> GLFW_KEY_GRAVE_ACCENT;
case KEY_WORLD_1 -> GLFW_KEY_WORLD_1;
case KEY_WORLD_2 -> GLFW_KEY_WORLD_2;
case KEY_ESCAPE -> GLFW_KEY_ESCAPE;
case KEY_ENTER -> GLFW_KEY_ENTER;
case KEY_TAB -> GLFW_KEY_TAB;
case KEY_BACKSPACE -> GLFW_KEY_BACKSPACE;
case KEY_INSERT -> GLFW_KEY_INSERT;
case KEY_DELETE -> GLFW_KEY_DELETE;
case KEY_RIGHT -> GLFW_KEY_RIGHT;
case KEY_LEFT -> GLFW_KEY_LEFT;
case KEY_DOWN -> GLFW_KEY_DOWN;
case KEY_UP -> GLFW_KEY_UP;
case KEY_PAGE_UP -> GLFW_KEY_PAGE_UP;
case KEY_PAGE_DOWN -> GLFW_KEY_PAGE_DOWN;
case KEY_HOME -> GLFW_KEY_HOME;
case KEY_END -> GLFW_KEY_END;
case KEY_CAPS_LOCK -> GLFW_KEY_CAPS_LOCK;
case KEY_SCROLL_LOCK -> GLFW_KEY_SCROLL_LOCK;
case KEY_NUM_LOCK -> GLFW_KEY_NUM_LOCK;
case KEY_PRINT_SCREEN -> GLFW_KEY_PRINT_SCREEN;
case KEY_PAUSE -> GLFW_KEY_PAUSE;
case KEY_F1 -> GLFW_KEY_F1;
case KEY_F2 -> GLFW_KEY_F2;
case KEY_F3 -> GLFW_KEY_F3;
case KEY_F4 -> GLFW_KEY_F4;
case KEY_F5 -> GLFW_KEY_F5;
case KEY_F6 -> GLFW_KEY_F6;
case KEY_F7 -> GLFW_KEY_F7;
case KEY_F8 -> GLFW_KEY_F8;
case KEY_F9 -> GLFW_KEY_F9;
case KEY_F10 -> GLFW_KEY_F10;
case KEY_F11 -> GLFW_KEY_F11;
case KEY_F12 -> GLFW_KEY_F12;
case KEY_F13 -> GLFW_KEY_F13;
case KEY_F14 -> GLFW_KEY_F14;
case KEY_F15 -> GLFW_KEY_F15;
case KEY_F16 -> GLFW_KEY_F16;
case KEY_F17 -> GLFW_KEY_F17;
case KEY_F18 -> GLFW_KEY_F18;
case KEY_F19 -> GLFW_KEY_F19;
case KEY_F20 -> GLFW_KEY_F20;
case KEY_F21 -> GLFW_KEY_F21;
case KEY_F22 -> GLFW_KEY_F22;
case KEY_F23 -> GLFW_KEY_F23;
case KEY_F24 -> GLFW_KEY_F24;
case KEY_F25 -> GLFW_KEY_F25;
case KEY_KP_0 -> GLFW_KEY_KP_0;
case KEY_KP_1 -> GLFW_KEY_KP_1;
case KEY_KP_2 -> GLFW_KEY_KP_2;
case KEY_KP_3 -> GLFW_KEY_KP_3;
case KEY_KP_4 -> GLFW_KEY_KP_4;
case KEY_KP_5 -> GLFW_KEY_KP_5;
case KEY_KP_6 -> GLFW_KEY_KP_6;
case KEY_KP_7 -> GLFW_KEY_KP_7;
case KEY_KP_8 -> GLFW_KEY_KP_8;
case KEY_KP_9 -> GLFW_KEY_KP_9;
case KEY_KP_DECIMAL -> GLFW_KEY_KP_DECIMAL;
case KEY_KP_DIVIDE -> GLFW_KEY_KP_DIVIDE;
case KEY_KP_MULTIPLY -> GLFW_KEY_KP_MULTIPLY;
case KEY_KP_SUBTRACT -> GLFW_KEY_KP_SUBTRACT;
case KEY_KP_ADD -> GLFW_KEY_KP_ADD;
case KEY_KP_ENTER -> GLFW_KEY_KP_ENTER;
case KEY_KP_EQUAL -> GLFW_KEY_KP_EQUAL;
case KEY_LEFT_SHIFT -> GLFW_KEY_LEFT_SHIFT;
case KEY_LEFT_CONTROL -> GLFW_KEY_LEFT_CONTROL;
case KEY_LEFT_ALT -> GLFW_KEY_LEFT_ALT;
case KEY_LEFT_SUPER -> GLFW_KEY_LEFT_SUPER;
case KEY_RIGHT_SHIFT -> GLFW_KEY_RIGHT_SHIFT;
case KEY_RIGHT_CONTROL -> GLFW_KEY_RIGHT_CONTROL;
case KEY_RIGHT_ALT -> GLFW_KEY_RIGHT_ALT;
case KEY_RIGHT_SUPER -> GLFW_KEY_RIGHT_SUPER;
case KEY_MENU -> GLFW_KEY_MENU;
case KEY_LAST -> GLFW_KEY_LAST;
};
}
}

View File

@@ -0,0 +1,82 @@
package com.bartlomiejpluta.base.engine.logic;
import com.bartlomiejpluta.base.api.runner.GameRunner;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderer;
import com.bartlomiejpluta.base.engine.project.loader.ClassLoader;
import com.bartlomiejpluta.base.engine.project.loader.ProjectLoader;
import com.bartlomiejpluta.base.engine.project.model.Project;
import com.bartlomiejpluta.base.engine.project.model.RenderableContext;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.util.profiling.fps.FPSMonitor;
import com.bartlomiejpluta.base.engine.util.profiling.time.TimeProfilerService;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.entity.manager.EntityManager;
import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager;
import com.bartlomiejpluta.base.engine.world.map.manager.MapManager;
import com.bartlomiejpluta.base.engine.world.tileset.manager.TileSetManager;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultGameLogic implements GameLogic {
private final Renderer renderer;
private final TileSetManager tileSetManager;
private final MeshManager meshManager;
private final TextureManager textureManager;
private final EntityManager entityManager;
private final ImageManager imageManager;
private final TimeProfilerService profiler;
private final FPSMonitor fpsMonitor;
private final MapManager mapManager;
private final ProjectLoader projectLoader;
private final ClassLoader classLoader;
private final RenderableContext context;
private final Camera camera = new Camera();
private Project project;
private GameRunner runner;
@SneakyThrows
@Override
public void init(Window window) {
log.info("Initializing game logic");
renderer.init();
context.init(window, camera);
project = projectLoader.loadProject();
var runnerClass = classLoader.<GameRunner>loadClass(project.getRunner());
runner = runnerClass.getConstructor().newInstance();
runner.init(context);
}
@Override
public void input(Window window) {
context.input();
}
@Override
public void update(float dt) {
context.update(dt);
fpsMonitor.update(dt);
}
@Override
public void render(Window window) {
renderer.render(window, camera, context);
}
@Override
public void cleanUp() {
log.info("There is nothing to clean up here");
}
}

View File

@@ -1,7 +1,7 @@
package com.bartlomiejpluta.base.core.logic;
package com.bartlomiejpluta.base.engine.logic;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.ui.Window;
public interface GameLogic extends Cleanable {
void init(Window window);

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.logic;
package com.bartlomiejpluta.base.engine.logic;
public interface Updatable {
void update(float dt);

View File

@@ -0,0 +1,20 @@
package com.bartlomiejpluta.base.engine.project.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.nio.file.Path;
@Data
@Configuration
@ConfigurationProperties(prefix = "app.project")
public class ProjectConfiguration {
private String mainFile;
private String resourcePath;
public String projectFile(String... path) {
return Path.of(resourcePath, path).toString().replaceAll("\\\\", "/");
}
}

View File

@@ -0,0 +1,5 @@
package com.bartlomiejpluta.base.engine.project.loader;
public interface ClassLoader {
<T> Class<T> loadClass(String className);
}

View File

@@ -0,0 +1,18 @@
package com.bartlomiejpluta.base.engine.project.loader;
import com.bartlomiejpluta.base.engine.error.AppException;
import org.springframework.stereotype.Component;
@Component
public class DefaultClassLoader implements ClassLoader {
@Override
@SuppressWarnings("unchecked")
public <T> Class<T> loadClass(String className) {
try {
return (Class<T>) getClass().getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
throw new AppException(e);
}
}
}

View File

@@ -0,0 +1,35 @@
package com.bartlomiejpluta.base.engine.project.loader;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.project.model.Project;
import com.bartlomiejpluta.base.engine.project.serial.ProjectDeserializer;
import com.bartlomiejpluta.base.engine.world.entity.manager.EntitySetManager;
import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager;
import com.bartlomiejpluta.base.engine.world.map.manager.MapManager;
import com.bartlomiejpluta.base.engine.world.tileset.manager.TileSetManager;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultProjectLoader implements ProjectLoader {
private final ProjectConfiguration configuration;
private final ProjectDeserializer projectDeserializer;
private final TileSetManager tileSetManager;
private final MapManager mapManager;
private final ImageManager imageManager;
private final EntitySetManager entitySetManager;
@Override
public Project loadProject() {
var resource = DefaultProjectLoader.class.getResourceAsStream(configuration.projectFile(configuration.getMainFile()));
var project = projectDeserializer.deserialize(resource);
project.getTileSetAssets().forEach(tileSetManager::registerAsset);
project.getMapAssets().forEach(mapManager::registerAsset);
project.getImageAssets().forEach(imageManager::registerAsset);
project.getEntitySetAssets().forEach(entitySetManager::registerAsset);
return project;
}
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.engine.project.loader;
import com.bartlomiejpluta.base.engine.project.model.Project;
public interface ProjectLoader {
Project loadProject();
}

View File

@@ -0,0 +1,34 @@
package com.bartlomiejpluta.base.engine.project.model;
import com.bartlomiejpluta.base.engine.world.entity.asset.EntitySetAsset;
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
import com.bartlomiejpluta.base.engine.world.map.asset.GameMapAsset;
import com.bartlomiejpluta.base.engine.world.tileset.asset.TileSetAsset;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.List;
@Getter
@RequiredArgsConstructor
public class Project {
@NonNull
private final String name;
@NonNull
private final String runner;
@NonNull
private final List<TileSetAsset> tileSetAssets;
@NonNull
private final List<GameMapAsset> mapAssets;
@NonNull
private final List<ImageAsset> imageAssets;
@NonNull
private final List<EntitySetAsset> entitySetAssets;
}

View File

@@ -0,0 +1,100 @@
package com.bartlomiejpluta.base.engine.project.model;
import com.bartlomiejpluta.base.api.context.Context;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.input.Keyboard;
import com.bartlomiejpluta.base.api.map.MapHandler;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderable;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.input.GLFWKeyboard;
import com.bartlomiejpluta.base.engine.logic.Updatable;
import com.bartlomiejpluta.base.engine.project.loader.ClassLoader;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.entity.manager.EntityManager;
import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager;
import com.bartlomiejpluta.base.engine.world.map.manager.MapManager;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
import com.bartlomiejpluta.base.engine.world.tileset.manager.TileSetManager;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.joml.Vector2f;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class RenderableContext implements Context, Updatable, Renderable {
private final TileSetManager tileSetManager;
private final MeshManager meshManager;
private final TextureManager textureManager;
private final EntityManager entityManager;
private final ImageManager imageManager;
private final MapManager mapManager;
private final ClassLoader classLoader;
private Keyboard keyboard;
private DefaultGameMap map;
private MapHandler mapHandler;
private Camera camera;
public void init(Window window, Camera camera) {
this.keyboard = new GLFWKeyboard(window);
this.camera = camera;
}
@SneakyThrows
@Override
public void openMap(String mapUid) {
map = mapManager.loadObject(mapUid);
var handlerClass = classLoader.<MapHandler>loadClass(map.getHandler());
mapHandler = handlerClass.getConstructor().newInstance();
mapHandler.init(this, map);
}
@Override
public Entity createEntity(String entitySetUid) {
return entityManager.createEntity(entitySetUid);
}
@Override
public void setCameraPosition(Vector2f position) {
camera.setPosition(position);
}
@Override
public void setCameraPosition(float x, float y) {
camera.setPosition(x, y);
}
public void input() {
mapHandler.input(keyboard);
}
@Override
public void update(float dt) {
if (mapHandler != null) {
mapHandler.update(this, map, dt);
}
if (map != null) {
map.update(dt);
}
}
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
if (map != null) {
map.render(window, camera, shaderManager);
}
if (mapHandler != null) {
mapHandler.postRender(window.getWidth(), window.getHeight());
}
}
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.engine.project.serial;
import com.bartlomiejpluta.base.engine.common.serial.Deserializer;
import com.bartlomiejpluta.base.engine.project.model.Project;
public abstract class ProjectDeserializer extends Deserializer<Project> {
}

View File

@@ -0,0 +1,46 @@
package com.bartlomiejpluta.base.engine.project.serial;
import com.bartlomiejpluta.base.engine.project.model.Project;
import com.bartlomiejpluta.base.engine.world.entity.asset.EntitySetAsset;
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
import com.bartlomiejpluta.base.engine.world.map.asset.GameMapAsset;
import com.bartlomiejpluta.base.engine.world.tileset.asset.TileSetAsset;
import com.bartlomiejpluta.base.proto.ProjectProto;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import static java.util.stream.Collectors.toList;
@Component
public class ProtobufProjectDeserializer extends ProjectDeserializer {
@Override
protected Project parse(InputStream input) throws Exception {
var proto = ProjectProto.Project.parseFrom(input);
var name = proto.getName();
var runner = proto.getRunner();
var tileSetAssets = proto.getTileSetsList().stream().map(this::parseTileSetAsset).collect(toList());
var mapAssets = proto.getMapsList().stream().map(this::parseGameMapAsset).collect(toList());
var imageAssets = proto.getImagesList().stream().map(this::parseImageAsset).collect(toList());
var entitySetAssets = proto.getEntitySetsList().stream().map(this::parseEntitySetAsset).collect(toList());
return new Project(name, runner, tileSetAssets, mapAssets, imageAssets, entitySetAssets);
}
private TileSetAsset parseTileSetAsset(ProjectProto.TileSetAsset proto) {
return new TileSetAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns());
}
private GameMapAsset parseGameMapAsset(ProjectProto.GameMapAsset proto) {
return new GameMapAsset(proto.getUid(), proto.getSource());
}
private ImageAsset parseImageAsset(ProjectProto.ImageAsset proto) {
return new ImageAsset(proto.getUid(), proto.getSource());
}
private EntitySetAsset parseEntitySetAsset(ProjectProto.EntitySetAsset proto) {
return new EntitySetAsset(proto.getUid(), proto.getSource(), proto.getRows(), proto.getColumns());
}
}

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.thread;
package com.bartlomiejpluta.base.engine.thread;
import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.time;
package com.bartlomiejpluta.base.engine.time;
public class ChronoMeter {
private double latchedTime;

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.ui;
package com.bartlomiejpluta.base.engine.ui;
import com.bartlomiejpluta.base.core.error.AppException;
import com.bartlomiejpluta.base.engine.error.AppException;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.ui;
package com.bartlomiejpluta.base.engine.ui;
import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.util.math;
package com.bartlomiejpluta.base.engine.util.math;
import static java.lang.Math.max;
import static java.lang.Math.min;

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.util.mesh;
package com.bartlomiejpluta.base.engine.util.mesh;
import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.joml.Vector2f;

View File

@@ -0,0 +1,8 @@
package com.bartlomiejpluta.base.engine.util.mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
public interface MeshManager extends Cleanable {
Mesh createQuad(float width, float height, float originX, float originY);
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.engine.util.profiling.fps;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.logic.Updatable;
public interface FPSMonitor extends Updatable, Cleanable {
}

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.profiling.fps;
package com.bartlomiejpluta.base.engine.util.profiling.fps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.profiling.time;
package com.bartlomiejpluta.base.engine.util.profiling.time;
import com.bartlomiejpluta.base.core.gc.Cleanable;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.profiling.time;
package com.bartlomiejpluta.base.engine.util.profiling.time;
public interface TimeProfilerService {
void measure(String key, Runnable task);

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.core.util.res;
package com.bartlomiejpluta.base.engine.util.res;
import com.bartlomiejpluta.base.core.error.AppException;
import com.bartlomiejpluta.base.engine.error.AppException;
import org.springframework.stereotype.Component;
import java.io.IOException;

View File

@@ -0,0 +1,41 @@
package com.bartlomiejpluta.base.engine.world.animation;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.EqualsAndHashCode;
import org.joml.Vector2f;
@EqualsAndHashCode(callSuper = true)
public abstract class AnimatedSprite extends Sprite {
public AnimatedSprite(Mesh mesh, Material material) {
super(mesh, material);
}
// Returns time in ms between frames
public abstract int getAnimationSpeed();
public abstract boolean shouldAnimate();
public abstract Vector2f[] getSpriteAnimationFramesPositions();
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
animate();
super.render(window, camera, shaderManager);
}
private void animate() {
if (shouldAnimate()) {
var positions = getSpriteAnimationFramesPositions();
var delay = getAnimationSpeed();
var currentPosition = (int) (System.currentTimeMillis() % (positions.length * delay)) / delay;
var current = positions[currentPosition];
material.setSpritePosition(current);
}
}
}

View File

@@ -1,9 +1,9 @@
package com.bartlomiejpluta.base.core.world.camera;
package com.bartlomiejpluta.base.engine.world.camera;
import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.object.Model;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.object.Model;
import org.joml.Matrix4f;
public class Camera extends Model {

View File

@@ -0,0 +1,17 @@
package com.bartlomiejpluta.base.engine.world.entity.asset;
import com.bartlomiejpluta.base.engine.common.asset.Asset;
import lombok.Getter;
import lombok.NonNull;
@Getter
public class EntitySetAsset extends Asset {
private final int rows;
private final int columns;
public EntitySetAsset(@NonNull String uid, @NonNull String source, int rows, int columns) {
super(uid, source);
this.rows = rows;
this.columns = columns;
}
}

View File

@@ -0,0 +1,28 @@
package com.bartlomiejpluta.base.engine.world.entity.config;
import com.bartlomiejpluta.base.api.entity.Direction;
import lombok.Data;
import org.joml.Vector2i;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
@Data
@Configuration
@ConfigurationProperties(prefix = "app.sprite.entity")
public class EntitySpriteConfiguration {
private EntitySpriteDimensionConfiguration dimension;
private int defaultSpriteColumn;
private Map<Direction, Integer> spriteDirectionRows;
@Data
public static class EntitySpriteDimensionConfiguration {
private int rows;
private int cols;
public Vector2i asVector() {
return new Vector2i(rows, cols);
}
}
}

View File

@@ -0,0 +1,39 @@
package com.bartlomiejpluta.base.engine.world.entity.manager;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.entity.config.EntitySpriteConfiguration;
import com.bartlomiejpluta.base.engine.world.entity.model.DefaultEntity;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultEntityManager implements EntityManager {
private final MeshManager meshManager;
private final EntitySetManager entitySetManager;
private final EntitySpriteConfiguration configuration;
@Override
public DefaultEntity createEntity(String entitySetUid) {
var material = entitySetManager.loadObject(entitySetUid);
return new DefaultEntity(buildMesh(material), material, configuration);
}
private Mesh buildMesh(Material material) {
var texture = material.getTexture();
var dimension = configuration.getDimension().asVector();
var spriteWidth = texture.getWidth() / (float) dimension.y;
var spriteHeight = texture.getHeight() / (float) dimension.x;
return meshManager.createQuad(spriteWidth, spriteHeight, spriteWidth / 2, spriteHeight*0.9f);
}
@Override
public void cleanUp() {
log.info("There is nothing to clean up here");
}
}

View File

@@ -0,0 +1,43 @@
package com.bartlomiejpluta.base.engine.world.entity.manager;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.world.entity.asset.EntitySetAsset;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultEntitySetManager implements EntitySetManager {
private final TextureManager textureManager;
private final Map<String, EntitySetAsset> assets = new HashMap<>();
private final ProjectConfiguration configuration;
@Override
public void registerAsset(EntitySetAsset asset) {
log.info("Registering [{}] entity set asset under UID: [{}]", asset.getSource(), asset.getUid());
assets.put(asset.getUid(), asset);
}
@Override
public Material loadObject(String uid) {
var asset = assets.get(uid);
if (asset == null) {
throw new AppException("The entity set asset with UID: [%s] does not exist", uid);
}
var source = configuration.projectFile("entsets", asset.getSource());
var texture = textureManager.loadTexture(source, asset.getRows(), asset.getColumns());
return Material.textured(texture);
}
}

View File

@@ -0,0 +1,8 @@
package com.bartlomiejpluta.base.engine.world.entity.manager;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.world.entity.model.DefaultEntity;
public interface EntityManager extends Cleanable {
DefaultEntity createEntity(String entitySetUid);
}

View File

@@ -0,0 +1,8 @@
package com.bartlomiejpluta.base.engine.world.entity.manager;
import com.bartlomiejpluta.base.engine.common.manager.AssetManager;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.world.entity.asset.EntitySetAsset;
public interface EntitySetManager extends AssetManager<EntitySetAsset, Material> {
}

View File

@@ -0,0 +1,82 @@
package com.bartlomiejpluta.base.engine.world.entity.model;
import com.bartlomiejpluta.base.api.entity.Direction;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.util.math.MathUtil;
import com.bartlomiejpluta.base.engine.world.entity.config.EntitySpriteConfiguration;
import com.bartlomiejpluta.base.engine.world.movement.MovableSprite;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.joml.Vector2f;
import java.util.Map;
@EqualsAndHashCode(callSuper = true)
public class DefaultEntity extends MovableSprite implements Entity {
private final Map<Direction, Integer> spriteDirectionRows;
private final int defaultSpriteColumn;
private int animationSpeed = 100;
@Setter
@Getter
private Direction faceDirection;
@Override
public int getAnimationSpeed() {
return animationSpeed;
}
@Override
public boolean shouldAnimate() {
return isMoving();
}
@Override
public Vector2f[] getSpriteAnimationFramesPositions() {
var row = spriteDirectionRows.get(faceDirection);
var frames = material.getTexture().getRows();
var array = new Vector2f[frames];
for (int column = 0; column < frames; ++column) {
array[column] = new Vector2f(column, row);
}
return array;
}
@Override
protected void setDefaultAnimationFrame() {
material.setSpritePosition(new Vector2f(defaultSpriteColumn, spriteDirectionRows.get(faceDirection)));
}
@Override
protected boolean move(Direction direction) {
if (super.move(direction)) {
faceDirection = direction;
return true;
}
return false;
}
@Override
public void setSpeed(float speed) {
framesToCrossOneTile = (int) (1 / MathUtil.clamp(speed, Float.MIN_VALUE, 1.0));
}
@Override
public void setAnimationSpeed(float speed) {
animationSpeed = (int) (1 / MathUtil.clamp(speed, Float.MIN_VALUE, 1.0));
}
public DefaultEntity(Mesh mesh, Material material, EntitySpriteConfiguration configuration) {
super(mesh, material);
this.defaultSpriteColumn = configuration.getDefaultSpriteColumn();
this.spriteDirectionRows = configuration.getSpriteDirectionRows();
this.faceDirection = Direction.DOWN;
}
}

View File

@@ -0,0 +1,10 @@
package com.bartlomiejpluta.base.engine.world.image.asset;
import com.bartlomiejpluta.base.engine.common.asset.Asset;
import lombok.NonNull;
public class ImageAsset extends Asset {
public ImageAsset(@NonNull String uid, @NonNull String source) {
super(uid, source);
}
}

View File

@@ -0,0 +1,61 @@
package com.bartlomiejpluta.base.engine.world.image.manager;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.util.math.MathUtil;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
import com.bartlomiejpluta.base.engine.world.image.model.Image;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultImageManager implements ImageManager {
private final MeshManager meshManager;
private final TextureManager textureManager;
private final Map<String, ImageAsset> assets = new HashMap<>();
private final ProjectConfiguration configuration;
@Override
public void registerAsset(ImageAsset asset) {
log.info("Registering [{}] image asset under UID: [{}]", asset.getSource(), asset.getUid());
assets.put(asset.getUid(), asset);
}
@Override
public Image loadObject(String uid) {
var asset = assets.get(uid);
if (asset == null) {
throw new AppException("The image asset with UID: [%s] does not exist", uid);
}
var source = configuration.projectFile("images", asset.getSource());
var texture = textureManager.loadTexture(source);
var width = texture.getWidth();
var height = texture.getHeight();
var gcd = MathUtil.gcdEuclidean(width, height);
var initialWidth = width / gcd;
var initialHeight = height / gcd;
var mesh = meshManager.createQuad(initialWidth, initialHeight, 0, 0);
var material = Material.textured(texture);
log.info("Creating new image on asset with UID: [{}]", uid);
return new Image(mesh, material, initialWidth, initialHeight, gcd);
}
@Override
public void cleanUp() {
log.info("There is nothing to clean up here");
}
}

View File

@@ -0,0 +1,9 @@
package com.bartlomiejpluta.base.engine.world.image.manager;
import com.bartlomiejpluta.base.engine.common.manager.AssetManager;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.world.image.asset.ImageAsset;
import com.bartlomiejpluta.base.engine.world.image.model.Image;
public interface ImageManager extends AssetManager<ImageAsset, Image>, Cleanable {
}

View File

@@ -0,0 +1,20 @@
package com.bartlomiejpluta.base.engine.world.image.model;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.Getter;
@Getter
public class Image extends Sprite {
private final int initialWidth;
private final int initialHeight;
private final int gcd;
public Image(Mesh mesh, Material texture, int initialWidth, int initialHeight, int gcd) {
super(mesh, texture);
this.initialWidth = initialWidth;
this.initialHeight = initialHeight;
this.gcd = gcd;
}
}

View File

@@ -0,0 +1,10 @@
package com.bartlomiejpluta.base.engine.world.map.asset;
import com.bartlomiejpluta.base.engine.common.asset.Asset;
import lombok.NonNull;
public class GameMapAsset extends Asset {
public GameMapAsset(@NonNull String uid, @NonNull String source) {
super(uid, source);
}
}

View File

@@ -0,0 +1,8 @@
package com.bartlomiejpluta.base.engine.world.map.layer.base;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderable;
import com.bartlomiejpluta.base.engine.logic.Updatable;
public interface Layer extends Renderable, Updatable {
}

View File

@@ -0,0 +1,37 @@
package com.bartlomiejpluta.base.engine.world.map.layer.color;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.map.layer.base.Layer;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.NonNull;
public class ColorLayer extends Sprite implements Layer {
private final float mapWidth;
private final float mapHeight;
public ColorLayer(@NonNull MeshManager meshManager, @NonNull DefaultGameMap map, float r, float g, float b, float alpha) {
super(meshManager.createQuad(1, 1, 0, 0), Material.colored(r, g, b, alpha));
this.mapWidth = map.getWidth();
this.mapHeight = map.getHeight();
setScale(mapWidth, mapHeight);
}
public void setColor(float r, float g, float b, float alpha) {
material.setColor(r, g, b, alpha);
}
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
super.render(window, camera, shaderManager);
}
@Override
public void update(float dt) {
}
}

View File

@@ -0,0 +1,77 @@
package com.bartlomiejpluta.base.engine.world.map.layer.image;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.image.model.Image;
import com.bartlomiejpluta.base.engine.world.map.layer.base.Layer;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
import lombok.NonNull;
public class ImageLayer implements Layer {
private final float mapWidth;
private final float mapHeight;
@NonNull
private Image image;
private float imageInitialWidth;
private float imageInitialHeight;
private float gcd;
private float x;
private float y;
private float scaleX;
private float scaleY;
@NonNull
private ImageLayerMode mode;
private boolean parallax;
public ImageLayer(DefaultGameMap map, Image image, float opacity, float x, float y, float scaleX, float scaleY, ImageLayerMode mode, boolean parallax) {
this.mapWidth = map.getWidth();
this.mapHeight = map.getHeight();
this.image = image;
this.imageInitialWidth = image.getInitialWidth();
this.imageInitialHeight = image.getInitialHeight();
this.gcd = image.getGcd();
this.x = x;
this.y = y;
this.scaleX = scaleX;
this.scaleY = scaleY;
this.mode = mode;
this.parallax = parallax;
this.image.getMaterial().setAlpha(opacity);
this.image.setPosition(x, y);
switch (mode) {
case NORMAL -> image.setScale(mapWidth / imageInitialWidth, mapHeight / imageInitialHeight);
case FIT_MAP -> image.setScale(image.getGcd() * imageInitialWidth * scaleX, image.getGcd() * imageInitialHeight * scaleY);
}
}
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
if (image != null) {
if (parallax) {
var cameraPosition = camera.getPosition();
image.setPosition(cameraPosition.x + x, cameraPosition.y + y);
}
if (mode == ImageLayerMode.FIT_SCREEN) {
image.setScale(window.getWidth() / imageInitialWidth, window.getHeight() / imageInitialHeight);
}
image.render(window, camera, shaderManager);
}
}
@Override
public void update(float dt) {
}
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.engine.world.map.layer.image;
public enum ImageLayerMode {
NORMAL,
FIT_MAP,
FIT_SCREEN
}

View File

@@ -0,0 +1,76 @@
package com.bartlomiejpluta.base.engine.world.map.layer.object;
import com.bartlomiejpluta.base.api.entity.Direction;
import com.bartlomiejpluta.base.api.map.PassageAbility;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.map.layer.base.Layer;
import com.bartlomiejpluta.base.engine.world.movement.MovableSprite;
import org.joml.Vector2i;
import java.util.List;
public class ObjectLayer implements Layer {
private final List<MovableSprite> objects;
private final PassageAbility[][] passageMap;
public ObjectLayer(List<MovableSprite> objects, PassageAbility[][] passageMap) {
this.objects = objects;
this.passageMap = passageMap;
}
public void addObject(MovableSprite object) {
objects.add(object);
}
public void removeObject(MovableSprite object) {
objects.remove(object);
}
public void setPassageAbility(int row, int column, PassageAbility passageAbility) {
passageMap[row][column] = passageAbility;
}
public boolean isMovementPossible(Vector2i source, Vector2i target, Direction direction) {
var isTargetReachable = switch (passageMap[target.y][target.x]) {
case UP_ONLY -> direction != Direction.DOWN;
case DOWN_ONLY -> direction != Direction.UP;
case LEFT_ONLY -> direction != Direction.RIGHT;
case RIGHT_ONLY -> direction != Direction.LEFT;
case BLOCK -> false;
case ALLOW -> true;
};
var canMoveFromCurrentTile = switch (passageMap[source.y][source.x]) {
case UP_ONLY -> direction == Direction.UP;
case DOWN_ONLY -> direction == Direction.DOWN;
case LEFT_ONLY -> direction == Direction.LEFT;
case RIGHT_ONLY -> direction == Direction.RIGHT;
default -> true;
};
return isTargetReachable && canMoveFromCurrentTile;
}
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
objects.sort(this::compareObjects);
for (var object : objects) {
object.render(window, camera, shaderManager);
}
}
private int compareObjects(MovableSprite a, MovableSprite b) {
return Float.compare(a.getPosition().y, b.getPosition().y);
}
@Override
public void update(float dt) {
for (var object : objects) {
object.update(dt);
}
}
}

View File

@@ -0,0 +1,46 @@
package com.bartlomiejpluta.base.engine.world.map.layer.tile;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.map.layer.base.Layer;
import com.bartlomiejpluta.base.engine.world.tileset.model.Tile;
import java.util.Arrays;
public class TileLayer implements Layer {
private final Tile[][] layer;
private final int rows;
private final int columns;
public TileLayer(int rows, int columns) {
this.rows = rows;
this.columns = columns;
layer = new Tile[rows][columns];
Arrays.stream(layer).forEach(tiles -> Arrays.fill(tiles, null));
}
public void setTile(int row, int column, Tile tile) {
layer[row][column] = tile;
if(tile != null) {
tile.setCoordinates(row, column);
}
}
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
for (var row : layer) {
for (var tile : row) {
if (tile != null) {
tile.render(window, camera, shaderManager);
}
}
}
}
@Override
public void update(float dt) {
}
}

View File

@@ -0,0 +1,55 @@
package com.bartlomiejpluta.base.engine.world.map.manager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.world.map.asset.GameMapAsset;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
import com.bartlomiejpluta.base.engine.world.map.serial.MapDeserializer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultMapManager implements MapManager {
private final Map<String, DefaultGameMap> maps = new HashMap<>();
private final Map<String, GameMapAsset> assets = new HashMap<>();
private final MapDeserializer mapDeserializer;
private final ProjectConfiguration configuration;
@Override
public void registerAsset(GameMapAsset asset) {
log.info("Registering [{}] map asset under UID: [{}]", asset.getSource(), asset.getUid());
assets.put(asset.getUid(), asset);
}
@Override
public DefaultGameMap loadObject(String uid) {
var map = maps.get(uid);
if (map == null) {
var asset = assets.get(uid);
if (asset == null) {
throw new AppException("The map asset with UID: [%s] does not exist", uid);
}
var source = configuration.projectFile("maps", asset.getSource());
map = mapDeserializer.deserialize(DefaultMapManager.class.getResourceAsStream(source));
log.info("Loading map from assets to cache under the key: [{}]", uid);
maps.put(uid, map);
}
return map;
}
@Override
public void cleanUp() {
log.info("There is nothing to clean up here");
}
}

View File

@@ -0,0 +1,9 @@
package com.bartlomiejpluta.base.engine.world.map.manager;
import com.bartlomiejpluta.base.engine.common.manager.AssetManager;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.world.map.asset.GameMapAsset;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
public interface MapManager extends AssetManager<GameMapAsset, DefaultGameMap>, Cleanable {
}

View File

@@ -0,0 +1,165 @@
package com.bartlomiejpluta.base.engine.world.map.model;
import com.bartlomiejpluta.base.api.entity.Entity;
import com.bartlomiejpluta.base.api.entity.Movement;
import com.bartlomiejpluta.base.api.map.GameMap;
import com.bartlomiejpluta.base.api.map.PassageAbility;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderable;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.logic.Updatable;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import com.bartlomiejpluta.base.engine.world.entity.model.DefaultEntity;
import com.bartlomiejpluta.base.engine.world.image.model.Image;
import com.bartlomiejpluta.base.engine.world.map.layer.base.Layer;
import com.bartlomiejpluta.base.engine.world.map.layer.color.ColorLayer;
import com.bartlomiejpluta.base.engine.world.map.layer.image.ImageLayer;
import com.bartlomiejpluta.base.engine.world.map.layer.image.ImageLayerMode;
import com.bartlomiejpluta.base.engine.world.map.layer.object.ObjectLayer;
import com.bartlomiejpluta.base.engine.world.map.layer.tile.TileLayer;
import com.bartlomiejpluta.base.engine.world.tileset.model.TileSet;
import lombok.Getter;
import lombok.NonNull;
import org.joml.Vector2f;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DefaultGameMap implements Renderable, Updatable, GameMap {
@Getter
private final List<Layer> layers = new ArrayList<>();
@NonNull
private final TileSet tileSet;
@Getter
private final int rows;
@Getter
private final int columns;
@Getter
private final float width;
@Getter
private final float height;
@Getter
private final Vector2f stepSize;
@Getter
private final String handler;
public DefaultGameMap(TileSet tileSet, int rows, int columns, String handler) {
this.tileSet = tileSet;
this.rows = rows;
this.columns = columns;
this.stepSize = new Vector2f(tileSet.getTileSet().getSpriteSize());
this.width = columns * stepSize.x;
this.height = rows * stepSize.y;
this.handler = handler;
}
@Override
public void render(Window window, Camera camera, ShaderManager shaderManager) {
for (var layer : layers) {
layer.render(window, camera, shaderManager);
}
}
@Override
public void update(float dt) {
for (var layer : layers) {
layer.update(dt);
}
}
@Override
public Vector2f getSize() {
return new Vector2f(columns * stepSize.x, rows * stepSize.y);
}
public int createObjectLayer() {
var passageMap = new PassageAbility[rows][columns];
for (int i = 0; i < rows; ++i) {
Arrays.fill(passageMap[i], 0, columns, PassageAbility.ALLOW);
}
layers.add(new ObjectLayer(new ArrayList<>(), passageMap));
return layers.size() - 1;
}
public int createTileLayer() {
layers.add(new TileLayer(rows, columns));
return layers.size() - 1;
}
public int createColorLayer(MeshManager meshManager, float r, float g, float b, float alpha) {
layers.add(new ColorLayer(meshManager, this, r, g, b, alpha));
return layers.size() - 1;
}
public int createImageLayer(Image image, float opacity, float x, float y, float scaleX, float scaleY, ImageLayerMode mode, boolean parallax) {
layers.add(new ImageLayer(this, image, opacity, x, y, scaleX, scaleY, mode, parallax));
return layers.size() - 1;
}
@Override
public void addEntity(int objectLayerIndex, Entity entity) {
var object = (DefaultEntity) entity;
object.setStepSize(stepSize.x, stepSize.y);
((ObjectLayer) layers.get(objectLayerIndex)).addObject(object);
}
@Override
public void removeEntity(int objectLayerIndex, Entity entity) {
((ObjectLayer) layers.get(objectLayerIndex)).removeObject((DefaultEntity) entity);
}
@Override
public void setTile(int tileLayerIndex, int row, int column, int tileId) {
((TileLayer) layers.get(tileLayerIndex)).setTile(row, column, tileSet.tileById(tileId));
}
@Override
public void setTile(int tileLayerIndex, int row, int column, int tileSetRow, int tileSetColumn) {
((TileLayer) layers.get(tileLayerIndex)).setTile(row, column, tileSet.tileAt(tileSetRow, tileSetColumn));
}
@Override
public void clearTile(int tileLayerIndex, int row, int column) {
((TileLayer) layers.get(tileLayerIndex)).setTile(row, column, null);
}
@Override
public void setPassageAbility(int objectLayerIndex, int row, int column, PassageAbility passageAbility) {
((ObjectLayer) layers.get(objectLayerIndex)).setPassageAbility(row, column, passageAbility);
}
@Override
public void setColor(int colorLayerIndex, float r, float g, float b, float alpha) {
((ColorLayer) layers.get(colorLayerIndex)).setColor(r, g, b, alpha);
}
@Override
public boolean isMovementPossible(int objectLayerIndex, Movement movement) {
var target = movement.getTo();
// Is trying to go beyond the map
if (target.x < 0 || target.y < 0 || target.x >= columns || target.y >= rows) {
return false;
}
var source = movement.getFrom();
var direction = movement.getDirection();
return ((ObjectLayer) layers.get(objectLayerIndex)).isMovementPossible(source, target, direction);
}
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.engine.world.map.serial;
import com.bartlomiejpluta.base.engine.common.serial.Deserializer;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
public abstract class MapDeserializer extends Deserializer<DefaultGameMap> {
}

View File

@@ -0,0 +1,116 @@
package com.bartlomiejpluta.base.engine.world.map.serial;
import com.bartlomiejpluta.base.api.map.PassageAbility;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.image.manager.ImageManager;
import com.bartlomiejpluta.base.engine.world.map.layer.image.ImageLayerMode;
import com.bartlomiejpluta.base.engine.world.map.model.DefaultGameMap;
import com.bartlomiejpluta.base.engine.world.tileset.manager.TileSetManager;
import com.bartlomiejpluta.base.proto.GameMapProto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.InputStream;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ProtobufMapDeserializer extends MapDeserializer {
private final MeshManager meshManager;
private final TileSetManager tileSetManager;
private final ImageManager imageManager;
@Override
protected DefaultGameMap parse(InputStream input) throws Exception {
var proto = GameMapProto.GameMap.parseFrom(input);
var tileSet = tileSetManager.loadObject(proto.getTileSetUID());
var map = new DefaultGameMap(tileSet, proto.getRows(), proto.getColumns(), proto.getHandler());
proto.getLayersList().forEach(layer -> deserializeLayer(map, layer));
return map;
}
private void deserializeLayer(DefaultGameMap map, GameMapProto.Layer proto) {
if (proto.hasTileLayer()) {
deserializeTileLayer(map, proto);
} else if (proto.hasObjectLayer()) {
deserializeObjectLayer(map, proto);
} else if (proto.hasColorLayer()) {
deserializeColorLayer(map, proto);
} else if (proto.hasImageLayer()) {
deserializeImageLayer(map, proto);
} else {
throw new AppException("Not supported layer type");
}
}
private void deserializeTileLayer(DefaultGameMap map, GameMapProto.Layer proto) {
var layer = map.createTileLayer();
var columns = map.getColumns();
var tiles = proto.getTileLayer().getTilesList();
for (var i = 0; i < tiles.size(); ++i) {
var tile = tiles.get(i);
if (tile == 0) {
map.clearTile(layer, i / columns, i % columns);
} else {
map.setTile(layer, i / columns, i % columns, tile - 1);
}
}
}
private void deserializeObjectLayer(DefaultGameMap map, GameMapProto.Layer proto) {
var layer = map.createObjectLayer();
var columns = map.getColumns();
var passageMap = proto.getObjectLayer().getPassageMapList();
for (var i = 0; i < passageMap.size(); ++i) {
map.setPassageAbility(layer, i / columns, i % columns, switch (passageMap.get(i)) {
case ALLOW -> PassageAbility.ALLOW;
case BLOCK -> PassageAbility.BLOCK;
case RIGHT_ONLY -> PassageAbility.RIGHT_ONLY;
case LEFT_ONLY -> PassageAbility.LEFT_ONLY;
case DOWN_ONLY -> PassageAbility.DOWN_ONLY;
case UP_ONLY -> PassageAbility.UP_ONLY;
});
}
}
private void deserializeColorLayer(DefaultGameMap map, GameMapProto.Layer proto) {
var protoColorLayer = proto.getColorLayer();
map.createColorLayer(
meshManager,
protoColorLayer.getRed() / 100.0f,
protoColorLayer.getGreen() / 100.0f,
protoColorLayer.getBlue() / 100.0f,
protoColorLayer.getAlpha() / 100.0f
);
}
private void deserializeImageLayer(DefaultGameMap map, GameMapProto.Layer proto) {
var protoImageLayer = proto.getImageLayer();
var image = imageManager.loadObject(protoImageLayer.getImageUID());
var mode = switch (proto.getImageLayer().getMode()) {
case NORMAL -> ImageLayerMode.NORMAL;
case FIT_MAP -> ImageLayerMode.FIT_MAP;
case FIT_SCREEN -> ImageLayerMode.FIT_SCREEN;
};
map.createImageLayer(
image,
protoImageLayer.getOpacity() / 100.0f,
protoImageLayer.getX(),
protoImageLayer.getY(),
(float) protoImageLayer.getScaleX(),
(float) protoImageLayer.getScaleY(),
mode,
protoImageLayer.getParallax()
);
}
}

View File

@@ -0,0 +1,37 @@
package com.bartlomiejpluta.base.engine.world.movement;
import com.bartlomiejpluta.base.api.entity.Direction;
import com.bartlomiejpluta.base.api.entity.Movement;
import lombok.AccessLevel;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.joml.Vector2i;
@Data
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class DefaultMovement implements Movement {
private final MovableSprite object;
private final Direction direction;
private boolean performed = false;
@Override
public boolean perform() {
performed = object.move(direction);
return performed;
}
@Override
public Movement another() {
return object.prepareMovement(direction);
}
@Override
public Vector2i getFrom() {
return object.getCoordinates();
}
@Override
public Vector2i getTo() {
return direction.asVector().add(object.getCoordinates());
}
}

View File

@@ -0,0 +1,87 @@
package com.bartlomiejpluta.base.engine.world.movement;
import com.bartlomiejpluta.base.api.entity.Direction;
import com.bartlomiejpluta.base.api.entity.Movement;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.logic.Updatable;
import com.bartlomiejpluta.base.engine.world.animation.AnimatedSprite;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.joml.Vector2f;
import org.joml.Vector2i;
@EqualsAndHashCode(callSuper = true)
public abstract class MovableSprite extends AnimatedSprite implements Updatable {
private final Vector2f coordinateStepSize = new Vector2f(0, 0);
private int moveTime = 0;
private Vector2f movementVector;
@Getter
private final Vector2i coordinates = new Vector2i(0, 0);
protected int framesToCrossOneTile = 1;
public boolean isMoving() {
return movementVector != null;
}
@Override
public void update(float dt) {
if (movementVector != null) {
if (moveTime > 0) {
--moveTime;
movePosition(movementVector);
} else {
adjustCoordinates();
setDefaultAnimationFrame();
movementVector = null;
}
}
}
protected abstract void setDefaultAnimationFrame();
private void adjustCoordinates() {
var position = new Vector2f(getPosition());
setCoordinates(new Vector2i((int) (position.x / coordinateStepSize.x), (int) (position.y / coordinateStepSize.y)));
}
public Movement prepareMovement(Direction direction) {
return new DefaultMovement(this, direction);
}
protected boolean move(Direction direction) {
if (this.movementVector != null) {
return false;
}
var speed = new Vector2f(coordinateStepSize).div(framesToCrossOneTile);
movementVector = new Vector2f(direction.x, direction.y).mul(speed);
moveTime = framesToCrossOneTile;
return true;
}
public void setCoordinates(int x, int y) {
coordinates.x = x;
coordinates.y = y;
setPosition((x + 0.5f) * coordinateStepSize.x, (y + 0.5f) * coordinateStepSize.y);
}
public void setCoordinates(Vector2i coordinates) {
setCoordinates(coordinates.x, coordinates.y);
}
public void setStepSize(float x, float y) {
coordinateStepSize.x = x;
coordinateStepSize.y = y;
setCoordinates(coordinates);
}
public MovableSprite(Mesh mesh, Material material) {
super(mesh, material);
setCoordinates(0, 0);
}
}

View File

@@ -1,4 +1,4 @@
package com.bartlomiejpluta.base.core.world.object;
package com.bartlomiejpluta.base.engine.world.object;
import lombok.EqualsAndHashCode;
import lombok.Getter;

View File

@@ -1,12 +1,12 @@
package com.bartlomiejpluta.base.core.world.object;
package com.bartlomiejpluta.base.engine.world.object;
import com.bartlomiejpluta.base.core.gl.object.material.Material;
import com.bartlomiejpluta.base.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.core.gl.render.Renderable;
import com.bartlomiejpluta.base.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.core.ui.Window;
import com.bartlomiejpluta.base.core.world.camera.Camera;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.render.Renderable;
import com.bartlomiejpluta.base.engine.core.gl.shader.constant.UniformName;
import com.bartlomiejpluta.base.engine.core.gl.shader.manager.ShaderManager;
import com.bartlomiejpluta.base.engine.ui.Window;
import com.bartlomiejpluta.base.engine.world.camera.Camera;
import lombok.*;
@RequiredArgsConstructor

View File

@@ -0,0 +1,17 @@
package com.bartlomiejpluta.base.engine.world.tileset.asset;
import com.bartlomiejpluta.base.engine.common.asset.Asset;
import lombok.Getter;
import lombok.NonNull;
@Getter
public class TileSetAsset extends Asset {
private final int rows;
private final int columns;
public TileSetAsset(@NonNull String uid, @NonNull String source, int rows, int columns) {
super(uid, source);
this.rows = rows;
this.columns = columns;
}
}

View File

@@ -0,0 +1,60 @@
package com.bartlomiejpluta.base.engine.world.tileset.manager;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.TextureManager;
import com.bartlomiejpluta.base.engine.error.AppException;
import com.bartlomiejpluta.base.engine.project.config.ProjectConfiguration;
import com.bartlomiejpluta.base.engine.util.mesh.MeshManager;
import com.bartlomiejpluta.base.engine.world.tileset.asset.TileSetAsset;
import com.bartlomiejpluta.base.engine.world.tileset.model.TileSet;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DefaultTileSetManager implements TileSetManager {
private final TextureManager textureManager;
private final MeshManager meshManager;
private final Map<String, TileSet> tileSets = new HashMap<>();
private final Map<String, TileSetAsset> assets = new HashMap<>();
private final ProjectConfiguration configuration;
@Override
public void registerAsset(TileSetAsset asset) {
log.info("Registering [{}] tile set asset under UID: [{}]", asset.getSource(), asset.getUid());
assets.put(asset.getUid(), asset);
}
@Override
public TileSet loadObject(String uid) {
var tileset = tileSets.get(uid);
if (tileset == null) {
var asset = assets.get(uid);
if (asset == null) {
throw new AppException("The tile set asset with UID: [%s] does not exist", uid);
}
var source = configuration.projectFile("tilesets", asset.getSource());
var texture = textureManager.loadTexture(source, asset.getRows(), asset.getColumns());
var size = texture.getSpriteSize();
var mesh = meshManager.createQuad(size.x, size.y, 0, 0);
tileset = new TileSet(texture, mesh);
log.info("Loading tile set from assets to cache under the key: [{}]", uid);
tileSets.put(uid, tileset);
}
return tileset;
}
@Override
public void cleanUp() {
log.info("There is nothing to clean up here");
}
}

View File

@@ -0,0 +1,9 @@
package com.bartlomiejpluta.base.engine.world.tileset.manager;
import com.bartlomiejpluta.base.engine.common.manager.AssetManager;
import com.bartlomiejpluta.base.engine.gc.Cleanable;
import com.bartlomiejpluta.base.engine.world.tileset.asset.TileSetAsset;
import com.bartlomiejpluta.base.engine.world.tileset.model.TileSet;
public interface TileSetManager extends AssetManager<TileSetAsset, TileSet>, Cleanable {
}

View File

@@ -0,0 +1,36 @@
package com.bartlomiejpluta.base.engine.world.tileset.model;
import com.bartlomiejpluta.base.engine.core.gl.object.material.Material;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import com.bartlomiejpluta.base.engine.world.object.Sprite;
import lombok.Getter;
@Getter
public class Tile extends Sprite {
private final int id;
private final int tileSetRow;
private final int tileSetColumn;
public Tile setCoordinates(int row, int column) {
var stepSize = material.getTexture().getSpriteSize();
setPosition(column * stepSize.x, row * stepSize.y);
return this;
}
public Tile(Mesh mesh, Texture tileSet, int id) {
super(mesh, Material.textured(tileSet));
this.id = id;
this.tileSetRow = id / tileSet.getColumns();
this.tileSetColumn = id % tileSet.getColumns();
material.setSpritePosition(tileSetColumn, tileSetRow);
}
public Tile(Mesh mesh, Texture tileSet, int row, int column) {
super(mesh, Material.textured(tileSet));
this.tileSetRow = row;
this.tileSetColumn = column;
this.id = row * tileSet.getColumns() + column;
material.setSpritePosition(tileSetColumn, tileSetRow);
}
}

View File

@@ -0,0 +1,21 @@
package com.bartlomiejpluta.base.engine.world.tileset.model;
import com.bartlomiejpluta.base.engine.core.gl.object.mesh.Mesh;
import com.bartlomiejpluta.base.engine.core.gl.object.texture.Texture;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class TileSet {
private final Texture tileSet;
private final Mesh mesh;
public Tile tileById(int id) {
return new Tile(mesh, tileSet, id);
}
public Tile tileAt(int row, int column) {
return new Tile(mesh, tileSet, row, column);
}
}

View File

@@ -0,0 +1,31 @@
app:
window:
title: "Simple Game"
width: 640
height: 480
core:
targetUps: 50 # Updates per second
project:
main-file: project.bep
resource-path: /project
sprite:
entity:
dimension:
rows: 4
cols: 4
default-sprite-column: 0
sprite-direction-rows:
down: 0
left: 1
right: 2
up: 3
logging:
level:
com.bartlomiejpluta.base.core.profiling.time.aspect.ExecutionTimeAspect: DEBUG