From 6534e64957b6aa310a8422946012030d458fea65 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 24 Feb 2026 03:54:56 +0000 Subject: [PATCH] collada_scene: colored materials and lighting --- _math.lua | 36 ++++++++++ collada_scene.lua | 157 ++++++++++++++++++++++++++++++++++++++++++++ main.lua | 163 ++++------------------------------------------ pixel.glsl | 44 +++++++++++++ vertex.glsl | 24 +++++++ 5 files changed, 272 insertions(+), 152 deletions(-) create mode 100644 collada_scene.lua create mode 100644 pixel.glsl create mode 100644 vertex.glsl diff --git a/_math.lua b/_math.lua index 6c5bce5..44b646a 100644 --- a/_math.lua +++ b/_math.lua @@ -450,6 +450,42 @@ mat4 = { return M end, + perspective_fov_rh = function(fov_angle_y, aspect_ratio, near_z, far_z) + assert(near_z > 0.0 and far_z > 0.0) + assert(not scalar.near_equal(fov_angle_y, 0.0, 0.00001 * 2.0)) + assert(not scalar.near_equal(aspect_ratio, 0.0, 0.00001)) + assert(not scalar.near_equal(far_z, near_z, 0.00001)) + + local sin_fov = sin(fov_angle_y) + local cos_fov = cos(fov_angle_y) + + local height = cos_fov / sin_fov + local width = height / aspect_ratio + local f_range = far_z / (near_z - far_z) + + local M = mat4() + M.m[0 * 4 + 0] = width + --M.m[0 * 4 + 1] = 0.0 + --M.m[0 * 4 + 2] = 0.0 + --M.m[0 * 4 + 3] = 0.0 + + --M.m[1 * 4 + 0] = 0.0 + M.m[1 * 4 + 1] = height + --M.m[1 * 4 + 2] = 0.0 + --M.m[1 * 4 + 3] = 0.0 + + --M.m[2 * 4 + 0] = 0.0 + --M.m[2 * 4 + 1] = 0.0 + M.m[2 * 4 + 2] = f_range + M.m[2 * 4 + 3] = -1.0 + + --M.m[3 * 4 + 0] = 0.0 + --M.m[3 * 4 + 1] = 0.0 + M.m[3 * 4 + 2] = f_range * near_z + --M.m[3 * 4 + 3] = 0.0 + return M + end, + near_equal = function(M1, M2, epsilon) local d00 = abs(M1.m[0 * 4 + 0] - M2.m[0 * 4 + 0]) local d01 = abs(M1.m[0 * 4 + 1] - M2.m[0 * 4 + 1]) diff --git a/collada_scene.lua b/collada_scene.lua new file mode 100644 index 0000000..95c05c7 --- /dev/null +++ b/collada_scene.lua @@ -0,0 +1,157 @@ +local _math = require '_math' +local mat4 = _math.mat4 +local vec3 = _math.vec3 +local vec4 = _math.vec4 +local scalar = _math.scalar + +local collada_types = require 'collada_types' + +local geometries_meshes = {} + +local pixel_data = love.filesystem.newFileData("pixel.glsl") +local vertex_data = love.filesystem.newFileData("vertex.glsl") +local shader = love.graphics.newShader(pixel_data, vertex_data) + +function load_geometries(vertex_buffer, index_buffer, geometries) + for _, geometry in ipairs(geometries) do + local offset = geometry.mesh.vertex_buffer_offset / (4 * 3 * 3) + local attribute_list = pnt_attribute_list(vertex_buffer, offset) + + local draw_mode = "triangles" + local mesh = love.graphics.newMesh(attribute_list, draw_mode) + mesh:setIndexBuffer(index_buffer) + + geometries_meshes[geometry] = mesh + end +end + +local node_instances = {} + +function node_world_transform(node) + local world + if node.parent_index >= 0 then + world = node_instances[node.parent_index].world + assert(world ~= nil) + else + world = mat4.identity() + end + + for _, transform in ipairs(node.transforms) do + local m + if transform.type == collada_types.transform_type.LOOKAT then + assert(false) + elseif transform.type == collada_types.transform_type.MATRIX then + m = mat4.load_table(transform.matrix) + elseif transform.type == collada_types.transform_type.ROTATE then + local rotate = vec4.load_table(transform.rotate) + local w = rotate.f[3] + m = mat4.rotation_axis(rotate, scalar.convert_to_radians(w)) + elseif transform.type == collada_types.transform_type.SCALE then + m = mat4.scaling_from_vector(vec3.load_table(transform.scale)) + elseif transform.type == collada_types.transform_type.TRANSLATE then + m = mat4.translation_from_vector(vec3.load_table(transform.translate)) + else + assert(false) + end + + world = m * world + end + return world +end + +function load_node_world_transforms(nodes) + local node_index = 0 + for _, node in ipairs(nodes) do + world = node_world_transform(node) + node_instances[node_index] = { world = world } + node_index = node_index + 1 + end +end + +function set_color_or_texture(color_or_texture, color_uniform, sampler_uniform) + if color_or_texture.type == collada_types.color_or_texture_type.COLOR then + shader:send(color_uniform, color_or_texture.color) + elseif color_or_texture.type == collada_types.color_or_texture_type.TEXTURE then + assert(false) + --shader:send(sampler_uniform, ) + else + assert(false) + end +end + +function set_instance_material(instance_material) + effect = instance_material.material.effect + + if effect.type == collada_types.effect_type.BLINN then + set_color_or_texture(effect.blinn.emission, "emission_color", "emission_sampler"); + set_color_or_texture(effect.blinn.ambient, "ambient_color", "ambient_sampler"); + set_color_or_texture(effect.blinn.diffuse, "diffuse_color", "diffuse_sampler"); + set_color_or_texture(effect.blinn.specular, "specular_color", "specular_sampler"); + shader:send("shininess", effect.blinn.shininess); + elseif effect.type == collada_types.effect_type.LAMBERT then + set_color_or_texture(effect.lambert.emission, "emission_color", "emission_sampler"); + set_color_or_texture(effect.lambert.ambient, "ambient_color", "ambient_sampler"); + set_color_or_texture(effect.lambert.diffuse, "diffuse_color", "diffuse_sampler"); + elseif effect.type == collada_types.effect_type.PHONG then + set_color_or_texture(effect.phong.emission, "emission_color", "emission_sampler"); + set_color_or_texture(effect.phong.ambient, "ambient_color", "ambient_sampler"); + set_color_or_texture(effect.phong.diffuse, "diffuse_color", "diffuse_sampler"); + set_color_or_texture(effect.phong.specular, "specular_color", "specular_sampler"); + shader:send("shininess", effect.phong.shininess); + elseif effect.type == collada_types.effect_type.CONSTANT then + shader:send("emission_color", effect.constant.color) + else + assert(false) + end +end + +function draw_geometry(geometry, instance_materials) + local base_index_buffer_offset = geometry.mesh.index_buffer_offset / 4 + + local mesh = geometries_meshes[geometry] + + for _, instance_material in ipairs(instance_materials) do + set_instance_material(instance_material) + local triangles = geometry.mesh.triangles[instance_material.element_index + 1] + + local index_offset = base_index_buffer_offset + triangles.index_offset + local index_count = triangles.count * 3 + mesh:setDrawRange(1 + index_offset, index_count) + love.graphics.draw(mesh, 0, 0, 0, 0, 0) + end +end + +function draw_node(node_index, node, transform) + if node.type ~= collada_types.node_type.NODE then + return + end + + if node.instance_geometries_count == 0 and node.instance_controllers_count == 0 then + return + end + + local world = node_instances[node_index].world + transform = world * transform + shader:send("world_transform", "column", world.data) + shader:send("transform", "column", transform.data) + + for _, instance_geometry in ipairs(node.instance_geometries) do + draw_geometry(instance_geometry.geometry, instance_geometry.instance_materials) + end +end + +function draw_nodes(nodes, transform) + love.graphics.setShader(shader) + shader:send("view_position", {-88.57101, -71.71298, 104.5738, 1.0}) + shader:send("light_position", {0.0, -56.804, 58.237, 1.0}) + + local node_index = 0 + for _, node in ipairs(nodes) do + draw_node(node_index, node, transform) + node_index = node_index + 1 + end +end + +return { + draw_nodes = draw_nodes +} diff --git a/main.lua b/main.lua index 335d4ed..b78b2cf 100644 --- a/main.lua +++ b/main.lua @@ -8,51 +8,7 @@ local vec3 = _math.vec3 local vec4 = _math.vec4 local scalar = _math.scalar local scene_test = require 'scene.test.test' -local collada_types = require 'collada_types' - -local pixelcode = [[ - #pragma language glsl3 - - varying vec4 PixelNormal; - varying vec4 PixelTexture; - varying float PixelId; - - uniform sampler2D texture_sampler; - - out vec4 outData; - - void pixelmain() - { - vec4 texColor = texture(texture_sampler, PixelTexture.xy); - - float intensity = min(max(dot(vec3(1, 1, 1), PixelNormal.xyz), 0), 1); - outData = vec4(texColor.xyz * (0.1 + intensity * intensity), 1.0); - } -]] - -local vertexcode = [[ - #pragma language glsl3 - - layout (location = 0) in vec4 VertexPosition; - layout (location = 1) in vec4 VertexNormal; - layout (location = 2) in vec4 VertexTexture; - - uniform mat4 transform; - - varying vec4 PixelNormal; - varying vec4 PixelTexture; - varying float PixelId; - - void vertexmain() - { - PixelNormal = VertexNormal * 0.5 + 0.5; - PixelTexture = VertexTexture; - PixelId = float(gl_VertexID) / (4800 * 3); - love_Position = transform * vec4(VertexPosition.xyz, 1); - } -]] - -local shader = love.graphics.newShader(pixelcode, vertexcode) +local collada_scene = require 'collada_scene' local vertexformat = { { name = 'VertexPosition', format = 'floatvec3', location = 0 }, @@ -89,65 +45,6 @@ function pnt_attribute_list(vertex_buffer, offset) } end -local geometries_meshes = {} - -function load_geometries(vertex_buffer, index_buffer, geometries) - for _, geometry in ipairs(geometries) do - local offset = geometry.mesh.vertex_buffer_offset / (4 * 3 * 3) - local attribute_list = pnt_attribute_list(vertex_buffer, offset) - - local draw_mode = "triangles" - local mesh = love.graphics.newMesh(attribute_list, draw_mode) - mesh:setIndexBuffer(index_buffer) - - geometries_meshes[geometry] = mesh - end -end - - -local node_instances = {} - -function node_world_transform(node) - local world - if node.parent_index >= 0 then - world = node_instances[node.parent_index].world - assert(world ~= nil) - else - world = mat4.identity() - end - - for _, transform in ipairs(node.transforms) do - local m - if transform.type == collada_types.transform_type.LOOKAT then - assert(false) - elseif transform.type == collada_types.transform_type.MATRIX then - m = mat4.load_table(transform.matrix) - elseif transform.type == collada_types.transform_type.ROTATE then - local rotate = vec4.load_table(transform.rotate) - local w = rotate.f[3] - m = mat4.rotation_axis(rotate, scalar.convert_to_radians(w)) - elseif transform.type == collada_types.transform_type.SCALE then - m = mat4.scaling_from_vector(vec3.load_table(transform.scale)) - elseif transform.type == collada_types.transform_type.TRANSLATE then - m = mat4.translation_from_vector(vec3.load_table(transform.translate)) - else - assert(false) - end - - world = m * world - end - return world -end - -function load_node_world_transforms(nodes) - local node_index = 0 - for _, node in ipairs(nodes) do - world = node_world_transform(node) - node_instances[node_index] = { world = world } - node_index = node_index + 1 - end -end - function love.load(args) love.window.setMode(1024, 1024, {depth=true}) @@ -164,44 +61,6 @@ function love.load(args) texture = love.graphics.newTexture(image_data) end -function draw_geometry(geometry) - local base_index_buffer_offset = geometry.mesh.index_buffer_offset / 4 - - local mesh = geometries_meshes[geometry] - for triangle_index, triangles in pairs(geometry.mesh.triangles) do - local index_offset = base_index_buffer_offset + triangles.index_offset - local index_count = triangles.count * 3 - mesh:setDrawRange(1 + index_offset, index_count) - love.graphics.draw(mesh, 0, 0, 0, 0, 0) - end -end - -function draw_node(node_index, node, transform) - if node.type ~= collada_types.node_type.NODE then - return - end - - if node.instance_geometries_count == 0 and node.instance_controllers_count == 0 then - return - end - - local world = node_instances[node_index].world - transform = world * transform - shader:send("transform", "column", transform.data) - - for _, instance_geometry in ipairs(node.instance_geometries) do - draw_geometry(instance_geometry.geometry) - end -end - -function draw_nodes(nodes, transform) - local node_index = 0 - for _, node in ipairs(nodes) do - draw_node(node_index, node, transform) - node_index = node_index + 1 - end -end - local rotation = 0.0 function love.draw() @@ -210,10 +69,16 @@ function love.draw() width, height = love.graphics.getDimensions() - local projection = mat4.perspective_rh(width / width * 0.1, - height / width * 0.1, - 0.1, - 1000.0) + -- local projection = mat4.perspective_rh(width / width * 0.1, + -- height / width * 0.1, + -- 0.1, + -- 1000.0) + local aspect_ratio = width / height + local projection = mat4.perspective_fov_rh(scalar.convert_to_radians(45 / 2), + aspect_ratio, + 0.1, + 1000.0) + local view = mat4.look_at_rh(vec3(-88.57101, -71.71298, 104.5738), vec3(-19.90239, -27.72767, 54.6898), vec3(0, 0, 1)) @@ -224,12 +89,6 @@ function love.draw() local transform = view * projection - shader:send("texture_sampler", texture) - - rotation = rotation + 0.01 - - love.graphics.setShader(shader) love.graphics.setDepthMode("less", true) - draw_nodes(scene_test.descriptor.nodes, transform) end diff --git a/pixel.glsl b/pixel.glsl new file mode 100644 index 0000000..654632e --- /dev/null +++ b/pixel.glsl @@ -0,0 +1,44 @@ +#pragma language glsl3 + +varying vec4 PixelNormal; +varying vec4 PixelTexture; +varying vec4 PixelWorldPosition; + +uniform vec4 emission_color; +uniform vec4 ambient_color; +uniform vec4 diffuse_color; +uniform vec4 specular_color; +uniform float shininess; + +uniform vec4 view_position; +uniform vec4 light_position; +//uniform int4 texture_channel; + +uniform sampler2D texture_sampler; + +out vec4 out_color; + +void pixelmain() +{ + //vec4 texColor = texture(texture_sampler, PixelTexture.xy); + + vec3 normal = normalize(PixelNormal.xyz); + vec3 view_direction = normalize(view_position.xyz - PixelWorldPosition.xyz); + vec3 light_direction = normalize(light_position.xyz - PixelWorldPosition.xyz); + vec3 reflect_light_direction = reflect(-light_direction, normal); + + vec3 emission = emission_color.xyz; + vec3 ambient = ambient_color.xyz; + vec3 diffuse = diffuse_color.xyz; + vec3 specular = specular_color.xyz; + + float diffuse_intensity = max(dot(normal, light_direction), 0.0); + float specular_intensity = pow(max(dot(view_direction, reflect_light_direction), 0.0), shininess); + + vec3 color = emission * 0; + color += ambient * 0; + color += diffuse * diffuse_intensity; + color += specular * specular_intensity * 0.3; + + out_color = vec4(color, 1.0); +} diff --git a/vertex.glsl b/vertex.glsl new file mode 100644 index 0000000..d88a889 --- /dev/null +++ b/vertex.glsl @@ -0,0 +1,24 @@ +#pragma language glsl3 + +layout (location = 0) in vec4 VertexPosition; +layout (location = 1) in vec4 VertexNormal; +layout (location = 2) in vec4 VertexTexture; + +uniform mat4 world_transform; +uniform mat4 transform; + +varying vec4 PixelNormal; +varying vec4 PixelTexture; +varying vec4 PixelWorldPosition; +varying float PixelId; + +void vertexmain() +{ + PixelNormal = world_transform * vec4(VertexNormal.xyz, 0); + PixelTexture = VertexTexture; + PixelId = float(gl_VertexID) / (4800 * 3); + + PixelWorldPosition = world_transform * vec4(VertexPosition.xyz, 1); + + love_Position = transform * vec4(VertexPosition.xyz, 1); +}