love-demo2/src/collada/scene.cpp

508 lines
18 KiB
C++

#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "glad/gl.h"
#include "directxmath/directxmath.h"
#include "new.h"
#include "file.h"
#include "view.h"
#include "opengl.h"
#include "collada/types.h"
#include "collada/instance_types.h"
#include "collada/scene.h"
#include "collada/effect.h"
#include "collada/animate.h"
namespace collada::scene {
struct layout {
struct {
unsigned int position;
unsigned int normal;
unsigned int texture;
unsigned int blend_indices;
unsigned int blend_weight;
} attribute;
struct {
// vertex
unsigned int transform;
// fragment
unsigned int emission_color;
unsigned int ambient_color;
unsigned int diffuse_color;
unsigned int specular_color;
unsigned int shininess;
unsigned int emission_sampler;
unsigned int ambient_sampler;
unsigned int diffuse_sampler;
unsigned int specular_sampler;
unsigned int texture_channels;
} uniform;
struct {
unsigned int emission;
unsigned int ambient;
unsigned int diffuse;
unsigned int specular;
} texture_unit;
};
const layout layout = {
.attribute = {
.position = 0,
.normal = 1,
.texture = 2,
.blend_indices = 3,
.blend_weight = 4,
},
.uniform = {
// vertex
.transform = 0,
// fragment
.emission_color = 10,
.ambient_color = 11,
.diffuse_color = 12,
.specular_color = 13,
.shininess = 14,
.emission_sampler = 15,
.ambient_sampler = 16,
.diffuse_sampler = 17,
.specular_sampler = 18,
.texture_channels = 19,
},
.texture_unit {
.emission = GL_TEXTURE0,
.ambient = GL_TEXTURE1,
.diffuse = GL_TEXTURE2,
.specular = GL_TEXTURE3,
},
};
unsigned int attribute_location(char const * const semantic,
int semantic_index)
{
if (strcmp(semantic, "POSITION") == 0 && semantic_index == 0) {
return layout.attribute.position;
}
if (strcmp(semantic, "NORMAL") == 0 && semantic_index == 0) {
return layout.attribute.normal;
}
if (strcmp(semantic, "TEXCOORD") == 0 && semantic_index == 0) {
return layout.attribute.texture;
}
if (strcmp(semantic, "BLENDINDICES") == 0 && semantic_index == 0) {
return layout.attribute.blend_indices;
}
if (strcmp(semantic, "BLENDWEIGHT") == 0 && semantic_index == 0) {
return layout.attribute.blend_weight;
}
printf("unknown semantic %s index %d\n",semantic, semantic_index);
assert(false);
}
unsigned int input_format_gl_size(types::input_format format)
{
switch (format) {
case types::input_format::FLOAT1: return 1;
case types::input_format::FLOAT2: return 2;
case types::input_format::FLOAT3: return 3;
case types::input_format::FLOAT4: return 4;
case types::input_format::INT1: return 1;
case types::input_format::INT2: return 2;
case types::input_format::INT3: return 3;
case types::input_format::INT4: return 4;
default: assert(false);
}
}
unsigned int input_format_gl_type(types::input_format format)
{
switch (format) {
case types::input_format::FLOAT1: return GL_FLOAT;
case types::input_format::FLOAT2: return GL_FLOAT;
case types::input_format::FLOAT3: return GL_FLOAT;
case types::input_format::FLOAT4: return GL_FLOAT;
case types::input_format::INT1: return GL_INT;
case types::input_format::INT2: return GL_INT;
case types::input_format::INT3: return GL_INT;
case types::input_format::INT4: return GL_INT;
default: assert(false);
}
}
// return stride
static inline int load_layout(types::inputs const & inputs,
int binding,
int start_offset,
unsigned int vertex_array)
{
glBindVertexArray(vertex_array);
int offset = start_offset;
for (int i = 0; i < inputs.elements_count; i++) {
unsigned int location = attribute_location(inputs.elements[i].semantic, inputs.elements[i].semantic_index);
glEnableVertexAttribArray(location);
unsigned int gl_size = input_format_gl_size(inputs.elements[i].format);
unsigned int gl_type = input_format_gl_type(inputs.elements[i].format);
glVertexAttribFormat(location, gl_size, gl_type, GL_FALSE, offset);
glVertexAttribBinding(location, binding);
offset += gl_size * 4;
}
int stride = offset - start_offset;
glBindVertexArray(0);
return stride;
}
types::input_element const input_elements_blendindices_0_4_blendweight_0_4[] = {
{
.semantic = "BLENDINDICES",
.semantic_index = 0,
.format = types::input_format::INT4,
},
{
.semantic = "BLENDWEIGHT",
.semantic_index = 0,
.format = types::input_format::FLOAT4,
},
};
types::inputs const skin_inputs = {
.elements = input_elements_blendindices_0_4_blendweight_0_4,
.elements_count = 2,
};
const int vertex_buffer_stride_jw
= input_format_gl_size(skin_inputs.elements[0].format)
+ input_format_gl_size(skin_inputs.elements[1].format);
void state::load_layouts()
{
vertex_arrays = New<static_skinned>(descriptor->inputs_list_count);
vertex_buffer_strides_pnt = New<int>(descriptor->inputs_list_count);
glGenVertexArrays(2 * descriptor->inputs_list_count, (unsigned int *)vertex_arrays);
for (int i = 0; i < descriptor->inputs_list_count; i++) {
// static
int stride = load_layout(descriptor->inputs_list[i],
0, // binding
0, // start_offset
vertex_arrays[i].static_mesh);
vertex_buffer_strides_pnt[i] = stride;
// skinned
load_layout(descriptor->inputs_list[i],
0, // binding
0, // start_offset
vertex_arrays[i].skinned_mesh);
load_layout(skin_inputs,
1, // binding
0, // start_offset
vertex_arrays[i].skinned_mesh);
}
}
unsigned int load_vertex_buffer(const char * filename)
{
int size;
void * data = read_file(filename, &size);
assert(data != NULL);
unsigned int vertex_buffer;
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
free(data);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return vertex_buffer;
}
unsigned int load_index_buffer(const char * filename)
{
int size;
void * data = read_file(filename, &size);
assert(data != NULL);
unsigned int index_buffer;
glGenBuffers(1, &index_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
free(data);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return index_buffer;
}
void state::load_images()
{
textures = New<unsigned int>(descriptor->images_count);
glGenTextures(descriptor->images_count, textures);
for (int i = 0; i < descriptor->images_count; i++) {
types::image const * const image = descriptor->images[i];
glBindTexture(GL_TEXTURE_2D, textures[i]);
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, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
load_dds_texture_2D(image->uri);
}
glBindTexture(GL_TEXTURE_2D, 0);
}
void state::load_scene(types::descriptor const * const descriptor)
{
this->descriptor = descriptor;
load_layouts();
vertex_buffer_pnt = load_vertex_buffer(descriptor->position_normal_texture_buffer);
vertex_buffer_jw = load_vertex_buffer(descriptor->joint_weight_buffer);
index_buffer = load_index_buffer(descriptor->index_buffer);
load_images();
node_state.allocate_node_instances(descriptor->nodes, descriptor->nodes_count);
}
void state::set_color_or_texture(types::color_or_texture const& color_or_texture,
unsigned int color_uniform,
unsigned int texture_unit)
{
switch (color_or_texture.type) {
case types::color_or_texture_type::COLOR:
glUniform4fv(color_uniform, 1, (float *)&color_or_texture.color);
break;
case types::color_or_texture_type::TEXTURE:
glActiveTexture(texture_unit);
glBindTexture(GL_TEXTURE_2D, textures[color_or_texture.texture.image_index]);
break;
default:
assert(false);
}
}
void state::set_instance_material(types::instance_material const& instance_material)
{
types::effect const& effect = *instance_material.material->effect;
switch (effect.type) {
case types::effect_type::BLINN:
set_color_or_texture(effect.blinn.emission, layout.uniform.emission_color, layout.texture_unit.emission);
set_color_or_texture(effect.blinn.ambient, layout.uniform.ambient_color, layout.texture_unit.ambient);
set_color_or_texture(effect.blinn.diffuse, layout.uniform.diffuse_color, layout.texture_unit.diffuse);
set_color_or_texture(effect.blinn.specular, layout.uniform.specular_color, layout.texture_unit.specular);
glUniform1f(layout.uniform.shininess, effect.blinn.shininess);
break;
case types::effect_type::LAMBERT:
set_color_or_texture(effect.lambert.emission, layout.uniform.emission_color, layout.texture_unit.emission);
set_color_or_texture(effect.lambert.ambient, layout.uniform.ambient_color, layout.texture_unit.ambient);
set_color_or_texture(effect.lambert.diffuse, layout.uniform.diffuse_color, layout.texture_unit.diffuse);
break;
case types::effect_type::PHONG:
set_color_or_texture(effect.phong.emission, layout.uniform.emission_color, layout.texture_unit.emission);
set_color_or_texture(effect.phong.ambient, layout.uniform.ambient_color, layout.texture_unit.ambient);
set_color_or_texture(effect.phong.diffuse, layout.uniform.diffuse_color, layout.texture_unit.diffuse);
set_color_or_texture(effect.phong.specular, layout.uniform.specular_color, layout.texture_unit.specular);
glUniform1f(layout.uniform.shininess, effect.phong.shininess);
break;
case types::effect_type::CONSTANT:
glUniform4fv(layout.uniform.emission_color, 1, (float *)&effect.constant.color);
break;
default:
break;
}
int texture_channels[4] = {
instance_material.emission.input_set,
instance_material.ambient.input_set,
instance_material.diffuse.input_set,
instance_material.specular.input_set,
};
glUniform4iv(layout.uniform.texture_channels, 1, texture_channels);
}
void state::draw_geometry(types::geometry const & geometry,
types::instance_material const * const instance_materials,
int const instance_materials_count)
{
types::mesh const& mesh = geometry.mesh;
for (int j = 0; j < instance_materials_count; j++) {
types::instance_material const& instance_material = instance_materials[j];
types::triangles const& triangles = mesh.triangles[instance_material.element_index];
set_instance_material(instance_material);
unsigned int vertex_array = vertex_arrays[triangles.inputs_index].static_mesh;
unsigned int vertex_buffer_offset_pnt = mesh.vertex_buffer_offset;
unsigned int vertex_buffer_stride_pnt = vertex_buffer_strides_pnt[triangles.inputs_index];
glBindVertexArray(vertex_array);
glBindVertexBuffer(0, vertex_buffer_pnt, vertex_buffer_offset_pnt, vertex_buffer_stride_pnt);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
unsigned int index_count = triangles.count * 3;
unsigned int indices = triangles.index_offset * (sizeof (unsigned int)) + mesh.index_buffer_offset;
unsigned int instance_count = 1;
unsigned int base_vertex = 0;
unsigned int base_instance = 0;
glDrawElementsInstancedBaseVertexBaseInstance(GL_TRIANGLES,
index_count,
GL_UNSIGNED_INT,
(void *)((ptrdiff_t)indices),
instance_count,
base_vertex,
base_instance);
}
}
void state::draw_instance_geometries(types::instance_geometry const * const instance_geometries,
int const instance_geometries_count)
{
for (int i = 0; i < instance_geometries_count; i++) {
types::instance_geometry const &instance_geometry = instance_geometries[i];
draw_geometry(*instance_geometry.geometry,
instance_geometry.instance_materials,
instance_geometry.instance_materials_count);
}
}
void state::draw_skin(types::skin const& skin,
types::instance_material const * const instance_materials,
int const instance_materials_count)
{
glUseProgram(collada::effect::program_skinned);
types::mesh const& mesh = skin.geometry->mesh;
for (int j = 0; j < instance_materials_count; j++) {
types::instance_material const& instance_material = instance_materials[j];
types::triangles const& triangles = mesh.triangles[instance_material.element_index];
set_instance_material(instance_material);
unsigned int vertex_array = vertex_arrays[triangles.inputs_index].skinned_mesh;
unsigned int vertex_buffer_offset_pnt = mesh.vertex_buffer_offset;
unsigned int vertex_buffer_stride_pnt = vertex_buffer_strides_pnt[triangles.inputs_index];
unsigned int vertex_buffer_offset_jw = skin.vertex_buffer_offset;
glBindVertexArray(vertex_array);
glBindVertexBuffer(0, vertex_buffer_pnt, vertex_buffer_offset_pnt, vertex_buffer_stride_pnt);
glBindVertexBuffer(1, vertex_buffer_jw, vertex_buffer_offset_jw, vertex_buffer_stride_jw);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
unsigned int index_count = triangles.count * 3;
unsigned int indices = triangles.index_offset * (sizeof (unsigned int)) + mesh.index_buffer_offset;
unsigned int instance_count = 1;
unsigned int base_vertex = 0;
unsigned int base_instance = 0;
glDrawElementsInstancedBaseVertexBaseInstance(GL_TRIANGLES,
index_count,
GL_UNSIGNED_INT,
(void *)((ptrdiff_t)indices),
instance_count,
base_vertex,
base_instance);
}
}
void state::draw_instance_controllers(types::instance_controller const * const instance_controllers,
int const instance_controllers_count)
{
for (int i = 0; i < instance_controllers_count; i++) {
types::instance_controller const &instance_controller = instance_controllers[i];
types::skin const &skin = instance_controller.controller->skin;
draw_skin(skin,
instance_controller.instance_materials,
instance_controller.instance_materials_count);
}
}
void state::draw_node(types::node const & node, instance_types::node const & node_instance)
{
XMMATRIX transform = node_instance.world * view::state.transform;
XMFLOAT4X4 float_transform;
XMStoreFloat4x4(&float_transform, transform);
if (node.instance_geometries_count) {
glUseProgram(collada::effect::program_static);
glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&float_transform);
draw_instance_geometries(node.instance_geometries, node.instance_geometries_count);
}
if (node.instance_controllers_count) {
glUseProgram(collada::effect::program_static);
glUniformMatrix4fv(layout.uniform.transform, 1, false, (float *)&float_transform);
draw_instance_controllers(node.instance_controllers, node.instance_controllers_count);
}
}
void state::draw()
{
unsigned int effects[] = {collada::effect::program_static, collada::effect::program_skinned};
for (int i = 0; i < 2; i++) {
glUseProgram(effects[i]);
glUniform1i(layout.uniform.emission_sampler, 0);
glUniform1i(layout.uniform.ambient_sampler, 1);
glUniform1i(layout.uniform.diffuse_sampler, 2);
glUniform1i(layout.uniform.specular_sampler, 3);
}
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_GREATER);
glDisable(GL_CULL_FACE);
//glCullFace(GL_FRONT);
//glFrontFace(GL_CCW);
for (int i = 0; i < descriptor->nodes_count; i++) {
types::node const & node = *descriptor->nodes[i];
// joints are not drawn
if (node.type != types::node_type::NODE)
continue;
draw_node(node, node_state.node_instances[i]);
}
}
void state::update(float t)
{
t = animate::loop(t / 4.0f, 3.333333f);
for (int i = 0; i < descriptor->nodes_count; i++) {
animate::animate_node(node_state.node_instances[i], t);
node_state.update_node_world_transform(node_state.node_instances[i]);
}
}
instance_types::node * state::find_node_by_name(char const * name)
{
for (int i = 0; i < descriptor->nodes_count; i++) {
if (strcmp(node_state.node_instances[i].node->name, name) == 0) {
return &node_state.node_instances[i];
}
}
return nullptr;
}
}