Add skeletal meshes, fix lighting a bit
This commit is contained in:
parent
e9883b8fb5
commit
251871c776
@ -42,6 +42,7 @@ add_executable(PortalGame
|
||||
"src/gfx/shader_sources.cpp"
|
||||
"src/gfx/texture.cpp"
|
||||
"src/gfx/texture.hpp"
|
||||
"src/gfx/uniform_buffer.hpp"
|
||||
"src/gfx/vertex_array.cpp"
|
||||
"src/gfx/vertex_array.hpp"
|
||||
)
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
## TODO
|
||||
|
||||
Fix known issues:
|
||||
- [ ] Fix apparently incorrect light radius of lights visible through scaled portals
|
||||
- [ ] Fix weird movement physics when having different rotation
|
||||
- [x] Fix apparently incorrect light radius of lights visible through scaled portals
|
||||
- [x] Fix GL_LINEAR_MIPMAP_LINEAR error
|
||||
- [x] Use cached textures when loading meshes instead of always loading the textures again for each mesh
|
||||
|
||||
Add new features:
|
||||
- [ ] (Skeletal) Models and animations
|
||||
- [ ] Minimap for debugging
|
||||
- [ ] Text and UI rendering
|
||||
- [ ] Audio
|
||||
- [ ] Dynamic lights
|
||||
- [x] (Skeletal) Models and animations
|
||||
|
||||
@ -10,8 +10,25 @@ os.makedirs(out_path, exist_ok=True)
|
||||
|
||||
roomnames = []
|
||||
armatures = {}
|
||||
armature_anims = {}
|
||||
|
||||
def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
||||
def get_armature_keep_bones(armature: bpy.types.Armature):
|
||||
keep_bones = set()
|
||||
|
||||
for bone in armature.data.bones:
|
||||
bone_name = bone.name
|
||||
is_tag = re.match(r"Tag.(\w+)", bone_name)
|
||||
|
||||
if bone.use_deform or is_tag:
|
||||
keep_bones.add(bone)
|
||||
|
||||
while bone.parent:
|
||||
bone = bone.parent
|
||||
keep_bones.add(bone)
|
||||
|
||||
return keep_bones
|
||||
|
||||
def export_mesh(obj, output_path, export_luv = False, armature = None):
|
||||
if obj.type != 'MESH':
|
||||
raise TypeError("Selected object is not a mesh")
|
||||
|
||||
@ -26,6 +43,11 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
||||
lightmap_layer = mesh.uv_layers.get("LightmapUV")
|
||||
lightmap_uv_data = lightmap_layer.data if lightmap_layer else None
|
||||
|
||||
export_bones = armature is not None
|
||||
keep_bone_names = None
|
||||
if export_bones:
|
||||
keep_bone_names = set(bone.name for bone in get_armature_keep_bones(armature))
|
||||
|
||||
# Vertex deduplication map and list
|
||||
vertex_map = {} # (position, normal, uv, lightmap_uv) -> vertex_index
|
||||
unique_vertices = [] # list of strings (v ...)
|
||||
@ -33,6 +55,15 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
||||
# faces = [] # list of (v1, v2, v3) indices
|
||||
material_faces = {} # material_index -> list of face indices
|
||||
|
||||
bone_map = {}
|
||||
unique_bones = []
|
||||
|
||||
def get_bone_index(bone_name):
|
||||
if bone_name not in bone_map:
|
||||
bone_map[bone_name] = len(unique_bones)
|
||||
unique_bones.append(bone_name)
|
||||
return bone_map[bone_name]
|
||||
|
||||
for tri in mesh.loop_triangles:
|
||||
face_indices = []
|
||||
|
||||
@ -63,7 +94,32 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
||||
else:
|
||||
luv = (0.0, 0.0)
|
||||
|
||||
key = (pos, normal, uv, luv)
|
||||
if export_bones:
|
||||
deform_bones = []
|
||||
|
||||
for vert_group in vertex.groups:
|
||||
weight = round(vert_group.weight, 3)
|
||||
|
||||
if weight < 0.001:
|
||||
continue
|
||||
|
||||
group = obj.vertex_groups[vert_group.group]
|
||||
|
||||
if group.name not in keep_bone_names:
|
||||
continue
|
||||
|
||||
deform_bones.append((group.name, weight))
|
||||
|
||||
deform_bones.sort(key=lambda x: x[1], reverse=True)
|
||||
deform_bones = deform_bones[:4]
|
||||
weight_sum = sum(w for _, w in deform_bones)
|
||||
|
||||
bones_data = tuple((get_bone_index(name), w / weight_sum) for name, w in deform_bones)
|
||||
|
||||
else:
|
||||
bones_data = tuple()
|
||||
|
||||
key = (pos, normal, uv, luv, bones_data)
|
||||
|
||||
if key in vertex_map:
|
||||
index = vertex_map[key]
|
||||
@ -81,6 +137,10 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
||||
if export_luv:
|
||||
vertex_line_comps.append(" ".join(f"{c:.3f}" for c in luv))
|
||||
|
||||
if export_bones:
|
||||
vertex_line_comps.append(str(len(bones_data)))
|
||||
vertex_line_comps.append(" ".join(f"{b[0]} {b[1]:.3f}" for b in bones_data))
|
||||
|
||||
vertex_line = "v " + " ".join(vertex_line_comps)
|
||||
unique_vertices.append(vertex_line)
|
||||
|
||||
@ -94,7 +154,13 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
||||
if export_luv:
|
||||
f.write("luv\n")
|
||||
|
||||
f.write(f"# x y z nx ny nz u v{' lu lv' if export_luv else ''}\n")
|
||||
if export_bones:
|
||||
f.write(f"skeleton {armature.name}\n")
|
||||
|
||||
for bone in unique_bones:
|
||||
f.write(f"d {bone}\n")
|
||||
|
||||
f.write(f"# v <x y z> <nx ny nz> <u v>{' <lu lv>' if export_luv else ''}{' <num_bones> [<idx w>...]' if export_bones else ''}\n")
|
||||
|
||||
for v in unique_vertices:
|
||||
f.write(v + "\n")
|
||||
@ -118,13 +184,13 @@ def rotation_str(rotation):
|
||||
return f"{rad_to_deg(rotation.x):.0f} {rad_to_deg(rotation.y):.0f} {rad_to_deg(rotation.z):.0f}"
|
||||
|
||||
def rotation_str2(rotation):
|
||||
return f"{rad_to_deg(rotation.x):.2f} {rad_to_deg(rotation.y):.2f} {rad_to_deg(rotation.z):.2f}"
|
||||
return f"{rad_to_deg(rotation.x):.3f} {rad_to_deg(rotation.y):.3f} {rad_to_deg(rotation.z):.3f}"
|
||||
|
||||
def position_str(position):
|
||||
return f"{position.x:.3f} {position.y:.3f} {position.z:.3f}"
|
||||
return f"{position.x:.4f} {position.y:.4f} {position.z:.4f}"
|
||||
|
||||
def scale_str(scale):
|
||||
return f"{scale.x:.3f}"
|
||||
return f"{scale.x:.4f}"
|
||||
|
||||
def matrix_decompose_str(matrix):
|
||||
translation, rotation, scale = matrix.decompose()
|
||||
@ -133,28 +199,12 @@ def matrix_decompose_str(matrix):
|
||||
def get_path(name, ext):
|
||||
return os.path.join(out_path, f"{name}.{ext}")
|
||||
|
||||
def get_armature_keep_bones(armature: bpy.types.Armature):
|
||||
keep_bones = set()
|
||||
|
||||
for bone in armature.data.bones:
|
||||
bone_name = bone.name
|
||||
is_tag = re.match(r"Tag.(\w+)", bone_name)
|
||||
|
||||
if bone.use_deform or is_tag:
|
||||
keep_bones.add(bone)
|
||||
|
||||
while bone.parent:
|
||||
bone = bone.parent
|
||||
keep_bones.add(bone)
|
||||
|
||||
return keep_bones
|
||||
|
||||
def export_armature(armature: bpy.types.Armature, output_path: str):
|
||||
keep_bones = get_armature_keep_bones(armature)
|
||||
|
||||
# Export armature data (bones, etc.)
|
||||
with open(output_path, 'w') as f:
|
||||
f.write("# name parent x y z yaw pitch roll scale\n")
|
||||
f.write("# b <name> <parent> <x y z> <yaw pitch roll> <scale>\n")
|
||||
|
||||
for bone in armature.data.bones:
|
||||
if not bone in keep_bones:
|
||||
@ -165,12 +215,16 @@ def export_armature(armature: bpy.types.Armature, output_path: str):
|
||||
bind_matrix = bone.matrix_local
|
||||
f.write(f"b {bone.name} {parent_name} {matrix_decompose_str(bind_matrix)}\n")
|
||||
|
||||
anims = armature_anims.get(armature.name, [])
|
||||
for (anim_name, anim_file_name) in anims:
|
||||
f.write(f"anim {anim_name} {anim_file_name}\n")
|
||||
|
||||
print(f"Exported Armature: {armature.name} to {output_path}")
|
||||
|
||||
def vectors_similar(v1, v2, threshold):
|
||||
return (v1 - v2).length < threshold
|
||||
|
||||
def frames_similar(f1, f2, threshold=0.001):
|
||||
def frames_similar(f1, f2, threshold=0.00001):
|
||||
_, t1, r1, s1, _ = f1
|
||||
_, t2, r2, s2, _ = f2
|
||||
|
||||
@ -197,8 +251,10 @@ def export_animation(action: bpy.types.Action, armature: bpy.types.Armature, out
|
||||
|
||||
bone_frames = {bone.name: [] for bone in keep_bones}
|
||||
|
||||
start, end = map(int, action.frame_range)
|
||||
for frame in range(start, end):
|
||||
_, end = map(int, action.frame_range)
|
||||
fps = bpy.context.scene.render.fps
|
||||
|
||||
for frame in range(0, end):
|
||||
bpy.context.scene.frame_set(frame)
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@ -225,6 +281,9 @@ def export_animation(action: bpy.types.Action, armature: bpy.types.Armature, out
|
||||
frame_list.append(current_frame)
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
f.write(f"frames {end}\n")
|
||||
f.write(f"fps {fps}\n")
|
||||
|
||||
for bone_name, frames in bone_frames.items():
|
||||
f.write(f"ch {bone_name}\n")
|
||||
|
||||
@ -274,17 +333,23 @@ for object in bpy.data.objects:
|
||||
if match:
|
||||
mesh_name = match.group(1)
|
||||
print(f"Found Non-Room Mesh: {mesh_name}")
|
||||
export_mesh(object, get_path(mesh_name, "mesh"))
|
||||
|
||||
armature = None
|
||||
|
||||
parent = object.parent
|
||||
if parent and parent.type == 'ARMATURE':
|
||||
armatures[parent.name] = parent
|
||||
print(f" Is skeletal, Parent Armature: {parent.name}")
|
||||
armature = parent
|
||||
|
||||
export_mesh(object, get_path(mesh_name, "mesh"), armature=armature)
|
||||
|
||||
|
||||
armature_names = [name for name, _ in armatures.items()]
|
||||
print("Armatures will be exported: ", armature_names)
|
||||
|
||||
actions = {}
|
||||
armature_anims = {name: [] for name in armature_names}
|
||||
|
||||
for action in bpy.data.actions:
|
||||
match = re.search(r"Anim.(\w+).(\w+)", action.name)
|
||||
@ -293,16 +358,19 @@ for action in bpy.data.actions:
|
||||
action_name = match.group(2)
|
||||
|
||||
if action_armature in armatures:
|
||||
actions[action.name] = (action, action_name, armatures[action_armature])
|
||||
armature = armatures[action_armature]
|
||||
action_file_name = f"{armature.name}_{action_name}"
|
||||
actions[action.name] = (action, action_file_name, armature)
|
||||
armature_anims[action_armature].append((action_name, action_file_name))
|
||||
|
||||
action_names = [f"{data[1]} ({data[2].name})" for name, data in actions.items()]
|
||||
print("Actions will be exported: ", action_names)
|
||||
|
||||
for armature_name, armature in armatures.items():
|
||||
export_armature(armature, get_path(armature_name, "sk"))
|
||||
export_armature(armature, get_path(armature_name, "skel"))
|
||||
|
||||
for action_name, (action, simple_action_name, armature) in actions.items():
|
||||
export_animation(action, armature, get_path(f"{armature.name}_{simple_action_name}", "anim"))
|
||||
for action_name, (action, action_file_name, armature) in actions.items():
|
||||
export_animation(action, armature, get_path(action_file_name, "anim"))
|
||||
|
||||
with open(get_path("rooms", "list"), 'w') as rooms_file:
|
||||
for room_name in roomnames:
|
||||
|
||||
10
src/app.cpp
10
src/app.cpp
@ -34,7 +34,7 @@ App::App()
|
||||
// scaling hallway
|
||||
size_t s3i = world_.AddSector(room003);
|
||||
game::Sector& s3 = world_.GetSector(s3i);
|
||||
s3.AddLight(glm::vec3(0.0f, 0.0f, 1.8f), glm::vec3(1.0f, 0.0f, 0.0f), 3.0f);
|
||||
s3.AddLight(glm::vec3(1.0f, 0.0f, 1.8f), glm::vec3(1.0f, 0.0f, 0.0f), 3.0f);
|
||||
|
||||
// purple
|
||||
size_t s4i = world_.AddSector(room001);
|
||||
@ -71,6 +71,9 @@ App::App()
|
||||
|
||||
world_.Bake();
|
||||
|
||||
auto skmesh = assets::Mesh::LoadFromFile("data/hands.mesh", false);
|
||||
|
||||
|
||||
player_ = world_.Spawn<game::Player>(s1i, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
}
|
||||
@ -94,14 +97,15 @@ void App::Frame()
|
||||
renderer_.Begin(viewport_size_.x, viewport_size_.y);
|
||||
|
||||
player_->SetInput(input_);
|
||||
player_->Update(delta_time);
|
||||
|
||||
world_.Update(delta_time);
|
||||
|
||||
//const auto& position = player_->GetOccurrence().GetPosition();
|
||||
size_t sector_idx;
|
||||
glm::vec3 position, forward, up;
|
||||
player_->GetPOV(sector_idx, position, forward, up);
|
||||
|
||||
renderer_.DrawWorld(world_, sector_idx, position, forward, up, aspect, 60.0f);
|
||||
renderer_.DrawWorld(world_, sector_idx, player_, position, forward, up, aspect, 60.0f);
|
||||
}
|
||||
|
||||
void App::MouseMove(const glm::vec2& delta)
|
||||
|
||||
@ -55,19 +55,16 @@ assets::Mesh::Mesh(std::span<MeshVertex> verts, std::span<MeshTriangle> tris, st
|
||||
|
||||
if (flags & MF_IS_SKELETAL)
|
||||
{
|
||||
// Bone indices as 4 ints
|
||||
int32_t bone_indices[4] = { 0, 0, 0, 0 };
|
||||
float bone_weights[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
BufferPut(buffer, static_cast<int32_t>(vert.bones[i].bone_index));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
bone_indices[i] = static_cast<int32_t>(vert.bones[i].bone_index);
|
||||
bone_weights[i] = vert.bones[i].weight;
|
||||
BufferPut(buffer, vert.bones[i].weight);
|
||||
}
|
||||
|
||||
BufferPut(buffer, bone_indices);
|
||||
BufferPut(buffer, bone_weights);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
va_.SetVBOData(buffer.data(), buffer.size());
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
#include "entity.hpp"
|
||||
#include "world.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
game::Entity::Entity(World* world, size_t sector_idx, const CapsuleShape& capsule_shape, const glm::vec3& position) :
|
||||
world_(world),
|
||||
capsule_(capsule_shape),
|
||||
velocity_(0.0f),
|
||||
touching_portal_(nullptr)
|
||||
touching_portal_(nullptr),
|
||||
light_trace_offset_(0.0f, 0.0f, capsule_shape.height * 0.5f),
|
||||
lights_valid_(false)
|
||||
{
|
||||
CreateOccurrenceParams occu_params;
|
||||
occu_params.sector = &world->GetSector(sector_idx);
|
||||
@ -122,6 +128,70 @@ void game::Entity::Move(glm::vec3& velocity, float dt)
|
||||
void game::Entity::Update(float dt)
|
||||
{
|
||||
Move(velocity_, dt);
|
||||
lights_valid_ = false;
|
||||
}
|
||||
|
||||
void game::Entity::UpdateLights()
|
||||
{
|
||||
if (lights_valid_)
|
||||
{
|
||||
return; // Already updated since last update
|
||||
}
|
||||
|
||||
const SectorEnvironment& env = occu_->sector_->GetEnvironment();
|
||||
glm::vec3 trace_pos = occu_->position_ + occu_->basis_ * light_trace_offset_;
|
||||
|
||||
static std::vector<Light> lights;
|
||||
lights.clear();
|
||||
occu_->sector_->GetLightsAt(trace_pos, lights);
|
||||
|
||||
// select only closest SD_MAX_LIGHTS lights
|
||||
if (lights.size() > SD_MAX_LIGHTS)
|
||||
{
|
||||
std::partial_sort(
|
||||
lights.begin(),
|
||||
lights.begin() + SD_MAX_LIGHTS,
|
||||
lights.end(),
|
||||
[&](const Light& a, const Light& b)
|
||||
{
|
||||
float aa = glm::distance2(a.position, trace_pos) / (a.radius * a.radius);
|
||||
float ab = glm::distance2(b.position, trace_pos) / (b.radius * b.radius);
|
||||
return aa < ab;
|
||||
}
|
||||
);
|
||||
|
||||
lights.resize(SD_MAX_LIGHTS);
|
||||
}
|
||||
|
||||
// Primary occurence
|
||||
occu_->lights_.ambient = env.ambient;
|
||||
occu_->lights_.num_lights = lights.size();
|
||||
for (size_t i = 0; i < lights.size(); ++i)
|
||||
{
|
||||
const Light& light = lights[i];
|
||||
occu_->lights_.colors_rs[i] = glm::vec4(light.color, light.radius);
|
||||
occu_->lights_.positions[i] = light.position;
|
||||
}
|
||||
|
||||
// Other occurence
|
||||
if (other_occu_ && touching_portal_)
|
||||
{
|
||||
other_occu_->lights_.ambient = env.ambient;
|
||||
other_occu_->lights_.num_lights = lights.size();
|
||||
for (size_t i = 0; i < lights.size(); ++i)
|
||||
{
|
||||
const Light& light = lights[i];
|
||||
|
||||
// transform pos through portal
|
||||
glm::vec3 new_pos = touching_portal_->tr_position * glm::vec4(light.position, 1.0f);
|
||||
float new_radius = light.radius * touching_portal_->tr_scale;
|
||||
|
||||
other_occu_->lights_.colors_rs[i] = glm::vec4(light.color, new_radius);
|
||||
other_occu_->lights_.positions[i] = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
lights_valid_ = true;
|
||||
}
|
||||
|
||||
void game::Entity::CreateOtherOccurence(const Portal& portal)
|
||||
@ -184,3 +254,9 @@ bool game::EntityOccurrence::Sweep(const glm::vec3& target_position, float& hit_
|
||||
hit_normal
|
||||
);
|
||||
}
|
||||
|
||||
const game::LightInfluences& game::EntityOccurrence::GetLights()
|
||||
{
|
||||
entity_->UpdateLights();
|
||||
return lights_;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include "sector.hpp"
|
||||
#include "gfx/shader_defs.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
@ -14,6 +15,15 @@ namespace game
|
||||
float height;
|
||||
};
|
||||
|
||||
struct LightInfluences
|
||||
{
|
||||
glm::vec3 ambient;
|
||||
// Point lights
|
||||
glm::vec4 colors_rs[SD_MAX_LIGHTS]; // rgb = color, a = r
|
||||
glm::vec3 positions[SD_MAX_LIGHTS];
|
||||
size_t num_lights = 0;
|
||||
};
|
||||
|
||||
struct CreateOccurrenceParams
|
||||
{
|
||||
Sector* sector;
|
||||
@ -38,6 +48,8 @@ namespace game
|
||||
|
||||
btCapsuleShapeZ bt_capsule_;
|
||||
|
||||
LightInfluences lights_;
|
||||
|
||||
friend class Entity;
|
||||
|
||||
public:
|
||||
@ -46,6 +58,8 @@ namespace game
|
||||
// pos in sector space
|
||||
bool Sweep(const glm::vec3& target_position, float& hit_fraction, glm::vec3& hit_normal, const Portal** hit_portal);
|
||||
|
||||
const LightInfluences& GetLights();
|
||||
|
||||
const Sector& GetSector() const { return *sector_; }
|
||||
const glm::vec3& GetPosition() const { return position_; }
|
||||
|
||||
@ -66,6 +80,9 @@ namespace game
|
||||
|
||||
glm::vec3 velocity_;
|
||||
|
||||
glm::vec3 light_trace_offset_; // In entity space
|
||||
bool lights_valid_;
|
||||
|
||||
public:
|
||||
Entity(World* world, size_t sector_idx, const CapsuleShape& capsule_shape, const glm::vec3& position);
|
||||
|
||||
@ -76,6 +93,8 @@ namespace game
|
||||
|
||||
void SetVelocity(const glm::vec3& velocity) { velocity_ = velocity; }
|
||||
|
||||
void UpdateLights();
|
||||
|
||||
const glm::vec3& GetVelocity() const { return velocity_; }
|
||||
const CapsuleShape& GetCapsuleShape() const { return capsule_; }
|
||||
EntityOccurrence& GetOccurrence() { return *occu_; }
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "meshinstance.hpp"
|
||||
#include "gfx/shader_defs.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@ -7,9 +8,13 @@ game::MeshInstance::MeshInstance(std::shared_ptr<const assets::Mesh> mesh, const
|
||||
root_node_(root_node),
|
||||
time_ptr_(time_ptr)
|
||||
{
|
||||
if (IsSkeletal())
|
||||
skeletal_ = (bool)mesh_->GetSkeleton();
|
||||
|
||||
if (skeletal_)
|
||||
{
|
||||
SetupBoneNodes();
|
||||
bone_ubo_ = std::make_unique<gfx::UniformBuffer<glm::mat4>>();
|
||||
bone_ubo_->SetData(nullptr, SD_MAX_BONES); // allocate max size
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,13 +80,43 @@ void game::MeshInstance::PlayAnim(const std::string& name)
|
||||
|
||||
void game::MeshInstance::Update()
|
||||
{
|
||||
if (IsSkeletal())
|
||||
if (skeletal_)
|
||||
{
|
||||
ApplyAnimFrame();
|
||||
UpdateBoneMatrices();
|
||||
bone_ubo_dirty_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void game::MeshInstance::UpdateBoneUBO()
|
||||
{
|
||||
if (!bone_ubo_ || !bone_ubo_dirty_)
|
||||
{
|
||||
return; // No need to update
|
||||
}
|
||||
|
||||
static glm::mat4 skin_mats[SD_MAX_BONES];
|
||||
|
||||
const auto& skeleton = mesh_->GetSkeleton();
|
||||
size_t num_mats = std::min(skeleton->GetNumBones(), static_cast<size_t>(SD_MAX_BONES));
|
||||
|
||||
for (size_t i = 0; i < num_mats; ++i)
|
||||
{
|
||||
const auto& bone = skeleton->GetBone(i);
|
||||
const TransformNode& node = bone_nodes_[i];
|
||||
skin_mats[i] = node.matrix * bone.inv_bind_matrix;
|
||||
}
|
||||
|
||||
bone_ubo_->SetData(skin_mats, num_mats);
|
||||
bone_ubo_dirty_ = false;
|
||||
}
|
||||
|
||||
const gfx::UniformBuffer<glm::mat4>& game::MeshInstance::GetBoneUBO()
|
||||
{
|
||||
UpdateBoneUBO();
|
||||
return *bone_ubo_;
|
||||
}
|
||||
|
||||
void game::MeshInstance::ApplyAnimFrame()
|
||||
{
|
||||
ApplySkelAnim(current_anim_, anim_time_, 1.0f);
|
||||
|
||||
@ -2,30 +2,37 @@
|
||||
|
||||
#include "assets/mesh.hpp"
|
||||
#include "transform_node.hpp"
|
||||
#include "gfx/uniform_buffer.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
class MeshInstance
|
||||
{
|
||||
std::shared_ptr<const assets::Mesh> mesh_;
|
||||
bool skeletal_;
|
||||
|
||||
const TransformNode* root_node_ = nullptr;
|
||||
std::vector<TransformNode> bone_nodes_; // Only used if the mesh is skeletal
|
||||
|
||||
const float* time_ptr_ = nullptr; // Pointer to game time variable
|
||||
|
||||
std::unique_ptr<gfx::UniformBuffer<glm::mat4>> bone_ubo_; // Only used if the mesh is skeletal
|
||||
bool bone_ubo_dirty_ = true;
|
||||
|
||||
const assets::Animation* current_anim_ = nullptr;
|
||||
float anim_time_ = 0.0f;
|
||||
|
||||
public:
|
||||
MeshInstance(std::shared_ptr<const assets::Mesh> mesh, const TransformNode* root_node, const float* time_ptr);
|
||||
|
||||
bool IsSkeletal() const { return (bool)mesh_->GetSkeleton(); }
|
||||
bool IsSkeletal() const { return skeletal_; }
|
||||
|
||||
void PlayAnim(const std::string& name);
|
||||
|
||||
void Update();
|
||||
|
||||
const gfx::UniformBuffer<glm::mat4>& GetBoneUBO();
|
||||
|
||||
const std::shared_ptr<const assets::Mesh>& GetMesh() const { return mesh_; }
|
||||
const TransformNode* GetRootNode() const { return root_node_; }
|
||||
const TransformNode& GetBoneNode(size_t index) const { return bone_nodes_[index]; }
|
||||
@ -35,6 +42,7 @@ namespace game
|
||||
void SetupBoneNodes();
|
||||
void UpdateBoneMatrices();
|
||||
|
||||
void UpdateBoneUBO();
|
||||
protected:
|
||||
virtual void ApplyAnimFrame();
|
||||
|
||||
|
||||
@ -19,7 +19,9 @@ game::Player::Player(World* world, size_t sector_idx, const glm::vec3& position)
|
||||
world->GetTimePtr()
|
||||
);
|
||||
|
||||
vm_hands_.PlayAnim("knife_attack1");
|
||||
vm_hands_.PlayAnim("knife_idle");
|
||||
|
||||
light_trace_offset_ = glm::vec3(0.0f, 0.0f, 0.6f);
|
||||
}
|
||||
|
||||
void game::Player::Rotate(float delta_yaw, float delta_pitch)
|
||||
@ -38,6 +40,7 @@ void game::Player::Update(float dt)
|
||||
{
|
||||
time_ += dt;
|
||||
|
||||
vm_node_.UpdateMatrix();
|
||||
vm_hands_.Update();
|
||||
|
||||
if (vm_weapon_)
|
||||
@ -122,18 +125,25 @@ void game::Player::Update(float dt)
|
||||
|
||||
}
|
||||
|
||||
void game::Player::GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& forward, glm::vec3& up) const
|
||||
void game::Player::GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& forward, glm::vec3& up)
|
||||
{
|
||||
sector_idx = occu_->GetSector().GetIndex();
|
||||
|
||||
glm::vec2 bobbing_offset = GetBobbingOffset(time_, 0.01f * current_speed_);
|
||||
glm::vec3 offset = cam_up_ * (0.7f + bobbing_offset.y) + cam_right_ * bobbing_offset.x;
|
||||
|
||||
glm::vec2 vm_offset = bobbing_offset * 0.2f;
|
||||
vm_offset.y -= 0.01f * current_speed_;
|
||||
vm_node_.local_transform.position = glm::vec3(vm_offset.x, 0.0f, vm_offset.y - 0.2f);
|
||||
vm_node_.UpdateMatrix();
|
||||
|
||||
position = occu_->GetPosition() + offset;
|
||||
|
||||
forward = cam_forward_;
|
||||
up = cam_up_;
|
||||
|
||||
other_pov_ = false;
|
||||
|
||||
if (touching_portal_ && touching_portal_->link)
|
||||
{
|
||||
float sd = glm::dot(glm::vec3(touching_portal_->plane), position) + touching_portal_->plane.w;
|
||||
@ -146,11 +156,12 @@ void game::Player::GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& fo
|
||||
forward = touching_portal_->tr_basis * forward;
|
||||
up = touching_portal_->tr_basis * up;
|
||||
|
||||
other_pov_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void game::Player::GetViewMeshes(std::vector<const MeshInstance*>& out_meshes) const
|
||||
void game::Player::GetViewMeshes(std::vector<MeshInstance*>& out_meshes)
|
||||
{
|
||||
out_meshes.push_back(&vm_hands_);
|
||||
|
||||
@ -160,6 +171,17 @@ void game::Player::GetViewMeshes(std::vector<const MeshInstance*>& out_meshes) c
|
||||
}
|
||||
}
|
||||
|
||||
const game::LightInfluences& game::Player::GetViewLights()
|
||||
{
|
||||
// Viewed from other occurrence sector
|
||||
if (other_pov_ && other_occu_)
|
||||
{
|
||||
return other_occu_->GetLights();
|
||||
}
|
||||
|
||||
return occu_->GetLights();
|
||||
}
|
||||
|
||||
glm::vec2 game::Player::GetBobbingOffset(float t, float amplitude)
|
||||
{
|
||||
// Frequency and amplitude can be adjusted to tweak the effect
|
||||
|
||||
@ -26,6 +26,8 @@ namespace game
|
||||
float time_ = 0.0f;
|
||||
float current_speed_ = 0.0f;
|
||||
|
||||
bool other_pov_ = false; // True is viewed from other occurence sector (camera behind portal plane)
|
||||
|
||||
// Viewmodel stuff
|
||||
TransformNode vm_node_; // Affected by bobbing
|
||||
MeshInstance vm_hands_;
|
||||
@ -41,9 +43,10 @@ namespace game
|
||||
|
||||
virtual void Update(float dt) override;
|
||||
|
||||
void GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& forward, glm::vec3& up) const;
|
||||
void GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& forward, glm::vec3& up);
|
||||
|
||||
void GetViewMeshes(std::vector<const MeshInstance*>& out_meshes) const;
|
||||
void GetViewMeshes(std::vector<MeshInstance*>& out_meshes);
|
||||
const LightInfluences& GetViewLights();
|
||||
|
||||
private:
|
||||
static glm::vec2 GetBobbingOffset(float t, float amplitude);
|
||||
|
||||
@ -74,6 +74,8 @@ game::Sector::Sector(World* world, size_t idx, std::shared_ptr<assets::SectorDef
|
||||
|
||||
bt_world_.addCollisionObject(portal.bt_col_obj.get());
|
||||
}
|
||||
|
||||
env_.ambient = glm::vec3(0.1f);
|
||||
}
|
||||
|
||||
void game::Sector::ComputePortalVertex(Portal& portal, size_t idx, const glm::vec2& base_vert)
|
||||
@ -468,7 +470,7 @@ void game::Sector::GenerateAllLights()
|
||||
{
|
||||
Light light = ext_light;
|
||||
light.position = glm::vec3(portal.link->tr_position * glm::vec4(ext_light.position, 1.0f)); // Transform light position to this sector's space
|
||||
light.radius = ext_light.radius * portal.tr_scale; // Scale the radius
|
||||
light.radius = ext_light.radius / portal.tr_scale; // Scale the radius
|
||||
light.through_portal = &portal;
|
||||
|
||||
all_lights_.push_back(light);
|
||||
@ -479,7 +481,6 @@ void game::Sector::GenerateAllLights()
|
||||
void game::Sector::BakeLightmap()
|
||||
{
|
||||
const size_t lightmap_size = 128;
|
||||
const glm::vec3 ambient_light(0.2f); // Ambient light color
|
||||
const float margin = 1.0f;
|
||||
|
||||
std::span<const assets::MeshVertex> mesh_verts = mesh_->GetVertices();
|
||||
@ -583,7 +584,7 @@ void game::Sector::BakeLightmap()
|
||||
glm::vec3 texel_pos_ws = u * vert_pos[0] + v * vert_pos[1] + w * vert_pos[2];
|
||||
glm::vec3 texel_norm_ws = glm::normalize(u * vert_norm[0] + v * vert_norm[1] + w * vert_norm[2]);
|
||||
|
||||
glm::vec3 light_color = ambient_light;
|
||||
glm::vec3 light_color = env_.ambient; // Start with ambient light
|
||||
|
||||
lights.clear();
|
||||
GetLightsAt(texel_pos_ws, lights);
|
||||
|
||||
@ -62,6 +62,14 @@ namespace game
|
||||
const Portal* through_portal;
|
||||
};
|
||||
|
||||
struct SectorEnvironment
|
||||
{
|
||||
glm::vec3 ambient;
|
||||
bool has_sun = false;
|
||||
glm::vec3 sun_direction;
|
||||
glm::vec3 sun_color;
|
||||
};
|
||||
|
||||
class Sector
|
||||
{
|
||||
World* world_;
|
||||
@ -70,6 +78,7 @@ namespace game
|
||||
|
||||
std::shared_ptr<const assets::Mesh> mesh_;
|
||||
|
||||
SectorEnvironment env_;
|
||||
std::vector<Light> lights_; // Light in this sector
|
||||
std::vector<Light> all_lights_; // Lights in this sector and linked sectors
|
||||
|
||||
@ -99,6 +108,7 @@ namespace game
|
||||
const std::shared_ptr<const assets::Mesh>& GetMesh() const { return mesh_; }
|
||||
const std::shared_ptr<gfx::Texture>& GetLightmap() const { return lightmap_; }
|
||||
btCollisionWorld& GetBtWorld() { return bt_world_; }
|
||||
const SectorEnvironment& GetEnvironment() const { return env_; }
|
||||
|
||||
int GetPortalIndex(const std::string& name) const;
|
||||
const Portal& GetPortal(size_t idx) const { return portals_[idx]; }
|
||||
|
||||
@ -35,3 +35,13 @@ void game::World::Bake()
|
||||
sector->Bake();
|
||||
}
|
||||
}
|
||||
|
||||
void game::World::Update(float dt)
|
||||
{
|
||||
time_ += dt;
|
||||
|
||||
for (auto& entity : entities_)
|
||||
{
|
||||
entity->Update(dt);
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,8 @@ namespace game
|
||||
return entity_ptr;
|
||||
}
|
||||
|
||||
void Update(float dt);
|
||||
|
||||
Sector& GetSector(size_t idx) { return *sectors_[idx]; }
|
||||
const Sector& GetSector(size_t idx) const { return *sectors_[idx]; }
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ gfx::Renderer::Renderer()
|
||||
ShaderSources::MakeShader(sector_shader_.shader, SS_SECTOR_MESH_VERT, SS_SECTOR_MESH_FRAG);
|
||||
ShaderSources::MakeShader(portal_shader_.shader, SS_PORTAL_VERT, SS_PORTAL_FRAG);
|
||||
ShaderSources::MakeShader(mesh_shader_.shader, SS_MESH_VERT, SS_MESH_FRAG);
|
||||
ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG);
|
||||
proj_ = glm::mat4(1.0f); // Initialize projection matrix to identity
|
||||
|
||||
SetupPortalVAO();
|
||||
@ -40,6 +41,7 @@ void gfx::Renderer::Begin(size_t width, size_t height)
|
||||
void gfx::Renderer::DrawWorld(
|
||||
const game::World& world,
|
||||
size_t sector_idx,
|
||||
game::Player* player,
|
||||
const glm::vec3& eye,
|
||||
const glm::vec3& dir,
|
||||
const glm::vec3& up,
|
||||
@ -93,16 +95,51 @@ void gfx::Renderer::DrawWorld(
|
||||
|
||||
DrawSector(params);
|
||||
|
||||
//glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
//glDisable(GL_CULL_FACE);
|
||||
//glDisable(GL_DEPTH_TEST);
|
||||
|
||||
if (player)
|
||||
{
|
||||
draw_meshes_.clear();
|
||||
player->GetViewMeshes(draw_meshes_);
|
||||
|
||||
if (!draw_meshes_.empty())
|
||||
{
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); // Clear depth and stencil for viewmodel
|
||||
|
||||
//glm::mat4 transform = glm::inverse(view);
|
||||
|
||||
// model is -Y forward, Z up, X right
|
||||
glm::mat4 transform = glm::mat4(
|
||||
-1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
);
|
||||
|
||||
// transform to camera
|
||||
transform = glm::inverse(view) * transform;
|
||||
|
||||
const game::LightInfluences& lights = player->GetViewLights();
|
||||
|
||||
for (auto mesh : draw_meshes_)
|
||||
{
|
||||
DrawMeshInstance(params, *mesh, transform, lights);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void gfx::Renderer::UnreadyShaders()
|
||||
{
|
||||
portal_shader_.setup_sector = 0;
|
||||
sector_shader_.setup_sector = 0;
|
||||
mesh_shader_.setup_sector = 0;
|
||||
skel_mesh_shader_.setup_sector = 0;
|
||||
}
|
||||
|
||||
void gfx::Renderer::SetupSectorShader(const DrawSectorParams& params, SectorShader& sshader)
|
||||
@ -382,3 +419,46 @@ void gfx::Renderer::DrawPortalPlane(const DrawSectorParams& params, const game::
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); // Draw the portal as a quad
|
||||
}
|
||||
|
||||
void gfx::Renderer::DrawMeshInstance(const DrawSectorParams& params, game::MeshInstance& mesh_instance, const glm::mat4& transform, const game::LightInfluences& lights)
|
||||
{
|
||||
SectorShader* sshader = &mesh_shader_;
|
||||
bool skeletal = mesh_instance.IsSkeletal();
|
||||
|
||||
if (skeletal)
|
||||
{
|
||||
sshader = &skel_mesh_shader_;
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, mesh_instance.GetBoneUBO().GetId());
|
||||
}
|
||||
|
||||
SetupSectorShader(params, *sshader);
|
||||
|
||||
const game::TransformNode* mesh_root = mesh_instance.GetRootNode();
|
||||
if (mesh_root && !skeletal) // Skeletal meshes have root transform applied in bone matrices
|
||||
{
|
||||
glm::mat4 combined = transform * mesh_root->matrix;
|
||||
glUniformMatrix4fv(sshader->shader->U(gfx::SU_MODEL), 1, GL_FALSE, &combined[0][0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniformMatrix4fv(sshader->shader->U(gfx::SU_MODEL), 1, GL_FALSE, &transform[0][0]);
|
||||
}
|
||||
|
||||
// setup lighting
|
||||
glUniform3fv(sshader->shader->U(gfx::SU_AMBIENT_LIGHT), 1, &lights.ambient[0]);
|
||||
glUniform1i(sshader->shader->U(gfx::SU_NUM_LIGHTS), (GLint)lights.num_lights);
|
||||
if (lights.num_lights > 0)
|
||||
{
|
||||
glUniform3fv(sshader->shader->U(gfx::SU_LIGHT_POSITIONS), (GLint)lights.num_lights, &lights.positions[0].x);
|
||||
glUniform4fv(sshader->shader->U(gfx::SU_LIGHT_COLORS_RS), (GLint)lights.num_lights, &lights.colors_rs[0].x);
|
||||
}
|
||||
|
||||
const assets::Mesh& mesh = *mesh_instance.GetMesh();
|
||||
|
||||
glBindVertexArray(mesh.GetVA().GetVAOId());
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
for (auto mesh_materials = mesh.GetMaterials(); const auto& mat : mesh_materials) {
|
||||
BindTexture(mat.texture.get()); // diffuse texture
|
||||
glDrawElements(GL_TRIANGLES, mat.num_tris * 3, GL_UNSIGNED_INT, (void*)(mat.first_tri * 3 * sizeof(uint32_t)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "game/world.hpp"
|
||||
#include "game/sector.hpp"
|
||||
#include "game/meshinstance.hpp"
|
||||
#include "game/player.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
@ -37,6 +38,7 @@ namespace gfx
|
||||
void DrawWorld(
|
||||
const game::World& world,
|
||||
size_t sector_idx,
|
||||
game::Player* player,
|
||||
const glm::vec3& eye,
|
||||
const glm::vec3& dir,
|
||||
const glm::vec3& up,
|
||||
@ -47,6 +49,7 @@ namespace gfx
|
||||
SectorShader sector_shader_;
|
||||
SectorShader portal_shader_;
|
||||
SectorShader mesh_shader_;
|
||||
SectorShader skel_mesh_shader_;
|
||||
|
||||
std::shared_ptr<VertexArray> portal_vao_;
|
||||
void SetupPortalVAO();
|
||||
@ -60,6 +63,7 @@ namespace gfx
|
||||
glm::mat4 proj_;
|
||||
size_t last_sector_id;
|
||||
const Shader* current_shader_;
|
||||
std::vector<game::MeshInstance*> draw_meshes_;
|
||||
|
||||
void UnreadyShaders();
|
||||
void SetupSectorShader(const DrawSectorParams& params, SectorShader& sshader);
|
||||
@ -70,7 +74,7 @@ namespace gfx
|
||||
static bool ComputeQuadScreenAABB(const glm::vec3* verts, const glm::mat4 view_proj, collision::AABB2& aabb);
|
||||
void DrawPortalPlane(const DrawSectorParams& params, const game::Portal& portal, const glm::mat4& trans, bool clear_depth);
|
||||
|
||||
void DrawMeshInstance(const DrawSectorParams& params, const game::MeshInstance& mesh_instance, const glm::mat4& model);
|
||||
void DrawMeshInstance(const DrawSectorParams& params, game::MeshInstance& mesh_instance, const glm::mat4& transform, const game::LightInfluences& lights);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -12,6 +12,10 @@ static const char* const s_uni_names[] = {
|
||||
"u_portal_size", // SU_PORTAL_SIZE
|
||||
"u_clear_depth", // SU_CLEAR_DEPTH
|
||||
"u_lightmap_tex", // SU_LIGHTMAP_TEX
|
||||
"u_num_lights", // SU_NUM_LIGHTS
|
||||
"u_light_positions", // SU_LIGHT_POSITIONS
|
||||
"u_light_colors_rs", // SU_LIGHT_COLORS_RS
|
||||
"u_ambient_light", // SU_AMBIENT_LIGHT
|
||||
};
|
||||
|
||||
// Vytvori shader z daneho zdroje
|
||||
@ -81,7 +85,7 @@ gfx::Shader::Shader(const char* vert_src, const char* frag_src) {
|
||||
for (size_t i = 0; i < SU_COUNT; i++)
|
||||
m_uni[i] = glGetUniformLocation(m_id, s_uni_names[i]);
|
||||
|
||||
SetupTextureBindings();
|
||||
SetupBindings();
|
||||
}
|
||||
|
||||
gfx::Shader::~Shader() {
|
||||
@ -89,12 +93,19 @@ gfx::Shader::~Shader() {
|
||||
glDeleteProgram(m_id);
|
||||
}
|
||||
|
||||
void gfx::Shader::SetupTextureBindings()
|
||||
void gfx::Shader::SetupBindings()
|
||||
{
|
||||
glUseProgram(m_id);
|
||||
|
||||
glUniform1i(m_uni[SU_TEX], 0);
|
||||
glUniform1i(m_uni[SU_LIGHTMAP_TEX], 1);
|
||||
|
||||
// Bones UBO
|
||||
int ubo_index = glGetUniformBlockIndex(m_id, "Bones");
|
||||
if (ubo_index != GL_INVALID_INDEX)
|
||||
{
|
||||
glUniformBlockBinding(m_id, ubo_index, 0); // bind to binding point 0
|
||||
}
|
||||
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
@ -17,6 +17,10 @@ namespace gfx
|
||||
SU_PORTAL_SIZE,
|
||||
SU_CLEAR_DEPTH,
|
||||
SU_LIGHTMAP_TEX,
|
||||
SU_NUM_LIGHTS,
|
||||
SU_LIGHT_POSITIONS,
|
||||
SU_LIGHT_COLORS_RS,
|
||||
SU_AMBIENT_LIGHT,
|
||||
|
||||
SU_COUNT
|
||||
};
|
||||
@ -49,7 +53,7 @@ namespace gfx
|
||||
GLuint GetId() const { return m_id; }
|
||||
|
||||
private:
|
||||
void SetupTextureBindings();
|
||||
void SetupBindings();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
4
src/gfx/shader_defs.hpp
Normal file
4
src/gfx/shader_defs.hpp
Normal file
@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#define SD_MAX_LIGHTS 4
|
||||
#define SD_MAX_BONES 256
|
||||
@ -1,4 +1,5 @@
|
||||
#include "shader_sources.hpp"
|
||||
#include "shader_defs.hpp"
|
||||
|
||||
#ifndef EMSCRIPTEN
|
||||
#define GLSL_VERSION \
|
||||
@ -11,11 +12,58 @@
|
||||
"\n"
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_HELPER(x) #x
|
||||
#define STRINGIFY(x) STRINGIFY_HELPER(x)
|
||||
|
||||
#define SHADER_DEFS \
|
||||
"#define MAX_LIGHTS " STRINGIFY(SD_MAX_LIGHTS) "\n" \
|
||||
"#define MAX_BONES " STRINGIFY(SD_MAX_BONES) "\n" \
|
||||
"\n"
|
||||
|
||||
#define SHADER_HEADER \
|
||||
GLSL_VERSION \
|
||||
SHADER_DEFS
|
||||
|
||||
#define MESH_MATRICES_GLSL R"GLSL(
|
||||
uniform mat4 u_view_proj;
|
||||
uniform vec4 u_clip_plane;
|
||||
uniform mat4 u_model; // Transform matrix
|
||||
)GLSL"
|
||||
|
||||
#define LIGHT_MATRICES_GLSL R"GLSL(
|
||||
uniform vec3 u_ambient_light;
|
||||
uniform int u_num_lights;
|
||||
uniform vec3 u_light_positions[MAX_LIGHTS];
|
||||
uniform vec4 u_light_colors_rs[MAX_LIGHTS]; // rgb = color, a = radius
|
||||
)GLSL"
|
||||
|
||||
#define COMPUTE_LIGHTS_GLSL R"GLSL(
|
||||
vec3 ComputeLights(in vec3 sector_pos, in vec3 sector_normal)
|
||||
{
|
||||
vec3 color = u_ambient_light;
|
||||
for (int i = 0; i < u_num_lights; ++i) {
|
||||
vec3 light_pos = u_light_positions[i];
|
||||
vec3 light_color = u_light_colors_rs[i].rgb;
|
||||
float light_radius = u_light_colors_rs[i].a;
|
||||
|
||||
vec3 to_light = light_pos - sector_pos.xyz;
|
||||
float dist2 = dot(to_light, to_light);
|
||||
if (dist2 < light_radius * light_radius) {
|
||||
float dist = sqrt(dist2);
|
||||
float attenuation = 1.0 - (dist / light_radius);
|
||||
float dot = max(dot(sector_normal, normalize(to_light)), 0.0);
|
||||
color += light_color * dot * attenuation;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
)GLSL"
|
||||
|
||||
// Zdrojove kody shaderu
|
||||
static const char* const s_srcs[] = {
|
||||
|
||||
// SS_SECTOR_MESH_VERT
|
||||
GLSL_VERSION
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
@ -43,7 +91,7 @@ void main() {
|
||||
)GLSL",
|
||||
|
||||
// SS_SECTOR_MESH_FRAG
|
||||
GLSL_VERSION
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in vec2 v_uv;
|
||||
in vec2 v_lightmap_uv;
|
||||
@ -67,7 +115,7 @@ void main() {
|
||||
)GLSL",
|
||||
|
||||
// SS_PORTAL_VERT
|
||||
GLSL_VERSION
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
|
||||
@ -90,7 +138,7 @@ void main() {
|
||||
)GLSL",
|
||||
|
||||
// SS_PORTAL_FRAG
|
||||
GLSL_VERSION
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in float v_clip_distance;
|
||||
|
||||
@ -113,16 +161,17 @@ void main() {
|
||||
)GLSL",
|
||||
|
||||
// SS_MESH_VERT
|
||||
GLSL_VERSION
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
|
||||
uniform mat4 u_view_proj;
|
||||
uniform vec4 u_clip_plane;
|
||||
|
||||
uniform mat4 u_model; // Transform matrix
|
||||
)GLSL"
|
||||
MESH_MATRICES_GLSL
|
||||
LIGHT_MATRICES_GLSL
|
||||
COMPUTE_LIGHTS_GLSL
|
||||
R"GLSL(
|
||||
|
||||
out vec2 v_uv;
|
||||
out float v_clip_distance;
|
||||
@ -131,6 +180,7 @@ out vec3 v_color;
|
||||
|
||||
void main() {
|
||||
vec4 sector_pos = u_model * vec4(a_pos, 1.0);
|
||||
vec3 sector_normal = normalize(mat3(u_model) * a_normal);
|
||||
gl_Position = u_view_proj * sector_pos;
|
||||
|
||||
// Clip against the plane
|
||||
@ -139,12 +189,82 @@ void main() {
|
||||
//v_normal = mat3(u_model) * a_normal;
|
||||
v_uv = vec2(a_uv.x, 1.0 - a_uv.y);
|
||||
|
||||
v_color = vec3(1.0, 1.0, 1.0);
|
||||
v_color = ComputeLights(sector_pos.xyz, sector_normal);
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
// SS_MESH_FRAG
|
||||
GLSL_VERSION
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in vec2 v_uv;
|
||||
in float v_clip_distance;
|
||||
|
||||
in vec3 v_color;
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
|
||||
layout (location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
if (v_clip_distance < 0.0) {
|
||||
discard; // Discard fragment if it is outside the clip plane
|
||||
}
|
||||
|
||||
o_color = vec4(texture(u_tex, v_uv));
|
||||
o_color.rgb *= v_color; // Apply vertex color
|
||||
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
)GLSL",
|
||||
|
||||
// SS_SKEL_MESH_VERT
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
layout (location = 5) in ivec4 a_bone_ids;
|
||||
layout (location = 6) in vec4 a_bone_weights;
|
||||
|
||||
layout (std140) uniform Bones {
|
||||
mat4 u_bone_matrices[MAX_BONES];
|
||||
};
|
||||
|
||||
)GLSL"
|
||||
MESH_MATRICES_GLSL
|
||||
LIGHT_MATRICES_GLSL
|
||||
COMPUTE_LIGHTS_GLSL
|
||||
R"GLSL(
|
||||
|
||||
out vec2 v_uv;
|
||||
out float v_clip_distance;
|
||||
|
||||
out vec3 v_color;
|
||||
|
||||
void main() {
|
||||
mat4 bone_transform = mat4(0.0);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
int bone_id = a_bone_ids[i];
|
||||
if (bone_id >= 0) {
|
||||
bone_transform += u_bone_matrices[bone_id] * a_bone_weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
vec4 sector_pos = u_model * bone_transform * vec4(a_pos, 1.0);
|
||||
vec3 sector_normal = normalize(mat3(u_model) * mat3(bone_transform) * a_normal);
|
||||
gl_Position = u_view_proj * sector_pos;
|
||||
|
||||
// Clip against the plane
|
||||
v_clip_distance = dot(sector_pos, u_clip_plane);
|
||||
|
||||
v_uv = vec2(a_uv.x, 1.0 - a_uv.y);
|
||||
|
||||
v_color = ComputeLights(sector_pos.xyz, sector_normal);
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
// SS_SKEL_MESH_FRAG
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
in vec2 v_uv;
|
||||
in float v_clip_distance;
|
||||
|
||||
@ -16,6 +16,9 @@ namespace gfx
|
||||
SS_MESH_VERT,
|
||||
SS_MESH_FRAG,
|
||||
|
||||
SS_SKEL_MESH_VERT,
|
||||
SS_SKEL_MESH_FRAG,
|
||||
|
||||
};
|
||||
|
||||
class ShaderSources
|
||||
|
||||
21
src/gfx/uniform_buffer.hpp
Normal file
21
src/gfx/uniform_buffer.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "buffer_object.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
template<class T>
|
||||
class UniformBuffer : public BufferObject
|
||||
{
|
||||
public:
|
||||
UniformBuffer() : BufferObject(GL_UNIFORM_BUFFER, GL_DYNAMIC_DRAW)
|
||||
{
|
||||
}
|
||||
|
||||
void SetData(const T* data, size_t count = 1)
|
||||
{
|
||||
BufferObject::SetData(data, sizeof(T) * count);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
@ -23,7 +23,7 @@ static const VertexAttribInfo s_ATTR_INFO[] = {
|
||||
};
|
||||
|
||||
// Pocet typu vertex atributu
|
||||
static const size_t s_ATTR_COUNT = 5;
|
||||
static const size_t s_ATTR_COUNT = 7;
|
||||
|
||||
gfx::VertexArray::VertexArray(int attrs, int flags) : m_usage(GL_STATIC_DRAW), m_num_indices(0) {
|
||||
glGenVertexArrays(1, &m_vao);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user