Normal mapping

This commit is contained in:
KP 2024-07-29 17:06:31 -05:00
parent 31bea0f6d1
commit 92caa54ec9
28 changed files with 350 additions and 238 deletions

View file

@ -1,36 +1,37 @@
#version 330 core #version 330 core
uniform vec3 u_campos; uniform sampler2D u_texture;
uniform sampler2D u_normal_texture;
uniform vec3 u_campos;
uniform vec3 u_highlight; uniform vec3 u_highlight;
uniform float u_time;
in vec3 v_position; in vec3 v_position;
in vec3 v_normal; in vec3 v_normal;
in vec3 v_proj_normal; in vec3 v_proj_normal;
in vec2 v_texcoord; in vec2 v_texcoord;
in vec3 v_tangent;
in vec3 v_bitangent;
layout (location=0) out vec4 v_output; layout (location=0) out vec4 v_output;
uniform sampler2D u_texture; float kpc(float d, float min, float max)
float get_fog_factor(float d)
{ {
const float FogMax = 10.0; if (d >= max) return 1.0;
const float FogMin = 0.0; if (d <= min) return 0.0;
if (d >= FogMax) return 1.0; return 1.0 - (max - d) / (max - min);
if (d <= FogMin) return 0.0;
return 1 - (FogMax - d) / (FogMax - FogMin);
} }
struct Light struct Light
{ {
vec3 position; vec3 position;
vec3 diffuse; vec3 diffuse;
float radius;
}; };
float lambert(vec3 n, vec3 l) float Lambert(vec3 n, vec3 l)
{ {
vec3 nrmn = normalize(n); vec3 nrmn = normalize(n);
vec3 nrml = normalize(l); vec3 nrml = normalize(l);
@ -38,55 +39,38 @@ float lambert(vec3 n, vec3 l)
return max(res, 0.0); return max(res, 0.0);
} }
float dist(vec2 p0, vec2 pf){return sqrt((pf.x-p0.x)*(pf.x-p0.x)+(pf.y-p0.y)*(pf.y-p0.y));} vec4 MakeLight(vec3 norm, vec3 position, vec3 diffuse, float radius, float sharpness, float power)
{
Light light;
light.position = position;
light.diffuse = diffuse;
light.radius = radius;
vec4 res = vec4(light.diffuse * Lambert(norm, normalize(light.position)), 1.0);
float alpha = kpc(distance(light.position, v_position), light.radius * sharpness, light.radius);
res *= mix(vec4(light.diffuse, 1), vec4(0,0,0,1.0), alpha);
return res;
}
void main() void main()
{ {
Light light; vec3 tangentNormal = texture(u_normal_texture, v_texcoord).xyz * 2.0 - 1.0;
light.position = vec3(5.0, 4.0, 5.0); vec3 T = normalize(v_tangent);
light.diffuse = vec3(1.0, 1.0, 1.0); vec3 B = normalize(v_bitangent);
vec3 N = normalize(v_normal);
mat3 TBN = mat3(T, B, N);
vec3 norm = normalize(TBN * tangentNormal);
vec4 texv = texture(u_texture, v_texcoord);
//norm = abs(norm);
vec4 ambient = vec4(0.1, 0.1, 0.1, 1.0) * 1.5, light = ambient;
vec4 tex = texture(u_texture, v_texcoord); light += MakeLight(norm, u_campos, vec3(1.0, 1.0, 1.0), 5.0, 0.0, 1.0);
vec3 norm = (v_normal); light += MakeLight(norm, vec3(5.0, 2.0, -7.0 + cos(u_time/35.0) * 2.0), vec3(1.0, 0.6, 0.0), 5.0, 0.6, 1.0);
light += MakeLight(norm, vec3(7, 2.0, -7.0 + -cos(u_time / 30.0) * 1.0), vec3(0.6, 0.9, 1.0), 5.0, 0.6, 1.0);
v_output = texv * light;
vec3 res = vec3(1.0, 1.0, 1.0) * lambert(norm, normalize(light.position)); // + editor highlighting
float a = dist(v_position.xy, vec2(0.0, 0.0)) * 2.0f;
// vec4 res2 = texture2D(u_texture, v_texcoord) * ((dot(-light.position, v_normal) * 0.5)+2.5);
vec4 texv = texture2D(u_texture, v_texcoord);
vec4 res2 = texv * (abs(dot(-light.position, v_normal) * 0.5));
//res2.xyz *= 0.2;
res2.a = texv.a;
v_output = res2;//vec4(res2, 1.0);
v_output = mix(v_output, texv, 0.66);
v_output.rgb *= u_highlight; v_output.rgb *= u_highlight;
/*
vec3 col;
vec4 tex = texture(u_texture, v_texcoord);
vec3 norm = normalize(v_normal);
vec3 dir = normalize(vec3(5.0, 0.25, -5.0) - v_position);
float diff = max(dot(norm, dir), 0.0);
float alpha = get_fog_factor(distance(u_campos, v_position));
vec3 diffuse = diff * vec3(0.8, 0.75, 0.7);
float ff = 1.75;
if (diffuse.x > 0.0) diffuse.x *= ff;
if (diffuse.y > 0.0) diffuse.y *= ff;
if (diffuse.z > 0.0) diffuse.z *= ff;
vec3 ambient = vec3(0.3, 0.3, 0.3);
col = (ambient + diffuse) * tex.rgb;
col *= mix(vec4(1.0), vec4(0.0f, 0.0, 0.0, 1.0), alpha-0.2).xyz;
*/
// vec4 tex = texture(u_texture, v_texcoord);
// v_output = tex * vec4(vec3(1.5), 1.0);
} }

View file

@ -5,11 +5,15 @@ uniform mat4 u_mvp;
layout (location = 0) in vec3 position; layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal; layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texcoord; layout (location = 2) in vec2 texcoord;
layout (location = 3) in vec3 tangent;
layout (location = 4) in vec3 bitangent;
out vec3 v_position; out vec3 v_position;
out vec3 v_normal; out vec3 v_normal;
out vec3 v_proj_normal; out vec3 v_proj_normal;
out vec2 v_texcoord; out vec2 v_texcoord;
out vec3 v_tangent;
out vec3 v_bitangent;
out float v_id; out float v_id;
@ -19,6 +23,8 @@ void main()
v_normal = normal; v_normal = normal;
v_proj_normal = vec4(u_mvp * vec4(normal, 1.0)).xyz; v_proj_normal = vec4(u_mvp * vec4(normal, 1.0)).xyz;
v_texcoord = texcoord; v_texcoord = texcoord;
v_tangent = tangent;
v_bitangent = bitangent;
v_id = gl_VertexID; v_id = gl_VertexID;

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View file

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

View file

@ -1,85 +0,0 @@
[02:17:44 AM] Info: Starting...
KP3D version 2
===============================
Copyright (C) kpworld.xyz 2018-2024
Contact me! @kp_cftsz
[02:17:44 AM] Info: Initializing SDL
[02:17:44 AM] Info: Initializing OpenGL
[02:17:44 AM] Info: OpenGL version: 4.6.0 NVIDIA 536.23
[02:17:44 AM] Info: Initializing GLEW
[02:17:44 AM] Info: Initializing SDL_mixer
[02:17:44 AM] Info: Reticulating splines...
[02:17:45 AM] Info: Ready!
[02:17:45 AM] Info: Finalized mesh with 45 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:53 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:54 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:58 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:17:59 AM] Info: Finalized mesh with 44 batches
[02:18:00 AM] Info: Finalized mesh with 44 batches
[02:18:00 AM] Info: Finalized mesh with 44 batches
[02:18:00 AM] Info: Finalized mesh with 44 batches
[02:18:00 AM] Info: Finalized mesh with 44 batches
[02:18:00 AM] Info: Finalized mesh with 44 batches
[02:18:00 AM] Info

View file

@ -129,6 +129,23 @@ const int* Bitmap::GetData() const
return m_data; return m_data;
} }
const byte* Bitmap::GetTextureData() const
{
byte* data = new byte[m_width * m_height * 4];
for (int i = 0; i < m_width; i++)
{
for (int j = 0; j < m_height; j++)
{
data[(i + j * m_width) * 4 + 0] = (m_data[i + j * m_width] >> 24) & 0xFF;
data[(i + j * m_width) * 4 + 1] = (m_data[i + j * m_width] >> 16) & 0xFF;
data[(i + j * m_width) * 4 + 2] = (m_data[i + j * m_width] >> 8) & 0xFF;
data[(i + j * m_width) * 4 + 3] = (m_data[i + j * m_width]) & 0xFF;
}
}
return data;
}
const int& Bitmap::PixelAt(int x, int y) const const int& Bitmap::PixelAt(int x, int y) const
{ {
assert(x >= 0 && y >= 0 && x < m_width && y < m_height); assert(x >= 0 && y >= 0 && x < m_width && y < m_height);

View file

@ -22,6 +22,7 @@ public:
int GetWidth() const; int GetWidth() const;
int GetHeight() const; int GetHeight() const;
const int* GetData() const; const int* GetData() const;
const byte* GetTextureData() const;
const int& PixelAt(int x, int y) const; const int& PixelAt(int x, int y) const;
int& PixelAt(int x, int y); int& PixelAt(int x, int y);

View file

@ -91,8 +91,11 @@ void Render()
return; return;
} }
if (!open)
return;
ImGui::SetNextWindowSize({640, 480}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize({640, 480}, ImGuiCond_FirstUseEver);
if (open && ImGui::Begin("KP3Dii Console", &open, ImGuiWindowFlags_NoCollapse)) if (ImGui::Begin("KP3Dii Console", &open, ImGuiWindowFlags_NoCollapse))
{ {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::BeginChild( ImGui::BeginChild(

View file

@ -11,6 +11,7 @@
#include "KP3D_Shader.h" #include "KP3D_Shader.h"
#include "KP3D_Noise.h" #include "KP3D_Noise.h"
#include "KP3D_Geometry.h" #include "KP3D_Geometry.h"
#include "KP3D_Resources.h"
namespace { namespace {
@ -116,15 +117,15 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
if (s->inverted) if (s->inverted)
{ {
if (FloatCmp(s->floor.base_height, sector.floor.base_height, e)) if (FloatCmp(s->floor.base_height, sector.floor.base_height, e))
s->floor.texture = nullptr; s->floor.material = nullptr;
if (FloatCmp(s->ceiling.base_height, sector.ceiling.base_height, e)) if (FloatCmp(s->ceiling.base_height, sector.ceiling.base_height, e))
s->ceiling.texture = nullptr; s->ceiling.material = nullptr;
} }
else else
{ {
if (!(l.flags & Wall::FLAG_OPENING)) if (!(l.flags & Wall::FLAG_OPENING))
{ {
l.textures[TEX_FRONT] = nullptr; l.materials[TEX_FRONT] = nullptr;
l.flags = Wall::FLAG_OPENING | Wall::FLAG_SUBSECTOR_OPENING; l.flags = Wall::FLAG_OPENING | Wall::FLAG_SUBSECTOR_OPENING;
l.portal = &sector; l.portal = &sector;
} }
@ -185,8 +186,8 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
if (FloatCmp(max_height, -MAX)) max_height = flat.base_height; if (FloatCmp(max_height, -MAX)) max_height = flat.base_height;
// Fix up the UVs so they keep the right scale // Fix up the UVs so they keep the right scale
float tw = flat.texture ? texture_scale / flat.texture->GetWidth() : 0.0f; float tw = flat.material ? texture_scale / flat.material->textures[MAT_TEX_DIFFUSE].GetWidth() : 0.0f;
float th = flat.texture ? texture_scale / flat.texture->GetHeight() : 0.0f; float th = flat.material ? texture_scale / flat.material->textures[MAT_TEX_DIFFUSE].GetHeight() : 0.0f;
Vertex3D vtxa = Vertex3D(fva, Vec2(au * tw, av * th)); Vertex3D vtxa = Vertex3D(fva, Vec2(au * tw, av * th));
Vertex3D vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th)); Vertex3D vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th));
Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th)); Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th));
@ -194,8 +195,14 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
flat.triangulated_data.push_back(vtxb); flat.triangulated_data.push_back(vtxb);
flat.triangulated_data.push_back(vtxc); flat.triangulated_data.push_back(vtxc);
BatchSectorInfo info = {BatchSectorInfo::BATCH_FLAT, &sector, &flat, nullptr}; BatchSectorInfo info = {BatchSectorInfo::BATCH_FLAT, &sector, &flat, nullptr};
if (flat.texture) if (flat.material)
m_mesh.AddBatch(flat.texture, { vtxa, vtxb, vtxc }, !invert, std::make_any<BatchSectorInfo>(info)); m_mesh.AddBatch(
&flat.material->textures[MAT_TEX_DIFFUSE],
{vtxa, vtxb, vtxc},
!invert,
std::make_any<BatchSectorInfo>(info),
&flat.material->textures[MAT_TEX_NORMAL]
);
} }
} }
@ -205,11 +212,11 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert)
* To do this we perform a similar approach to BuildFlat, but because the walls aren't strictly along one axis we have to * To do this we perform a similar approach to BuildFlat, but because the walls aren't strictly along one axis we have to
* "flatten" them to perform the math to triangulate them. This is done by projecting the 3D points to 2D. * "flatten" them to perform the math to triangulate them. This is done by projecting the 3D points to 2D.
*/ */
void Map::BuildQuad(Sector& sector, Wall& wall, Flat& flat_topr, Flat& flat_bottomr, const Texture* texture, Vec3 pos_a, Vec3 pos_b, bool flip, bool flip_u, bool flip_v, XYf uv_offset) void Map::BuildQuad(Sector& sector, Wall& wall, Flat& flat_topr, Flat& flat_bottomr, const Material* material, Vec3 pos_a, Vec3 pos_b, bool flip, bool flip_u, bool flip_v, XYf uv_offset)
{ {
const float E = 4.0f / 128.0f; const float E = 4.0f / 128.0f;
if (!texture) if (!material)
return; return;
if (sector.inverted) if (sector.inverted)
@ -290,14 +297,14 @@ void Map::BuildQuad(Sector& sector, Wall& wall, Flat& flat_topr, Flat& flat_bott
Vec3 fvc = Vec3(c.x, c.y, 0.0f).Rotated({0, 1, 0}, -ToDegrees(angle)); fvc.x += pos_a.x; fvc.z += pos_a.z; Vec3 fvc = Vec3(c.x, c.y, 0.0f).Rotated({0, 1, 0}, -ToDegrees(angle)); fvc.x += pos_a.x; fvc.z += pos_a.z;
// Fix up the UVs so they keep the right scale // Fix up the UVs so they keep the right scale
float tw = texture ? texture_scale / texture->GetWidth() : 0.0f; float tw = material ? texture_scale / material->textures[MAT_TEX_DIFFUSE].GetWidth() : 0.0f;
float th = texture ? texture_scale / texture->GetHeight() : 0.0f; float th = material ? texture_scale / material->textures[MAT_TEX_DIFFUSE].GetHeight() : 0.0f;
Vertex3D vtxa = Vertex3D(fva, Vec2(au * tw, av * th)); Vertex3D vtxa = Vertex3D(fva, Vec2(au * tw, av * th));
Vertex3D vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th)); Vertex3D vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th));
Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th)); Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th));
BatchSectorInfo info = {BatchSectorInfo::BATCH_WALL, &sector, nullptr, &wall}; BatchSectorInfo info = {BatchSectorInfo::BATCH_WALL, &sector, nullptr, &wall};
if (texture) if (material)
m_mesh.AddBatch(texture, {vtxa, vtxb, vtxc}, flip, std::make_any<BatchSectorInfo>(info)); m_mesh.AddBatch(&material->textures[MAT_TEX_DIFFUSE], {vtxa, vtxb, vtxc}, flip, std::make_any<BatchSectorInfo>(info), &material->textures[MAT_TEX_NORMAL]);
} }
} }
} }
@ -309,7 +316,7 @@ void Map::BuildWall(Sector& sector, Wall& wall)
{ {
Vec3 a = {wall.start.x, 0.0f, wall.start.y}; Vec3 a = {wall.start.x, 0.0f, wall.start.y};
Vec3 b = {wall.end.x, 0.0f, wall.end.y}; Vec3 b = {wall.end.x, 0.0f, wall.end.y};
BuildQuad(sector, wall, sector.floor, sector.ceiling, wall.textures[TEX_FRONT], a, b, false, false, false, wall.uv_offset[TEX_FRONT]); BuildQuad(sector, wall, sector.floor, sector.ceiling, wall.materials[TEX_FRONT], a, b, false, false, false, wall.uv_offset[TEX_FRONT]);
if (!(wall.flags & Wall::FLAG_SUBSECTOR_OPENING)) if (!(wall.flags & Wall::FLAG_SUBSECTOR_OPENING))
{ {
@ -318,10 +325,10 @@ void Map::BuildWall(Sector& sector, Wall& wall)
// Build upper and lower walls for sectors connected to other sectors (or sectors within sectors) // Build upper and lower walls for sectors connected to other sectors (or sectors within sectors)
bool flip = wall.portal->floor.base_height < sector.floor.base_height; bool flip = wall.portal->floor.base_height < sector.floor.base_height;
if (flip && !FloatCmp(wall.portal->floor.base_height, sector.floor.base_height)) if (flip && !FloatCmp(wall.portal->floor.base_height, sector.floor.base_height))
BuildQuad(sector, wall, sector.floor, wall.portal->floor, wall.textures[TEX_LOWER], a, b, flip, false, false, wall.uv_offset[TEX_LOWER]); BuildQuad(sector, wall, sector.floor, wall.portal->floor, wall.materials[TEX_LOWER], a, b, flip, false, false, wall.uv_offset[TEX_LOWER]);
flip = wall.portal->ceiling.base_height < sector.ceiling.base_height; flip = wall.portal->ceiling.base_height < sector.ceiling.base_height;
if (!flip && !FloatCmp(wall.portal->ceiling.base_height, sector.ceiling.base_height)) if (!flip && !FloatCmp(wall.portal->ceiling.base_height, sector.ceiling.base_height))
BuildQuad(sector, wall, sector.ceiling, wall.portal->ceiling, wall.textures[TEX_UPPER], a, b, !flip, false, false, wall.uv_offset[TEX_UPPER]); BuildQuad(sector, wall, sector.ceiling, wall.portal->ceiling, wall.materials[TEX_UPPER], a, b, !flip, false, false, wall.uv_offset[TEX_UPPER]);
wall.flags |= Wall::FLAG_JOINED; wall.flags |= Wall::FLAG_JOINED;
} }
@ -331,9 +338,9 @@ void Map::BuildWall(Sector& sector, Wall& wall)
if (!(wall.flags & Wall::FLAG_JOINED)) if (!(wall.flags & Wall::FLAG_JOINED))
{ {
bool flip = wall.portal->floor.base_height < sector.floor.base_height; bool flip = wall.portal->floor.base_height < sector.floor.base_height;
BuildQuad(sector, wall, sector.floor, wall.portal->floor, wall.textures[TEX_LOWER], a, b, flip, false, false, wall.uv_offset[TEX_LOWER]); BuildQuad(sector, wall, sector.floor, wall.portal->floor, wall.materials[TEX_LOWER], a, b, flip, false, false, wall.uv_offset[TEX_LOWER]);
flip = wall.portal->ceiling.base_height < sector.ceiling.base_height; flip = wall.portal->ceiling.base_height < sector.ceiling.base_height;
BuildQuad(sector, wall, sector.ceiling, wall.portal->ceiling, wall.textures[TEX_UPPER], a, b, !flip, false, false, wall.uv_offset[TEX_UPPER]); BuildQuad(sector, wall, sector.ceiling, wall.portal->ceiling, wall.materials[TEX_UPPER], a, b, !flip, false, false, wall.uv_offset[TEX_UPPER]);
wall.flags |= Wall::FLAG_JOINED; wall.flags |= Wall::FLAG_JOINED;
} }
@ -425,7 +432,7 @@ void Map::JoinSectors(Sector& sector)
w1.start.y = ld.end.y; w1.start.y = ld.end.y;
w1.end.x = l.start.x; w1.end.x = l.start.x;
w1.end.y = l.start.y; w1.end.y = l.start.y;
w1.textures[TEX_FRONT] = nullptr; w1.materials[TEX_FRONT] = nullptr;
w1.portal = &s; w1.portal = &s;
w1.flags = Wall::FLAG_TOUCHED; w1.flags = Wall::FLAG_TOUCHED;
InsertLine(sector.walls, i + 1, w1); InsertLine(sector.walls, i + 1, w1);
@ -451,7 +458,7 @@ void Map::JoinSectors(Sector& sector)
w1.start.y = ld.end.y; w1.start.y = ld.end.y;
w1.end.x = l.start.x; w1.end.x = l.start.x;
w1.end.y = l.start.y; w1.end.y = l.start.y;
w1.textures[TEX_FRONT] = nullptr; w1.materials[TEX_FRONT] = nullptr;
w1.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED; w1.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED;
w1.portal = &s; w1.portal = &s;
InsertLine(sector.walls, i + 1, w1); InsertLine(sector.walls, i + 1, w1);
@ -466,7 +473,7 @@ void Map::JoinSectors(Sector& sector)
XYf old_end = ld.end; XYf old_end = ld.end;
ld.end.x = l.start.x; ld.end.x = l.start.x;
ld.end.y = l.start.y; ld.end.y = l.start.y;
ld.textures[TEX_FRONT] = nullptr; ld.materials[TEX_FRONT] = nullptr;
ld.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED; ld.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED;
ld.portal = &s; ld.portal = &s;
w1.start.x = ld.end.x; w1.start.x = ld.end.x;
@ -485,7 +492,7 @@ void Map::JoinSectors(Sector& sector)
if (!sector.inverted) // not sure if we want to discount inverted sectors for just this case or all of if (!sector.inverted) // not sure if we want to discount inverted sectors for just this case or all of
// them; revisit me! // them; revisit me!
{ {
ld.textures[TEX_FRONT] = nullptr; ld.materials[TEX_FRONT] = nullptr;
ld.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED; ld.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED;
ld.portal = &s; ld.portal = &s;
} }
@ -496,7 +503,7 @@ void Map::JoinSectors(Sector& sector)
// Mark the other one as an opening // Mark the other one as an opening
if (!(l.flags & Wall::FLAG_OPENING)) // <- do we want to check this? not sure... if (!(l.flags & Wall::FLAG_OPENING)) // <- do we want to check this? not sure...
{ {
l.textures[TEX_FRONT] = nullptr; l.materials[TEX_FRONT] = nullptr;
l.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED; l.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED;
l.portal = &sector; l.portal = &sector;
} }
@ -543,13 +550,25 @@ void Map::Init()
BuildSkybox("skybox_16.jpg"); BuildSkybox("skybox_16.jpg");
BuildGrid(); BuildGrid();
static Texture tex, tex2, tex3, tex4; //static Texture tex, tex2, tex3, tex4;
tex.Load("GRASS2.png", true); //tex.Load("GRASS2.png", true);
tex2.Load("floor0.png", true); //tex2.Load("floor0.png", true);
tex3.Load("FLAT5_7.png", true); //tex3.Load("FLAT5_7.png", true);
tex4.Load("block.png", true); //tex4.Load("block.png", true);
m_dot.Load("dot.png"); m_dot.Load("dot.png");
static Material* tex;
static Material* tex2;
static Material* tex3;
static Material* tex4;
tex = res::material_cache["GRASS2.png"].get();
tex2 = res::material_cache["floor1.png"].get();
tex3 = res::material_cache["FLAT5_7.png"].get();
tex4 = res::material_cache["block.png"].get();
// Build a stupid little test map // Build a stupid little test map
// ------------------------------ // ------------------------------
// Main sector // Main sector
@ -564,11 +583,11 @@ void Map::Init()
XYf points5[] = {{11, 4}, {14, 4}, {14, 2}, {16, 2}, {16, 10}, {11, 10}}; XYf points5[] = {{11, 4}, {14, 4}, {14, 2}, {16, 2}, {16, 10}, {11, 10}};
// Platform above main sector (room over room testing) // Platform above main sector (room over room testing)
XYf points6[] = {{1, 4}, {3, 4}, {3, 7}, {1, 7}}; XYf points6[] = {{1, 4}, {3, 4}, {3, 7}, {1, 7}};
auto build_sector = [&](Texture* wall, Texture* lower, Texture* floor, Texture* ceil, float floor_height, float ceil_height, int id, XYf* points, size_t num_points, bool inverted = false) auto build_sector = [&](Material* wall, Material* lower, Material* floor, Material* ceil, float floor_height, float ceil_height, int id, XYf* points, size_t num_points, bool inverted = false)
{ {
std::shared_ptr<Sector> s = std::make_unique<Sector>(); std::shared_ptr<Sector> s = std::make_unique<Sector>();
s->ceiling.texture = ceil; s->ceiling.material = ceil;
s->floor.texture = floor; s->floor.material = floor;
s->floor.floor = true; s->floor.floor = true;
s->floor.base_height = floor_height; s->floor.base_height = floor_height;
s->ceiling.base_height = ceil_height; s->ceiling.base_height = ceil_height;
@ -580,12 +599,12 @@ void Map::Init()
s->walls.push_back(Wall{ {wall,wall,lower}, {{0, 0}}, points[i], points[(i + 1) % num_points], Wall::NO_FLAGS, Wall::NO_UFLAGS, (uint)i }); s->walls.push_back(Wall{ {wall,wall,lower}, {{0, 0}}, points[i], points[(i + 1) % num_points], Wall::NO_FLAGS, Wall::NO_UFLAGS, (uint)i });
sectors.push_back(s); sectors.push_back(s);
}; };
build_sector(&tex3, &tex3, &tex, &tex2, -1.0f, 5.0f, 1, points, std::size(points)); build_sector(tex3, tex3, tex, tex2, -1.0f, 5.0f, 1, points, std::size(points));
build_sector(&tex4, &tex4, &tex2, &tex2, -1.5f, 4.0f, 2, points2, std::size(points2)); build_sector(tex4, tex4, tex2, tex2, -1.5f, 4.0f, 2, points2, std::size(points2));
build_sector(&tex4, &tex4, &tex, &tex2, -1.5f, 5.0f, 3, points3, std::size(points3), true); build_sector(tex4, tex4, tex, tex2, -1.5f, 5.0f, 3, points3, std::size(points3), true);
build_sector(&tex4, &tex4, &tex, &tex2, -1.5f, 4.0f, 4, points4, std::size(points4), true); build_sector(tex4, tex4, tex, tex2, -1.5f, 4.0f, 4, points4, std::size(points4), true);
build_sector(&tex3, &tex4, &tex, &tex2, 0.0f, 2.0f, 5, points5, std::size(points5), false); build_sector(tex3, tex4, tex, tex2, 0.0f, 2.0f, 5, points5, std::size(points5), false);
build_sector(&tex4, &tex4, &tex4, &tex4, 1.0f, 1.4f, 6, points6, std::size(points6), true); build_sector(tex4, tex4, tex4, tex4, 1.0f, 1.4f, 6, points6, std::size(points6), true);
Rebuild(GEN_NORMALS); Rebuild(GEN_NORMALS);
} }
@ -701,12 +720,12 @@ void Map::Rebuild(NormalGenType gen_normals)
continue; continue;
//if (s.id == 1 || s.id == 5) //if (s.id == 1 || s.id == 5)
{ //{
//float yv0 = PerlinNoise2D(steiner_point.x, steiner_point.y, 1.0, 10.0); // float yv0 = PerlinNoise2D(steiner_point.x, steiner_point.y, 1.0, 10.0);
//float yv1 = -PerlinNoise2D(steiner_point.x, steiner_point.y, 0.5, 9.0); // float yv1 = -PerlinNoise2D(steiner_point.x, steiner_point.y, 0.5, 9.0);
//s.floor.steiner_points.push_back({ steiner_point.x, yv0, steiner_point.y }); // s.floor.steiner_points.push_back({ steiner_point.x, yv0, steiner_point.y });
//s.ceiling.steiner_points.push_back({ steiner_point.x, yv1, steiner_point.y }); // s.ceiling.steiner_points.push_back({ steiner_point.x, yv1, steiner_point.y });
} //}
} }
} }
} }
@ -737,7 +756,7 @@ void Map::Rebuild(NormalGenType gen_normals)
// Finalize the mesh // Finalize the mesh
m_mesh.Finalize( m_mesh.Finalize(
gen_normals, GEN_SMOOTH_NORMALS,
[](const RenderBatch3D& a, const RenderBatch3D& b) -> bool [](const RenderBatch3D& a, const RenderBatch3D& b) -> bool
{ {
return /*a.texture->GetGLID() < b.texture->GetGLID() &&*/ return /*a.texture->GetGLID() < b.texture->GetGLID() &&*/
@ -763,10 +782,10 @@ void Map::Update()
void Map::Render() void Map::Render()
{ {
// Skybox // Skybox
RenderSkybox(); // RenderSkybox();
// Map // Map
glDisable(GL_CULL_FACE); // <- we shouldn't count on this //glDisable(GL_CULL_FACE); // <- we shouldn't count on this
kp3d::Renderer3D::PushShader(kp3d::Renderer3D::GetMapShader()); kp3d::Renderer3D::PushShader(kp3d::Renderer3D::GetMapShader());
kp3d::Renderer3D::DrawMesh(m_mesh, m_transform, true, ShouldHighlight); kp3d::Renderer3D::DrawMesh(m_mesh, m_transform, true, ShouldHighlight);
kp3d::Renderer3D::PopShader(); kp3d::Renderer3D::PopShader();

View file

@ -7,6 +7,7 @@
#include "KP3D_StaticMesh.h" #include "KP3D_StaticMesh.h"
#include "KP3D_Transform.h" #include "KP3D_Transform.h"
#include "KP3D_Shader.h" #include "KP3D_Shader.h"
#include "KP3D_Material.h"
namespace kp3d { namespace kp3d {
@ -35,10 +36,11 @@ struct Wall
enum UserFlags: byte enum UserFlags: byte
{ {
NO_UFLAGS, NO_UFLAGS,
UFLAG_ALIGN_WITH_HEIGHTMAP = 1 << 0 UFLAG_ALIGN_WITH_HEIGHTMAP = 1 << 0,
UFLAG_DOUBLESIDED = 1 << 1
}; };
const Texture* textures[3]; const Material* materials[3];
XYf uv_offset[3]; XYf uv_offset[3];
XYf start; XYf start;
@ -54,7 +56,7 @@ struct Wall
struct Flat struct Flat
{ {
const Texture* texture; const Material* material;
float base_height; float base_height;
float min_height; float min_height;
float max_height; float max_height;
@ -130,7 +132,7 @@ public:
ErrCode SaveToFile(const std::string& path); ErrCode SaveToFile(const std::string& path);
void BuildFlat(Sector& sector, Flat& flat, bool invert); void BuildFlat(Sector& sector, Flat& flat, bool invert);
void BuildQuad(Sector& sector, Wall& wall, Flat& flat_top, Flat& flat_bottom, const Texture* texture, Vec3 pos_a, Vec3 pos_b, bool flip, bool flip_u, bool flip_v, XYf uv_offset); void BuildQuad(Sector& sector, Wall& wall, Flat& flat_top, Flat& flat_bottom, const Material* material, Vec3 pos_a, Vec3 pos_b, bool flip, bool flip_u, bool flip_v, XYf uv_offset);
void BuildWall(Sector& sector, Wall& wall); void BuildWall(Sector& sector, Wall& wall);
void JoinSectors(Sector& sector); void JoinSectors(Sector& sector);
void SanitizeSectors(); void SanitizeSectors();

View file

@ -0,0 +1,71 @@
#include "KP3D_Material.h"
#include <stdlib.h>
#include <filesystem>
#include "KP3D_StringUtils.h"
#include "KP3D_SystemUtils.h"
#include "KP3D_Log.h"
#include "KP3D_Bitmap.h"
namespace kp3d {
static Bitmap placeholder_normal(128, 128);
static bool built_placeholder_normal = false;
void LoadMaterial(Material& material, std::string path)
{
path = "materials/" + path;
if (str::EndsWith(path, "_n.png"))
return;
// Diffuse texture is just gonna be the regular path we give it
material.textures[MAT_TEX_DIFFUSE].Load(path, true, Texture::FILTER_LINEAR);
// Only gonna support .png and .jpg (not .jpeg) for now.
// If you need something else, get bent loser
std::string ext = ".png";
if (str::EndsWith(path, ext))
{
str::ReplaceAll(path, ext, "");
}
else if (str::EndsWith(path, ".jpg"))
{
ext = ".jpg";
str::ReplaceAll(path, ext, "");
}
else if (str::EndsWith(path, ".webp"))
{
KP3D_LOG_ERROR("FUCK WEBP!!! ALL MY HOMIES HATE WEBP!!!");
exit(1337);
}
// Now look for normal textures in the form of _n suffixes
std::string normal_path = path + "_n" + ext;
if (std::filesystem::exists(sys::GetTextureDir() + normal_path))
{
KP3D_LOG_INFO("Found normal map texture: {}", normal_path);
material.textures[MAT_TEX_NORMAL].Load(normal_path, true, Texture::FILTER_LINEAR);
}
else
{
// Make a little placeholder normal texture (this could be done more efficiently but whatever)
if (!built_placeholder_normal)
{
int w = placeholder_normal.GetWidth(), h = placeholder_normal.GetHeight();
for (int i = 0; i < w * h; i++)
placeholder_normal.PixelAt(i % w, i / w) = 0x7F7FFFFF;
built_placeholder_normal = true;
}
const byte* data = placeholder_normal.GetTextureData();
material.textures[MAT_TEX_NORMAL].Load(data, placeholder_normal.GetWidth(), placeholder_normal.GetHeight(), true, Texture::FILTER_LINEAR);
delete[] data;
}
// & perhaps in the future we'll support roughness maps, metallic maps, heightmaps, etc. but for now, just this
// ...
}
} // namespace kp3d

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <string>
#include "KP3D_Common.h" #include "KP3D_Common.h"
#include "KP3D_Texture.h" #include "KP3D_Texture.h"
@ -17,4 +19,6 @@ struct Material
Texture textures[NUM_MAT_TEX]; Texture textures[NUM_MAT_TEX];
}; };
void LoadMaterial(Material& material, std::string path);
} // namespace kp3d } // namespace kp3d

View file

@ -201,17 +201,32 @@ void Renderer3D::DrawMesh(StaticMesh& mesh, const Mat4& m, bool bind_shader, uin
bool skinned = !batch.bone_data.empty(); bool skinned = !batch.bone_data.empty();
if (batch.texture) if (batch.texture)
{
glActiveTexture(GL_TEXTURE0);
batch.texture->Bind(); batch.texture->Bind();
}
if (batch.normal_texture)
{
glActiveTexture(GL_TEXTURE1);
batch.normal_texture->Bind();
r3d_state->shader->SetUniform("u_normal_texture", 1);
}
else
{
glActiveTexture(GL_TEXTURE0);
}
glBindBuffer(GL_ARRAY_BUFFER, batch.gl_vbo_id); glBindBuffer(GL_ARRAY_BUFFER, batch.gl_vbo_id);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
if (skinned) if (skinned)
{ {
glEnableVertexAttribArray(3); glEnableVertexAttribArray(5);
glEnableVertexAttribArray(4); glEnableVertexAttribArray(6);
} }
glVertexAttribPointer( glVertexAttribPointer(
@ -219,17 +234,19 @@ void Renderer3D::DrawMesh(StaticMesh& mesh, const Mat4& m, bool bind_shader, uin
); );
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast<GLvoid*>(offsetof(Vertex3D, normal))); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast<GLvoid*>(offsetof(Vertex3D, normal)));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast<GLvoid*>(offsetof(Vertex3D, uv))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast<GLvoid*>(offsetof(Vertex3D, uv)));
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast<GLvoid*>(offsetof(Vertex3D, atangent)));
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast<GLvoid*>(offsetof(Vertex3D, bitangent)));
if (skinned) if (skinned)
{ {
glVertexAttribIPointer( glVertexAttribIPointer(
3, 5,
MAX_NUM_BONES_PER_VERTEX, MAX_NUM_BONES_PER_VERTEX,
GL_UNSIGNED_INT, GL_UNSIGNED_INT,
sizeof(Vertex3D), sizeof(Vertex3D),
reinterpret_cast<GLvoid*>(offsetof(Vertex3D, bone_ids)) reinterpret_cast<GLvoid*>(offsetof(Vertex3D, bone_ids))
); );
glVertexAttribPointer( glVertexAttribPointer(
4, 6,
MAX_NUM_BONES_PER_VERTEX, MAX_NUM_BONES_PER_VERTEX,
GL_FLOAT, GL_FLOAT,
GL_FALSE, GL_FALSE,
@ -250,9 +267,11 @@ void Renderer3D::DrawMesh(StaticMesh& mesh, const Mat4& m, bool bind_shader, uin
if (skinned) if (skinned)
{ {
glDisableVertexAttribArray(6);
glDisableVertexAttribArray(5);
}
glDisableVertexAttribArray(4); glDisableVertexAttribArray(4);
glDisableVertexAttribArray(3); glDisableVertexAttribArray(3);
}
glDisableVertexAttribArray(2); glDisableVertexAttribArray(2);
glDisableVertexAttribArray(1); glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0); glDisableVertexAttribArray(0);
@ -437,6 +456,9 @@ void Renderer3D::ReloadShaders()
void Renderer3D::Tick() void Renderer3D::Tick()
{ {
r3d_state->time += 1.0f; r3d_state->time += 1.0f;
if ((int)r3d_state->time % 60 == 0)
ReloadShaders();
} }
const Shader& Renderer3D::GetShader() const Shader& Renderer3D::GetShader()

View file

@ -5,15 +5,18 @@
#include "KP3D_StringUtils.h" #include "KP3D_StringUtils.h"
#include "KP3D_SystemUtils.h" #include "KP3D_SystemUtils.h"
#include "KP3D_Log.h"
namespace kp3d::res { namespace kp3d::res {
std::unordered_map<std::string, std::shared_ptr<Material>> material_cache;
void LoadMaterials() void LoadMaterials()
{ {
texture_cache.clear(); material_cache.clear();
// Load every available texture // Load every available texture
for (const auto& entry: std::filesystem::recursive_directory_iterator(kp3d::sys::GetTextureDir())) for (const auto& entry: std::filesystem::recursive_directory_iterator(kp3d::sys::GetTextureDir() + "materials/"))
{ {
if (entry.is_directory()) if (entry.is_directory())
continue; continue;
@ -24,10 +27,16 @@ void LoadMaterials()
if (!kp3d::str::EndsWith(path_str, ".png") && !kp3d::str::EndsWith(path_str, ".jpg")) if (!kp3d::str::EndsWith(path_str, ".png") && !kp3d::str::EndsWith(path_str, ".jpg"))
continue; continue;
std::string filename = std::filesystem::proximate(path_str, kp3d::sys::GetTextureDir()).string(); std::string filename = std::filesystem::proximate(path_str, kp3d::sys::GetTextureDir() + "materials/").string();
std::replace(filename.begin(), filename.end(), '\\', '/'); std::replace(filename.begin(), filename.end(), '\\', '/');
// texture_cache.emplace(filename, ); if (kp3d::str::EndsWith(path_str, "_n.png"))
continue;
KP3D_LOG_INFO("Loading material resource: {}", filename);
material_cache.emplace(filename, std::make_shared<Material>());
LoadMaterial(*material_cache[filename], filename);
// new: // new:
// m_loaded_res_mutex.lock(); // m_loaded_res_mutex.lock();

View file

@ -2,6 +2,7 @@
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <memory>
#include "KP3D_Common.h" #include "KP3D_Common.h"
#include "KP3D_Texture.h" #include "KP3D_Texture.h"
@ -9,7 +10,7 @@
namespace kp3d::res { namespace kp3d::res {
std::unordered_map<std::string, kp3d::Texture*> texture_cache; extern std::unordered_map<std::string, std::shared_ptr<Material>> material_cache;
void LoadMaterials(); void LoadMaterials();

View file

@ -78,7 +78,7 @@ StaticMesh::~StaticMesh()
glDeleteVertexArrays(1, &m_gl_vao_id); glDeleteVertexArrays(1, &m_gl_vao_id);
} }
void StaticMesh::AddBatch(const Texture* texture, const std::vector<Vertex3D>& vertex_data, bool flip, std::any user_data) void StaticMesh::AddBatch(const Texture* texture, const std::vector<Vertex3D>& vertex_data, bool flip, std::any user_data, const Texture* normal_texture)
{ {
// "flipping" the vertex data basically means changing the order of the vertices so it faces // "flipping" the vertex data basically means changing the order of the vertices so it faces
// a different direction than it did previously, useful for making sure stuff works with culling // a different direction than it did previously, useful for making sure stuff works with culling
@ -93,11 +93,11 @@ void StaticMesh::AddBatch(const Texture* texture, const std::vector<Vertex3D>& v
flipped_data[i + 2] = vertex_data[i + 1]; flipped_data[i + 2] = vertex_data[i + 1];
} }
m_batches.push_back({0, texture, flipped_data}); m_batches.push_back({0, texture, normal_texture, flipped_data});
} }
else else
{ {
m_batches.push_back({0, texture, vertex_data}); m_batches.push_back({0, texture, normal_texture, vertex_data});
} }
// Clean me/move me somewhere else // Clean me/move me somewhere else
@ -125,7 +125,8 @@ void StaticMesh::AddBatch(const Texture* texture, const std::vector<Vertex3D>& v
void StaticMesh::AddIndexedBatch( void StaticMesh::AddIndexedBatch(
const Texture* texture, const Texture* texture,
const std::vector<Vertex3D>& vertex_data, const std::vector<Vertex3D>& vertex_data,
const std::vector<uint>& index_data const std::vector<uint>& index_data,
const Texture* normal_texture
) )
{ {
// Do we really need to do this flipping all the time? // Do we really need to do this flipping all the time?
@ -138,7 +139,7 @@ void StaticMesh::AddIndexedBatch(
flipped_data[i + 2] = index_data[i + 1]; flipped_data[i + 2] = index_data[i + 1];
} }
m_batches.push_back({0, texture, vertex_data, 0, flipped_data}); m_batches.push_back({0, texture, normal_texture, vertex_data, 0, flipped_data});
} }
// Experimental feature for KP3D 0.2 -- skinned (skeletal animated) meshes using bones // Experimental feature for KP3D 0.2 -- skinned (skeletal animated) meshes using bones
@ -147,7 +148,8 @@ void StaticMesh::AddSkinnedBatch(
const std::vector<Vertex3D>& vertex_data, const std::vector<Vertex3D>& vertex_data,
const std::vector<uint>& index_data, const std::vector<uint>& index_data,
const std::vector<VertexBoneData>& bone_data, const std::vector<VertexBoneData>& bone_data,
bool flipped bool flipped,
const Texture* normal_texture
) )
{ {
skinnable = true; skinnable = true;
@ -170,7 +172,7 @@ void StaticMesh::AddSkinnedBatch(
} }
} }
m_batches.push_back({0, texture, vertex_data, 0, flipped_data, bone_data}); m_batches.push_back({0, texture, normal_texture, vertex_data, 0, flipped_data, bone_data});
} }
// Take the batches, order them by texture ID, and have one draw call per texture use. // Take the batches, order them by texture ID, and have one draw call per texture use.
@ -251,6 +253,19 @@ void StaticMesh::Finalize(NormalGenType gen_normals, BatchComparison sort, Batch
v0.normal += n; v0.normal += n;
v1.normal += n; v1.normal += n;
v2.normal += n; v2.normal += n;
// While we're at it let's calculate the tangents and bitangents
Vec2 delta_uv1 = v1.uv - v0.uv;
Vec2 delta_uv2 = v2.uv - v0.uv;
float f = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv2.x * delta_uv1.y);
Vec3 tangent = f * (delta_uv2.y * vz1 - delta_uv1.y * vz2);
Vec3 bitangent = f * (-delta_uv2.x * vz1 + delta_uv1.x * vz2);
v0.atangent += tangent;
v1.atangent += tangent;
v2.atangent += tangent;
v0.bitangent += bitangent;
v1.bitangent += bitangent;
v2.bitangent += bitangent;
} }
for (size_t j = 0; j < current.vertex_data.size(); j++) for (size_t j = 0; j < current.vertex_data.size(); j++)
{ {

View file

@ -25,6 +25,8 @@ public:
Vec3 position; Vec3 position;
Vec3 normal; Vec3 normal;
Vec2 uv; Vec2 uv;
Vec3 atangent;
Vec3 bitangent;
uint bone_ids[MAX_NUM_BONES_PER_VERTEX] = {0}; uint bone_ids[MAX_NUM_BONES_PER_VERTEX] = {0};
float weights[MAX_NUM_BONES_PER_VERTEX] = {0.0f}; float weights[MAX_NUM_BONES_PER_VERTEX] = {0.0f};
}; };
@ -54,6 +56,7 @@ struct RenderBatch3D
{ {
GLuint gl_vbo_id = 0; GLuint gl_vbo_id = 0;
const Texture* texture; const Texture* texture;
const Texture* normal_texture;
std::vector<Vertex3D> vertex_data; std::vector<Vertex3D> vertex_data;
GLuint gl_ibo_id = 0; GLuint gl_ibo_id = 0;
std::vector<uint> index_data; std::vector<uint> index_data;
@ -77,14 +80,15 @@ public:
StaticMesh(); StaticMesh();
~StaticMesh(); ~StaticMesh();
void AddBatch(const Texture* texture, const std::vector<Vertex3D>& vertex_data, bool flip = false, std::any user_data = {}); void AddBatch(const Texture* texture, const std::vector<Vertex3D>& vertex_data, bool flip = false, std::any user_data = {}, const Texture* normal_texture = nullptr);
void AddIndexedBatch(const Texture* texture, const std::vector<Vertex3D>& vertex_data, const std::vector<uint>& index_data); void AddIndexedBatch(const Texture* texture, const std::vector<Vertex3D>& vertex_data, const std::vector<uint>& index_data, const Texture* normal_texture = nullptr);
void AddSkinnedBatch( void AddSkinnedBatch(
const Texture* texture, const Texture* texture,
const std::vector<Vertex3D>& vertex_data, const std::vector<Vertex3D>& vertex_data,
const std::vector<uint>& index_data, const std::vector<uint>& index_data,
const std::vector<VertexBoneData>& bone_data, const std::vector<VertexBoneData>& bone_data,
bool flip = true bool flip = true,
const Texture* normal_texture = nullptr
); );
void Finalize(NormalGenType gen_normals = GEN_NORMALS, BatchComparison sort = nullptr, BatchComparison criteria = nullptr); void Finalize(NormalGenType gen_normals = GEN_NORMALS, BatchComparison sort = nullptr, BatchComparison criteria = nullptr);
void Reset(); void Reset();

View file

@ -8,6 +8,9 @@
#include "KP3D_Geometry.h" #include "KP3D_Geometry.h"
#include "KP3D_Console.h" #include "KP3D_Console.h"
#include "KP3D_EventBus.h" #include "KP3D_EventBus.h"
#include "KP3D_StringUtils.h"
#include "KP3D_SystemUtils.h"
#include "KP3D_Resources.h"
#include "Sandbox.h" #include "Sandbox.h"
@ -33,8 +36,8 @@ Editor::Editor()
{ {
m_banner.Load(".kp3d/banner.png"); m_banner.Load(".kp3d/banner.png");
m_stem.Load("editor/stem.png"); m_stem.Load("editor/stem.png");
m_block.Load("block.png", true); // m_block.Load("block.png", true);
m_mode = MODE_BUILD; m_mode = MODE_NORMAL;
kp3d::EventBus::Subscribe(this, &Editor::OnScrollWheel); kp3d::EventBus::Subscribe(this, &Editor::OnScrollWheel);
kp3d::EventBus::Subscribe(this, &Editor::OnKeyPress); kp3d::EventBus::Subscribe(this, &Editor::OnKeyPress);
@ -163,8 +166,8 @@ void Editor::UpdateModeBuild()
if (points.size() >= 3 && kp3d::PosCmp(points.back(), points.front(), 1.0f / 128.0f))// points.back() == points.front()) if (points.size() >= 3 && kp3d::PosCmp(points.back(), points.front(), 1.0f / 128.0f))// points.back() == points.front())
{ {
std::shared_ptr<kp3d::Sector> s = std::make_shared<kp3d::Sector>(); std::shared_ptr<kp3d::Sector> s = std::make_shared<kp3d::Sector>();
s->ceiling.texture = &m_block; s->ceiling.material = kp3d::res::material_cache["block.png"].get();
s->floor.texture = &m_block; s->floor.material = kp3d::res::material_cache["block.png"].get();
s->floor.floor = true; s->floor.floor = true;
s->floor.base_height = 0.0f; s->floor.base_height = 0.0f;
s->ceiling.base_height = 2.0f; s->ceiling.base_height = 2.0f;
@ -178,7 +181,7 @@ void Editor::UpdateModeBuild()
wall.end = { points[i + 1].x, -points[i + 1].z }; wall.end = { points[i + 1].x, -points[i + 1].z };
wall.uid = i; wall.uid = i;
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
wall.textures[j] = &m_block; wall.materials[j] = kp3d::res::material_cache["block.png"].get();
s->walls.push_back(wall); s->walls.push_back(wall);
} }
@ -213,8 +216,8 @@ void Editor::UpdateModeBuild()
if (has_start_pos) if (has_start_pos)
{ {
std::shared_ptr<kp3d::Sector> s = std::make_shared<kp3d::Sector>(); std::shared_ptr<kp3d::Sector> s = std::make_shared<kp3d::Sector>();
s->ceiling.texture = &m_block; s->ceiling.material = kp3d::res::material_cache["block.png"].get();
s->floor.texture = &m_block; s->floor.material = kp3d::res::material_cache["block.png"].get();
s->floor.floor = true; s->floor.floor = true;
s->floor.base_height = 0.0f; s->floor.base_height = 0.0f;
s->ceiling.base_height = 4.0f; s->ceiling.base_height = 4.0f;
@ -230,7 +233,7 @@ void Editor::UpdateModeBuild()
wall.flags = kp3d::Wall::NO_FLAGS; wall.flags = kp3d::Wall::NO_FLAGS;
wall.user_flags = kp3d::Wall::NO_UFLAGS; wall.user_flags = kp3d::Wall::NO_UFLAGS;
for (int j = 0; j < 3; j++) for (int j = 0; j < 3; j++)
wall.textures[j] = &m_block; wall.materials[j] = kp3d::res::material_cache["block.png"].get();
s->walls.push_back(wall); s->walls.push_back(wall);
} }
@ -260,8 +263,8 @@ void Editor::RenderUI()
} }
if (ImGui::BeginMenu("View")) if (ImGui::BeginMenu("View"))
{ {
ImGui::Checkbox("Info overlay", &show_info_overlay); ImGui::Checkbox("Info Overlay", &show_info_overlay);
ImGui::Checkbox("Selection information", &show_selection_info); ImGui::Checkbox("Selection Info", &show_selection_info);
ImGui::Checkbox("Console", &kp3d::console::open); ImGui::Checkbox("Console", &kp3d::console::open);
ImGui::EndMenu(); ImGui::EndMenu();
} }
@ -277,24 +280,40 @@ void Editor::RenderUI()
RenderUIAbout(); RenderUIAbout();
// Sector window // Sector window
// ImGui::SetNextWindowSize({400, 500}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize({400, 500}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos({20, 200}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos({20, 200}, ImGuiCond_FirstUseEver);
if (show_selection_info) if (show_selection_info)
{ {
if (ImGui::Begin("Selection Information", &show_selection_info, ImGuiWindowFlags_AlwaysAutoResize)) if (ImGui::Begin("Selection Info", &show_selection_info))
{ {
if (m_mode == MODE_BUILD) if (m_mode != MODE_NORMAL)
{ {
ImGui::Text("No selection"); ImGui::Text("No selection");
} }
else if (m_mode == MODE_NORMAL) else
{ {
if (kp3d::editor_hovered_batch) if (kp3d::editor_hovered_batch)
{ {
try try
{ {
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch->userdata); const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch->userdata);
if (info.sector) ImGui::SeparatorText("Type");
ImGui::Text(info.wall ? "Wall" : "Flat/Sector");
if (info.wall)
{
ImGui::SeparatorText("Hierarchy");
ImGui::Text("Sector ID: %u\n", info.sector->id);
ImGui::SeparatorText("Properties");
ImGui::CheckboxFlags("Align with heightmap", (unsigned*)&info.wall->user_flags, kp3d::Wall::UFLAG_ALIGN_WITH_HEIGHTMAP);
ImGui::CheckboxFlags("Double-sided", (unsigned*)&info.wall->user_flags, kp3d::Wall::UFLAG_DOUBLESIDED);
ImGui::SeparatorText("Materials");
RenderUIMaterialSelect("Middle", info.wall->materials[kp3d::TEX_FRONT]);
RenderUIMaterialSelect("Upper", info.wall->materials[kp3d::TEX_UPPER]);
RenderUIMaterialSelect("Lower", info.wall->materials[kp3d::TEX_LOWER]);
}
else if (info.sector)
{ {
ImGui::SeparatorText("Hierarchy"); ImGui::SeparatorText("Hierarchy");
ImGui::Text("ID: %u\n", info.sector->id); ImGui::Text("ID: %u\n", info.sector->id);
@ -305,14 +324,15 @@ void Editor::RenderUI()
ImGui::Text("- ID: %u", sc->id); ImGui::Text("- ID: %u", sc->id);
ImGui::Unindent(); ImGui::Unindent();
ImGui::SeparatorText("Properties"); ImGui::SeparatorText("Properties");
ImGui::InputFloat("Floor height", &info.sector->floor.base_height); int changed = 0;
ImGui::InputFloat("Ceiling height", &info.sector->ceiling.base_height); changed |= ImGui::InputFloat("Floor height", &info.sector->floor.base_height);
ImGui::Checkbox("Inverted", &info.sector->inverted); changed |= ImGui::InputFloat("Ceiling height", &info.sector->ceiling.base_height);
changed |= ImGui::Checkbox("Inverted", &info.sector->inverted);
if (changed)
sandbox->map.Rebuild(kp3d::GEN_NORMALS);
ImGui::SeparatorText("Materials"); ImGui::SeparatorText("Materials");
ImGui::Text("Floor:"); RenderUIMaterialSelect("Floor", info.sector->floor.material);
ImGui::ImageButton((ImTextureID) info.sector->floor.texture->GetGLID(), {128, 128}); RenderUIMaterialSelect("Ceiling", info.sector->ceiling.material);
ImGui::Text("Ceiling:");
ImGui::ImageButton((ImTextureID)info.sector->ceiling.texture->GetGLID(), { 128, 128 });
} }
} }
catch (std::bad_any_cast& e) catch (std::bad_any_cast& e)
@ -380,6 +400,21 @@ void Editor::RenderUIAbout()
} }
} }
void Editor::RenderUIMaterialSelect(const char* name, const kp3d::Material* material)
{
ImGui::Text("%s:", name);
if (material)
{
if (ImGui::ImageButton((ImTextureID)material->textures[kp3d::MAT_TEX_DIFFUSE].GetGLID(), {96, 96})) {}
}
else
{
std::string str = "Set " + kp3d::str::ToLower(name) + " material...";
if (ImGui::Button(str.c_str())) {}
}
}
void Editor::OnScrollWheel(const kp3d::ScrollWheelEvent* e) void Editor::OnScrollWheel(const kp3d::ScrollWheelEvent* e)
{ {
using namespace kp3d; using namespace kp3d;

View file

@ -3,6 +3,7 @@
#include "KP3D_Texture.h" #include "KP3D_Texture.h"
#include "KP3D_Math.h" #include "KP3D_Math.h"
#include "KP3D_IOEvents.h" #include "KP3D_IOEvents.h"
#include "KP3D_Material.h"
enum EditMode enum EditMode
{ {
@ -30,6 +31,7 @@ public:
void RenderUI(); void RenderUI();
void RenderUIInfo(); void RenderUIInfo();
void RenderUIAbout(); void RenderUIAbout();
void RenderUIMaterialSelect(const char* name, const kp3d::Material* material);
void OnScrollWheel(const kp3d::ScrollWheelEvent* e); void OnScrollWheel(const kp3d::ScrollWheelEvent* e);
void OnKeyPress(const kp3d::KeyPressEvent* e); void OnKeyPress(const kp3d::KeyPressEvent* e);
@ -40,7 +42,6 @@ private:
private: private:
kp3d::Texture m_banner; kp3d::Texture m_banner;
kp3d::Texture m_stem; kp3d::Texture m_stem;
kp3d::Texture m_block;
kp3d::Vec3 m_stem_pos; kp3d::Vec3 m_stem_pos;
EditMode m_mode; EditMode m_mode;

View file

@ -8,6 +8,7 @@
#include <KP3D_Renderer3D.h> #include <KP3D_Renderer3D.h>
#include <KP3D_Console.h> #include <KP3D_Console.h>
#include <KP3D_StringUtils.h> #include <KP3D_StringUtils.h>
#include <KP3D_Resources.h>
static kp3d::Texture tex; static kp3d::Texture tex;
static std::vector<kp3d::XYf> points; static std::vector<kp3d::XYf> points;
@ -15,6 +16,8 @@ static std::vector<kp3d::XYf> points;
Sandbox::Sandbox(const std::string& path): Sandbox::Sandbox(const std::string& path):
kp3d::Game(path, "sandbox-cfg.json", "sandbox-log.txt") kp3d::Game(path, "sandbox-cfg.json", "sandbox-log.txt")
{ {
kp3d::res::LoadMaterials();
tex.Load("logo.png"); tex.Load("logo.png");
crosshair.Load(".kp3d/crosshair.png"); crosshair.Load(".kp3d/crosshair.png");
m_projection.InitPerspective(70.0f, (float) GetWidth() / (float) GetHeight()); m_projection.InitPerspective(70.0f, (float) GetWidth() / (float) GetHeight());