diff --git a/Data/resources/shaders/.kp3d/map_f.glsl b/Data/resources/shaders/.kp3d/map_f.glsl index bbe7096..1efad68 100644 --- a/Data/resources/shaders/.kp3d/map_f.glsl +++ b/Data/resources/shaders/.kp3d/map_f.glsl @@ -1,36 +1,37 @@ #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 float u_time; in vec3 v_position; in vec3 v_normal; in vec3 v_proj_normal; in vec2 v_texcoord; +in vec3 v_tangent; +in vec3 v_bitangent; layout (location=0) out vec4 v_output; -uniform sampler2D u_texture; - -float get_fog_factor(float d) +float kpc(float d, float min, float max) { - const float FogMax = 10.0; - const float FogMin = 0.0; + if (d >= max) return 1.0; + if (d <= min) return 0.0; - if (d >= FogMax) return 1.0; - if (d <= FogMin) return 0.0; - - return 1 - (FogMax - d) / (FogMax - FogMin); + return 1.0 - (max - d) / (max - min); } struct Light { vec3 position; vec3 diffuse; + float radius; }; -float lambert(vec3 n, vec3 l) +float Lambert(vec3 n, vec3 l) { vec3 nrmn = normalize(n); vec3 nrml = normalize(l); @@ -38,55 +39,38 @@ float lambert(vec3 n, vec3 l) 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() { - Light light; - light.position = vec3(5.0, 4.0, 5.0); - light.diffuse = vec3(1.0, 1.0, 1.0); - - - vec4 tex = texture(u_texture, v_texcoord); - vec3 norm = (v_normal); + vec3 tangentNormal = texture(u_normal_texture, v_texcoord).xyz * 2.0 - 1.0; + vec3 T = normalize(v_tangent); + 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; + + light += MakeLight(norm, u_campos, vec3(1.0, 1.0, 1.0), 5.0, 0.0, 1.0); + 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)); - - 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; - - - -/* - 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); + // + editor highlighting + v_output.rgb *= u_highlight; } diff --git a/Data/resources/shaders/.kp3d/map_v.glsl b/Data/resources/shaders/.kp3d/map_v.glsl index ba9cb43..84d06de 100644 --- a/Data/resources/shaders/.kp3d/map_v.glsl +++ b/Data/resources/shaders/.kp3d/map_v.glsl @@ -5,11 +5,15 @@ uniform mat4 u_mvp; layout (location = 0) in vec3 position; layout (location = 1) in vec3 normal; 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_normal; out vec3 v_proj_normal; out vec2 v_texcoord; +out vec3 v_tangent; +out vec3 v_bitangent; out float v_id; @@ -19,8 +23,10 @@ void main() v_normal = normal; v_proj_normal = vec4(u_mvp * vec4(normal, 1.0)).xyz; v_texcoord = texcoord; + v_tangent = tangent; + v_bitangent = bitangent; v_id = gl_VertexID; gl_Position = u_mvp * vec4(v_position, 1.0); -} \ No newline at end of file +} diff --git a/Data/resources/textures/FLAT5_7.png b/Data/resources/textures/materials/FLAT5_7.png similarity index 100% rename from Data/resources/textures/FLAT5_7.png rename to Data/resources/textures/materials/FLAT5_7.png diff --git a/Data/resources/textures/materials/FLAT5_7_n.png b/Data/resources/textures/materials/FLAT5_7_n.png new file mode 100644 index 0000000..b051a7d Binary files /dev/null and b/Data/resources/textures/materials/FLAT5_7_n.png differ diff --git a/Data/resources/textures/GRASS2.png b/Data/resources/textures/materials/GRASS2.png similarity index 100% rename from Data/resources/textures/GRASS2.png rename to Data/resources/textures/materials/GRASS2.png diff --git a/Data/resources/textures/materials/GRASS2_n.png b/Data/resources/textures/materials/GRASS2_n.png new file mode 100644 index 0000000..fed79b2 Binary files /dev/null and b/Data/resources/textures/materials/GRASS2_n.png differ diff --git a/Data/resources/textures/block.png b/Data/resources/textures/materials/block.png similarity index 100% rename from Data/resources/textures/block.png rename to Data/resources/textures/materials/block.png diff --git a/Data/resources/textures/materials/block_n.png b/Data/resources/textures/materials/block_n.png new file mode 100644 index 0000000..25211b4 Binary files /dev/null and b/Data/resources/textures/materials/block_n.png differ diff --git a/Data/resources/textures/floor0.png b/Data/resources/textures/materials/floor0.png similarity index 100% rename from Data/resources/textures/floor0.png rename to Data/resources/textures/materials/floor0.png diff --git a/Data/resources/textures/materials/floor0_n.png b/Data/resources/textures/materials/floor0_n.png new file mode 100644 index 0000000..b076fd4 Binary files /dev/null and b/Data/resources/textures/materials/floor0_n.png differ diff --git a/Data/resources/textures/floor1.png b/Data/resources/textures/materials/floor1.png similarity index 100% rename from Data/resources/textures/floor1.png rename to Data/resources/textures/materials/floor1.png diff --git a/Data/resources/textures/materials/floor1_n.png b/Data/resources/textures/materials/floor1_n.png new file mode 100644 index 0000000..f4dcd58 Binary files /dev/null and b/Data/resources/textures/materials/floor1_n.png differ diff --git a/Data/sandbox-log.txt b/Data/sandbox-log.txt index 07ebbbe..e69de29 100644 --- a/Data/sandbox-log.txt +++ b/Data/sandbox-log.txt @@ -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 \ No newline at end of file diff --git a/KP3Dii/src/KP3D_Bitmap.cpp b/KP3Dii/src/KP3D_Bitmap.cpp index 8b7ac02..587eb28 100644 --- a/KP3Dii/src/KP3D_Bitmap.cpp +++ b/KP3Dii/src/KP3D_Bitmap.cpp @@ -129,6 +129,23 @@ const int* Bitmap::GetData() const 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 { assert(x >= 0 && y >= 0 && x < m_width && y < m_height); diff --git a/KP3Dii/src/KP3D_Bitmap.h b/KP3Dii/src/KP3D_Bitmap.h index f19f2dc..ce5d36b 100644 --- a/KP3Dii/src/KP3D_Bitmap.h +++ b/KP3Dii/src/KP3D_Bitmap.h @@ -22,6 +22,7 @@ public: int GetWidth() const; int GetHeight() const; const int* GetData() const; + const byte* GetTextureData() const; const int& PixelAt(int x, int y) const; int& PixelAt(int x, int y); diff --git a/KP3Dii/src/KP3D_Console.cpp b/KP3Dii/src/KP3D_Console.cpp index 334cf92..407ccf0 100644 --- a/KP3Dii/src/KP3D_Console.cpp +++ b/KP3Dii/src/KP3D_Console.cpp @@ -91,8 +91,11 @@ void Render() return; } + if (!open) + return; + 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::BeginChild( diff --git a/KP3Dii/src/KP3D_Map.cpp b/KP3Dii/src/KP3D_Map.cpp index 5dacd91..8a020f0 100644 --- a/KP3Dii/src/KP3D_Map.cpp +++ b/KP3Dii/src/KP3D_Map.cpp @@ -11,6 +11,7 @@ #include "KP3D_Shader.h" #include "KP3D_Noise.h" #include "KP3D_Geometry.h" +#include "KP3D_Resources.h" namespace { @@ -116,15 +117,15 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert) if (s->inverted) { 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)) - s->ceiling.texture = nullptr; + s->ceiling.material = nullptr; } else { 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.portal = §or; } @@ -185,8 +186,8 @@ void Map::BuildFlat(Sector& sector, Flat& flat, bool invert) if (FloatCmp(max_height, -MAX)) max_height = flat.base_height; // Fix up the UVs so they keep the right scale - float tw = flat.texture ? texture_scale / flat.texture->GetWidth() : 0.0f; - float th = flat.texture ? texture_scale / flat.texture->GetHeight() : 0.0f; + float tw = flat.material ? texture_scale / flat.material->textures[MAT_TEX_DIFFUSE].GetWidth() : 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 vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * 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(vtxc); BatchSectorInfo info = {BatchSectorInfo::BATCH_FLAT, §or, &flat, nullptr}; - if (flat.texture) - m_mesh.AddBatch(flat.texture, { vtxa, vtxb, vtxc }, !invert, std::make_any(info)); + if (flat.material) + m_mesh.AddBatch( + &flat.material->textures[MAT_TEX_DIFFUSE], + {vtxa, vtxb, vtxc}, + !invert, + std::make_any(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 * "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; - if (!texture) + if (!material) return; 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; // Fix up the UVs so they keep the right scale - float tw = texture ? texture_scale / texture->GetWidth() : 0.0f; - float th = texture ? texture_scale / texture->GetHeight() : 0.0f; + float tw = material ? texture_scale / material->textures[MAT_TEX_DIFFUSE].GetWidth() : 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 vtxb = Vertex3D(fvb, Vec2(bu * tw, bv * th)); Vertex3D vtxc = Vertex3D(fvc, Vec2(cu * tw, cv * th)); BatchSectorInfo info = {BatchSectorInfo::BATCH_WALL, §or, nullptr, &wall}; - if (texture) - m_mesh.AddBatch(texture, {vtxa, vtxb, vtxc}, flip, std::make_any(info)); + if (material) + m_mesh.AddBatch(&material->textures[MAT_TEX_DIFFUSE], {vtxa, vtxb, vtxc}, flip, std::make_any(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 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)) { @@ -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) bool flip = 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; 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; } @@ -331,9 +338,9 @@ void Map::BuildWall(Sector& sector, Wall& wall) if (!(wall.flags & Wall::FLAG_JOINED)) { 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; - 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; } @@ -425,7 +432,7 @@ void Map::JoinSectors(Sector& sector) w1.start.y = ld.end.y; w1.end.x = l.start.x; w1.end.y = l.start.y; - w1.textures[TEX_FRONT] = nullptr; + w1.materials[TEX_FRONT] = nullptr; w1.portal = &s; w1.flags = Wall::FLAG_TOUCHED; InsertLine(sector.walls, i + 1, w1); @@ -451,7 +458,7 @@ void Map::JoinSectors(Sector& sector) w1.start.y = ld.end.y; w1.end.x = l.start.x; 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.portal = &s; InsertLine(sector.walls, i + 1, w1); @@ -466,7 +473,7 @@ void Map::JoinSectors(Sector& sector) XYf old_end = ld.end; ld.end.x = l.start.x; 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.portal = &s; 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 // them; revisit me! { - ld.textures[TEX_FRONT] = nullptr; + ld.materials[TEX_FRONT] = nullptr; ld.flags = Wall::FLAG_OPENING | Wall::FLAG_TOUCHED; ld.portal = &s; } @@ -496,7 +503,7 @@ void Map::JoinSectors(Sector& sector) // Mark the other one as an opening 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.portal = §or; } @@ -543,13 +550,25 @@ void Map::Init() BuildSkybox("skybox_16.jpg"); BuildGrid(); - static Texture tex, tex2, tex3, tex4; - tex.Load("GRASS2.png", true); - tex2.Load("floor0.png", true); - tex3.Load("FLAT5_7.png", true); - tex4.Load("block.png", true); + //static Texture tex, tex2, tex3, tex4; + //tex.Load("GRASS2.png", true); + //tex2.Load("floor0.png", true); + //tex3.Load("FLAT5_7.png", true); + //tex4.Load("block.png", true); 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 // ------------------------------ // Main sector @@ -564,11 +583,11 @@ void Map::Init() XYf points5[] = {{11, 4}, {14, 4}, {14, 2}, {16, 2}, {16, 10}, {11, 10}}; // Platform above main sector (room over room testing) 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 s = std::make_unique(); - s->ceiling.texture = ceil; - s->floor.texture = floor; + s->ceiling.material = ceil; + s->floor.material = floor; s->floor.floor = true; s->floor.base_height = floor_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 }); sectors.push_back(s); }; - 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, &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(&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(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, 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(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); Rebuild(GEN_NORMALS); } @@ -701,12 +720,12 @@ void Map::Rebuild(NormalGenType gen_normals) continue; //if (s.id == 1 || s.id == 5) - { - //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); - //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 }); - } + //{ + // 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); + // 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 }); + //} } } } @@ -737,7 +756,7 @@ void Map::Rebuild(NormalGenType gen_normals) // Finalize the mesh m_mesh.Finalize( - gen_normals, + GEN_SMOOTH_NORMALS, [](const RenderBatch3D& a, const RenderBatch3D& b) -> bool { return /*a.texture->GetGLID() < b.texture->GetGLID() &&*/ @@ -763,10 +782,10 @@ void Map::Update() void Map::Render() { // Skybox - RenderSkybox(); + // RenderSkybox(); // 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::DrawMesh(m_mesh, m_transform, true, ShouldHighlight); kp3d::Renderer3D::PopShader(); diff --git a/KP3Dii/src/KP3D_Map.h b/KP3Dii/src/KP3D_Map.h index 4aae9c6..5b2faa5 100644 --- a/KP3Dii/src/KP3D_Map.h +++ b/KP3Dii/src/KP3D_Map.h @@ -7,6 +7,7 @@ #include "KP3D_StaticMesh.h" #include "KP3D_Transform.h" #include "KP3D_Shader.h" +#include "KP3D_Material.h" namespace kp3d { @@ -35,10 +36,11 @@ struct Wall enum UserFlags: byte { 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 start; @@ -54,7 +56,7 @@ struct Wall struct Flat { - const Texture* texture; + const Material* material; float base_height; float min_height; float max_height; @@ -130,7 +132,7 @@ public: ErrCode SaveToFile(const std::string& path); 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 JoinSectors(Sector& sector); void SanitizeSectors(); diff --git a/KP3Dii/src/KP3D_Material.cpp b/KP3Dii/src/KP3D_Material.cpp index e69de29..f78629b 100644 --- a/KP3Dii/src/KP3D_Material.cpp +++ b/KP3Dii/src/KP3D_Material.cpp @@ -0,0 +1,71 @@ +#include "KP3D_Material.h" + +#include + +#include + +#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 diff --git a/KP3Dii/src/KP3D_Material.h b/KP3Dii/src/KP3D_Material.h index cddc394..0bc1284 100644 --- a/KP3Dii/src/KP3D_Material.h +++ b/KP3Dii/src/KP3D_Material.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "KP3D_Common.h" #include "KP3D_Texture.h" @@ -17,4 +19,6 @@ struct Material Texture textures[NUM_MAT_TEX]; }; +void LoadMaterial(Material& material, std::string path); + } // namespace kp3d diff --git a/KP3Dii/src/KP3D_Renderer3D.cpp b/KP3Dii/src/KP3D_Renderer3D.cpp index 4852274..3b64c81 100644 --- a/KP3Dii/src/KP3D_Renderer3D.cpp +++ b/KP3Dii/src/KP3D_Renderer3D.cpp @@ -201,17 +201,32 @@ void Renderer3D::DrawMesh(StaticMesh& mesh, const Mat4& m, bool bind_shader, uin bool skinned = !batch.bone_data.empty(); if (batch.texture) + { + glActiveTexture(GL_TEXTURE0); 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); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); + glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); if (skinned) { - glEnableVertexAttribArray(3); - glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); + glEnableVertexAttribArray(6); } 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(offsetof(Vertex3D, normal))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast(offsetof(Vertex3D, uv))); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast(offsetof(Vertex3D, atangent))); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), reinterpret_cast(offsetof(Vertex3D, bitangent))); if (skinned) { glVertexAttribIPointer( - 3, + 5, MAX_NUM_BONES_PER_VERTEX, GL_UNSIGNED_INT, sizeof(Vertex3D), reinterpret_cast(offsetof(Vertex3D, bone_ids)) ); glVertexAttribPointer( - 4, + 6, MAX_NUM_BONES_PER_VERTEX, GL_FLOAT, GL_FALSE, @@ -250,9 +267,11 @@ void Renderer3D::DrawMesh(StaticMesh& mesh, const Mat4& m, bool bind_shader, uin if (skinned) { - glDisableVertexAttribArray(4); - glDisableVertexAttribArray(3); + glDisableVertexAttribArray(6); + glDisableVertexAttribArray(5); } + glDisableVertexAttribArray(4); + glDisableVertexAttribArray(3); glDisableVertexAttribArray(2); glDisableVertexAttribArray(1); glDisableVertexAttribArray(0); @@ -437,6 +456,9 @@ void Renderer3D::ReloadShaders() void Renderer3D::Tick() { r3d_state->time += 1.0f; + + if ((int)r3d_state->time % 60 == 0) + ReloadShaders(); } const Shader& Renderer3D::GetShader() diff --git a/KP3Dii/src/KP3D_Resources.cpp b/KP3Dii/src/KP3D_Resources.cpp index d70dc56..1fade6f 100644 --- a/KP3Dii/src/KP3D_Resources.cpp +++ b/KP3Dii/src/KP3D_Resources.cpp @@ -5,15 +5,18 @@ #include "KP3D_StringUtils.h" #include "KP3D_SystemUtils.h" +#include "KP3D_Log.h" namespace kp3d::res { +std::unordered_map> material_cache; + void LoadMaterials() { - texture_cache.clear(); + material_cache.clear(); // 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()) continue; @@ -24,10 +27,16 @@ void LoadMaterials() if (!kp3d::str::EndsWith(path_str, ".png") && !kp3d::str::EndsWith(path_str, ".jpg")) 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(), '\\', '/'); - // 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()); + LoadMaterial(*material_cache[filename], filename); // new: // m_loaded_res_mutex.lock(); diff --git a/KP3Dii/src/KP3D_Resources.h b/KP3Dii/src/KP3D_Resources.h index 9bfc53d..08f47d9 100644 --- a/KP3Dii/src/KP3D_Resources.h +++ b/KP3Dii/src/KP3D_Resources.h @@ -2,6 +2,7 @@ #include #include +#include #include "KP3D_Common.h" #include "KP3D_Texture.h" @@ -9,7 +10,7 @@ namespace kp3d::res { -std::unordered_map texture_cache; +extern std::unordered_map> material_cache; void LoadMaterials(); diff --git a/KP3Dii/src/KP3D_StaticMesh.cpp b/KP3Dii/src/KP3D_StaticMesh.cpp index cd97322..fd6bf22 100644 --- a/KP3Dii/src/KP3D_StaticMesh.cpp +++ b/KP3Dii/src/KP3D_StaticMesh.cpp @@ -78,7 +78,7 @@ StaticMesh::~StaticMesh() glDeleteVertexArrays(1, &m_gl_vao_id); } -void StaticMesh::AddBatch(const Texture* texture, const std::vector& vertex_data, bool flip, std::any user_data) +void StaticMesh::AddBatch(const Texture* texture, const std::vector& 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 // 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& v 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 { - m_batches.push_back({0, texture, vertex_data}); + m_batches.push_back({0, texture, normal_texture, vertex_data}); } // Clean me/move me somewhere else @@ -125,7 +125,8 @@ void StaticMesh::AddBatch(const Texture* texture, const std::vector& v void StaticMesh::AddIndexedBatch( const Texture* texture, const std::vector& vertex_data, - const std::vector& index_data + const std::vector& index_data, + const Texture* normal_texture ) { // 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]; } - 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 @@ -147,7 +148,8 @@ void StaticMesh::AddSkinnedBatch( const std::vector& vertex_data, const std::vector& index_data, const std::vector& bone_data, - bool flipped + bool flipped, + const Texture* normal_texture ) { 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. @@ -251,6 +253,19 @@ void StaticMesh::Finalize(NormalGenType gen_normals, BatchComparison sort, Batch v0.normal += n; v1.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++) { diff --git a/KP3Dii/src/KP3D_StaticMesh.h b/KP3Dii/src/KP3D_StaticMesh.h index 13a2495..048c21e 100644 --- a/KP3Dii/src/KP3D_StaticMesh.h +++ b/KP3Dii/src/KP3D_StaticMesh.h @@ -25,6 +25,8 @@ public: Vec3 position; Vec3 normal; Vec2 uv; + Vec3 atangent; + Vec3 bitangent; uint bone_ids[MAX_NUM_BONES_PER_VERTEX] = {0}; float weights[MAX_NUM_BONES_PER_VERTEX] = {0.0f}; }; @@ -54,6 +56,7 @@ struct RenderBatch3D { GLuint gl_vbo_id = 0; const Texture* texture; + const Texture* normal_texture; std::vector vertex_data; GLuint gl_ibo_id = 0; std::vector index_data; @@ -77,14 +80,15 @@ public: StaticMesh(); ~StaticMesh(); - void AddBatch(const Texture* texture, const std::vector& vertex_data, bool flip = false, std::any user_data = {}); - void AddIndexedBatch(const Texture* texture, const std::vector& vertex_data, const std::vector& index_data); + void AddBatch(const Texture* texture, const std::vector& vertex_data, bool flip = false, std::any user_data = {}, const Texture* normal_texture = nullptr); + void AddIndexedBatch(const Texture* texture, const std::vector& vertex_data, const std::vector& index_data, const Texture* normal_texture = nullptr); void AddSkinnedBatch( const Texture* texture, const std::vector& vertex_data, const std::vector& index_data, const std::vector& 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 Reset(); diff --git a/Sandbox/src/Editor.cpp b/Sandbox/src/Editor.cpp index 56da3e6..f0c01f2 100644 --- a/Sandbox/src/Editor.cpp +++ b/Sandbox/src/Editor.cpp @@ -8,6 +8,9 @@ #include "KP3D_Geometry.h" #include "KP3D_Console.h" #include "KP3D_EventBus.h" +#include "KP3D_StringUtils.h" +#include "KP3D_SystemUtils.h" +#include "KP3D_Resources.h" #include "Sandbox.h" @@ -33,8 +36,8 @@ Editor::Editor() { m_banner.Load(".kp3d/banner.png"); m_stem.Load("editor/stem.png"); - m_block.Load("block.png", true); - m_mode = MODE_BUILD; + // m_block.Load("block.png", true); + m_mode = MODE_NORMAL; kp3d::EventBus::Subscribe(this, &Editor::OnScrollWheel); 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()) { std::shared_ptr s = std::make_shared(); - s->ceiling.texture = &m_block; - s->floor.texture = &m_block; + s->ceiling.material = kp3d::res::material_cache["block.png"].get(); + s->floor.material = kp3d::res::material_cache["block.png"].get(); s->floor.floor = true; s->floor.base_height = 0.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.uid = i; 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); } @@ -213,8 +216,8 @@ void Editor::UpdateModeBuild() if (has_start_pos) { std::shared_ptr s = std::make_shared(); - s->ceiling.texture = &m_block; - s->floor.texture = &m_block; + s->ceiling.material = kp3d::res::material_cache["block.png"].get(); + s->floor.material = kp3d::res::material_cache["block.png"].get(); s->floor.floor = true; s->floor.base_height = 0.0f; s->ceiling.base_height = 4.0f; @@ -230,7 +233,7 @@ void Editor::UpdateModeBuild() wall.flags = kp3d::Wall::NO_FLAGS; wall.user_flags = kp3d::Wall::NO_UFLAGS; 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); } @@ -260,8 +263,8 @@ void Editor::RenderUI() } if (ImGui::BeginMenu("View")) { - ImGui::Checkbox("Info overlay", &show_info_overlay); - ImGui::Checkbox("Selection information", &show_selection_info); + ImGui::Checkbox("Info Overlay", &show_info_overlay); + ImGui::Checkbox("Selection Info", &show_selection_info); ImGui::Checkbox("Console", &kp3d::console::open); ImGui::EndMenu(); } @@ -277,24 +280,40 @@ void Editor::RenderUI() RenderUIAbout(); // Sector window - // ImGui::SetNextWindowSize({400, 500}, ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize({400, 500}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos({20, 200}, ImGuiCond_FirstUseEver); 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"); } - else if (m_mode == MODE_NORMAL) + else { if (kp3d::editor_hovered_batch) { try { const auto& info = std::any_cast(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::Text("ID: %u\n", info.sector->id); @@ -305,14 +324,15 @@ void Editor::RenderUI() ImGui::Text("- ID: %u", sc->id); ImGui::Unindent(); ImGui::SeparatorText("Properties"); - ImGui::InputFloat("Floor height", &info.sector->floor.base_height); - ImGui::InputFloat("Ceiling height", &info.sector->ceiling.base_height); - ImGui::Checkbox("Inverted", &info.sector->inverted); + int changed = 0; + changed |= ImGui::InputFloat("Floor height", &info.sector->floor.base_height); + 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::Text("Floor:"); - ImGui::ImageButton((ImTextureID) info.sector->floor.texture->GetGLID(), {128, 128}); - ImGui::Text("Ceiling:"); - ImGui::ImageButton((ImTextureID)info.sector->ceiling.texture->GetGLID(), { 128, 128 }); + RenderUIMaterialSelect("Floor", info.sector->floor.material); + RenderUIMaterialSelect("Ceiling", info.sector->ceiling.material); } } 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) { using namespace kp3d; diff --git a/Sandbox/src/Editor.h b/Sandbox/src/Editor.h index 933b096..c721080 100644 --- a/Sandbox/src/Editor.h +++ b/Sandbox/src/Editor.h @@ -3,6 +3,7 @@ #include "KP3D_Texture.h" #include "KP3D_Math.h" #include "KP3D_IOEvents.h" +#include "KP3D_Material.h" enum EditMode { @@ -30,6 +31,7 @@ public: void RenderUI(); void RenderUIInfo(); void RenderUIAbout(); + void RenderUIMaterialSelect(const char* name, const kp3d::Material* material); void OnScrollWheel(const kp3d::ScrollWheelEvent* e); void OnKeyPress(const kp3d::KeyPressEvent* e); @@ -40,7 +42,6 @@ private: private: kp3d::Texture m_banner; kp3d::Texture m_stem; - kp3d::Texture m_block; kp3d::Vec3 m_stem_pos; EditMode m_mode; diff --git a/Sandbox/src/Sandbox.cpp b/Sandbox/src/Sandbox.cpp index 483312f..7114370 100644 --- a/Sandbox/src/Sandbox.cpp +++ b/Sandbox/src/Sandbox.cpp @@ -8,6 +8,7 @@ #include #include #include +#include static kp3d::Texture tex; static std::vector points; @@ -15,6 +16,8 @@ static std::vector points; Sandbox::Sandbox(const std::string& path): kp3d::Game(path, "sandbox-cfg.json", "sandbox-log.txt") { + kp3d::res::LoadMaterials(); + tex.Load("logo.png"); crosshair.Load(".kp3d/crosshair.png"); m_projection.InitPerspective(70.0f, (float) GetWidth() / (float) GetHeight());