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/shader_sources.cpp"
|
||||||
"src/gfx/texture.cpp"
|
"src/gfx/texture.cpp"
|
||||||
"src/gfx/texture.hpp"
|
"src/gfx/texture.hpp"
|
||||||
|
"src/gfx/uniform_buffer.hpp"
|
||||||
"src/gfx/vertex_array.cpp"
|
"src/gfx/vertex_array.cpp"
|
||||||
"src/gfx/vertex_array.hpp"
|
"src/gfx/vertex_array.hpp"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,14 +3,14 @@
|
|||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
Fix known issues:
|
Fix known issues:
|
||||||
- [ ] Fix apparently incorrect light radius of lights visible through scaled portals
|
|
||||||
- [ ] Fix weird movement physics when having different rotation
|
- [ ] 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] Fix GL_LINEAR_MIPMAP_LINEAR error
|
||||||
- [x] Use cached textures when loading meshes instead of always loading the textures again for each mesh
|
- [x] Use cached textures when loading meshes instead of always loading the textures again for each mesh
|
||||||
|
|
||||||
Add new features:
|
Add new features:
|
||||||
- [ ] (Skeletal) Models and animations
|
|
||||||
- [ ] Minimap for debugging
|
- [ ] Minimap for debugging
|
||||||
- [ ] Text and UI rendering
|
- [ ] Text and UI rendering
|
||||||
- [ ] Audio
|
- [ ] Audio
|
||||||
- [ ] Dynamic lights
|
- [ ] Dynamic lights
|
||||||
|
- [x] (Skeletal) Models and animations
|
||||||
|
|||||||
@ -10,8 +10,25 @@ os.makedirs(out_path, exist_ok=True)
|
|||||||
|
|
||||||
roomnames = []
|
roomnames = []
|
||||||
armatures = {}
|
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':
|
if obj.type != 'MESH':
|
||||||
raise TypeError("Selected object is not a 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_layer = mesh.uv_layers.get("LightmapUV")
|
||||||
lightmap_uv_data = lightmap_layer.data if lightmap_layer else None
|
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 deduplication map and list
|
||||||
vertex_map = {} # (position, normal, uv, lightmap_uv) -> vertex_index
|
vertex_map = {} # (position, normal, uv, lightmap_uv) -> vertex_index
|
||||||
unique_vertices = [] # list of strings (v ...)
|
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
|
# faces = [] # list of (v1, v2, v3) indices
|
||||||
material_faces = {} # material_index -> list of face 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:
|
for tri in mesh.loop_triangles:
|
||||||
face_indices = []
|
face_indices = []
|
||||||
|
|
||||||
@ -63,7 +94,32 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
|||||||
else:
|
else:
|
||||||
luv = (0.0, 0.0)
|
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:
|
if key in vertex_map:
|
||||||
index = vertex_map[key]
|
index = vertex_map[key]
|
||||||
@ -81,6 +137,10 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
|||||||
if export_luv:
|
if export_luv:
|
||||||
vertex_line_comps.append(" ".join(f"{c:.3f}" for c in 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)
|
vertex_line = "v " + " ".join(vertex_line_comps)
|
||||||
unique_vertices.append(vertex_line)
|
unique_vertices.append(vertex_line)
|
||||||
|
|
||||||
@ -94,7 +154,13 @@ def export_mesh(obj, output_path, export_luv = False, export_bones = False):
|
|||||||
if export_luv:
|
if export_luv:
|
||||||
f.write("luv\n")
|
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:
|
for v in unique_vertices:
|
||||||
f.write(v + "\n")
|
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}"
|
return f"{rad_to_deg(rotation.x):.0f} {rad_to_deg(rotation.y):.0f} {rad_to_deg(rotation.z):.0f}"
|
||||||
|
|
||||||
def rotation_str2(rotation):
|
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):
|
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):
|
def scale_str(scale):
|
||||||
return f"{scale.x:.3f}"
|
return f"{scale.x:.4f}"
|
||||||
|
|
||||||
def matrix_decompose_str(matrix):
|
def matrix_decompose_str(matrix):
|
||||||
translation, rotation, scale = matrix.decompose()
|
translation, rotation, scale = matrix.decompose()
|
||||||
@ -133,28 +199,12 @@ def matrix_decompose_str(matrix):
|
|||||||
def get_path(name, ext):
|
def get_path(name, ext):
|
||||||
return os.path.join(out_path, f"{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):
|
def export_armature(armature: bpy.types.Armature, output_path: str):
|
||||||
keep_bones = get_armature_keep_bones(armature)
|
keep_bones = get_armature_keep_bones(armature)
|
||||||
|
|
||||||
# Export armature data (bones, etc.)
|
# Export armature data (bones, etc.)
|
||||||
with open(output_path, 'w') as f:
|
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:
|
for bone in armature.data.bones:
|
||||||
if not bone in keep_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
|
bind_matrix = bone.matrix_local
|
||||||
f.write(f"b {bone.name} {parent_name} {matrix_decompose_str(bind_matrix)}\n")
|
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}")
|
print(f"Exported Armature: {armature.name} to {output_path}")
|
||||||
|
|
||||||
def vectors_similar(v1, v2, threshold):
|
def vectors_similar(v1, v2, threshold):
|
||||||
return (v1 - v2).length < 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
|
_, t1, r1, s1, _ = f1
|
||||||
_, t2, r2, s2, _ = f2
|
_, 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}
|
bone_frames = {bone.name: [] for bone in keep_bones}
|
||||||
|
|
||||||
start, end = map(int, action.frame_range)
|
_, end = map(int, action.frame_range)
|
||||||
for frame in range(start, end):
|
fps = bpy.context.scene.render.fps
|
||||||
|
|
||||||
|
for frame in range(0, end):
|
||||||
bpy.context.scene.frame_set(frame)
|
bpy.context.scene.frame_set(frame)
|
||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
@ -221,10 +277,13 @@ def export_animation(action: bpy.types.Action, armature: bpy.types.Armature, out
|
|||||||
|
|
||||||
if frames_similar(last_frame, current_frame):
|
if frames_similar(last_frame, current_frame):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
frame_list.append(current_frame)
|
frame_list.append(current_frame)
|
||||||
|
|
||||||
with open(output_path, 'w') as f:
|
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():
|
for bone_name, frames in bone_frames.items():
|
||||||
f.write(f"ch {bone_name}\n")
|
f.write(f"ch {bone_name}\n")
|
||||||
|
|
||||||
@ -274,17 +333,23 @@ for object in bpy.data.objects:
|
|||||||
if match:
|
if match:
|
||||||
mesh_name = match.group(1)
|
mesh_name = match.group(1)
|
||||||
print(f"Found Non-Room Mesh: {mesh_name}")
|
print(f"Found Non-Room Mesh: {mesh_name}")
|
||||||
export_mesh(object, get_path(mesh_name, "mesh"))
|
|
||||||
|
armature = None
|
||||||
|
|
||||||
parent = object.parent
|
parent = object.parent
|
||||||
if parent and parent.type == 'ARMATURE':
|
if parent and parent.type == 'ARMATURE':
|
||||||
armatures[parent.name] = parent
|
armatures[parent.name] = parent
|
||||||
print(f" Is skeletal, Parent Armature: {parent.name}")
|
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()]
|
armature_names = [name for name, _ in armatures.items()]
|
||||||
print("Armatures will be exported: ", armature_names)
|
print("Armatures will be exported: ", armature_names)
|
||||||
|
|
||||||
actions = {}
|
actions = {}
|
||||||
|
armature_anims = {name: [] for name in armature_names}
|
||||||
|
|
||||||
for action in bpy.data.actions:
|
for action in bpy.data.actions:
|
||||||
match = re.search(r"Anim.(\w+).(\w+)", action.name)
|
match = re.search(r"Anim.(\w+).(\w+)", action.name)
|
||||||
@ -293,16 +358,19 @@ for action in bpy.data.actions:
|
|||||||
action_name = match.group(2)
|
action_name = match.group(2)
|
||||||
|
|
||||||
if action_armature in armatures:
|
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()]
|
action_names = [f"{data[1]} ({data[2].name})" for name, data in actions.items()]
|
||||||
print("Actions will be exported: ", action_names)
|
print("Actions will be exported: ", action_names)
|
||||||
|
|
||||||
for armature_name, armature in armatures.items():
|
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():
|
for action_name, (action, action_file_name, armature) in actions.items():
|
||||||
export_animation(action, armature, get_path(f"{armature.name}_{simple_action_name}", "anim"))
|
export_animation(action, armature, get_path(action_file_name, "anim"))
|
||||||
|
|
||||||
with open(get_path("rooms", "list"), 'w') as rooms_file:
|
with open(get_path("rooms", "list"), 'w') as rooms_file:
|
||||||
for room_name in roomnames:
|
for room_name in roomnames:
|
||||||
|
|||||||
10
src/app.cpp
10
src/app.cpp
@ -34,7 +34,7 @@ App::App()
|
|||||||
// scaling hallway
|
// scaling hallway
|
||||||
size_t s3i = world_.AddSector(room003);
|
size_t s3i = world_.AddSector(room003);
|
||||||
game::Sector& s3 = world_.GetSector(s3i);
|
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
|
// purple
|
||||||
size_t s4i = world_.AddSector(room001);
|
size_t s4i = world_.AddSector(room001);
|
||||||
@ -71,6 +71,9 @@ App::App()
|
|||||||
|
|
||||||
world_.Bake();
|
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));
|
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);
|
renderer_.Begin(viewport_size_.x, viewport_size_.y);
|
||||||
|
|
||||||
player_->SetInput(input_);
|
player_->SetInput(input_);
|
||||||
player_->Update(delta_time);
|
|
||||||
|
world_.Update(delta_time);
|
||||||
|
|
||||||
//const auto& position = player_->GetOccurrence().GetPosition();
|
//const auto& position = player_->GetOccurrence().GetPosition();
|
||||||
size_t sector_idx;
|
size_t sector_idx;
|
||||||
glm::vec3 position, forward, up;
|
glm::vec3 position, forward, up;
|
||||||
player_->GetPOV(sector_idx, 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)
|
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)
|
if (flags & MF_IS_SKELETAL)
|
||||||
{
|
{
|
||||||
// Bone indices as 4 ints
|
for (int i = 0; i < 4; ++i)
|
||||||
int32_t bone_indices[4] = { 0, 0, 0, 0 };
|
{
|
||||||
float bone_weights[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
BufferPut(buffer, static_cast<int32_t>(vert.bones[i].bone_index));
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
bone_indices[i] = static_cast<int32_t>(vert.bones[i].bone_index);
|
BufferPut(buffer, vert.bones[i].weight);
|
||||||
bone_weights[i] = vert.bones[i].weight;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BufferPut(buffer, bone_indices);
|
|
||||||
BufferPut(buffer, bone_weights);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
va_.SetVBOData(buffer.data(), buffer.size());
|
va_.SetVBOData(buffer.data(), buffer.size());
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
#include "entity.hpp"
|
#include "entity.hpp"
|
||||||
#include "world.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) :
|
game::Entity::Entity(World* world, size_t sector_idx, const CapsuleShape& capsule_shape, const glm::vec3& position) :
|
||||||
world_(world),
|
world_(world),
|
||||||
capsule_(capsule_shape),
|
capsule_(capsule_shape),
|
||||||
velocity_(0.0f),
|
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;
|
CreateOccurrenceParams occu_params;
|
||||||
occu_params.sector = &world->GetSector(sector_idx);
|
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)
|
void game::Entity::Update(float dt)
|
||||||
{
|
{
|
||||||
Move(velocity_, 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)
|
void game::Entity::CreateOtherOccurence(const Portal& portal)
|
||||||
@ -184,3 +254,9 @@ bool game::EntityOccurrence::Sweep(const glm::vec3& target_position, float& hit_
|
|||||||
hit_normal
|
hit_normal
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const game::LightInfluences& game::EntityOccurrence::GetLights()
|
||||||
|
{
|
||||||
|
entity_->UpdateLights();
|
||||||
|
return lights_;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "sector.hpp"
|
#include "sector.hpp"
|
||||||
|
#include "gfx/shader_defs.hpp"
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
@ -14,6 +15,15 @@ namespace game
|
|||||||
float height;
|
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
|
struct CreateOccurrenceParams
|
||||||
{
|
{
|
||||||
Sector* sector;
|
Sector* sector;
|
||||||
@ -38,6 +48,8 @@ namespace game
|
|||||||
|
|
||||||
btCapsuleShapeZ bt_capsule_;
|
btCapsuleShapeZ bt_capsule_;
|
||||||
|
|
||||||
|
LightInfluences lights_;
|
||||||
|
|
||||||
friend class Entity;
|
friend class Entity;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -46,6 +58,8 @@ namespace game
|
|||||||
// pos in sector space
|
// pos in sector space
|
||||||
bool Sweep(const glm::vec3& target_position, float& hit_fraction, glm::vec3& hit_normal, const Portal** hit_portal);
|
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 Sector& GetSector() const { return *sector_; }
|
||||||
const glm::vec3& GetPosition() const { return position_; }
|
const glm::vec3& GetPosition() const { return position_; }
|
||||||
|
|
||||||
@ -66,6 +80,9 @@ namespace game
|
|||||||
|
|
||||||
glm::vec3 velocity_;
|
glm::vec3 velocity_;
|
||||||
|
|
||||||
|
glm::vec3 light_trace_offset_; // In entity space
|
||||||
|
bool lights_valid_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Entity(World* world, size_t sector_idx, const CapsuleShape& capsule_shape, const glm::vec3& position);
|
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 SetVelocity(const glm::vec3& velocity) { velocity_ = velocity; }
|
||||||
|
|
||||||
|
void UpdateLights();
|
||||||
|
|
||||||
const glm::vec3& GetVelocity() const { return velocity_; }
|
const glm::vec3& GetVelocity() const { return velocity_; }
|
||||||
const CapsuleShape& GetCapsuleShape() const { return capsule_; }
|
const CapsuleShape& GetCapsuleShape() const { return capsule_; }
|
||||||
EntityOccurrence& GetOccurrence() { return *occu_; }
|
EntityOccurrence& GetOccurrence() { return *occu_; }
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
#include "meshinstance.hpp"
|
#include "meshinstance.hpp"
|
||||||
|
#include "gfx/shader_defs.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@ -7,9 +8,13 @@ game::MeshInstance::MeshInstance(std::shared_ptr<const assets::Mesh> mesh, const
|
|||||||
root_node_(root_node),
|
root_node_(root_node),
|
||||||
time_ptr_(time_ptr)
|
time_ptr_(time_ptr)
|
||||||
{
|
{
|
||||||
if (IsSkeletal())
|
skeletal_ = (bool)mesh_->GetSkeleton();
|
||||||
|
|
||||||
|
if (skeletal_)
|
||||||
{
|
{
|
||||||
SetupBoneNodes();
|
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()
|
void game::MeshInstance::Update()
|
||||||
{
|
{
|
||||||
if (IsSkeletal())
|
if (skeletal_)
|
||||||
{
|
{
|
||||||
ApplyAnimFrame();
|
ApplyAnimFrame();
|
||||||
UpdateBoneMatrices();
|
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()
|
void game::MeshInstance::ApplyAnimFrame()
|
||||||
{
|
{
|
||||||
ApplySkelAnim(current_anim_, anim_time_, 1.0f);
|
ApplySkelAnim(current_anim_, anim_time_, 1.0f);
|
||||||
|
|||||||
@ -2,30 +2,37 @@
|
|||||||
|
|
||||||
#include "assets/mesh.hpp"
|
#include "assets/mesh.hpp"
|
||||||
#include "transform_node.hpp"
|
#include "transform_node.hpp"
|
||||||
|
#include "gfx/uniform_buffer.hpp"
|
||||||
|
|
||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
class MeshInstance
|
class MeshInstance
|
||||||
{
|
{
|
||||||
std::shared_ptr<const assets::Mesh> mesh_;
|
std::shared_ptr<const assets::Mesh> mesh_;
|
||||||
|
bool skeletal_;
|
||||||
|
|
||||||
const TransformNode* root_node_ = nullptr;
|
const TransformNode* root_node_ = nullptr;
|
||||||
std::vector<TransformNode> bone_nodes_; // Only used if the mesh is skeletal
|
std::vector<TransformNode> bone_nodes_; // Only used if the mesh is skeletal
|
||||||
|
|
||||||
const float* time_ptr_ = nullptr; // Pointer to game time variable
|
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;
|
const assets::Animation* current_anim_ = nullptr;
|
||||||
float anim_time_ = 0.0f;
|
float anim_time_ = 0.0f;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MeshInstance(std::shared_ptr<const assets::Mesh> mesh, const TransformNode* root_node, const float* time_ptr);
|
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 PlayAnim(const std::string& name);
|
||||||
|
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
|
const gfx::UniformBuffer<glm::mat4>& GetBoneUBO();
|
||||||
|
|
||||||
const std::shared_ptr<const assets::Mesh>& GetMesh() const { return mesh_; }
|
const std::shared_ptr<const assets::Mesh>& GetMesh() const { return mesh_; }
|
||||||
const TransformNode* GetRootNode() const { return root_node_; }
|
const TransformNode* GetRootNode() const { return root_node_; }
|
||||||
const TransformNode& GetBoneNode(size_t index) const { return bone_nodes_[index]; }
|
const TransformNode& GetBoneNode(size_t index) const { return bone_nodes_[index]; }
|
||||||
@ -35,6 +42,7 @@ namespace game
|
|||||||
void SetupBoneNodes();
|
void SetupBoneNodes();
|
||||||
void UpdateBoneMatrices();
|
void UpdateBoneMatrices();
|
||||||
|
|
||||||
|
void UpdateBoneUBO();
|
||||||
protected:
|
protected:
|
||||||
virtual void ApplyAnimFrame();
|
virtual void ApplyAnimFrame();
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,9 @@ game::Player::Player(World* world, size_t sector_idx, const glm::vec3& position)
|
|||||||
world->GetTimePtr()
|
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)
|
void game::Player::Rotate(float delta_yaw, float delta_pitch)
|
||||||
@ -38,6 +40,7 @@ void game::Player::Update(float dt)
|
|||||||
{
|
{
|
||||||
time_ += dt;
|
time_ += dt;
|
||||||
|
|
||||||
|
vm_node_.UpdateMatrix();
|
||||||
vm_hands_.Update();
|
vm_hands_.Update();
|
||||||
|
|
||||||
if (vm_weapon_)
|
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();
|
sector_idx = occu_->GetSector().GetIndex();
|
||||||
|
|
||||||
glm::vec2 bobbing_offset = GetBobbingOffset(time_, 0.01f * current_speed_);
|
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::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;
|
position = occu_->GetPosition() + offset;
|
||||||
|
|
||||||
forward = cam_forward_;
|
forward = cam_forward_;
|
||||||
up = cam_up_;
|
up = cam_up_;
|
||||||
|
|
||||||
|
other_pov_ = false;
|
||||||
|
|
||||||
if (touching_portal_ && touching_portal_->link)
|
if (touching_portal_ && touching_portal_->link)
|
||||||
{
|
{
|
||||||
float sd = glm::dot(glm::vec3(touching_portal_->plane), position) + touching_portal_->plane.w;
|
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;
|
forward = touching_portal_->tr_basis * forward;
|
||||||
up = touching_portal_->tr_basis * up;
|
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_);
|
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)
|
glm::vec2 game::Player::GetBobbingOffset(float t, float amplitude)
|
||||||
{
|
{
|
||||||
// Frequency and amplitude can be adjusted to tweak the effect
|
// Frequency and amplitude can be adjusted to tweak the effect
|
||||||
|
|||||||
@ -26,11 +26,13 @@ namespace game
|
|||||||
float time_ = 0.0f;
|
float time_ = 0.0f;
|
||||||
float current_speed_ = 0.0f;
|
float current_speed_ = 0.0f;
|
||||||
|
|
||||||
|
bool other_pov_ = false; // True is viewed from other occurence sector (camera behind portal plane)
|
||||||
|
|
||||||
// Viewmodel stuff
|
// Viewmodel stuff
|
||||||
TransformNode vm_node_; // Affected by bobbing
|
TransformNode vm_node_; // Affected by bobbing
|
||||||
MeshInstance vm_hands_;
|
MeshInstance vm_hands_;
|
||||||
std::unique_ptr<MeshInstance> vm_weapon_; // Can be null
|
std::unique_ptr<MeshInstance> vm_weapon_; // Can be null
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Player(World* world, size_t sector_idx, const glm::vec3& position);
|
Player(World* world, size_t sector_idx, const glm::vec3& position);
|
||||||
|
|
||||||
@ -41,9 +43,10 @@ namespace game
|
|||||||
|
|
||||||
virtual void Update(float dt) override;
|
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:
|
private:
|
||||||
static glm::vec2 GetBobbingOffset(float t, float amplitude);
|
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());
|
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)
|
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 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.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;
|
light.through_portal = &portal;
|
||||||
|
|
||||||
all_lights_.push_back(light);
|
all_lights_.push_back(light);
|
||||||
@ -479,7 +481,6 @@ void game::Sector::GenerateAllLights()
|
|||||||
void game::Sector::BakeLightmap()
|
void game::Sector::BakeLightmap()
|
||||||
{
|
{
|
||||||
const size_t lightmap_size = 128;
|
const size_t lightmap_size = 128;
|
||||||
const glm::vec3 ambient_light(0.2f); // Ambient light color
|
|
||||||
const float margin = 1.0f;
|
const float margin = 1.0f;
|
||||||
|
|
||||||
std::span<const assets::MeshVertex> mesh_verts = mesh_->GetVertices();
|
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_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 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();
|
lights.clear();
|
||||||
GetLightsAt(texel_pos_ws, lights);
|
GetLightsAt(texel_pos_ws, lights);
|
||||||
|
|||||||
@ -62,6 +62,14 @@ namespace game
|
|||||||
const Portal* through_portal;
|
const Portal* through_portal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SectorEnvironment
|
||||||
|
{
|
||||||
|
glm::vec3 ambient;
|
||||||
|
bool has_sun = false;
|
||||||
|
glm::vec3 sun_direction;
|
||||||
|
glm::vec3 sun_color;
|
||||||
|
};
|
||||||
|
|
||||||
class Sector
|
class Sector
|
||||||
{
|
{
|
||||||
World* world_;
|
World* world_;
|
||||||
@ -70,6 +78,7 @@ namespace game
|
|||||||
|
|
||||||
std::shared_ptr<const assets::Mesh> mesh_;
|
std::shared_ptr<const assets::Mesh> mesh_;
|
||||||
|
|
||||||
|
SectorEnvironment env_;
|
||||||
std::vector<Light> lights_; // Light in this sector
|
std::vector<Light> lights_; // Light in this sector
|
||||||
std::vector<Light> all_lights_; // Lights in this sector and linked sectors
|
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<const assets::Mesh>& GetMesh() const { return mesh_; }
|
||||||
const std::shared_ptr<gfx::Texture>& GetLightmap() const { return lightmap_; }
|
const std::shared_ptr<gfx::Texture>& GetLightmap() const { return lightmap_; }
|
||||||
btCollisionWorld& GetBtWorld() { return bt_world_; }
|
btCollisionWorld& GetBtWorld() { return bt_world_; }
|
||||||
|
const SectorEnvironment& GetEnvironment() const { return env_; }
|
||||||
|
|
||||||
int GetPortalIndex(const std::string& name) const;
|
int GetPortalIndex(const std::string& name) const;
|
||||||
const Portal& GetPortal(size_t idx) const { return portals_[idx]; }
|
const Portal& GetPortal(size_t idx) const { return portals_[idx]; }
|
||||||
|
|||||||
@ -35,3 +35,13 @@ void game::World::Bake()
|
|||||||
sector->Bake();
|
sector->Bake();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::World::Update(float dt)
|
||||||
|
{
|
||||||
|
time_ += dt;
|
||||||
|
|
||||||
|
for (auto& entity : entities_)
|
||||||
|
{
|
||||||
|
entity->Update(dt);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -32,6 +32,8 @@ namespace game
|
|||||||
entities_.push_back(std::move(entity));
|
entities_.push_back(std::move(entity));
|
||||||
return entity_ptr;
|
return entity_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Update(float dt);
|
||||||
|
|
||||||
Sector& GetSector(size_t idx) { return *sectors_[idx]; }
|
Sector& GetSector(size_t idx) { return *sectors_[idx]; }
|
||||||
const Sector& GetSector(size_t idx) const { 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(sector_shader_.shader, SS_SECTOR_MESH_VERT, SS_SECTOR_MESH_FRAG);
|
||||||
ShaderSources::MakeShader(portal_shader_.shader, SS_PORTAL_VERT, SS_PORTAL_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(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
|
proj_ = glm::mat4(1.0f); // Initialize projection matrix to identity
|
||||||
|
|
||||||
SetupPortalVAO();
|
SetupPortalVAO();
|
||||||
@ -40,6 +41,7 @@ void gfx::Renderer::Begin(size_t width, size_t height)
|
|||||||
void gfx::Renderer::DrawWorld(
|
void gfx::Renderer::DrawWorld(
|
||||||
const game::World& world,
|
const game::World& world,
|
||||||
size_t sector_idx,
|
size_t sector_idx,
|
||||||
|
game::Player* player,
|
||||||
const glm::vec3& eye,
|
const glm::vec3& eye,
|
||||||
const glm::vec3& dir,
|
const glm::vec3& dir,
|
||||||
const glm::vec3& up,
|
const glm::vec3& up,
|
||||||
@ -93,16 +95,51 @@ void gfx::Renderer::DrawWorld(
|
|||||||
|
|
||||||
DrawSector(params);
|
DrawSector(params);
|
||||||
|
|
||||||
//glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
//glDisable(GL_CULL_FACE);
|
//glDisable(GL_CULL_FACE);
|
||||||
//glDisable(GL_DEPTH_TEST);
|
//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()
|
void gfx::Renderer::UnreadyShaders()
|
||||||
{
|
{
|
||||||
portal_shader_.setup_sector = 0;
|
portal_shader_.setup_sector = 0;
|
||||||
sector_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)
|
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
|
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/world.hpp"
|
||||||
#include "game/sector.hpp"
|
#include "game/sector.hpp"
|
||||||
#include "game/meshinstance.hpp"
|
#include "game/meshinstance.hpp"
|
||||||
|
#include "game/player.hpp"
|
||||||
|
|
||||||
namespace gfx
|
namespace gfx
|
||||||
{
|
{
|
||||||
@ -37,6 +38,7 @@ namespace gfx
|
|||||||
void DrawWorld(
|
void DrawWorld(
|
||||||
const game::World& world,
|
const game::World& world,
|
||||||
size_t sector_idx,
|
size_t sector_idx,
|
||||||
|
game::Player* player,
|
||||||
const glm::vec3& eye,
|
const glm::vec3& eye,
|
||||||
const glm::vec3& dir,
|
const glm::vec3& dir,
|
||||||
const glm::vec3& up,
|
const glm::vec3& up,
|
||||||
@ -47,6 +49,7 @@ namespace gfx
|
|||||||
SectorShader sector_shader_;
|
SectorShader sector_shader_;
|
||||||
SectorShader portal_shader_;
|
SectorShader portal_shader_;
|
||||||
SectorShader mesh_shader_;
|
SectorShader mesh_shader_;
|
||||||
|
SectorShader skel_mesh_shader_;
|
||||||
|
|
||||||
std::shared_ptr<VertexArray> portal_vao_;
|
std::shared_ptr<VertexArray> portal_vao_;
|
||||||
void SetupPortalVAO();
|
void SetupPortalVAO();
|
||||||
@ -60,6 +63,7 @@ namespace gfx
|
|||||||
glm::mat4 proj_;
|
glm::mat4 proj_;
|
||||||
size_t last_sector_id;
|
size_t last_sector_id;
|
||||||
const Shader* current_shader_;
|
const Shader* current_shader_;
|
||||||
|
std::vector<game::MeshInstance*> draw_meshes_;
|
||||||
|
|
||||||
void UnreadyShaders();
|
void UnreadyShaders();
|
||||||
void SetupSectorShader(const DrawSectorParams& params, SectorShader& sshader);
|
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);
|
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 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_portal_size", // SU_PORTAL_SIZE
|
||||||
"u_clear_depth", // SU_CLEAR_DEPTH
|
"u_clear_depth", // SU_CLEAR_DEPTH
|
||||||
"u_lightmap_tex", // SU_LIGHTMAP_TEX
|
"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
|
// 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++)
|
for (size_t i = 0; i < SU_COUNT; i++)
|
||||||
m_uni[i] = glGetUniformLocation(m_id, s_uni_names[i]);
|
m_uni[i] = glGetUniformLocation(m_id, s_uni_names[i]);
|
||||||
|
|
||||||
SetupTextureBindings();
|
SetupBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx::Shader::~Shader() {
|
gfx::Shader::~Shader() {
|
||||||
@ -89,12 +93,19 @@ gfx::Shader::~Shader() {
|
|||||||
glDeleteProgram(m_id);
|
glDeleteProgram(m_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gfx::Shader::SetupTextureBindings()
|
void gfx::Shader::SetupBindings()
|
||||||
{
|
{
|
||||||
glUseProgram(m_id);
|
glUseProgram(m_id);
|
||||||
|
|
||||||
glUniform1i(m_uni[SU_TEX], 0);
|
glUniform1i(m_uni[SU_TEX], 0);
|
||||||
glUniform1i(m_uni[SU_LIGHTMAP_TEX], 1);
|
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);
|
glUseProgram(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,10 @@ namespace gfx
|
|||||||
SU_PORTAL_SIZE,
|
SU_PORTAL_SIZE,
|
||||||
SU_CLEAR_DEPTH,
|
SU_CLEAR_DEPTH,
|
||||||
SU_LIGHTMAP_TEX,
|
SU_LIGHTMAP_TEX,
|
||||||
|
SU_NUM_LIGHTS,
|
||||||
|
SU_LIGHT_POSITIONS,
|
||||||
|
SU_LIGHT_COLORS_RS,
|
||||||
|
SU_AMBIENT_LIGHT,
|
||||||
|
|
||||||
SU_COUNT
|
SU_COUNT
|
||||||
};
|
};
|
||||||
@ -49,7 +53,7 @@ namespace gfx
|
|||||||
GLuint GetId() const { return m_id; }
|
GLuint GetId() const { return m_id; }
|
||||||
|
|
||||||
private:
|
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_sources.hpp"
|
||||||
|
#include "shader_defs.hpp"
|
||||||
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
#define GLSL_VERSION \
|
#define GLSL_VERSION \
|
||||||
@ -11,11 +12,58 @@
|
|||||||
"\n"
|
"\n"
|
||||||
#endif
|
#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
|
// Zdrojove kody shaderu
|
||||||
static const char* const s_srcs[] = {
|
static const char* const s_srcs[] = {
|
||||||
|
|
||||||
// SS_SECTOR_MESH_VERT
|
// SS_SECTOR_MESH_VERT
|
||||||
GLSL_VERSION
|
SHADER_HEADER
|
||||||
R"GLSL(
|
R"GLSL(
|
||||||
layout (location = 0) in vec3 a_pos;
|
layout (location = 0) in vec3 a_pos;
|
||||||
layout (location = 1) in vec3 a_normal;
|
layout (location = 1) in vec3 a_normal;
|
||||||
@ -43,7 +91,7 @@ void main() {
|
|||||||
)GLSL",
|
)GLSL",
|
||||||
|
|
||||||
// SS_SECTOR_MESH_FRAG
|
// SS_SECTOR_MESH_FRAG
|
||||||
GLSL_VERSION
|
SHADER_HEADER
|
||||||
R"GLSL(
|
R"GLSL(
|
||||||
in vec2 v_uv;
|
in vec2 v_uv;
|
||||||
in vec2 v_lightmap_uv;
|
in vec2 v_lightmap_uv;
|
||||||
@ -67,7 +115,7 @@ void main() {
|
|||||||
)GLSL",
|
)GLSL",
|
||||||
|
|
||||||
// SS_PORTAL_VERT
|
// SS_PORTAL_VERT
|
||||||
GLSL_VERSION
|
SHADER_HEADER
|
||||||
R"GLSL(
|
R"GLSL(
|
||||||
layout (location = 0) in vec3 a_pos;
|
layout (location = 0) in vec3 a_pos;
|
||||||
|
|
||||||
@ -90,7 +138,7 @@ void main() {
|
|||||||
)GLSL",
|
)GLSL",
|
||||||
|
|
||||||
// SS_PORTAL_FRAG
|
// SS_PORTAL_FRAG
|
||||||
GLSL_VERSION
|
SHADER_HEADER
|
||||||
R"GLSL(
|
R"GLSL(
|
||||||
in float v_clip_distance;
|
in float v_clip_distance;
|
||||||
|
|
||||||
@ -113,16 +161,17 @@ void main() {
|
|||||||
)GLSL",
|
)GLSL",
|
||||||
|
|
||||||
// SS_MESH_VERT
|
// SS_MESH_VERT
|
||||||
GLSL_VERSION
|
SHADER_HEADER
|
||||||
R"GLSL(
|
R"GLSL(
|
||||||
layout (location = 0) in vec3 a_pos;
|
layout (location = 0) in vec3 a_pos;
|
||||||
layout (location = 1) in vec3 a_normal;
|
layout (location = 1) in vec3 a_normal;
|
||||||
layout (location = 3) in vec2 a_uv;
|
layout (location = 3) in vec2 a_uv;
|
||||||
|
|
||||||
uniform mat4 u_view_proj;
|
)GLSL"
|
||||||
uniform vec4 u_clip_plane;
|
MESH_MATRICES_GLSL
|
||||||
|
LIGHT_MATRICES_GLSL
|
||||||
uniform mat4 u_model; // Transform matrix
|
COMPUTE_LIGHTS_GLSL
|
||||||
|
R"GLSL(
|
||||||
|
|
||||||
out vec2 v_uv;
|
out vec2 v_uv;
|
||||||
out float v_clip_distance;
|
out float v_clip_distance;
|
||||||
@ -131,6 +180,7 @@ out vec3 v_color;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 sector_pos = u_model * vec4(a_pos, 1.0);
|
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;
|
gl_Position = u_view_proj * sector_pos;
|
||||||
|
|
||||||
// Clip against the plane
|
// Clip against the plane
|
||||||
@ -139,12 +189,82 @@ void main() {
|
|||||||
//v_normal = mat3(u_model) * a_normal;
|
//v_normal = mat3(u_model) * a_normal;
|
||||||
v_uv = vec2(a_uv.x, 1.0 - a_uv.y);
|
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",
|
)GLSL",
|
||||||
|
|
||||||
// SS_MESH_FRAG
|
// 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(
|
R"GLSL(
|
||||||
in vec2 v_uv;
|
in vec2 v_uv;
|
||||||
in float v_clip_distance;
|
in float v_clip_distance;
|
||||||
|
|||||||
@ -16,6 +16,9 @@ namespace gfx
|
|||||||
SS_MESH_VERT,
|
SS_MESH_VERT,
|
||||||
SS_MESH_FRAG,
|
SS_MESH_FRAG,
|
||||||
|
|
||||||
|
SS_SKEL_MESH_VERT,
|
||||||
|
SS_SKEL_MESH_FRAG,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ShaderSources
|
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
|
// 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) {
|
gfx::VertexArray::VertexArray(int attrs, int flags) : m_usage(GL_STATIC_DRAW), m_num_indices(0) {
|
||||||
glGenVertexArrays(1, &m_vao);
|
glGenVertexArrays(1, &m_vao);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user