model loading, load material from model, changed some architecture.

This commit is contained in:
2026-04-11 19:21:13 -05:00
parent 36e3f83df1
commit 74ef4b9ea4
14 changed files with 432 additions and 69 deletions

View File

@@ -2,15 +2,19 @@ import org.joml.Vector3f;
public class DirectionalLight { public class DirectionalLight {
public Vector3f direction; public Vector3f direction;
public Vector3f color;
public float intensity; public Vector3f ambient;
public Vector3f diffuse;
public Vector3f specular;
public DirectionalLight ( public DirectionalLight (
Vector3f direction, Vector3f direction,
Vector3f color, Vector3f ambient,
float intensity) { Vector3f diffuse,
Vector3f specular) {
this.direction = direction; this.direction = direction;
this.color = color; this.ambient = ambient;
this.intensity = intensity; this.diffuse = diffuse;
this.specular = specular;
} }
} }

View File

@@ -0,0 +1,41 @@
import org.joml.Vector3f;
import org.joml.Vector4f;
public class Material {
// Phong
public Vector3f ambient = new Vector3f(1.0f);
public Vector3f diffuse = new Vector3f(1.0f);
public Vector3f specular = new Vector3f(1.0f);
public float shininess = 32.0f;
public Texture diffuseMap;
public Texture specularMap;
public boolean hasDiffuseMap;
public boolean hasSpecularMap;
public Material() {
this.ambient = new Vector3f(1.f, 1.f, 1.f);
this.diffuse = new Vector3f(1.f, 1.f, 1.f);
this.specular = new Vector3f(1.f, 1.f, 1.f);
diffuseMap = null;
specularMap = null;
hasDiffuseMap = false;
hasSpecularMap = false;
}
public Material(Vector3f ambient, Vector3f diffuse, Vector3f specular, float shininess) {
this.ambient = ambient;
this.diffuse = diffuse;
this.specular = specular;
this.shininess = shininess;
diffuseMap = null;
specularMap = null;
hasDiffuseMap = false;
hasSpecularMap = false;
}
}

View File

@@ -1,3 +1,4 @@
import org.lwjgl.BufferUtils;
import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryStack;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
@@ -15,11 +16,14 @@ public class Mesh {
private int _vbo; private int _vbo;
private int _ebo; private int _ebo;
private int vertexCount; private final int vertexCount;
public Mesh(float[] p, float[] u, float[] n, int[] indices) { public int material;
public Mesh(float[] p, float[] u, float[] n, int[] indices, int material) {
vertexCount = indices.length; vertexCount = indices.length;
sendToGPU(Vertex.interleaveVertexData(p, u, n), indices); sendToGPU(Vertex.interleaveVertexData(p, u, n), indices);
this.material = material;
} }
public void render() { public void render() {
@@ -35,18 +39,21 @@ public class Mesh {
} }
private void sendToGPU(float[] vertices, int[] indices) { private void sendToGPU(float[] vertices, int[] indices) {
try (MemoryStack stack = MemoryStack.stackPush()) {
_vao = glGenVertexArrays(); _vao = glGenVertexArrays();
glBindVertexArray(_vao); glBindVertexArray(_vao);
// pos // pos
_vbo = glGenBuffers(); _vbo = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, stack.floats(vertices), GL_STATIC_DRAW); FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(vertices.length);
vertexBuffer.put(vertices).flip();
glBufferData(GL_ARRAY_BUFFER, vertexBuffer, GL_STATIC_DRAW);
_ebo = glGenBuffers(); _ebo = glGenBuffers();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, stack.ints(indices), GL_STATIC_DRAW); IntBuffer indicesBuffer = BufferUtils.createIntBuffer(indices.length);
indicesBuffer.put(indices).flip();
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);
// set vao positions // set vao positions
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
@@ -59,5 +66,4 @@ public class Mesh {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0); glBindVertexArray(0);
} }
}
} }

47
src/main/java/Model.java Normal file
View File

@@ -0,0 +1,47 @@
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import java.util.ArrayList;
import java.util.List;
public class Model {
public List<Mesh> meshes;
public List<Material> materials;
public Matrix4f modelMatrix;
public Model(Mesh mesh, Material mat) {
meshes = new ArrayList<>();
meshes.add(mesh);
materials = new ArrayList<>();
materials.add(mat);
}
public Model(List<Mesh> meshes, List<Material> materials) {
this.meshes = meshes;
this.materials = materials;
}
public void render(Shader shader) {
shader.setUniform("model", modelMatrix);
Matrix3f normalMatrix = new Matrix3f(modelMatrix).invert().transpose();
shader.setUniform("normalMatrix", normalMatrix);
for (Mesh m : meshes) {
Material mat = materials.get(m.material);
// set material uniforms
shader.setUniform("matAmbient", mat.ambient);
shader.setUniform("matDiffuse", mat.diffuse);
shader.setUniform("matSpecular", mat.specular);
shader.setUniform("shininess", mat.shininess);
shader.setUniform("hasDiffuseMap", false);
shader.setUniform("hasSpecularMap", false);
m.render();
}
}
}

View File

@@ -0,0 +1,117 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joml.Vector4f;
import org.lwjgl.PointerBuffer;
import org.lwjgl.assimp.*;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;
import static org.lwjgl.assimp.Assimp.*;
public class ModelLoader {
private static final Logger logger = LogManager.getLogger(ModelLoader.class);
public static Model loadModel(String resourcePath) {
AIScene scene = aiImportFile(resourcePath,
aiProcess_Triangulate |
aiProcess_FlipUVs |
aiProcess_JoinIdenticalVertices |
aiProcess_GenSmoothNormals |
aiProcess_ConvertToLeftHanded);
if (scene == null || scene.mRootNode() == null) {
logger.error("Failed to load model %s".formatted(resourcePath));
return null;
}
List<Mesh> meshList = new ArrayList<>();
int numMeshes = scene.mNumMeshes();
PointerBuffer aiMeshes = scene.mMeshes();
for (int i = 0; i < numMeshes; i++) {
AIMesh aiMesh = AIMesh.create(aiMeshes.get(i));
Mesh mesh = processMesh(aiMesh);
meshList.add(mesh);
}
int numMaterials = scene.mNumMaterials();
PointerBuffer aiMaterials = scene.mMaterials();
List<Material> materialList = new ArrayList<>();
for (int i = 0; i < numMaterials; i++) {
AIMaterial aiMaterial = AIMaterial.create(aiMaterials.get(i));
materialList.add(processMaterial(aiMaterial));
}
return new Model(meshList, materialList);
}
private static Mesh processMesh(AIMesh aiMesh) {
int numVertices = aiMesh.mNumVertices();
float[] positions = new float[numVertices * 3];
float[] uvs = new float[numVertices * 2];
float[] normals = new float[numVertices * 3];
for (int i = 0; i < numVertices; i++) {
AIVector3D pos = aiMesh.mVertices().get(i);
positions[i * 3] = (pos.x());
positions[i * 3 + 1] = (pos.y());
positions[i * 3 + 2] = (pos.z());
if (aiMesh.mTextureCoords(0) != null) {
AIVector3D tex = aiMesh.mTextureCoords(0).get(i);
uvs[i * 2] = tex.x();
uvs[i * 2 + 1] = tex.y();
} else {
uvs[i * 2] = 0.0f;
uvs[i * 2 + 1] = 0.0f;
}
AIVector3D norm = aiMesh.mNormals().get(i);
normals[i * 3] = norm.x();
normals[i * 3 + 1] = norm.y();
normals[i * 3 + 2] = norm.z();
}
List<Integer> indicesList = new ArrayList<>();
int numFaces = aiMesh.mNumFaces();
AIFace.Buffer aiFaces = aiMesh.mFaces();
for (int i = 0; i < numFaces; i++) {
AIFace aiFace = aiFaces.get(i);
IntBuffer buffer = aiFace.mIndices();
while (buffer.hasRemaining()) {
indicesList.add(buffer.get());
}
}
int[] indices = new int[indicesList.size()];
for (int i = 0; i < indicesList.size(); i++) {
indices[i] = indicesList.get(i);
}
return new Mesh(positions, uvs, normals, indices, aiMesh.mMaterialIndex());
}
private static Material processMaterial(AIMaterial aiMaterial) {
Material material = new Material();
AIColor4D color = AIColor4D.create();
if (Assimp.aiGetMaterialColor(aiMaterial, Assimp.AI_MATKEY_COLOR_DIFFUSE, 0, 0, color) == Assimp.aiReturn_SUCCESS) {
material.diffuse.set(color.r(), color.g(), color.b());
}
if (Assimp.aiGetMaterialColor(aiMaterial, Assimp.AI_MATKEY_COLOR_AMBIENT, 0, 0, color) == Assimp.aiReturn_SUCCESS) {
material.ambient.set(color.r(), color.g(), color.b());
}
if (Assimp.aiGetMaterialColor(aiMaterial, Assimp.AI_MATKEY_COLOR_SPECULAR, 0, 0, color) == Assimp.aiReturn_SUCCESS) {
material.specular.set(color.r(), color.g(), color.b());
}
return material;
}
}

View File

@@ -32,4 +32,6 @@ public class OrthoCamera {
public Matrix4f getView() { public Matrix4f getView() {
return view; return view;
} }
public Vector3f getPosition() { return position; };
} }

View File

@@ -2,7 +2,7 @@ import org.joml.Vector2f;
import org.joml.Vector3f; import org.joml.Vector3f;
public class PrimitiveGenerator { public class PrimitiveGenerator {
public static Mesh getCube() { public static Model getCube() {
float s = 0.5f; float s = 0.5f;
// Positions: 24 vertices (6 faces * 4 vertices) // Positions: 24 vertices (6 faces * 4 vertices)
@@ -54,6 +54,9 @@ public class PrimitiveGenerator {
indices[idx + 5] = offset + 0; indices[idx + 5] = offset + 0;
} }
return new Mesh(p, u, n, indices); Mesh mesh = new Mesh(p, u, n, indices, 0);
Material mat = new Material();
return new Model(mesh, mat);
} }
} }

View File

@@ -1,8 +1,11 @@
import org.joml.Matrix3f;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.joml.Vector4f;
import org.lwjgl.system.MemoryStack; import org.lwjgl.system.MemoryStack;
import java.nio.FloatBuffer; import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.HashMap; import java.util.HashMap;
import java.util.Vector; import java.util.Vector;
@@ -74,7 +77,21 @@ public class Shader {
logger.error(msg); logger.error(msg);
return; return;
} }
glUniform3fv(location, fb.array()); glUniform3f(location, fb.get(0), fb.get(1), fb.get(2));
}
}
public void setUniform(String uniformName, Vector4f data) {
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer fb = stack.mallocFloat(4);
data.get(fb);
int location = getUniformLocation(uniformName);
if (location < 0) {
var msg = "Failed to find location for uniform name %s".formatted(uniformName);
logger.error(msg);
return;
}
glUniform4f(location, fb.get(0), fb.get(1), fb.get(2), fb.get(3));
} }
} }
@@ -92,6 +109,34 @@ public class Shader {
} }
} }
public void setUniform(String uniformName, Matrix3f data) {
try (MemoryStack stack = MemoryStack.stackPush()) {
FloatBuffer fb = stack.mallocFloat(9);
data.get(fb);
int location = getUniformLocation(uniformName);
if (location < 0) {
var msg = "Failed to find location for uniform name %s".formatted(uniformName);
logger.error(msg);
return;
}
glUniformMatrix3fv(location, false, fb);
}
}
public void setUniform(String uniformName, boolean data) {
try (MemoryStack stack = MemoryStack.stackPush()) {
IntBuffer fb = stack.mallocInt(1);
fb.put(data ? 1 : 0);
int location = getUniformLocation(uniformName);
if (location < 0) {
var msg = "Failed to find location for uniform name %s".formatted(uniformName);
logger.error(msg);
return;
}
glUniform1i(location, fb.get(0));
}
}
private int getUniformLocation(String uniformName) { private int getUniformLocation(String uniformName) {
if (!uniforms.containsKey(uniformName)) { if (!uniforms.containsKey(uniformName)) {
int location = glGetUniformLocation(id, uniformName); int location = glGetUniformLocation(id, uniformName);

View File

@@ -0,0 +1,44 @@
import java.nio.ByteBuffer;
import static org.lwjgl.opengl.GL40.*;
import static org.lwjgl.stb.STBImage.*;
public class Texture {
private final int id;
private final int width;
private final int height;
public Texture(String filePath) {
int[] w = new int[1];
int[] h = new int[1];
int[] channels = new int[1];
// Load image pixels
ByteBuffer data = stbi_load(filePath, w, h, channels, 4);
if (data == null) {
throw new RuntimeException("Texture file not found: " + filePath);
}
this.width = w[0];
this.height = h[0];
// Create OpenGL texture
this.id = glGenTextures();
glBindTexture(GL_TEXTURE_2D, id);
// Texture Parameters (Essential for your Isometric view)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Upload data
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);
}
public void bind(int unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, id);
}
}

View File

@@ -0,0 +1,2 @@
public class TextureLoader {
}

View File

@@ -1,3 +1,4 @@
import org.joml.Matrix3f;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector3f; import org.joml.Vector3f;
import org.lwjgl.*; import org.lwjgl.*;
@@ -6,6 +7,7 @@ import org.lwjgl.opengl.*;
import org.lwjgl.system.*; import org.lwjgl.system.*;
import java.nio.*; import java.nio.*;
import java.util.List;
import static org.lwjgl.glfw.Callbacks.*; import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*; import static org.lwjgl.glfw.GLFW.*;
@@ -19,9 +21,10 @@ import org.apache.logging.log4j.Logger;
public class Tony { public class Tony {
private long window; private long window;
private Shader shader; private Shader shader;
private Mesh cube; private Model cube;
private OrthoCamera camera; private OrthoCamera camera;
private DirectionalLight sun; private DirectionalLight sun;
private Model tree;
private static final Logger logger = LogManager.getLogger(Tony.class); private static final Logger logger = LogManager.getLogger(Tony.class);
@@ -35,12 +38,19 @@ public class Tony {
shader = new Shader("/shaders/basic.vert", "/shaders/basic.frag"); shader = new Shader("/shaders/basic.vert", "/shaders/basic.frag");
cube = PrimitiveGenerator.getCube(); cube = PrimitiveGenerator.getCube();
tree = ModelLoader.loadModel("tree.fbx");
tree.modelMatrix = new Matrix4f()
.translation(new Vector3f(0.f, 0.f, 0.f))
.rotate((float)Math.toRadians(90), 1, 0, 0)
.scale(.2f);
camera = new OrthoCamera(1024.f / 720); camera = new OrthoCamera(1024.f / 720);
sun = new DirectionalLight( sun = new DirectionalLight(
new Vector3f(-1.0f, -1.0f, -0.5f), // Pointing Down, Left, and slightly Back new Vector3f(-3.0f, -3.0f, -0.5f),
new Vector3f(1.0f, 1.0f, 0.9f), // Warm yellowish sun new Vector3f(.2f, .2f, .2f),
1.2f // Brightness new Vector3f(0.5f, 0.5f, 0.5f),
new Vector3f(1.f, 1.f, 1.f)
); );
loop(); loop();
@@ -90,34 +100,31 @@ public class Tony {
} // the stack frame is popped automatically } // the stack frame is popped automatically
glfwMakeContextCurrent(window); glfwMakeContextCurrent(window);
GL.createCapabilities();
glViewport(0, 0, 1024, 720);
glfwSwapInterval(1); glfwSwapInterval(1);
glfwShowWindow(window); glfwShowWindow(window);
GL.createCapabilities();
glViewport(0, 0, 1024, 720);
glEnable(GL_DEPTH_TEST);
glClearColor(0.33f, 0.48f, 0.48f, 1.0f);
} }
private void loop() { private void loop() {
glClearColor(0.33f, 0.48f, 0.48f, 1.0f);
Matrix4f modelMatrix = new Matrix4f();
while ( !glfwWindowShouldClose(window) ) { while ( !glfwWindowShouldClose(window) ) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.bind(); shader.bind();
modelMatrix.rotateXYZ(0, 0.01f, 0);
shader.setUniform("projection", camera.getProjection()); shader.setUniform("projection", camera.getProjection());
shader.setUniform("view", camera.getView()); shader.setUniform("view", camera.getView());
shader.setUniform("model", modelMatrix); shader.setUniform("viewPos", camera.getPosition());
shader.setUniform("lightDirection", sun.direction);
shader.setUniform("lightColor", sun.color);
shader.setUniform("lightIntensity", sun.intensity);
cube.render(); shader.setUniform("lightDirection", sun.direction);
shader.setUniform("lightAmbient", sun.ambient);
shader.setUniform("lightDiffuse", sun.diffuse);
shader.setUniform("lightSpecular", sun.specular);
tree.render(shader);
shader.unbind(); shader.unbind();

View File

@@ -1,25 +1,59 @@
#version 400 core #version 400 core
out vec4 color; out vec4 FragColor;
in vec2 texCoord; in vec3 FragPos;
in vec3 normal; in vec3 Normal;
in vec2 TexCoords;
uniform vec3 viewPos;
uniform sampler2D texture_diffuse;
uniform vec3 lightDirection; uniform vec3 lightDirection;
uniform vec3 lightColor; uniform vec3 lightAmbient;
uniform float lightIntensity; uniform vec3 lightDiffuse;
uniform vec3 lightSpecular;
void main() { uniform vec3 matAmbient;
vec3 normal = normalize(outNormal); uniform vec3 matDiffuse;
vec3 lightDir = normalize(-lightDirection); uniform vec3 matSpecular;
uniform float shininess;
float diff = max(dot(normal, lightDir), 0.0); uniform sampler2D diffuseMap;
uniform sampler2D specularMap;
uniform bool hasDiffuseMap;
uniform bool hasSpecularMap;
float ambient = 0.2; void main()
{
vec3 N = normalize(Normal);
vec3 V = normalize(viewPos - FragPos);
vec3 L = normalize(-lightDirection);
vec3 diffuse = diff * lightColor * lightIntensity; vec3 texDiffuse = hasDiffuseMap
vec4 texColor = texture(texture_diffuse, texCoord); ? texture(diffuseMap, TexCoords).rgb
: vec3(1.0);
color = vec4(ambient + diffuse, 1.0) * texColor; vec3 texSpecular = hasSpecularMap
? texture(specularMap, TexCoords).rgb
: vec3(1.0);
vec3 albedo = matDiffuse * texDiffuse;
vec3 specColor = matSpecular * texSpecular;
vec3 ambientCol = matAmbient * texDiffuse;
vec3 ambient = lightAmbient * ambientCol;
float diff = max(dot(N, L), 0.0);
vec3 diffuse = lightDiffuse * diff * albedo;
vec3 specular = vec3(0.0);
if (diff > 0.0) {
vec3 H = normalize(L + V);
float spec = pow(max(dot(N, H), 0.0), shininess);
specular = lightSpecular * spec * specColor;
}
vec3 result = ambient + diffuse + specular;
FragColor = vec4(result, 1.0);
} }

View File

@@ -1,13 +1,24 @@
#version 400 core #version 400 core
layout (location = 0) in vec3 position; layout (location = 0) in vec3 aPosition;
layout (location = 1) in vec2 uv; layout (location = 1) in vec2 aTexCoord;
layout (location = 2) in vec3 layout (location = 2) in vec3 aNormal;
uniform mat4 model;
uniform mat4 view; uniform mat4 view;
uniform mat4 projection; uniform mat4 projection;
uniform mat4 model;
uniform mat3 normalMatrix;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
void main() { void main() {
gl_Position = projection * view * model * vec4(position, 1.0); vec4 position = projection * view * model * vec4(aPosition, 1.0);
FragPos = vec3(position.x, position.y, position.z);
TexCoords = aTexCoord;
Normal = normalize(normalMatrix * aNormal);
gl_Position = position;
} }

BIN
tree.fbx Normal file

Binary file not shown.