Final product
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.idea/
|
||||||
|
out/
|
||||||
|
*.iml
|
||||||
|
|
1132
res/illustrator/player.ai
Normal file
BIN
res/img/player/body.png
Normal file
After Width: | Height: | Size: 905 B |
BIN
res/img/player/confused_eyes.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
res/img/player/normal_eyes.png
Normal file
After Width: | Height: | Size: 475 B |
BIN
res/img/player/particle.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
res/img/player/squint_eyes.png
Normal file
After Width: | Height: | Size: 431 B |
BIN
res/img/player/three_smoaks_eyes.png
Normal file
After Width: | Height: | Size: 645 B |
BIN
res/map/layout.png
Normal file
After Width: | Height: | Size: 395 B |
11
res/shaders/gradient/frag.gls
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform vec4 input_color;
|
||||||
|
|
||||||
|
in float value;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
color = input_color * vec4(value, value, value, 1);
|
||||||
|
}
|
13
res/shaders/gradient/vert.gls
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform mat4 mvp;
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 vertices;
|
||||||
|
layout (location = 1) in float in_value;
|
||||||
|
|
||||||
|
out float value;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
value = in_value;
|
||||||
|
gl_Position = mvp * vec4(vertices, 1);
|
||||||
|
}
|
15
res/shaders/player/frag.gls
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform float rotation;
|
||||||
|
uniform sampler2D sampler;
|
||||||
|
|
||||||
|
in vec2 texCoords;
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
color = texture(sampler, texCoords + vec2(0, rotation));
|
||||||
|
if (color.a == 0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
13
res/shaders/player/vert.gls
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
uniform mat4 mvp;
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 vertices;
|
||||||
|
layout (location = 1) in vec2 itexCoords;
|
||||||
|
|
||||||
|
out vec2 texCoords;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
texCoords = itexCoords;
|
||||||
|
gl_Position = mvp * vec4(vertices, 1);
|
||||||
|
}
|
BIN
res/thumbnail.png
Normal file
After Width: | Height: | Size: 16 KiB |
3
src/META-INF/MANIFEST.MF
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Manifest-Version: 1.0
|
||||||
|
Main-Class: com.gnarwhal.ld48.game.Main
|
||||||
|
|
37
src/com/gnarwhal/ld48/engine/audio/ALManagement.java
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package com.gnarwhal.ld48.engine.audio;
|
||||||
|
|
||||||
|
import org.lwjgl.openal.AL;
|
||||||
|
import org.lwjgl.openal.ALC;
|
||||||
|
import org.lwjgl.openal.ALC10;
|
||||||
|
import org.lwjgl.openal.ALCCapabilities;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.openal.ALC10.*;
|
||||||
|
|
||||||
|
public class ALManagement {
|
||||||
|
|
||||||
|
private long device, context;
|
||||||
|
private ALCCapabilities deviceCaps;
|
||||||
|
|
||||||
|
public ALManagement() {
|
||||||
|
device = alcOpenDevice((ByteBuffer) null);
|
||||||
|
if (device == 0)
|
||||||
|
throw new IllegalStateException("Failed to open the default device.");
|
||||||
|
|
||||||
|
deviceCaps = ALC.createCapabilities(device);
|
||||||
|
|
||||||
|
context = alcCreateContext(device, (IntBuffer) null);
|
||||||
|
if (context == 0)
|
||||||
|
throw new IllegalStateException("Failed to create an OpenAL context.");
|
||||||
|
|
||||||
|
alcMakeContextCurrent(context);
|
||||||
|
AL.createCapabilities(deviceCaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
ALC10.alcDestroyContext(context);
|
||||||
|
ALC10.alcCloseDevice(device);
|
||||||
|
}
|
||||||
|
}
|
38
src/com/gnarwhal/ld48/engine/audio/Sound.java
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package com.gnarwhal.ld48.engine.audio;
|
||||||
|
|
||||||
|
import org.lwjgl.openal.AL10;
|
||||||
|
|
||||||
|
public class Sound {
|
||||||
|
|
||||||
|
private int buffer;
|
||||||
|
private int sourceId;
|
||||||
|
|
||||||
|
public Sound(String path) {
|
||||||
|
sourceId = AL10.alGenSources();
|
||||||
|
buffer = AL10.alGenBuffers();
|
||||||
|
WaveData waveData = WaveData.create(path);
|
||||||
|
AL10.alBufferData(buffer, waveData.format, waveData.data, waveData.samplerate);
|
||||||
|
AL10.alSourcei(sourceId, AL10.AL_BUFFER, buffer);
|
||||||
|
AL10.alSourcef(sourceId, AL10.AL_GAIN, 1);
|
||||||
|
AL10.alSourcef(sourceId, AL10.AL_PITCH, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void play(boolean loop) {
|
||||||
|
AL10.alSourcei(sourceId, AL10.AL_LOOPING, loop ? 1 : 0);
|
||||||
|
AL10.alSource3f(sourceId, AL10.AL_POSITION, 0, 0, 0);
|
||||||
|
AL10.alSourcePlay(sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
AL10.alSourceStop(sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVolume(float volume) {
|
||||||
|
AL10.alSourcef(sourceId, AL10.AL_GAIN, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
AL10.alDeleteBuffers(buffer);
|
||||||
|
AL10.alDeleteSources(sourceId);
|
||||||
|
}
|
||||||
|
}
|
83
src/com/gnarwhal/ld48/engine/audio/WaveData.java
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
package com.gnarwhal.ld48.engine.audio;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
import org.lwjgl.openal.AL10;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
|
import javax.sound.sampled.AudioInputStream;
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
public class WaveData {
|
||||||
|
|
||||||
|
final int format;
|
||||||
|
final int samplerate;
|
||||||
|
final int totalBytes;
|
||||||
|
final int bytesPerFrame;
|
||||||
|
final ByteBuffer data;
|
||||||
|
|
||||||
|
private final AudioInputStream audioStream;
|
||||||
|
private final byte[] dataArray;
|
||||||
|
|
||||||
|
private WaveData(AudioInputStream stream) {
|
||||||
|
this.audioStream = stream;
|
||||||
|
AudioFormat audioFormat = stream.getFormat();
|
||||||
|
format = getOpenAlFormat(audioFormat.getChannels(), audioFormat.getSampleSizeInBits());
|
||||||
|
this.samplerate = (int) audioFormat.getSampleRate();
|
||||||
|
this.bytesPerFrame = audioFormat.getFrameSize();
|
||||||
|
this.totalBytes = (int) (stream.getFrameLength() * bytesPerFrame);
|
||||||
|
this.data = BufferUtils.createByteBuffer(totalBytes);
|
||||||
|
this.dataArray = new byte[totalBytes];
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dispose() {
|
||||||
|
try {
|
||||||
|
audioStream.close();
|
||||||
|
data.clear();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByteBuffer loadData() {
|
||||||
|
try {
|
||||||
|
int bytesRead = audioStream.read(dataArray, 0, totalBytes);
|
||||||
|
data.clear();
|
||||||
|
data.put(dataArray, 0, bytesRead);
|
||||||
|
data.flip();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.err.println("Couldn't read bytes from audio stream!");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WaveData create(String file) {
|
||||||
|
WaveData wavStream = null;
|
||||||
|
try {
|
||||||
|
InputStream stream = new FileInputStream(new File(file));
|
||||||
|
InputStream bufferedInput = new BufferedInputStream(stream);
|
||||||
|
AudioInputStream audioStream = null;
|
||||||
|
audioStream = AudioSystem.getAudioInputStream(bufferedInput);
|
||||||
|
wavStream = new WaveData(audioStream);
|
||||||
|
} catch (UnsupportedAudioFileException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return wavStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static int getOpenAlFormat(int channels, int bitsPerSample) {
|
||||||
|
if (channels == 1) {
|
||||||
|
return bitsPerSample == 8 ? AL10.AL_FORMAT_MONO8 : AL10.AL_FORMAT_MONO16;
|
||||||
|
} else {
|
||||||
|
return bitsPerSample == 8 ? AL10.AL_FORMAT_STEREO8 : AL10.AL_FORMAT_STEREO16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
100
src/com/gnarwhal/ld48/engine/display/Camera.java
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
package com.gnarwhal.ld48.engine.display;
|
||||||
|
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class Camera {
|
||||||
|
|
||||||
|
private Matrix4f projection, projView;
|
||||||
|
|
||||||
|
private float width, height;
|
||||||
|
private Vector3f position;
|
||||||
|
private float rotation;
|
||||||
|
|
||||||
|
public Camera(float width, float height) {
|
||||||
|
setDims(width, height);
|
||||||
|
position = new Vector3f();
|
||||||
|
rotation = 0;
|
||||||
|
projView = new Matrix4f();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDims(float width, float height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
projection = new Matrix4f().setOrtho(0, width, height, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
projection.translate(position.negate(new Vector3f()), projView).rotateZ(-rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4f getProjection() {
|
||||||
|
return new Matrix4f(projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Matrix4f getMatrix() {
|
||||||
|
return new Matrix4f(projView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX() {
|
||||||
|
return position.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY() {
|
||||||
|
return position.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getPosition() {
|
||||||
|
return new Vector3f(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(float x) {
|
||||||
|
position.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(float y) {
|
||||||
|
position.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(float x, float y) {
|
||||||
|
position.set(x, y, position.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPosition(Vector3f position) {
|
||||||
|
this.position.x = position.x;
|
||||||
|
this.position.y = position.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCenter(float x, float y) {
|
||||||
|
position.set(x - width / 2, y - height / 2, position.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCenter(Vector3f position) {
|
||||||
|
this.position.x = position.x - width / 2;
|
||||||
|
this.position.y = position.y - height / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void translate(float x, float y, float z) {
|
||||||
|
position.add(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void translate(Vector3f transform) {
|
||||||
|
position.add(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRotation(float angle) {
|
||||||
|
rotation = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rotate(float angle) {
|
||||||
|
rotation += angle;
|
||||||
|
}
|
||||||
|
}
|
93
src/com/gnarwhal/ld48/engine/display/Framebuffer.java
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package com.gnarwhal.ld48.engine.display;
|
||||||
|
|
||||||
|
import com.gnarwhal.ld48.engine.texture.Texture;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
import static org.lwjgl.opengl.GL30.*;
|
||||||
|
import static org.lwjgl.opengl.GL32.glFramebufferTexture;
|
||||||
|
|
||||||
|
public class Framebuffer {
|
||||||
|
|
||||||
|
int fbo, rbo, width, height;
|
||||||
|
int colorBuf, depthTex;
|
||||||
|
float r, g, b, a;
|
||||||
|
|
||||||
|
Framebuffer(int width, int height, float r, float g, float b, float a) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
|
||||||
|
fbo = glGenFramebuffers();
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||||
|
|
||||||
|
rbo = 0;
|
||||||
|
colorBuf = 0;
|
||||||
|
depthTex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer addColorAttachment(Texture texture) {
|
||||||
|
if (colorBuf == 0) {
|
||||||
|
int id = glGenTextures();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||||
|
texture = new Texture(id, width, height);
|
||||||
|
colorBuf = 1;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer addDepthTextureAttachment(Texture texture) {
|
||||||
|
if (depthTex == 0) {
|
||||||
|
int id = glGenTextures();
|
||||||
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
||||||
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
||||||
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, id, 0);
|
||||||
|
texture = new Texture(id, width, height);
|
||||||
|
depthTex = 1;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer addDepthBufferAttachment() {
|
||||||
|
if (rbo == 0) {
|
||||||
|
rbo = glGenRenderbuffers();
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind() {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
|
||||||
|
glClearColor(r, g, b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbind(Window window) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glViewport(0, 0, window.getWidth(), window.getHeight());
|
||||||
|
window.activateClearColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {
|
||||||
|
if (rbo != 0)
|
||||||
|
glDeleteRenderbuffers(rbo);
|
||||||
|
glDeleteFramebuffers(fbo);
|
||||||
|
}
|
||||||
|
}
|
227
src/com/gnarwhal/ld48/engine/display/Window.java
Normal file
|
@ -0,0 +1,227 @@
|
||||||
|
package com.gnarwhal.ld48.engine.display;
|
||||||
|
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||||
|
import org.lwjgl.glfw.GLFWGamepadState;
|
||||||
|
import org.lwjgl.glfw.GLFWVidMode;
|
||||||
|
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
import static org.lwjgl.opengl.GL.createCapabilities;
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
import static org.lwjgl.opengl.GL13.GL_MULTISAMPLE;
|
||||||
|
|
||||||
|
public class Window {
|
||||||
|
|
||||||
|
public static int
|
||||||
|
SCREEN_WIDTH,
|
||||||
|
SCREEN_HEIGHT,
|
||||||
|
REFRESH_RATE;
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
BUTTON_RELEASED = 0,
|
||||||
|
BUTTON_UNPRESSED = 1,
|
||||||
|
BUTTON_PRESSED = 2,
|
||||||
|
BUTTON_HELD = 3,
|
||||||
|
BUTTON_REPEAT = 4;
|
||||||
|
|
||||||
|
public static float SCALE;
|
||||||
|
|
||||||
|
private long window;
|
||||||
|
private int width, height;
|
||||||
|
private boolean resized;
|
||||||
|
|
||||||
|
private int[] mouseButtons = new int[GLFW_MOUSE_BUTTON_LAST + 1];
|
||||||
|
private int[] keys = new int[GLFW_KEY_LAST + 1];
|
||||||
|
private int[] gamepadButtons = new int[GLFW_GAMEPAD_BUTTON_LAST];
|
||||||
|
private GLFWGamepadState gamepadState;
|
||||||
|
|
||||||
|
public Window(String title, boolean vSync) {
|
||||||
|
init(0, 0, title, vSync, false, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window(String title, boolean vSync, boolean resizable, boolean decorated) {
|
||||||
|
init(800, 500, title, vSync, resizable, decorated, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Window(int width, int height, String title, boolean vSync, boolean resizable, boolean decorated) {
|
||||||
|
init(width, height, title, vSync, resizable, decorated, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(int lwidth, int lheight, String title, boolean vSync, boolean resizable, boolean decorated, boolean maximized) {
|
||||||
|
glfwSetErrorCallback(GLFWErrorCallback.createPrint(System.err));
|
||||||
|
|
||||||
|
for (int i = 0; i < mouseButtons.length; i++)
|
||||||
|
mouseButtons[i] = 0;
|
||||||
|
|
||||||
|
if(!glfwInit()) {
|
||||||
|
System.err.println("GLFW failed to initialize!");
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_SAMPLES, 8);
|
||||||
|
glfwWindowHint(GLFW_RESIZABLE, resizable ? GLFW_TRUE : GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_DECORATED, decorated ? GLFW_TRUE : GLFW_FALSE);
|
||||||
|
glfwWindowHint(GLFW_MAXIMIZED, maximized ? GLFW_TRUE : GLFW_FALSE);
|
||||||
|
|
||||||
|
GLFWVidMode vidMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
|
||||||
|
SCREEN_WIDTH = vidMode.width();
|
||||||
|
SCREEN_HEIGHT = vidMode.height();
|
||||||
|
SCALE = SCREEN_HEIGHT / 1080f;
|
||||||
|
REFRESH_RATE = vidMode.refreshRate();
|
||||||
|
if(lwidth == 0 || lheight == 0) {
|
||||||
|
width = vidMode.width();
|
||||||
|
height = vidMode.height();
|
||||||
|
window = glfwCreateWindow(width, height, title, glfwGetPrimaryMonitor(), 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.width = lwidth;
|
||||||
|
this.height = lheight;
|
||||||
|
window = glfwCreateWindow(width, height, title, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
createCapabilities();
|
||||||
|
|
||||||
|
glfwSwapInterval(vSync ? 1 : 0);
|
||||||
|
|
||||||
|
glfwSetWindowSizeCallback(window, (long window, int w, int h) -> {
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
resized = true;
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
});
|
||||||
|
|
||||||
|
glfwSetMouseButtonCallback(window, (long window, int button, int action, int mods) -> {
|
||||||
|
if (action == GLFW_RELEASE)
|
||||||
|
mouseButtons[button] = BUTTON_RELEASED;
|
||||||
|
if (action == GLFW_PRESS)
|
||||||
|
mouseButtons[button] = BUTTON_PRESSED;
|
||||||
|
if (action == GLFW_REPEAT)
|
||||||
|
mouseButtons[button] = BUTTON_REPEAT;
|
||||||
|
});
|
||||||
|
|
||||||
|
glfwSetKeyCallback(window, (long window, int key, int scancode, int action, int mods) -> {
|
||||||
|
if (key != -1) {
|
||||||
|
if (action == GLFW_RELEASE)
|
||||||
|
keys[key] = BUTTON_RELEASED;
|
||||||
|
if (action == GLFW_PRESS)
|
||||||
|
keys[key] = BUTTON_PRESSED;
|
||||||
|
if (action == GLFW_REPEAT)
|
||||||
|
keys[key] = BUTTON_REPEAT;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
activateClearColor();
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
glEnable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||||
|
|
||||||
|
int[] awidth = new int[1], aheight = new int[1];
|
||||||
|
glfwGetWindowSize(window, awidth, aheight);
|
||||||
|
width = awidth[0];
|
||||||
|
height = aheight[0];
|
||||||
|
|
||||||
|
gamepadState = GLFWGamepadState.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
for (int i = 0; i < mouseButtons.length; i++)
|
||||||
|
if (mouseButtons[i] == BUTTON_RELEASED || mouseButtons[i] == BUTTON_PRESSED)
|
||||||
|
++mouseButtons[i];
|
||||||
|
for (int i = 0; i < keys.length; i++)
|
||||||
|
if (keys[i] == BUTTON_RELEASED || keys[i] == BUTTON_PRESSED)
|
||||||
|
++keys[i];
|
||||||
|
if (glfwGetGamepadState(GLFW_JOYSTICK_1, gamepadState)) {
|
||||||
|
for (int i = 0; i < gamepadButtons.length; ++i) {
|
||||||
|
if (gamepadState.buttons(i) == GLFW_RELEASE) {
|
||||||
|
if (gamepadButtons[i] == BUTTON_RELEASED) {
|
||||||
|
gamepadButtons[i] = BUTTON_UNPRESSED;
|
||||||
|
} else if (gamepadButtons[i] != BUTTON_UNPRESSED) {
|
||||||
|
gamepadButtons[i] = BUTTON_RELEASED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (gamepadButtons[i] == BUTTON_PRESSED) {
|
||||||
|
gamepadButtons[i] = BUTTON_HELD;
|
||||||
|
} else if (gamepadButtons[i] != BUTTON_HELD) {
|
||||||
|
gamepadButtons[i] = BUTTON_PRESSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resized = false;
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void activateClearColor() {
|
||||||
|
glClearColor(0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swap() {
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
glfwSetWindowShouldClose(window, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void terminate() {
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldClose() {
|
||||||
|
return glfwWindowShouldClose(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int keyPressed(int keyCode) {
|
||||||
|
return keys[keyCode];
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f getMouseCoords(Camera camera) {
|
||||||
|
double[] x = new double[1], y = new double[1];
|
||||||
|
glfwGetCursorPos(window, x, y);
|
||||||
|
Vector3f ret = new Vector3f((float) x[0], (float) y[0], 0);
|
||||||
|
return ret.mul(camera.getWidth() / this.width, camera.getHeight() / this.height, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int mousePressed(int button) {
|
||||||
|
return mouseButtons[button];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean joystick(int joystick) {
|
||||||
|
return glfwJoystickPresent(joystick) && glfwJoystickIsGamepad(joystick);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getJoystickAxis(int axis) {
|
||||||
|
return gamepadState.axes(axis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int controllerButtonPressed(int button) {
|
||||||
|
return gamepadButtons[button];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wasResized() {
|
||||||
|
return resized;
|
||||||
|
}
|
||||||
|
}
|
50
src/com/gnarwhal/ld48/engine/model/Vao.java
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package com.gnarwhal.ld48.engine.model;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
import static org.lwjgl.opengl.GL15.*;
|
||||||
|
import static org.lwjgl.opengl.GL20.*;
|
||||||
|
import static org.lwjgl.opengl.GL30.*;
|
||||||
|
|
||||||
|
public class Vao {
|
||||||
|
|
||||||
|
private int numAttribs = 0;
|
||||||
|
|
||||||
|
private int vao, ibo, count;
|
||||||
|
|
||||||
|
private int[] vbos = new int[15];
|
||||||
|
|
||||||
|
public Vao(float[] vertices, int[] indices) {
|
||||||
|
vao = glGenVertexArrays();
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
addAttrib(vertices, 3);
|
||||||
|
ibo = glGenBuffers();
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW);
|
||||||
|
count = indices.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttrib(float[] data, int size) {
|
||||||
|
int vbo = glGenBuffers();
|
||||||
|
vbos[numAttribs] = vbo;
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, data, GL_STATIC_DRAW);
|
||||||
|
glVertexAttribPointer(numAttribs, size, GL_FLOAT, false, 0, 0);
|
||||||
|
++numAttribs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render() {
|
||||||
|
glBindVertexArray(vao);
|
||||||
|
for(int i = 0; i < numAttribs; ++i)
|
||||||
|
glEnableVertexAttribArray(i);
|
||||||
|
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, 0);
|
||||||
|
for(int i = 0; i < numAttribs; ++i)
|
||||||
|
glDisableVertexAttribArray(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
for(int vbo : vbos)
|
||||||
|
glDeleteBuffers(vbo);
|
||||||
|
glDeleteBuffers(ibo);
|
||||||
|
glDeleteVertexArrays(vao);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.gnarwhal.ld48.engine.properties;
|
||||||
|
|
||||||
|
public class ImproperFormattingException extends RuntimeException {
|
||||||
|
|
||||||
|
public ImproperFormattingException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
104
src/com/gnarwhal/ld48/engine/properties/Properties.java
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package com.gnarwhal.ld48.engine.properties;
|
||||||
|
|
||||||
|
public class Properties {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private PropNode head, cur;
|
||||||
|
|
||||||
|
public Properties(String name) {
|
||||||
|
this.name = new String(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(PropNode node) {
|
||||||
|
if(head == null) {
|
||||||
|
head = node;
|
||||||
|
cur = node;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cur.next = node;
|
||||||
|
cur = cur.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PropNode get(String key) throws UndeclaredPropertyException {
|
||||||
|
String[] keys = key.split("\\.");
|
||||||
|
PropNode mobile = head;
|
||||||
|
while (mobile != null) {
|
||||||
|
if(mobile.key.equals(keys[0])) {
|
||||||
|
if(keys.length > 1 && mobile instanceof BlockNode)
|
||||||
|
return ((BlockNode) mobile).data.get(key.substring(keys[0].length() + 1));
|
||||||
|
else
|
||||||
|
return mobile;
|
||||||
|
}
|
||||||
|
mobile = mobile.next;
|
||||||
|
}
|
||||||
|
throw new UndeclaredPropertyException("Property '" + key + "' in properties '" + name + "' was not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAsString(String key) throws UndeclaredPropertyException {
|
||||||
|
return ((StringNode) get(key)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAsInt(String key) throws UndeclaredPropertyException {
|
||||||
|
return ((IntNode) get(key)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getAsIntArray(String key) throws UndeclaredPropertyException {
|
||||||
|
PropNode node = get(key);
|
||||||
|
if(node instanceof IntNode)
|
||||||
|
return new int[] { ((IntNode) node).data };
|
||||||
|
return ((IntArrayNode) get(key)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAsDouble(String key) throws UndeclaredPropertyException {
|
||||||
|
PropNode node = get(key);
|
||||||
|
if(node instanceof IntNode)
|
||||||
|
return (double) ((IntNode) node).data;
|
||||||
|
return ((DoubleNode) get(key)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] getAsDoubleArray(String key) throws UndeclaredPropertyException {
|
||||||
|
PropNode node = get(key);
|
||||||
|
if(node instanceof DoubleNode)
|
||||||
|
return new double[] { ((DoubleNode) node).data };
|
||||||
|
if(node instanceof IntNode)
|
||||||
|
return new double[] { ((IntNode) node).data };
|
||||||
|
if(node instanceof IntArrayNode) {
|
||||||
|
int[] ints = getAsIntArray(key);
|
||||||
|
double[] ret = new double[ints.length];
|
||||||
|
for (int i = 0; i < ints.length; ++i)
|
||||||
|
ret[i] = ints[i];
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return ((DoubleArrayNode) get(key)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PropNode {
|
||||||
|
public String key;
|
||||||
|
public PropNode next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BlockNode extends PropNode {
|
||||||
|
public Properties data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StringNode extends PropNode {
|
||||||
|
public String data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IntNode extends PropNode {
|
||||||
|
public int data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IntArrayNode extends PropNode {
|
||||||
|
public int[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DoubleNode extends PropNode {
|
||||||
|
public double data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DoubleArrayNode extends PropNode {
|
||||||
|
public double[] data;
|
||||||
|
}
|
||||||
|
}
|
91
src/com/gnarwhal/ld48/engine/properties/PropertyReader.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package com.gnarwhal.ld48.engine.properties;
|
||||||
|
|
||||||
|
import com.gnarwhal.ld48.engine.properties.Properties.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
public class PropertyReader {
|
||||||
|
|
||||||
|
private static int lineNum;
|
||||||
|
private static String path;
|
||||||
|
|
||||||
|
public static Properties readProperties(String path) {
|
||||||
|
Properties props = null;
|
||||||
|
try {
|
||||||
|
File file = new File(path);
|
||||||
|
Scanner scanner = new Scanner(file);
|
||||||
|
PropertyReader.path = path;
|
||||||
|
lineNum = 0;
|
||||||
|
props = readBlock(file.getName(), scanner).data;
|
||||||
|
scanner.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockNode readBlock(String name, Scanner scanner) {
|
||||||
|
BlockNode props = new BlockNode();
|
||||||
|
props.key = name;
|
||||||
|
props.data = new Properties(name);
|
||||||
|
while (scanner.hasNextLine()) {
|
||||||
|
String line = scanner.nextLine();
|
||||||
|
line = line.replaceAll("\\s", "");
|
||||||
|
if(line.equals("}"))
|
||||||
|
break;
|
||||||
|
else if(line.length() < 2 || !line.substring(0, 2).equals("//")){
|
||||||
|
String[] pair = line.split(":");
|
||||||
|
if (pair.length != 2)
|
||||||
|
throw new ImproperFormattingException("Formatting exception on line " + line + " in file '" + path + "!");
|
||||||
|
pair[1] = pair[1].replaceAll("\\s", "");
|
||||||
|
if (pair[1].equals("{"))
|
||||||
|
props.data.add(readBlock(pair[0], scanner));
|
||||||
|
else if (pair[1].matches("(\\d+|0x[\\da-f]+)")) {
|
||||||
|
IntNode node = new IntNode();
|
||||||
|
node.key = pair[0];
|
||||||
|
node.data = Integer.decode(pair[1]);
|
||||||
|
props.data.add(node);
|
||||||
|
}
|
||||||
|
else if (pair[1].matches("(\\d+|0x[\\d0-9]+)(,(\\d+|0x[\\d0-9]+))+")) {
|
||||||
|
String[] data = pair[1].split(",");
|
||||||
|
int[] ints = new int[data.length];
|
||||||
|
for (int i = 0; i < ints.length; ++i)
|
||||||
|
ints[i] = Integer.decode(data[i]);
|
||||||
|
IntArrayNode node = new IntArrayNode();
|
||||||
|
node.key = pair[0];
|
||||||
|
node.data = ints;
|
||||||
|
props.data.add(node);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (pair[1].matches("\\d+\\.\\d+")) {
|
||||||
|
DoubleNode node = new DoubleNode();
|
||||||
|
node.key = pair[0];
|
||||||
|
node.data = Double.parseDouble(pair[1]);
|
||||||
|
props.data.add(node);
|
||||||
|
}
|
||||||
|
else if (pair[1].matches("\\d+\\.\\d+(,\\d+\\.\\d+)+")) {
|
||||||
|
String[] data = pair[1].split(",");
|
||||||
|
double[] doubles = new double[data.length];
|
||||||
|
for (int i = 0; i < doubles.length; ++i)
|
||||||
|
doubles[i] = Double.parseDouble(data[i]);
|
||||||
|
DoubleArrayNode node = new DoubleArrayNode();
|
||||||
|
node.key = pair[0];
|
||||||
|
node.data = doubles;
|
||||||
|
props.data.add(node);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
StringNode node = new StringNode();
|
||||||
|
node.key = pair[0];
|
||||||
|
node.data = pair[1];
|
||||||
|
props.data.add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++lineNum;
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.gnarwhal.ld48.engine.properties;
|
||||||
|
|
||||||
|
public class UndeclaredPropertyException extends Exception {
|
||||||
|
|
||||||
|
public UndeclaredPropertyException(String exception) {
|
||||||
|
super(exception);
|
||||||
|
}
|
||||||
|
}
|
22
src/com/gnarwhal/ld48/engine/shaders/GradientShader.java
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package com.gnarwhal.ld48.engine.shaders;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL20.*;
|
||||||
|
|
||||||
|
public class GradientShader extends Shader {
|
||||||
|
|
||||||
|
private int color_loc;
|
||||||
|
|
||||||
|
public GradientShader() {
|
||||||
|
super("res/shaders/gradient/vert.gls", "res/shaders/gradient/frag.gls");
|
||||||
|
getUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void getUniforms() {
|
||||||
|
color_loc = glGetUniformLocation(program, "input_color");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(float r, float g, float b, float a) {
|
||||||
|
glUniform4f(color_loc, r, g, b, a);
|
||||||
|
}
|
||||||
|
}
|
27
src/com/gnarwhal/ld48/engine/shaders/PlayerShader.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.gnarwhal.ld48.engine.shaders;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
|
||||||
|
import static org.lwjgl.opengl.GL20.glUniform1f;
|
||||||
|
|
||||||
|
public class PlayerShader extends Shader {
|
||||||
|
|
||||||
|
private int rotation_loc;
|
||||||
|
|
||||||
|
public PlayerShader() {
|
||||||
|
super("res/shaders/player/vert.gls", "res/shaders/player/frag.gls");
|
||||||
|
getUniforms();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void getUniforms() {
|
||||||
|
rotation_loc = glGetUniformLocation(program, "rotation");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRotation(float rotation) {
|
||||||
|
rotation = (rotation % 1) * 2;
|
||||||
|
if (rotation > 1) {
|
||||||
|
rotation -= 2;
|
||||||
|
}
|
||||||
|
glUniform1f(rotation_loc, rotation);
|
||||||
|
}
|
||||||
|
}
|
74
src/com/gnarwhal/ld48/engine/shaders/Shader.java
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package com.gnarwhal.ld48.engine.shaders;
|
||||||
|
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL20.*;
|
||||||
|
|
||||||
|
public abstract class Shader {
|
||||||
|
|
||||||
|
protected int program;
|
||||||
|
protected int mvpLoc;
|
||||||
|
|
||||||
|
protected Shader(String vertPath, String fragPath) {
|
||||||
|
program = glCreateProgram();
|
||||||
|
|
||||||
|
int vert = loadShader(vertPath, GL_VERTEX_SHADER);
|
||||||
|
int frag = loadShader(fragPath, GL_FRAGMENT_SHADER);
|
||||||
|
|
||||||
|
glAttachShader(program, vert);
|
||||||
|
glAttachShader(program, frag);
|
||||||
|
|
||||||
|
glLinkProgram(program);
|
||||||
|
|
||||||
|
glDetachShader(program, vert);
|
||||||
|
glDetachShader(program, frag);
|
||||||
|
|
||||||
|
glDeleteShader(vert);
|
||||||
|
glDeleteShader(frag);
|
||||||
|
|
||||||
|
mvpLoc = glGetUniformLocation(program, "mvp");
|
||||||
|
}
|
||||||
|
|
||||||
|
private int loadShader(String path, int type) {
|
||||||
|
StringBuilder file = new StringBuilder();
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new FileReader(new File(path)));
|
||||||
|
String line;
|
||||||
|
while((line = reader.readLine()) != null)
|
||||||
|
file.append(line + '\n');
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
String source = file.toString();
|
||||||
|
int shader = glCreateShader(type);
|
||||||
|
glShaderSource(shader, source);
|
||||||
|
glCompileShader(shader);
|
||||||
|
if(glGetShaderi(shader, GL_COMPILE_STATUS) != 1)
|
||||||
|
throw new RuntimeException("Failed to compile shader: " + path + "! " + glGetShaderInfoLog(shader));
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void getUniforms();
|
||||||
|
|
||||||
|
public void setMVP(Matrix4f matrix) {
|
||||||
|
glUniformMatrix4fv(mvpLoc, false, matrix.get(new float[16]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enable() {
|
||||||
|
glUseProgram(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disable() {
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
glDeleteProgram(program);
|
||||||
|
}
|
||||||
|
}
|
88
src/com/gnarwhal/ld48/engine/texture/Texture.java
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package com.gnarwhal.ld48.engine.texture;
|
||||||
|
|
||||||
|
import org.lwjgl.BufferUtils;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
|
||||||
|
import static org.lwjgl.opengl.GL13.glActiveTexture;
|
||||||
|
|
||||||
|
public class Texture {
|
||||||
|
|
||||||
|
protected int id, width, height;
|
||||||
|
|
||||||
|
public Texture(String name) {
|
||||||
|
this(name, GL_CLAMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture(String name, int wrap) {
|
||||||
|
BufferedImage bi = null;
|
||||||
|
try {
|
||||||
|
bi = ImageIO.read(new File(name));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (bi != null) {
|
||||||
|
width = bi.getWidth();
|
||||||
|
height = bi.getHeight();
|
||||||
|
int[] pixels = bi.getRGB(0, 0, width, height, null, 0, width);
|
||||||
|
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 4);
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
for (int j = 0; j < width; j++) {
|
||||||
|
int pixel = pixels[i * width + j];
|
||||||
|
buffer.put((byte)((pixel >> 16) & 0xFF)); // Red
|
||||||
|
buffer.put((byte)((pixel >> 8) & 0xFF)); // Green
|
||||||
|
buffer.put((byte)((pixel ) & 0xFF)); // Blue
|
||||||
|
buffer.put((byte)((pixel >> 24) & 0xFF)); // Alpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
id = glGenTextures();
|
||||||
|
bind();
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||||
|
unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture(int id, int width, int height) {
|
||||||
|
this.id = id;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind() {
|
||||||
|
bind(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(int activeTexture) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + activeTexture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbind() {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
glDeleteTextures(id);
|
||||||
|
}
|
||||||
|
}
|
27
src/com/gnarwhal/ld48/game/GamePanel.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.gnarwhal.ld48.game;
|
||||||
|
|
||||||
|
import com.gnarwhal.ld48.engine.display.Camera;
|
||||||
|
import com.gnarwhal.ld48.engine.display.Window;
|
||||||
|
|
||||||
|
public class GamePanel {
|
||||||
|
|
||||||
|
private Map map;
|
||||||
|
private Player player;
|
||||||
|
|
||||||
|
public GamePanel() {
|
||||||
|
map = new Map();
|
||||||
|
player = new Player();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Window window, Camera camera) {
|
||||||
|
player.move(window);
|
||||||
|
map.check_collisions(player);
|
||||||
|
player.update(camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(Camera camera) {
|
||||||
|
map.render_floor(camera);
|
||||||
|
player.render(camera);
|
||||||
|
map.render_walls(camera);
|
||||||
|
}
|
||||||
|
}
|
93
src/com/gnarwhal/ld48/game/Main.java
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
package com.gnarwhal.ld48.game;
|
||||||
|
|
||||||
|
import com.gnarwhal.ld48.engine.audio.ALManagement;
|
||||||
|
import com.gnarwhal.ld48.engine.display.Camera;
|
||||||
|
import com.gnarwhal.ld48.engine.display.Window;
|
||||||
|
import com.gnarwhal.ld48.engine.shaders.Shader;
|
||||||
|
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
public static int fps;
|
||||||
|
public static double dtime;
|
||||||
|
public static double adtime;
|
||||||
|
|
||||||
|
private static double freezeDuration;
|
||||||
|
private static double freezeTime;
|
||||||
|
|
||||||
|
private ALManagement al;
|
||||||
|
|
||||||
|
private Window window;
|
||||||
|
private Camera camera;
|
||||||
|
|
||||||
|
private GamePanel panel;
|
||||||
|
|
||||||
|
public static void freeze(float duration) {
|
||||||
|
freezeDuration = duration;
|
||||||
|
freezeTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
init();
|
||||||
|
int frames = 0;
|
||||||
|
long curTime, pastTime, pastSec, nspf = 1000000000 / Window.REFRESH_RATE;
|
||||||
|
pastTime = System.nanoTime();
|
||||||
|
pastSec = pastTime;
|
||||||
|
while(!window.shouldClose()) {
|
||||||
|
curTime = System.nanoTime();
|
||||||
|
if (curTime - pastTime > nspf) {
|
||||||
|
adtime = nspf / 1000000000d;
|
||||||
|
if (freezeDuration > freezeTime + adtime) {
|
||||||
|
dtime = 0;
|
||||||
|
} else if (freezeDuration > freezeTime) {
|
||||||
|
dtime = adtime - (freezeDuration - freezeTime);
|
||||||
|
} else {
|
||||||
|
dtime = adtime;
|
||||||
|
}
|
||||||
|
freezeTime += adtime;
|
||||||
|
update();
|
||||||
|
render();
|
||||||
|
pastTime += nspf;
|
||||||
|
++frames;
|
||||||
|
}
|
||||||
|
if (curTime - pastSec > 1000000000) {
|
||||||
|
fps = frames;
|
||||||
|
frames = 0;
|
||||||
|
pastSec += 1000000000;
|
||||||
|
}
|
||||||
|
if (nspf - curTime + pastTime > 10000000) try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
al.destroy();
|
||||||
|
Window.terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
al = new ALManagement();
|
||||||
|
|
||||||
|
final int WIN_WIDTH = 1920, WIN_HEIGHT = 1080;
|
||||||
|
window = new Window("Ludum Dare 48", true);
|
||||||
|
//window = new Window(WIN_WIDTH * 3/4, WIN_HEIGHT * 3/4, "Ludum Dare 48", true, true, true);
|
||||||
|
camera = new Camera(WIN_WIDTH, WIN_HEIGHT);
|
||||||
|
|
||||||
|
panel = new GamePanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
window.update();
|
||||||
|
panel.update(window, camera);
|
||||||
|
camera.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void render() {
|
||||||
|
window.clear();
|
||||||
|
panel.render(camera);
|
||||||
|
window.swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
new Main().start();
|
||||||
|
}
|
||||||
|
}
|
381
src/com/gnarwhal/ld48/game/Map.java
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
package com.gnarwhal.ld48.game;
|
||||||
|
|
||||||
|
import com.gnarwhal.ld48.engine.display.Camera;
|
||||||
|
import com.gnarwhal.ld48.engine.model.Vao;
|
||||||
|
import com.gnarwhal.ld48.engine.shaders.GradientShader;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class Map {
|
||||||
|
private static class Tile {
|
||||||
|
public static abstract class RenderPass {
|
||||||
|
protected float offset;
|
||||||
|
|
||||||
|
protected RenderPass(float offset) {
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void render(Camera camera, int x, int y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GradientPass extends RenderPass {
|
||||||
|
private static GradientShader shader = null;
|
||||||
|
|
||||||
|
private Vao vao;
|
||||||
|
|
||||||
|
private float r, g, b, a;
|
||||||
|
|
||||||
|
public GradientPass(Vao vao, float offset, float r, float g, float b, float a) {
|
||||||
|
super(offset);
|
||||||
|
|
||||||
|
if (shader == null) {
|
||||||
|
shader = new GradientShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.vao = vao;
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(Camera camera, int x, int y) {
|
||||||
|
shader.enable();
|
||||||
|
shader.setColor(r, g, b, a);
|
||||||
|
shader.setMVP(camera.getMatrix().translate(x * TILE_DIMS, y * TILE_DIMS - offset, 0).scale(TILE_DIMS, TILE_DIMS, 1));
|
||||||
|
vao.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
LEVEL_GROUND = 0,
|
||||||
|
LEVEL_WALL = 1;
|
||||||
|
|
||||||
|
public int level;
|
||||||
|
public boolean solid;
|
||||||
|
public ArrayList<RenderPass> render_passes;
|
||||||
|
|
||||||
|
public Tile(int level, boolean solid, ArrayList<RenderPass> render_passes) {
|
||||||
|
this.level = level;
|
||||||
|
this.solid = solid;
|
||||||
|
this.render_passes = render_passes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tile clone() {
|
||||||
|
return new Tile(
|
||||||
|
this.level,
|
||||||
|
this.solid,
|
||||||
|
this.render_passes == null ? null : new ArrayList<>(this.render_passes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float TILE_DIMS = 128.0f;
|
||||||
|
|
||||||
|
private static final ArrayList<Tile.RenderPass> TOP_GROUND_RENDER = new ArrayList<>();
|
||||||
|
private static final ArrayList<Tile.RenderPass> MID_GROUND_RENDER = new ArrayList<>();
|
||||||
|
private static final ArrayList<Tile.RenderPass>[] WALL_BORDER_RENDER = new ArrayList[256];
|
||||||
|
|
||||||
|
private static final Tile
|
||||||
|
GROUND_TILE = new Tile(Tile.LEVEL_GROUND, true, null),
|
||||||
|
WALL_TILE = new Tile(Tile.LEVEL_WALL, true, null);
|
||||||
|
|
||||||
|
private Tile[][] map;
|
||||||
|
|
||||||
|
public Map() {
|
||||||
|
if (TOP_GROUND_RENDER.size() == 0) {
|
||||||
|
Vao vao = new Vao(
|
||||||
|
new float[] {
|
||||||
|
1, 0, 0,
|
||||||
|
1, 1, 0,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 0
|
||||||
|
},
|
||||||
|
new int[] {
|
||||||
|
0, 1, 3,
|
||||||
|
1, 2, 3
|
||||||
|
}
|
||||||
|
);
|
||||||
|
vao.addAttrib(
|
||||||
|
new float[] { 0.5f, 0.5f, 0.5f, 0.5f },
|
||||||
|
1
|
||||||
|
);
|
||||||
|
Tile.GradientPass floor_pass = new Tile.GradientPass(vao, 0, 1, 1, 1, 1);
|
||||||
|
MID_GROUND_RENDER.add(floor_pass);
|
||||||
|
|
||||||
|
vao = new Vao(
|
||||||
|
new float[] {
|
||||||
|
1, 0, 0,
|
||||||
|
1, 0.75f, 0,
|
||||||
|
0, 0.75f, 0,
|
||||||
|
0, 0, 0
|
||||||
|
},
|
||||||
|
new int[] {
|
||||||
|
0, 1, 3,
|
||||||
|
1, 2, 3
|
||||||
|
}
|
||||||
|
);
|
||||||
|
vao.addAttrib(
|
||||||
|
new float[] { 0.35f, 1.0f, 1.0f, 0.35f },
|
||||||
|
1
|
||||||
|
);
|
||||||
|
Tile.GradientPass wall_pass = new Tile.GradientPass(vao, TILE_DIMS * 0.75f, 1, 1, 1, 1);
|
||||||
|
TOP_GROUND_RENDER.add(wall_pass);
|
||||||
|
TOP_GROUND_RENDER.add(floor_pass);
|
||||||
|
|
||||||
|
float[] vertices = new float[] {
|
||||||
|
0, 0, 0,
|
||||||
|
0.25f, 0, 0,
|
||||||
|
0.75f, 0, 0,
|
||||||
|
1, 0, 0,
|
||||||
|
1, 0.15f, 0,
|
||||||
|
1, 0.85f, 0,
|
||||||
|
1, 1, 0,
|
||||||
|
0.75f, 1, 0,
|
||||||
|
0.25f, 1, 0,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0.85f, 0,
|
||||||
|
0, 0.15f, 0,
|
||||||
|
0.25f, 0.15f, 0,
|
||||||
|
0.75f, 0.15f, 0,
|
||||||
|
0.75f, 0.85f, 0,
|
||||||
|
0.25f, 0.85f, 0
|
||||||
|
};
|
||||||
|
int[] indices = new int[] {
|
||||||
|
0, 11, 12,
|
||||||
|
0, 12, 1,
|
||||||
|
1, 12, 2,
|
||||||
|
12, 13, 2,
|
||||||
|
2, 13, 3,
|
||||||
|
3, 13, 4,
|
||||||
|
4, 13, 5,
|
||||||
|
13, 14, 5,
|
||||||
|
5, 14, 6,
|
||||||
|
6, 14, 7,
|
||||||
|
7, 14, 8,
|
||||||
|
14, 15, 8,
|
||||||
|
8, 15, 9,
|
||||||
|
9, 15, 10,
|
||||||
|
10, 15, 11,
|
||||||
|
15, 12, 11,
|
||||||
|
12, 15, 13,
|
||||||
|
13, 15, 14
|
||||||
|
};
|
||||||
|
|
||||||
|
float[] values = new float[16];
|
||||||
|
for (int i = 0; i <= 0b11111111; ++i) {
|
||||||
|
for (int j = 0; j < 16; ++j) {
|
||||||
|
values[j] = 0;
|
||||||
|
}
|
||||||
|
int mask = i;
|
||||||
|
for (int j = 0; j < 4; ++j) {
|
||||||
|
if ((mask & 1) != 0) {
|
||||||
|
for (int k = 0; k < 4; ++k) {
|
||||||
|
values[(j * 3 + k) % 12] = 0.25f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < 4; ++j) {
|
||||||
|
if ((mask & 1) != 0) {
|
||||||
|
values[j * 3] = 0.25f;
|
||||||
|
}
|
||||||
|
mask >>= 1;
|
||||||
|
}
|
||||||
|
vao = new Vao(vertices, indices);
|
||||||
|
vao.addAttrib(values, 1);
|
||||||
|
|
||||||
|
ArrayList<Tile.RenderPass> render_pass = new ArrayList<>();
|
||||||
|
render_pass.add(new Tile.GradientPass(vao, TILE_DIMS * 0.75f, 1, 1, 1, 1));
|
||||||
|
WALL_BORDER_RENDER[i] = render_pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final int WALL_COLOR = 0xFF000000;
|
||||||
|
final int GROUND_COLOR = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
BufferedImage map_layout = ImageIO.read(new File("res/map/layout.png"));
|
||||||
|
map = new Tile[map_layout.getWidth()][map_layout.getHeight()];
|
||||||
|
for (int x = 0; x < map_layout.getWidth(); ++x) {
|
||||||
|
for (int y = 0; y < map_layout.getHeight(); ++y) {
|
||||||
|
if (map_layout.getRGB(x, y) == WALL_COLOR) {
|
||||||
|
map[x][y] = WALL_TILE.clone();
|
||||||
|
} else if (map_layout.getRGB(x, y) == GROUND_COLOR) {
|
||||||
|
map[x][y] = GROUND_TILE.clone();
|
||||||
|
if (y > 0 && map[x][y - 1].level == Tile.LEVEL_WALL) {
|
||||||
|
map[x][y].render_passes = TOP_GROUND_RENDER;
|
||||||
|
} else {
|
||||||
|
map[x][y].render_passes = MID_GROUND_RENDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int x = 0; x < map_layout.getWidth(); ++x) {
|
||||||
|
for (int y = 0; y < map_layout.getHeight(); ++y) {
|
||||||
|
if (map[x][y].level == Tile.LEVEL_WALL) {
|
||||||
|
int result = 0;
|
||||||
|
if ( y > 0 && map[x ][y - 1].level == Tile.LEVEL_GROUND) { result |= 1 << 0; }
|
||||||
|
if (x < map.length - 1 && map[x + 1][y ].level == Tile.LEVEL_GROUND) { result |= 1 << 1; }
|
||||||
|
if ( y < map[0].length - 1 && map[x ][y + 1].level == Tile.LEVEL_GROUND) { result |= 1 << 2; }
|
||||||
|
if (x > 0 && map[x - 1][y ].level == Tile.LEVEL_GROUND) { result |= 1 << 3; }
|
||||||
|
if (x > 0 && y > 0 && map[x - 1][y - 1].level == Tile.LEVEL_GROUND) { result |= 1 << 4; }
|
||||||
|
if (x < map.length - 1 && y > 0 && map[x + 1][y - 1].level == Tile.LEVEL_GROUND) { result |= 1 << 5; }
|
||||||
|
if (x < map.length - 1 && y < map[0].length - 1 && map[x + 1][y + 1].level == Tile.LEVEL_GROUND) { result |= 1 << 6; }
|
||||||
|
if (x > 0 && y < map[0].length - 1 && map[x - 1][y + 1].level == Tile.LEVEL_GROUND) { result |= 1 << 7; }
|
||||||
|
map[x][y].render_passes = new ArrayList<>(WALL_BORDER_RENDER[result]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void check_collisions(Player player) {
|
||||||
|
Vector2f player_motion = new Vector2f(player.velocity);
|
||||||
|
Vector2f player_position = player.base_position.add(0, 0.5f * TILE_DIMS, new Vector2f());
|
||||||
|
|
||||||
|
Vector2f player_pos_min = new Vector2f(
|
||||||
|
Math.min(0, player_motion.x),
|
||||||
|
Math.min(0, player_motion.y)
|
||||||
|
).add(player_position).add(new Vector2f(-Player.PLAYER_DIMS * 0.5f));
|
||||||
|
Vector2f player_pos_max = new Vector2f(
|
||||||
|
Math.max(0, player_motion.x),
|
||||||
|
Math.max(0, player_motion.y)
|
||||||
|
).add(player_position).add(new Vector2f(Player.PLAYER_DIMS * 0.5f));
|
||||||
|
|
||||||
|
int min_x = (int) (player_pos_min.x / TILE_DIMS);
|
||||||
|
int min_y = (int) (player_pos_min.y / TILE_DIMS);
|
||||||
|
int max_x = (int) (player_pos_max.x / TILE_DIMS);
|
||||||
|
int max_y = (int) (player_pos_max.y / TILE_DIMS);
|
||||||
|
|
||||||
|
float min_t = 0.0f;
|
||||||
|
while (min_t < 1.0f) {
|
||||||
|
player_position = player.base_position.add(0, 0.5f * TILE_DIMS, new Vector2f());
|
||||||
|
min_t = 1.0f;
|
||||||
|
|
||||||
|
float neutralization_direction = 0.0f;
|
||||||
|
for (int x = min_x; x <= max_x; ++x) {
|
||||||
|
for (int y = min_y; y <= max_y; ++y) {
|
||||||
|
if (map[x][y].level == Tile.LEVEL_WALL) {
|
||||||
|
Vector2f[] recall_distances = new Vector2f[] {
|
||||||
|
check_intersection(x, y, player_position.add(-Player.PLAYER_DIMS * 0.5f, -Player.PLAYER_DIMS * 0.5f, new Vector2f()), player_motion),
|
||||||
|
check_intersection(x, y, player_position.add( Player.PLAYER_DIMS * 0.5f, -Player.PLAYER_DIMS * 0.5f, new Vector2f()), player_motion),
|
||||||
|
check_intersection(x, y, player_position.add(-Player.PLAYER_DIMS * 0.5f, Player.PLAYER_DIMS * 0.5f, new Vector2f()), player_motion),
|
||||||
|
check_intersection(x, y, player_position.add( Player.PLAYER_DIMS * 0.5f, Player.PLAYER_DIMS * 0.5f, new Vector2f()), player_motion)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (recall_distances[i] != null && recall_distances[i].x < min_t) {
|
||||||
|
min_t = recall_distances[i].x;
|
||||||
|
neutralization_direction = recall_distances[i].y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min_t < 1.0f) {
|
||||||
|
Vector2f valid_motion = player_motion.mul(min_t, new Vector2f());
|
||||||
|
|
||||||
|
player.base_position.add(valid_motion);
|
||||||
|
player_motion.sub(valid_motion).mul(neutralization_direction, 1 - neutralization_direction);
|
||||||
|
|
||||||
|
player.proc_collision();
|
||||||
|
|
||||||
|
if (player_motion.lengthSquared() < 0.001f) {
|
||||||
|
min_t = 1.0f;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.base_position.add(player_motion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2f check_intersection(int x, int y, Vector2f position, Vector2f motion) {
|
||||||
|
Vector2f wall_start = new Vector2f(x * TILE_DIMS, y * TILE_DIMS);
|
||||||
|
Vector2f wall_end = new Vector2f(0, TILE_DIMS);
|
||||||
|
|
||||||
|
Vector2f min = new Vector2f(1, 0);
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (motion.dot(new Vector2f(-wall_end.y, wall_end.x)) < 0) {
|
||||||
|
float t0, t1;
|
||||||
|
if (motion.y == 0) {
|
||||||
|
t0 = (motion.y * (wall_start.x - position.x) / motion.x + position.y - wall_start.y) / (wall_end.y - motion.y * wall_end.x / motion.x);
|
||||||
|
t1 = (wall_end.x * t0 + wall_start.x - position.x) / motion.x;
|
||||||
|
} else {
|
||||||
|
t0 = (motion.x * (wall_start.y - position.y) / motion.y + position.x - wall_start.x) / (wall_end.x - motion.x * wall_end.y / motion.y);
|
||||||
|
t1 = (wall_end.y * t0 + wall_start.y - position.y) / motion.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 < t0 && t0 < 1
|
||||||
|
&& -0.0001f <= t1 && t1 < 1
|
||||||
|
&& t1 < min.x) {
|
||||||
|
min.set(t1, i % 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
a0 * t + b0 = c0 * s + d0
|
||||||
|
a1 * t + b1 = c1 * s + d1
|
||||||
|
|
||||||
|
s = (a0 * t + b0 - d0) / c0
|
||||||
|
a1 * t + b1 = c1 * (a0 * t + b0 - d0) / c0 + d1
|
||||||
|
a1 * t + b1 - c1 * a0 * t / c0 = c1 * (b0 - d0) / c0 + d1
|
||||||
|
(a1 - c1 * a0 / c0) * t + b1 = c1 * (b0 - d0) / c0 + d1
|
||||||
|
t = (c1 * (b0 - d0) / c0 + d1 - b1) / (a1 - c1 * a0 / c0)
|
||||||
|
*/
|
||||||
|
|
||||||
|
wall_start.add(wall_end);
|
||||||
|
wall_end.set(wall_end.y, -wall_end.x);
|
||||||
|
}
|
||||||
|
if (min.x < 1) {
|
||||||
|
return min;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render_floor(Camera camera) {
|
||||||
|
int min_x = (int) Math.max((camera.getX() / TILE_DIMS), 0);
|
||||||
|
int min_y = (int) Math.max((camera.getY() / TILE_DIMS), 0);
|
||||||
|
int max_x = (int) Math.min(((camera.getX() + camera.getWidth()) / TILE_DIMS), map.length - 1);
|
||||||
|
int max_y = (int) Math.min(((camera.getY() + camera.getHeight()) / TILE_DIMS) + 1, map[0].length - 1);
|
||||||
|
for (int x = min_x; x <= max_x; ++x) {
|
||||||
|
for (int y = min_y; y <= max_y; ++y) {
|
||||||
|
if (map[x][y].level == Tile.LEVEL_GROUND) {
|
||||||
|
ArrayList<Tile.RenderPass> render_passes = map[x][y].render_passes;
|
||||||
|
if (render_passes != null) {
|
||||||
|
for (int i = 0; i < render_passes.size(); ++i) {
|
||||||
|
render_passes.get(i).render(camera, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render_walls(Camera camera) {
|
||||||
|
int min_x = (int) Math.max((camera.getX() / TILE_DIMS), 0);
|
||||||
|
int min_y = (int) Math.max(((camera.getY() + 0.75f * TILE_DIMS) / TILE_DIMS), 0);
|
||||||
|
int max_x = (int) Math.min(((camera.getX() + camera.getWidth()) / TILE_DIMS), map.length - 1);
|
||||||
|
int max_y = (int) Math.min(((camera.getY() + camera.getHeight() + 0.75f * TILE_DIMS) / TILE_DIMS), map[0].length - 1);
|
||||||
|
for (int x = min_x; x <= max_x; ++x) {
|
||||||
|
for (int y = min_y; y <= max_y; ++y) {
|
||||||
|
if (map[x][y].level == Tile.LEVEL_WALL) {
|
||||||
|
ArrayList<Tile.RenderPass> render_passes = map[x][y].render_passes;
|
||||||
|
if (render_passes != null) {
|
||||||
|
for (int i = 0; i < render_passes.size(); ++i) {
|
||||||
|
render_passes.get(i).render(camera, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
419
src/com/gnarwhal/ld48/game/Player.java
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
package com.gnarwhal.ld48.game;
|
||||||
|
|
||||||
|
import com.gnarwhal.ld48.engine.display.Camera;
|
||||||
|
import com.gnarwhal.ld48.engine.display.Window;
|
||||||
|
import com.gnarwhal.ld48.engine.model.Vao;
|
||||||
|
import com.gnarwhal.ld48.engine.shaders.PlayerShader;
|
||||||
|
import com.gnarwhal.ld48.engine.texture.Texture;
|
||||||
|
import org.joml.Vector2f;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
|
||||||
|
public class Player {
|
||||||
|
private static class Particle {
|
||||||
|
public Vector2f position;
|
||||||
|
public Vector2f velocity;
|
||||||
|
public float dimensions;
|
||||||
|
|
||||||
|
public float kill_hour;
|
||||||
|
public float clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final float PLAYER_DIMS = 64.0f;
|
||||||
|
|
||||||
|
public static final int
|
||||||
|
EXPR_NORMAL = 0,
|
||||||
|
EXPR_THREE_SMOAKS = 1,
|
||||||
|
EXPR_CONFUSED = 2,
|
||||||
|
EXPR_SQUINT = 3;
|
||||||
|
|
||||||
|
private static final float
|
||||||
|
HOVER_FLUCTUATION = 42.0f,
|
||||||
|
HOVER_CYCLE_RATE = 2.5f;
|
||||||
|
|
||||||
|
private static final int
|
||||||
|
NO_ACTION = 0,
|
||||||
|
QUICK_ATTACK = 1,
|
||||||
|
DASH = 2;
|
||||||
|
|
||||||
|
public float hover_offset;
|
||||||
|
public float hover_clock;
|
||||||
|
public Vector2f base_position;
|
||||||
|
public Vector2f position;
|
||||||
|
public Vector2f velocity;
|
||||||
|
|
||||||
|
private static PlayerShader shader;
|
||||||
|
private static Vao vao;
|
||||||
|
|
||||||
|
private Texture body;
|
||||||
|
private Texture[] eyes;
|
||||||
|
private Texture particle;
|
||||||
|
|
||||||
|
private int expression;
|
||||||
|
private float eye_rotation;
|
||||||
|
private float direction;
|
||||||
|
|
||||||
|
private float rate_bias;
|
||||||
|
private float spawn_trigger;
|
||||||
|
private float position_bias;
|
||||||
|
private float target_interp_clock;
|
||||||
|
private float particle_spawn_offset;
|
||||||
|
private Vector2f particle_base_target;
|
||||||
|
private Vector2f particle_target;
|
||||||
|
private ArrayList<Particle> particles;
|
||||||
|
|
||||||
|
private int performing_action;
|
||||||
|
private float action_clock;
|
||||||
|
private float action_progress;
|
||||||
|
private boolean cancel_action;
|
||||||
|
|
||||||
|
private float quick_attack_rotation;
|
||||||
|
private float vertical_offset;
|
||||||
|
private Vector2f quick_attack_direction;
|
||||||
|
|
||||||
|
private Vector2f dash_direction;
|
||||||
|
|
||||||
|
public Player() {
|
||||||
|
if (vao == null) {
|
||||||
|
shader = new PlayerShader();
|
||||||
|
vao = new Vao(
|
||||||
|
new float[] {
|
||||||
|
0.5f, -0.5f, 0, // Top left
|
||||||
|
0.5f, 0.5f, 0, // Bottom left
|
||||||
|
-0.5f, 0.5f, 0, // Bottom right
|
||||||
|
-0.5f, -0.5f, 0 // Top right
|
||||||
|
},
|
||||||
|
new int[] {
|
||||||
|
0, 1, 3,
|
||||||
|
1, 2, 3
|
||||||
|
}
|
||||||
|
);
|
||||||
|
vao.addAttrib(
|
||||||
|
new float[] {
|
||||||
|
1, 0,
|
||||||
|
1, 1,
|
||||||
|
0, 1,
|
||||||
|
0, 0
|
||||||
|
},
|
||||||
|
2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
body = new Texture("res/img/player/body.png");
|
||||||
|
eyes = new Texture[] {
|
||||||
|
new Texture("res/img/player/normal_eyes.png"),
|
||||||
|
new Texture("res/img/player/three_smoaks_eyes.png"),
|
||||||
|
new Texture("res/img/player/confused_eyes.png"),
|
||||||
|
new Texture("res/img/player/squint_eyes.png")
|
||||||
|
};
|
||||||
|
particle = new Texture("res/img/player/particle.png");
|
||||||
|
|
||||||
|
hover_offset = 0.0f;
|
||||||
|
base_position = new Vector2f(5 * Map.TILE_DIMS + PLAYER_DIMS * 0.5f, 8 * Map.TILE_DIMS);
|
||||||
|
position = new Vector2f(base_position);
|
||||||
|
velocity = new Vector2f();
|
||||||
|
|
||||||
|
expression = EXPR_NORMAL;
|
||||||
|
direction = 1.0f;
|
||||||
|
|
||||||
|
particles = new ArrayList<>();
|
||||||
|
rate_bias = 1.0f;
|
||||||
|
spawn_trigger = 0.0f;
|
||||||
|
target_interp_clock = 1.0f;
|
||||||
|
particle_spawn_offset = 0.0f;
|
||||||
|
particle_base_target = new Vector2f(-PLAYER_DIMS, -PLAYER_DIMS * 1.5f);
|
||||||
|
particle_target = new Vector2f(particle_base_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float rescale(float progress, float start, float end) {
|
||||||
|
return (progress - start) / (end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void move(Window window) {
|
||||||
|
final float RUN_VELOCITY = Map.TILE_DIMS * 3.5f;
|
||||||
|
final float WALK_VELOCITY = Map.TILE_DIMS * 1.5f;
|
||||||
|
final float QUICK_ATTACK_VELOCITY = Map.TILE_DIMS * 0.5f;
|
||||||
|
final float VERTICAL_VELOCITY_SCALAR = 0.75f;
|
||||||
|
|
||||||
|
float target_velocity = RUN_VELOCITY;
|
||||||
|
if (window.keyPressed(GLFW.GLFW_KEY_LEFT_CONTROL) >= Window.BUTTON_PRESSED || window.controllerButtonPressed(GLFW_GAMEPAD_BUTTON_B) >= Window.BUTTON_PRESSED) {
|
||||||
|
target_velocity = WALK_VELOCITY;
|
||||||
|
}
|
||||||
|
if (performing_action == QUICK_ATTACK) {
|
||||||
|
target_velocity = QUICK_ATTACK_VELOCITY;
|
||||||
|
} else if (performing_action == DASH) {
|
||||||
|
target_velocity = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2f input_velocity = new Vector2f(0);
|
||||||
|
if (window.joystick(GLFW_JOYSTICK_1)) {
|
||||||
|
input_velocity.x = window.getJoystickAxis(GLFW_GAMEPAD_AXIS_LEFT_X);
|
||||||
|
input_velocity.y = window.getJoystickAxis(GLFW_GAMEPAD_AXIS_LEFT_Y);
|
||||||
|
if (Math.abs(input_velocity.x) < 0.25f) { input_velocity.x = 0; }
|
||||||
|
if (Math.abs(input_velocity.y) < 0.25f) { input_velocity.y = 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_velocity.lengthSquared() == 0) {
|
||||||
|
if (window.keyPressed(GLFW.GLFW_KEY_A) >= Window.BUTTON_PRESSED) {
|
||||||
|
input_velocity.x -= 1;
|
||||||
|
}
|
||||||
|
if (window.keyPressed(GLFW.GLFW_KEY_D) >= Window.BUTTON_PRESSED) {
|
||||||
|
input_velocity.x += 1;
|
||||||
|
}
|
||||||
|
if (window.keyPressed(GLFW.GLFW_KEY_W) >= Window.BUTTON_PRESSED) {
|
||||||
|
input_velocity.y -= 1;
|
||||||
|
}
|
||||||
|
if (window.keyPressed(GLFW.GLFW_KEY_S) >= Window.BUTTON_PRESSED) {
|
||||||
|
input_velocity.y += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_velocity.lengthSquared() != 0) {
|
||||||
|
input_velocity.normalize(target_velocity);
|
||||||
|
input_velocity.y *= VERTICAL_VELOCITY_SCALAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action_clock < 0.0f) {
|
||||||
|
action_clock += Main.dtime;
|
||||||
|
} else if (performing_action == NO_ACTION) {
|
||||||
|
if (window.keyPressed(GLFW.GLFW_KEY_SPACE) == Window.BUTTON_PRESSED || window.controllerButtonPressed(GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER) == Window.BUTTON_PRESSED) {
|
||||||
|
performing_action = QUICK_ATTACK;
|
||||||
|
action_clock = 0.0f;
|
||||||
|
if (input_velocity.lengthSquared() != 0) {
|
||||||
|
quick_attack_direction = input_velocity.normalize(new Vector2f());
|
||||||
|
} else {
|
||||||
|
quick_attack_direction = new Vector2f(direction, 0);
|
||||||
|
}
|
||||||
|
} else if (window.keyPressed(GLFW.GLFW_KEY_LEFT_SHIFT) == Window.BUTTON_PRESSED || window.controllerButtonPressed(GLFW_GAMEPAD_BUTTON_LEFT_BUMPER) == Window.BUTTON_PRESSED) {
|
||||||
|
performing_action = DASH;
|
||||||
|
action_clock = 0.0f;
|
||||||
|
if (input_velocity.lengthSquared() != 0) {
|
||||||
|
dash_direction = input_velocity.normalize(new Vector2f());
|
||||||
|
} else {
|
||||||
|
dash_direction = new Vector2f(direction, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final float MAX_BIAS = 0.25f;
|
||||||
|
rate_bias = Math.max(MAX_BIAS, lerp(MAX_BIAS, 1.0f, 1 - (input_velocity.length() / RUN_VELOCITY)));
|
||||||
|
|
||||||
|
if (performing_action == DASH) {
|
||||||
|
action_clock += Main.dtime;
|
||||||
|
|
||||||
|
final float DASH_TIME = 0.15f;
|
||||||
|
final float DASH_COOLDOWN = 0.25f;
|
||||||
|
if (cancel_action) {
|
||||||
|
action_clock = DASH_TIME;
|
||||||
|
cancel_action = false;
|
||||||
|
}
|
||||||
|
if (action_clock >= DASH_TIME) {
|
||||||
|
performing_action = NO_ACTION;
|
||||||
|
action_clock = -DASH_COOLDOWN;
|
||||||
|
expression = EXPR_NORMAL;
|
||||||
|
rate_bias = 1.0f;
|
||||||
|
|
||||||
|
particle_spawn_offset = 0;
|
||||||
|
} else {
|
||||||
|
rate_bias = 0.01f;
|
||||||
|
particle_spawn_offset = dash_direction.angle(new Vector2f(direction, -1)) * 2.0f / (float) Math.PI;
|
||||||
|
final float DASH_SPEED = Map.TILE_DIMS * 15;
|
||||||
|
input_velocity.set(dash_direction).mul(DASH_SPEED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (performing_action == NO_ACTION && direction * input_velocity.x < 0) {
|
||||||
|
direction = -direction;
|
||||||
|
target_interp_clock = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
velocity = input_velocity.mul((float) Main.dtime);
|
||||||
|
|
||||||
|
if (window.keyPressed(GLFW_KEY_Q) == Window.BUTTON_PRESSED) {
|
||||||
|
velocity.x = -400;
|
||||||
|
velocity.y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void proc_collision() {}
|
||||||
|
|
||||||
|
private float lerp(float start, float end, float lerp) {
|
||||||
|
return start + (end - start) * lerp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(Camera camera) {
|
||||||
|
camera.setCenter(base_position.x, base_position.y);
|
||||||
|
|
||||||
|
if (performing_action == NO_ACTION) {
|
||||||
|
hover_clock = (hover_clock + (float) Main.dtime) % HOVER_CYCLE_RATE;
|
||||||
|
hover_offset = (float) Math.sin(2 * Math.PI * hover_clock * (1 / HOVER_CYCLE_RATE)) * HOVER_FLUCTUATION * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
position.set(base_position);
|
||||||
|
if (performing_action == QUICK_ATTACK) {
|
||||||
|
action_clock += Main.dtime;
|
||||||
|
|
||||||
|
final float QUICK_ATTACK_TIME = 0.5f;
|
||||||
|
final float QUICK_ATTACK_COOLDOWN = 0.1f;
|
||||||
|
if (cancel_action) {
|
||||||
|
action_clock = QUICK_ATTACK_TIME;
|
||||||
|
cancel_action = false;
|
||||||
|
}
|
||||||
|
if (action_clock >= QUICK_ATTACK_TIME) {
|
||||||
|
performing_action = NO_ACTION;
|
||||||
|
action_clock = -QUICK_ATTACK_COOLDOWN;
|
||||||
|
expression = EXPR_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
action_progress = action_clock / QUICK_ATTACK_TIME;
|
||||||
|
|
||||||
|
vertical_offset = 0;
|
||||||
|
quick_attack_rotation = 0;
|
||||||
|
eye_rotation = 0;
|
||||||
|
particle_spawn_offset = 0;
|
||||||
|
particle_target.set(particle_base_target);
|
||||||
|
|
||||||
|
if (action_progress < 0.5f) {
|
||||||
|
float smooth = (1 - (float) Math.cos(Math.PI * 2 * rescale(action_progress, 0, 0.6f))) * 0.5f;
|
||||||
|
|
||||||
|
final float RECOIL_AMOUNT = PLAYER_DIMS * 0.4f;
|
||||||
|
vertical_offset = -smooth * RECOIL_AMOUNT;
|
||||||
|
|
||||||
|
position.y += vertical_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action_progress > 0.25f) {
|
||||||
|
float progress = rescale(action_progress, 0.15f, 1);
|
||||||
|
quick_attack_rotation = (float) Math.PI * 2 * progress * direction;
|
||||||
|
eye_rotation = progress;
|
||||||
|
particle_spawn_offset = progress * 4 * -direction;
|
||||||
|
|
||||||
|
final float PARTICLE_BOOST = 2;
|
||||||
|
|
||||||
|
rate_bias = 0.1f;
|
||||||
|
float sin = (float) Math.sin(quick_attack_rotation);
|
||||||
|
float cos = (float) Math.cos(quick_attack_rotation);
|
||||||
|
particle_target.set(
|
||||||
|
cos * particle_base_target.x - sin * particle_base_target.y,
|
||||||
|
sin * particle_base_target.x + cos * particle_base_target.y
|
||||||
|
).mul(PARTICLE_BOOST);
|
||||||
|
|
||||||
|
final float ORTHOGONAL = PLAYER_DIMS * 0.5f;
|
||||||
|
final float PARALLEL = PLAYER_DIMS * 1.15f;
|
||||||
|
|
||||||
|
position
|
||||||
|
.add(quick_attack_direction.mul((1 - (float) Math.cos(quick_attack_rotation)) * PARALLEL, new Vector2f()))
|
||||||
|
.add(new Vector2f(-quick_attack_direction.y, quick_attack_direction.x).mul((float) Math.sin(quick_attack_rotation) * ORTHOGONAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////// PARTICLE SYSTEM ////////
|
||||||
|
|
||||||
|
for (int i = 0; i < particles.size(); ++i) {
|
||||||
|
Particle p = particles.get(i);
|
||||||
|
p.clock += Main.dtime;
|
||||||
|
if (p.clock >= p.kill_hour) {
|
||||||
|
particles.remove(i);
|
||||||
|
--i;
|
||||||
|
} else {
|
||||||
|
final float PARTICLE_MIN_DIMS = 16.0f;
|
||||||
|
final float PARTICLE_MAX_DIMS = 28.0f;
|
||||||
|
|
||||||
|
float interp = p.clock / p.kill_hour;
|
||||||
|
p.dimensions = lerp(PARTICLE_MAX_DIMS, PARTICLE_MIN_DIMS, interp);
|
||||||
|
p.position.add(p.velocity.mul((float) Main.dtime, new Vector2f()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final float TURN_RATE = 0.7f;
|
||||||
|
target_interp_clock = Math.min(target_interp_clock + (float) Main.dtime / TURN_RATE, 1);
|
||||||
|
|
||||||
|
spawn_trigger -= Main.dtime;
|
||||||
|
if (spawn_trigger < 0) {
|
||||||
|
Particle p = new Particle();
|
||||||
|
|
||||||
|
final float PARTICLE_MIN_LIFETIME = 0.5f;
|
||||||
|
final float PARTICLE_MAX_LIFETIME = 1.0f;
|
||||||
|
p.kill_hour = lerp(PARTICLE_MIN_LIFETIME, PARTICLE_MAX_LIFETIME, (float) Math.random());
|
||||||
|
|
||||||
|
Vector2f spawn_position = new Vector2f(position).add(0, hover_offset);
|
||||||
|
position_bias = (4.5f + direction * 0.5f + (float) Math.random() * 2 - 1 + particle_spawn_offset) % 4;
|
||||||
|
if (0 <= position_bias && position_bias < 1) {
|
||||||
|
spawn_position.add(
|
||||||
|
-PLAYER_DIMS * 0.4f * (position_bias * 2.0f - 1.0f),
|
||||||
|
-PLAYER_DIMS * 0.4f
|
||||||
|
);
|
||||||
|
} else if (1 <= position_bias && position_bias < 2) {
|
||||||
|
position_bias -= 1;
|
||||||
|
spawn_position.add(
|
||||||
|
-PLAYER_DIMS * 0.4f,
|
||||||
|
PLAYER_DIMS * 0.4f * (position_bias * 2.0f - 1.0f)
|
||||||
|
);
|
||||||
|
} else if (2 <= position_bias && position_bias < 3) {
|
||||||
|
position_bias -= 2;
|
||||||
|
spawn_position.add(
|
||||||
|
PLAYER_DIMS * 0.4f * (position_bias * 2.0f - 1.0f),
|
||||||
|
PLAYER_DIMS * 0.4f
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
position_bias -= 3;
|
||||||
|
spawn_position.add(
|
||||||
|
PLAYER_DIMS * 0.4f,
|
||||||
|
-PLAYER_DIMS * 0.4f * (position_bias * 2.0f - 1.0f)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
p.position = spawn_position;
|
||||||
|
|
||||||
|
p.velocity = new Vector2f(particle_target).mul((2 * target_interp_clock - 1) * direction, 1).add(position).sub(spawn_position).add(velocity.mul(0.1f, new Vector2f()));
|
||||||
|
|
||||||
|
particles.add(p);
|
||||||
|
|
||||||
|
final float BASE_SPAWN_RATE = 0.15f;
|
||||||
|
spawn_trigger = BASE_SPAWN_RATE * rate_bias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render(Camera camera) {
|
||||||
|
shader.enable();
|
||||||
|
|
||||||
|
particle.bind();
|
||||||
|
for (int i = 0; i < particles.size(); ++i) {
|
||||||
|
Particle p = particles.get(i);
|
||||||
|
shader.setMVP(
|
||||||
|
camera
|
||||||
|
.getMatrix()
|
||||||
|
.translate(
|
||||||
|
p.position.x,
|
||||||
|
p.position.y,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.scaleXY(
|
||||||
|
p.dimensions,
|
||||||
|
p.dimensions
|
||||||
|
)
|
||||||
|
);
|
||||||
|
vao.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
shader.setMVP(
|
||||||
|
camera
|
||||||
|
.getMatrix()
|
||||||
|
.translate(
|
||||||
|
position.x,
|
||||||
|
position.y + hover_offset,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.scaleXY(direction * PLAYER_DIMS, PLAYER_DIMS));
|
||||||
|
|
||||||
|
shader.setRotation(0);
|
||||||
|
body.bind();
|
||||||
|
vao.render();
|
||||||
|
|
||||||
|
shader.setRotation(eye_rotation);
|
||||||
|
eyes[expression].bind();
|
||||||
|
vao.render();
|
||||||
|
}
|
||||||
|
}
|