Add support for textures
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -132,4 +132,7 @@ gradle-app.setting
|
||||
### Gradle Patch ###
|
||||
**/build/
|
||||
|
||||
### Textures and other resources ###
|
||||
*.png
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/java,gradle,intellij
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.bartlomiejpluta.samplegame.core.gl.object.material;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.gl.object.texture.Texture;
|
||||
import lombok.Getter;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
@Getter
|
||||
public class Material {
|
||||
private final Vector4f color = new Vector4f();
|
||||
private final Texture texture;
|
||||
|
||||
private Material(float r, float g, float b, float alpha) {
|
||||
private Material(Texture texture, float r, float g, float b, float alpha) {
|
||||
this.texture = texture;
|
||||
setColor(r, g, b, alpha);
|
||||
}
|
||||
|
||||
@@ -18,7 +21,29 @@ public class Material {
|
||||
color.w = alpha;
|
||||
}
|
||||
|
||||
public boolean hasTexture() {
|
||||
return texture != null;
|
||||
}
|
||||
|
||||
public void activateTextureIfExists() {
|
||||
if(hasTexture()) {
|
||||
texture.activate();
|
||||
}
|
||||
}
|
||||
|
||||
public static Material colored(float r, float g, float b, float alpha) {
|
||||
return new Material(r, g, b, alpha);
|
||||
return new Material(null, r, g, b, alpha);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture) {
|
||||
return new Material(texture, 1, 1, 1, 1);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture, float r, float g, float b, float alpha) {
|
||||
return new Material(texture, r, g, b, alpha);
|
||||
}
|
||||
|
||||
public static Material textured(Texture texture, float alpha) {
|
||||
return new Material(texture, 1, 1, 1, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,14 @@ public class Mesh implements Renderable {
|
||||
@Setter
|
||||
private Material material;
|
||||
|
||||
public Mesh(float[] vertices, int[] elements) {
|
||||
public Mesh(float[] vertices, float[] texCoords, int[] elements) {
|
||||
try(var stack = MemoryStack.stackPush()) {
|
||||
elementsCount = elements.length;
|
||||
var verticesBuffer = stack.mallocFloat(vertices.length);
|
||||
var texCoordsBuffer = stack.mallocFloat(texCoords.length);
|
||||
var elementsBuffer = stack.mallocInt(elementsCount);
|
||||
verticesBuffer.put(vertices).flip();
|
||||
texCoordsBuffer.put(texCoords).flip();
|
||||
elementsBuffer.put(elements).flip();
|
||||
|
||||
vaoId = glGenVertexArrays();
|
||||
@@ -41,7 +43,13 @@ public class Mesh implements Renderable {
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
|
||||
|
||||
vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, vboId);
|
||||
glBufferData(GL_ARRAY_BUFFER, texCoordsBuffer, GL_STATIC_DRAW);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, false, 0, 0);
|
||||
|
||||
vboId = glGenBuffers();
|
||||
vboIds.add(vboId);
|
||||
@@ -57,8 +65,10 @@ public class Mesh implements Renderable {
|
||||
public void render(Window window, ShaderManager shaderManager) {
|
||||
glBindVertexArray(vaoId);
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_INT, 0);
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.bartlomiejpluta.samplegame.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.util.res.ResourcesManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultTextureManager implements TextureManager {
|
||||
private final ResourcesManager resourcesManager;
|
||||
private final Map<String, Texture> loadedTextures = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Texture loadTexture(String textureFileName) {
|
||||
var texture = loadedTextures.get(textureFileName);
|
||||
if(texture != null) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
var buffer = resourcesManager.loadResourceAsByteBuffer(textureFileName);
|
||||
texture = new Texture(textureFileName, buffer);
|
||||
loadedTextures.put(textureFileName, texture);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.bartlomiejpluta.samplegame.core.gl.object.texture;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.error.AppException;
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
|
||||
import static org.lwjgl.opengl.GL13.glActiveTexture;
|
||||
import static org.lwjgl.opengl.GL30.glGenerateMipmap;
|
||||
import static org.lwjgl.stb.STBImage.*;
|
||||
|
||||
public class Texture {
|
||||
private static final int DESIRED_CHANNELS = 4;
|
||||
|
||||
private final int textureId;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
Texture(String textureFilename, ByteBuffer buffer) {
|
||||
try (var stack = MemoryStack.stackPush()) {
|
||||
var widthBuffer = stack.mallocInt(1);
|
||||
var heightBuffer = stack.mallocInt(1);
|
||||
var channelsBuffer = stack.mallocInt(1);
|
||||
|
||||
buffer = stbi_load_from_memory(buffer, widthBuffer, heightBuffer, channelsBuffer, DESIRED_CHANNELS);
|
||||
if (buffer == null) {
|
||||
throw new AppException("Image file [%s] could not be loaded: %s", textureFilename, stbi_failure_reason());
|
||||
}
|
||||
|
||||
width = widthBuffer.get();
|
||||
height = heightBuffer.get();
|
||||
|
||||
textureId = glGenTextures();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.bartlomiejpluta.samplegame.core.gl.object.texture;
|
||||
|
||||
public interface TextureManager {
|
||||
Texture loadTexture(String textureFileName);
|
||||
}
|
||||
@@ -25,7 +25,9 @@ public class DefaultRenderer implements Renderer {
|
||||
.createUniform(UniformName.UNI_MODEL_MATRIX)
|
||||
.createUniform(UniformName.UNI_VIEW_MATRIX)
|
||||
.createUniform(UniformName.UNI_PROJECTION_MATRIX)
|
||||
.createUniform(UniformName.UNI_OBJECT_COLOR);
|
||||
.createUniform(UniformName.UNI_OBJECT_COLOR)
|
||||
.createUniform(UniformName.UNI_HAS_OBJECT_TEXTURE)
|
||||
.createUniform(UniformName.UNI_TEXTURE_SAMPLER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,7 +47,7 @@ public class DefaultRenderer implements Renderer {
|
||||
}
|
||||
|
||||
private void updateViewport(Window window) {
|
||||
if(window.isResized()) {
|
||||
if (window.isResized()) {
|
||||
glViewport(0, 0, window.getWidth(), window.getHeight());
|
||||
window.setResized(false);
|
||||
}
|
||||
|
||||
@@ -5,4 +5,6 @@ public interface UniformName {
|
||||
String UNI_VIEW_MATRIX = "viewMatrix";
|
||||
String UNI_PROJECTION_MATRIX = "projectionMatrix";
|
||||
String UNI_OBJECT_COLOR = "objectColor";
|
||||
String UNI_HAS_OBJECT_TEXTURE = "hasTexture";
|
||||
String UNI_TEXTURE_SAMPLER = "sampler";
|
||||
}
|
||||
|
||||
@@ -83,6 +83,12 @@ public class DefaultShaderManager implements ShaderManager {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, boolean value) {
|
||||
current.setUniform(uniformName, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShaderManager setUniform(String uniformName, float value) {
|
||||
current.setUniform(uniformName, value);
|
||||
|
||||
@@ -17,40 +17,30 @@ public interface ShaderManager {
|
||||
|
||||
ShaderManager createUniform(String uniformName);
|
||||
|
||||
|
||||
ShaderManager createUniform(String uniformName, Uniform uniform);
|
||||
|
||||
|
||||
ShaderManager createUniforms(String uniformName, int size);
|
||||
|
||||
|
||||
ShaderManager createUniforms(String uniformName, int size, Uniform uniform);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, int value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, boolean value);
|
||||
|
||||
ShaderManager setUniform(String uniformName, float value);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector3f value);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, Vector4f value);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, Matrix3f value);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, Matrix4f value);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, Uniform uniform);
|
||||
|
||||
|
||||
ShaderManager setUniform(String uniformName, int index, Uniform uniform);
|
||||
|
||||
|
||||
ShaderManager setUniforms(String uniformName, Uniform[] uniforms);
|
||||
|
||||
void cleanUp();
|
||||
|
||||
@@ -103,6 +103,10 @@ public class ShaderProgram {
|
||||
glUniform1i(uniforms.get(uniformName), value);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, boolean value) {
|
||||
glUniform1i(uniforms.get(uniformName), value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setUniform(String uniformName, float value) {
|
||||
glUniform1f(uniforms.get(uniformName), value);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.bartlomiejpluta.samplegame.core.util.res;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.error.AppException;
|
||||
import com.bartlomiejpluta.samplegame.core.gl.object.texture.Texture;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Scanner;
|
||||
|
||||
@Component
|
||||
@@ -16,4 +20,17 @@ public class ResourcesManager {
|
||||
throw new AppException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer loadResourceAsByteBuffer(String fileName) {
|
||||
try {
|
||||
var bytes = Texture.class.getResourceAsStream(fileName).readAllBytes();
|
||||
return ByteBuffer
|
||||
.allocateDirect(bytes.length)
|
||||
.order(ByteOrder.nativeOrder())
|
||||
.put(bytes)
|
||||
.flip();
|
||||
} catch (IOException e) {
|
||||
throw new AppException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ public abstract class RenderableObject extends Object implements Renderable {
|
||||
|
||||
@Override
|
||||
public void render(Window window, ShaderManager shaderManager) {
|
||||
getMaterial().activateTextureIfExists();
|
||||
mesh.render(window, shaderManager);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ public class Scene implements Renderable {
|
||||
for(var object : objects) {
|
||||
shaderManager.setUniform(UniformName.UNI_MODEL_MATRIX, object.getModelMatrix());
|
||||
shaderManager.setUniform(UniformName.UNI_OBJECT_COLOR, object.getMaterial().getColor());
|
||||
shaderManager.setUniform(UniformName.UNI_HAS_OBJECT_TEXTURE, object.getMaterial().hasTexture());
|
||||
shaderManager.setUniform(UniformName.UNI_TEXTURE_SAMPLER, 0);
|
||||
|
||||
object.render(window, shaderManager);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package com.bartlomiejpluta.samplegame.game.logic;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.samplegame.core.gl.object.texture.TextureManager;
|
||||
import com.bartlomiejpluta.samplegame.core.gl.render.Renderer;
|
||||
import com.bartlomiejpluta.samplegame.core.logic.GameLogic;
|
||||
import com.bartlomiejpluta.samplegame.core.ui.Window;
|
||||
import com.bartlomiejpluta.samplegame.core.world.camera.Camera;
|
||||
import com.bartlomiejpluta.samplegame.core.world.scene.Scene;
|
||||
import com.bartlomiejpluta.samplegame.game.object.Sprite;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -16,6 +18,8 @@ import org.springframework.stereotype.Component;
|
||||
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
|
||||
public class DefaultGameLogic implements GameLogic {
|
||||
private final Renderer renderer;
|
||||
private final TextureManager textureManager;
|
||||
|
||||
private final Camera camera = new Camera();
|
||||
private final Scene scene = new Scene(camera);
|
||||
|
||||
@@ -24,7 +28,12 @@ public class DefaultGameLogic implements GameLogic {
|
||||
log.info("Initializing game logic");
|
||||
renderer.init();
|
||||
|
||||
|
||||
var sprite = new Sprite(Material.textured(textureManager.loadTexture("/textures/grass.png")));
|
||||
var sprite2 = new Sprite(Material.colored(0.7f, 1.0f, 0.4f, 1.0f));
|
||||
sprite.setPosition(320, 240);
|
||||
sprite2.setPosition(110, 110);
|
||||
scene.add(sprite2);
|
||||
scene.add(sprite);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
31
app/src/main/java/com/bartlomiejpluta/samplegame/game/object/Sprite.java
Executable file
31
app/src/main/java/com/bartlomiejpluta/samplegame/game/object/Sprite.java
Executable file
@@ -0,0 +1,31 @@
|
||||
package com.bartlomiejpluta.samplegame.game.object;
|
||||
|
||||
import com.bartlomiejpluta.samplegame.core.gl.object.material.Material;
|
||||
import com.bartlomiejpluta.samplegame.core.gl.object.mesh.Mesh;
|
||||
import com.bartlomiejpluta.samplegame.core.world.object.RenderableObject;
|
||||
|
||||
public class Sprite extends RenderableObject {
|
||||
private static final float[] VERTICES = new float[]{
|
||||
-100, 100,
|
||||
-100, -100,
|
||||
100, -100,
|
||||
100, 100
|
||||
};
|
||||
|
||||
private static final float[] TEX_COORDS = new float[] {
|
||||
0, 0,
|
||||
0, 1,
|
||||
1, 1,
|
||||
1, 0
|
||||
};
|
||||
|
||||
private static final int[] ELEMENTS = new int[]{
|
||||
0, 1, 2,
|
||||
2, 3, 0
|
||||
};
|
||||
|
||||
public Sprite(Material material) {
|
||||
super(new Mesh(VERTICES, TEX_COORDS, ELEMENTS));
|
||||
setMaterial(material);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,21 @@
|
||||
#version 330
|
||||
|
||||
uniform vec4 objectColor;
|
||||
uniform int hasTexture;
|
||||
uniform sampler2D sampler;
|
||||
|
||||
in vec2 fragmentTexCoord;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragColor = objectColor;
|
||||
if(hasTexture == 1)
|
||||
{
|
||||
fragColor = objectColor * texture(sampler, fragmentTexCoord);
|
||||
}
|
||||
else
|
||||
{
|
||||
fragColor = objectColor;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,13 @@ uniform mat4 modelMatrix;
|
||||
uniform mat4 viewMatrix;
|
||||
uniform mat4 projectionMatrix;
|
||||
|
||||
layout(location=0) in vec3 position;
|
||||
layout(location=0) in vec2 position;
|
||||
layout(location=1) in vec2 texCoord;
|
||||
|
||||
out vec2 fragmentTexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
|
||||
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 0.0, 1.0);
|
||||
fragmentTexCoord = texCoord;
|
||||
}
|
||||
Reference in New Issue
Block a user