feat: pbr (#20)

* feat: add pbr texture

* feat(rendering): add normal mapping support for blocks

* fix: normal map load

* feat(scripts): add batch nearest neighbor upscale script
This commit is contained in:
zhenyan121
2026-06-20 15:03:40 +08:00
committed by GitHub
parent a8d2ddbacd
commit 5385876a8a
52 changed files with 462 additions and 65 deletions

1
.gitignore vendored
View File

@@ -42,3 +42,4 @@ CMakeError.log
.DS_Store .DS_Store
assets/config.toml assets/config.toml
.venv/ .venv/
pyout/

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -3,13 +3,15 @@
in vec2 tc; in vec2 tc;
in vec3 normal; in vec3 normal;
in vec3 vert_pos; in vec3 vert_pos;
in vec3 tangent;
in vec3 bitangent;
in vec4 FragPosLightSpace; in vec4 FragPosLightSpace;
in float roughness; in float roughness;
flat in int tex_layer; flat in int tex_layer;
out vec4 color; out vec4 color;
layout (binding = 0) uniform sampler2D shadowMap; layout (binding = 0) uniform sampler2D shadowMap;
layout (binding = 1) uniform sampler2DArray samp; layout (binding = 1) uniform sampler2DArray samp;
layout (binding = 2) uniform sampler2DArray normMap;
uniform float ambientStrength; uniform float ambientStrength;
uniform vec3 sunlightColor; uniform vec3 sunlightColor;
uniform vec3 ambientColor; uniform vec3 ambientColor;
@@ -21,6 +23,8 @@ uniform float specularStrength;
uniform float lightSizeUV; uniform float lightSizeUV;
uniform float minRadius; uniform float minRadius;
uniform float maxRadius; uniform float maxRadius;
uniform bool enablePBR;
uniform bool flipY;
const vec2 poissonDisk32[32] = vec2[]( const vec2 poissonDisk32[32] = vec2[](
vec2(-0.975402, -0.071138), vec2(-0.975402, -0.071138),
vec2(-0.920347, -0.411420), vec2(-0.920347, -0.411420),
@@ -259,6 +263,16 @@ float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir)
return shadow; return shadow;
} }
vec3 calcNewNormal() {
mat3 TBN = mat3(normalize(tangent), normalize(bitangent), normalize(normal));
vec3 retrievedNormal = texture(normMap, vec3(tc, tex_layer)).xyz;
retrievedNormal = retrievedNormal * 2.0 - 1.0;
if (flipY) {
retrievedNormal.y = -retrievedNormal.y;
}
vec3 newNormal = TBN * retrievedNormal;
return normalize(newNormal);
}
void main(void) { void main(void) {
vec4 objectColor = texture(samp, vec3(tc, tex_layer)); vec4 objectColor = texture(samp, vec3(tc, tex_layer));
@@ -273,8 +287,13 @@ void main(void) {
vec3 lightDir = normalize(-sunlightDir); vec3 lightDir = normalize(-sunlightDir);
vec3 norm;
if (enablePBR) {
norm = calcNewNormal();
} else {
norm = normalize(normal);
}
vec3 norm = normalize(normal);
vec3 V = vec3 V =
normalize(cameraPos - vert_pos); normalize(cameraPos - vert_pos);
@@ -323,7 +342,9 @@ void main(void) {
float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir);
color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular, objectColor.a); color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular * objectColor.rgb, objectColor.a);
//color = vec4(normal * 0.5 + 0.5, 1.0);
//color = varyingColor; //color = vec4(tangent * 0.5 + 0.5, 1.0);;
//color = vec4(norm * 0.5 + 0.5, 1.0);
//color = vec4(calcNewNormal(), 1.0);
} }

View File

@@ -5,9 +5,11 @@ layout (location = 1) in vec2 texCoord;
layout (location = 2) in float layer; layout (location = 2) in float layer;
layout (location = 3) in vec3 aNormal; layout (location = 3) in vec3 aNormal;
layout (location = 4) in float Roughness; layout (location = 4) in float Roughness;
layout (location = 5) in vec3 aTangent;
out vec2 tc; out vec2 tc;
out vec3 normal; out vec3 normal;
out vec3 tangent;
out vec3 bitangent;
out vec3 vert_pos; out vec3 vert_pos;
flat out int tex_layer; flat out int tex_layer;
out vec4 FragPosLightSpace; out vec4 FragPosLightSpace;
@@ -20,6 +22,7 @@ mat4 buildTranslate(float x, float y, float z);
uniform mat4 mv_matrix; uniform mat4 mv_matrix;
uniform mat4 proj_matrix; uniform mat4 proj_matrix;
uniform mat4 model_matrix;
uniform mat4 norm_matrix; uniform mat4 norm_matrix;
uniform mat4 lightSpaceMatrix; uniform mat4 lightSpaceMatrix;
@@ -32,7 +35,9 @@ void main(void) {
tex_layer = int(layer); tex_layer = int(layer);
roughness = Roughness; roughness = Roughness;
normal = mat3(norm_matrix) * aNormal; normal = normalize(mat3(norm_matrix) * aNormal);
tangent = normalize(mat3(model_matrix) * aTangent);
bitangent = cross(normal, tangent);
FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0); FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0);
gl_Position = proj_matrix * viewPos; gl_Position = proj_matrix * viewPos;
} }

View File

@@ -136,6 +136,50 @@ constexpr float NORMALS[6][6][3] = {
{0.0f, -1.0f, 0.0f}, {0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f}}}; {0.0f, -1.0f, 0.0f}}};
constexpr float TANGENTS[6][6][3] = {
// ===== front (z = +1) =====
{{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f}},
// ===== right (x = +1) =====
{{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f}},
// ===== back (z = -1) =====
{{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f}},
// ===== left (x = -1) =====
{{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f}},
// ===== top (y = +1) =====
{{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f}},
// ===== bottom (y = -1) =====
{{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f}}};
#pragma endregion #pragma endregion
constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0,
0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0,
@@ -211,6 +255,22 @@ constexpr float CROSS_NORMALS[2][6][3] = {
{0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f}}}; {0.0f, 1.0f, 0.0f}}};
constexpr float CROSS_TANGENTS[2][6][3] = {
// ===== Plane 1 =====
{{0.7071f, 0.0f, 0.7071f},
{0.7071f, 0.0f, 0.7071f},
{0.7071f, 0.0f, 0.7071f},
{0.7071f, 0.0f, 0.7071f},
{0.7071f, 0.0f, 0.7071f},
{0.7071f, 0.0f, 0.7071f}},
// ===== Plane 2 =====
{{-0.7071f, 0.0f, 0.7071f},
{-0.7071f, 0.0f, 0.7071f},
{-0.7071f, 0.0f, 0.7071f},
{-0.7071f, 0.0f, 0.7071f},
{-0.7071f, 0.0f, 0.7071f},
{-0.7071f, 0.0f, 0.7071f}}};
#pragma endregion #pragma endregion
// [-1, 1] // [-1, 1]
constexpr float QUAD_VERTICES[] = { constexpr float QUAD_VERTICES[] = {
@@ -225,6 +285,7 @@ struct Vertex3D {
float layer = 0.0f; float layer = 0.0f;
float nx = 0.0f, ny = 0.0f, nz = 0.0f; float nx = 0.0f, ny = 0.0f, nz = 0.0f;
float roughness = 1.0f; float roughness = 1.0f;
float tx = 0.0f, ty = 0.0f, tz = 0.0f;
}; };
struct Vertex2D { struct Vertex2D {

View File

@@ -34,6 +34,8 @@ public:
bool& shader_on(); bool& shader_on();
bool& water_perturb(); bool& water_perturb();
bool& water_depth_fade(); bool& water_depth_fade();
bool& pbr();
bool& flip_y();
int& shadow_mode(); int& shadow_mode();
int& light_cull_face(); int& light_cull_face();
int& light_size_uv(); int& light_size_uv();
@@ -95,6 +97,8 @@ private:
bool m_shader_on = true; bool m_shader_on = true;
bool m_water_perturb = true; bool m_water_perturb = true;
bool m_water_depth_fade = true; bool m_water_depth_fade = true;
bool m_pbr = true;
bool m_flip_y = false;
int m_shadow_mode = 0; int m_shadow_mode = 0;
int m_light_cull_face = 0; int m_light_cull_face = 0;
float m_aspect = 0.0f; float m_aspect = 0.0f;

View File

@@ -12,7 +12,9 @@ private:
GLuint m_texture_array = 0; GLuint m_texture_array = 0;
GLuint m_cross_plane_array = 0; GLuint m_cross_plane_array = 0;
GLuint m_ui_array = 0; GLuint m_ui_array = 0;
GLuint m_pbr_texture_array = 0;
GLfloat m_max_aniso = 0.0f; GLfloat m_max_aniso = 0.0f;
int m_aniso = 1; int m_aniso = 1;
std::vector<GLuint> m_item_textures; std::vector<GLuint> m_item_textures;
@@ -22,6 +24,7 @@ private:
void load_block_item_texture(unsigned id); void load_block_item_texture(unsigned id);
void load_cross_plane_texture(unsigned id); void load_cross_plane_texture(unsigned id);
void load_ui_texture(unsigned id); void load_ui_texture(unsigned id);
void load_pbr_texture(unsigned id);
void init_item(); void init_item();
void init_block(); void init_block();
void init_ui(); void init_ui();
@@ -36,6 +39,7 @@ public:
GLuint get_texture_array() const; GLuint get_texture_array() const;
GLuint get_cross_plane_array() const; GLuint get_cross_plane_array() const;
GLuint get_ui_array() const; GLuint get_ui_array() const;
GLuint get_pbr_texture() const;
const std::vector<GLuint>& item_textures() const; const std::vector<GLuint>& item_textures() const;
// Must call after MapTable::init_map() and glfwMakeContextCurrent(window); // Must call after MapTable::init_map() and glfwMakeContextCurrent(window);
void init_texture(); void init_texture();

View File

@@ -14,7 +14,8 @@ void print_program_info(int prog);
bool check_opengl_error(); bool check_opengl_error();
std::string read_shader_source(const std::string& file_path); std::string read_shader_source(const std::string& file_path);
void delete_image_data(unsigned char* data); void delete_image_data(unsigned char* data);
unsigned char* load_image_data(const std::string& tex_image_path); unsigned char* load_image_data(const std::string& tex_image_path,
bool check_exist = true);
} // namespace Tools } // namespace Tools

View File

@@ -6,6 +6,7 @@ readme = "README.md"
requires-python = ">=3.14" requires-python = ">=3.14"
dependencies = [ dependencies = [
"loguru>=0.7.3", "loguru>=0.7.3",
"pillow>=12.2.0",
"pytomlpp>=1.1.0", "pytomlpp>=1.1.0",
] ]

141
scripts/upscale_nearest.py Normal file
View File

@@ -0,0 +1,141 @@
"""
Batch upscale images using nearest neighbor interpolation (preserve hard pixel edges, no blur).
Usage:
python upscale_nearest.py <input_folder> <output_folder> --scale 8
python upscale_nearest.py <input_folder> <output_folder> --size 128
--scale N Upscale by factor N, e.g., 16x16 image --scale 8 -> 128x128
--size N Directly specify the target side length (square), e.g., --size 128
(--scale and --size are mutually exclusive; --size takes precedence)
Dependencies:
pip install pillow
"""
import argparse
import os
import sys
from pathlib import Path
from loguru import logger
from PIL import Image
TEXTURE_PATH = "assets/texture/block"
TEMP_PATH = "pyout"
work_path = Path(__file__).parent.parent
input_path = work_path / TEXTURE_PATH
output_path = work_path / TEMP_PATH
skip_already_output = True
def upscale_image(
in_path: str, out_path: str, scale: int | None, target_size: int | None
) -> bool:
img = Image.open(in_path)
w, h = img.size
if target_size is not None:
new_w, new_h = target_size, target_size
else:
if scale is None:
raise ValueError("Either target_size or scale must be provided")
new_w, new_h = w * scale, h * scale
if skip_already_output:
out = Path(out_path)
if out.is_file():
return False
if w > new_w:
logger.warning(f"w: {w} is greater than target: {new_w}")
if h > new_h:
logger.warning(f"h: {h} is greater than target: {new_h}")
# NEAREST ensures crisp pixel boundaries without any blurring/interpolation artifacts
out = img.resize((new_w, new_h), Image.NEAREST) # type: ignore
out.save(out_path)
return True
def process_folder(
scale: int | None,
target_size: int | None,
exts: tuple[str, ...],
) -> None:
os.makedirs(output_path, exist_ok=True)
processed = 0
for root, _, files in os.walk(input_path):
for fname in files:
if not fname.lower().endswith(exts):
continue
in_file = os.path.join(root, fname)
rel_path = os.path.relpath(root, input_path)
out_dir = os.path.join(output_path, rel_path)
os.makedirs(out_dir, exist_ok=True)
out_file = os.path.join(out_dir, fname)
ans = upscale_image(in_file, out_file, scale, target_size)
processed += 1
if ans:
print(f"{os.path.relpath(in_file, input_path)} -> upscaled")
if processed == 0:
print(
f"No images with extensions {exts} found in {input_path}.",
file=sys.stderr,
)
else:
print(f"\nDone, processed {processed} images, output to {output_path}")
def main():
parser = argparse.ArgumentParser(
description="Batch nearest neighbor upscale (keep hard pixel edges)"
)
parser.add_argument("input_dir", nargs="?", default=None, help="Input image folder")
parser.add_argument(
"output_dir", nargs="?", default=None, help="Output image folder"
)
parser.add_argument(
"--scale", type=int, default=None, help="Upscale factor, e.g., 8"
)
parser.add_argument(
"--size", type=int, default=None, help="Target side length (square), e.g., 128"
)
parser.add_argument(
"--ext",
default=".png,.jpg,.jpeg,.bmp",
help="File extensions to process, comma-separated (default: .png,.jpg,.jpeg,.bmp)",
)
parser.add_argument(
"--noskip", action="store_true", help="Not skip the file if it is esisted"
)
args = parser.parse_args()
if args.scale is None and args.size is None:
print("Either --scale or --size must be specified.", file=sys.stderr)
sys.exit(1)
exts = tuple(e.strip().lower() for e in args.ext.split(","))
global input_path
global output_path
global skip_already_output
if args.input_dir:
input_path = Path(args.input_dir)
if args.output_dir:
output_path = Path(args.output_dir)
if args.noskip:
skip_already_output = False
process_folder(args.scale, args.size, exts)
if __name__ == "__main__":
main()

View File

@@ -617,6 +617,10 @@ void DevPanel::show_shader_tab_item() {
static const char* samples[] = {"8", "16", "32"}; static const char* samples[] = {"8", "16", "32"};
if (ImGui::BeginTabItem("shader")) { if (ImGui::BeginTabItem("shader")) {
ImGui::Checkbox("Shader", &m_app.renderer().shader_on()); ImGui::Checkbox("Shader", &m_app.renderer().shader_on());
ImGui::SameLine();
ImGui::Checkbox("PBR", &m_app.renderer().pbr());
ImGui::SameLine();
ImGui::Checkbox("Flip Y", &m_app.renderer().flip_y());
if (ImGui::SliderFloat("AmbientStrength", if (ImGui::SliderFloat("AmbientStrength",
&m_app.renderer().ambient_strength(), 0.0f, &m_app.renderer().ambient_strength(), 0.0f,
0.35f)) 0.35f))

View File

@@ -383,7 +383,10 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) {
NORMALS[face][i][0], NORMALS[face][i][0],
NORMALS[face][i][1], NORMALS[face][i][1],
NORMALS[face][i][2], NORMALS[face][i][2],
BlockManager::roughness(cur_id) BlockManager::roughness(cur_id),
TANGENTS[face][i][0],
TANGENTS[face][i][1],
TANGENTS[face][i][2]
}; };
if (BlockManager::is_transparent(cur_id)) { if (BlockManager::is_transparent(cur_id)) {
@@ -441,7 +444,10 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z,
CROSS_NORMALS[face][i][0], CROSS_NORMALS[face][i][0],
CROSS_NORMALS[face][i][1], CROSS_NORMALS[face][i][1],
CROSS_NORMALS[face][i][2], CROSS_NORMALS[face][i][2],
BlockManager::roughness(id) BlockManager::roughness(id),
CROSS_TANGENTS[face][i][0],
CROSS_TANGENTS[face][i][1],
CROSS_TANGENTS[face][i][2]
}; };
m_vertex_data[1].m_vertices.emplace_back(vex); m_vertex_data[1].m_vertices.emplace_back(vex);

View File

@@ -53,12 +53,14 @@ void VertexData::upload() {
(void*)offsetof(Vertex3D, nx)); (void*)offsetof(Vertex3D, nx));
glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), glVertexAttribPointer(4, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, roughness)); (void*)offsetof(Vertex3D, roughness));
glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, tx));
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3); glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4); glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glBindVertexArray(0); glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }

View File

@@ -677,7 +677,7 @@ void Renderer::render_world() {
float dist2d = glm::distance(camera_pos_xz, center_xz); float dist2d = glm::distance(camera_pos_xz, center_xz);
if (dist2d <= CROSS_PLANE_DISTANCE * 16) { if (dist2d <= CROSS_PLANE_DISTANCE * 16) {
glBindTexture(GL_TEXTURE_2D_ARRAY, glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array()); m_texture_manager.get_cross_plane_array());
glBindVertexArray(snapshot.cross_vao); glBindVertexArray(snapshot.cross_vao);
glDrawArrays(GL_TRIANGLES, 0, glDrawArrays(GL_TRIANGLES, 0,
@@ -694,7 +694,6 @@ void Renderer::render_world() {
} }
} }
} }
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glCullFace(GL_BACK); glCullFace(GL_BACK);
@@ -702,16 +701,12 @@ void Renderer::render_world() {
const auto& normal_block_shader = get_shader("normal_block"); const auto& normal_block_shader = get_shader("normal_block");
normal_block_shader.use(); normal_block_shader.use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_depth_map_texture);
glActiveTexture(GL_TEXTURE1);
m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
m_v_mat = m_camera.get_camera_lookat(); m_v_mat = m_camera.get_camera_lookat();
m_mv_mat = m_v_mat * m_m_mat; m_mv_mat = m_v_mat * m_m_mat;
m_norm_mat = glm::transpose(glm::inverse(m_mv_mat)); m_norm_mat = glm::transpose(glm::inverse(m_mv_mat));
glm::vec3 light_dir_view = glm::normalize(glm::mat3(m_v_mat) * lightdir); glm::vec3 light_dir_view = glm::normalize(glm::mat3(m_v_mat) * lightdir);
normal_block_shader.set_loc("model_matrix", m_m_mat);
normal_block_shader.set_loc("mv_matrix", m_mv_mat); normal_block_shader.set_loc("mv_matrix", m_mv_mat);
normal_block_shader.set_loc("proj_matrix", m_p_mat); normal_block_shader.set_loc("proj_matrix", m_p_mat);
normal_block_shader.set_loc("norm_matrix", m_norm_mat); normal_block_shader.set_loc("norm_matrix", m_norm_mat);
@@ -732,7 +727,7 @@ void Renderer::render_world() {
normal_block_shader.set_loc("samples", m_samples); normal_block_shader.set_loc("samples", m_samples);
normal_block_shader.set_loc("specularStrength", m_specular_strength); normal_block_shader.set_loc("specularStrength", m_specular_strength);
normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos()); normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos());
normal_block_shader.set_loc("flipY", m_flip_y);
m_mvp_mat = m_p_mat * m_mv_mat; m_mvp_mat = m_p_mat * m_mv_mat;
auto& m_planes = m_world.planes(); auto& m_planes = m_world.planes();
@@ -741,12 +736,18 @@ void Renderer::render_world() {
int rendered_sum = 0; int rendered_sum = 0;
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_depth_map_texture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array());
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_pbr_texture());
normal_block_shader.set_loc("enablePBR", m_pbr);
for (const auto& snapshot : m_render_snapshots) { for (const auto& snapshot : m_render_snapshots) {
if (Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, if (Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) { m_planes)) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_vao); glBindVertexArray(snapshot.normal_vao);
@@ -755,8 +756,24 @@ void Renderer::render_world() {
rendered_sum++; rendered_sum++;
} }
} }
// cross_plane and discard // discard
for (const auto& snapshot : m_render_snapshots) {
if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) {
continue;
}
if (snapshot.normal_discard_vertices_count != 0) {
glBindVertexArray(snapshot.normal_discard_vao);
glDrawArrays(GL_TRIANGLES, 0,
snapshot.normal_discard_vertices_count);
}
}
// cross_plane
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_cross_plane_array());
normal_block_shader.set_loc("enablePBR", false);
for (const auto& snapshot : m_render_snapshots) { for (const auto& snapshot : m_render_snapshots) {
if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) { m_planes)) {
@@ -767,22 +784,11 @@ void Renderer::render_world() {
glm::vec2 center_xz{snapshot.center.x, snapshot.center.z}; glm::vec2 center_xz{snapshot.center.x, snapshot.center.z};
float dist2d = glm::distance(camera_pos_xz, center_xz); float dist2d = glm::distance(camera_pos_xz, center_xz);
if (dist2d <= CROSS_PLANE_DISTANCE * 16) { if (dist2d <= CROSS_PLANE_DISTANCE * 16) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_cross_plane_array());
glBindVertexArray(snapshot.cross_vao); glBindVertexArray(snapshot.cross_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count); glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count);
} }
} }
if (snapshot.normal_discard_vertices_count != 0) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_discard_vao);
glDrawArrays(GL_TRIANGLES, 0,
snapshot.normal_discard_vertices_count);
}
} }
// copy depth buffer // copy depth buffer
@@ -830,6 +836,7 @@ void Renderer::render_world() {
set_accum_loc(accum_shader); set_accum_loc(accum_shader);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array());
for (const auto& snapshot : m_render_snapshots) { for (const auto& snapshot : m_render_snapshots) {
if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) { m_planes)) {
@@ -837,8 +844,7 @@ void Renderer::render_world() {
} }
if (snapshot.normal_blend_vertices_count != 0) { if (snapshot.normal_blend_vertices_count != 0) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_blend_vao); glBindVertexArray(snapshot.normal_blend_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count); glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count);
@@ -879,6 +885,7 @@ void Renderer::render_world() {
glBindTexture(GL_TEXTURE_2D, m_screen_depth_texture); glBindTexture(GL_TEXTURE_2D, m_screen_depth_texture);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array());
for (const auto& snapshot : m_render_snapshots) { for (const auto& snapshot : m_render_snapshots) {
if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents, if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) { m_planes)) {
@@ -886,8 +893,7 @@ void Renderer::render_world() {
} }
if (snapshot.water_vertices_count != 0) { if (snapshot.water_vertices_count != 0) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.water_vao); glBindVertexArray(snapshot.water_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.water_vertices_count); glDrawArrays(GL_TRIANGLES, 0, snapshot.water_vertices_count);
@@ -896,9 +902,10 @@ void Renderer::render_world() {
auto& composite_shader = get_shader("composite"); auto& composite_shader = get_shader("composite");
glDisable(GL_BLEND); glDisable(GL_BLEND);
composite_shader.use(); composite_shader.use();
glUniform1i(composite_shader.loc("u_accumTex"), 0); composite_shader.set_loc("u_accumTex", 0);
glUniform1i(composite_shader.loc("u_revealTex"), 1); composite_shader.set_loc("u_revealTex", 1);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@@ -977,6 +984,8 @@ bool& Renderer::discard_transparent() { return m_discard_tranparent; }
bool& Renderer::shader_on() { return m_shader_on; } bool& Renderer::shader_on() { return m_shader_on; }
bool& Renderer::water_perturb() { return m_water_perturb; } bool& Renderer::water_perturb() { return m_water_perturb; }
bool& Renderer::water_depth_fade() { return m_water_depth_fade; } bool& Renderer::water_depth_fade() { return m_water_depth_fade; }
bool& Renderer::pbr() { return m_pbr; }
bool& Renderer::flip_y() { return m_flip_y; }
int& Renderer::shadow_mode() { return m_shadow_mode; } int& Renderer::shadow_mode() { return m_shadow_mode; }
int& Renderer::light_cull_face() { return m_light_cull_face; } int& Renderer::light_cull_face() { return m_light_cull_face; }
int& Renderer::light_size_uv() { return m_light_size_uv; } int& Renderer::light_size_uv() { return m_light_size_uv; }

View File

@@ -7,6 +7,28 @@
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include "Cubed/tools/shader_tools.hpp" #include "Cubed/tools/shader_tools.hpp"
namespace {
constexpr int BLOCK_SIZE = 16;
constexpr int BLOCK_NORMAL_SIZE = 128;
constexpr int CROSS_PLANE_SIZE = 16;
constexpr int BLOCK_ITEM_SIZE = 16;
constexpr int UI_SIZE = 16;
constexpr int BLOCK_STATUS_SIZE = 16;
unsigned char* generate_flat_normal_map(int width = BLOCK_NORMAL_SIZE,
int height = BLOCK_NORMAL_SIZE) {
unsigned char* data = new unsigned char[width * height * 4];
for (int i = 0; i < width * height; i++) {
data[i * 4 + 0] = 128; // R -> X = 0
data[i * 4 + 1] = 128; // G -> Y = 0
data[i * 4 + 2] = 255; // B -> Z = 1
data[i * 4 + 3] = 255; // A
}
return data;
}
} // namespace
namespace Cubed { namespace Cubed {
TextureManager::TextureManager() {} TextureManager::TextureManager() {}
@@ -17,6 +39,7 @@ void TextureManager::delet_texture() {
glDeleteTextures(1, &m_texture_array); glDeleteTextures(1, &m_texture_array);
glDeleteTextures(1, &m_block_status_array); glDeleteTextures(1, &m_block_status_array);
glDeleteTextures(1, &m_cross_plane_array); glDeleteTextures(1, &m_cross_plane_array);
glDeleteBuffers(1, &m_pbr_texture_array);
for (auto& id : m_item_textures) { for (auto& id : m_item_textures) {
glDeleteTextures(1, &id); glDeleteTextures(1, &id);
} }
@@ -34,6 +57,8 @@ GLuint TextureManager::get_cross_plane_array() const {
} }
GLuint TextureManager::get_ui_array() const { return m_ui_array; } GLuint TextureManager::get_ui_array() const { return m_ui_array; }
GLuint TextureManager::get_pbr_texture() const { return m_pbr_texture_array; }
const std::vector<GLuint>& TextureManager::item_textures() const { const std::vector<GLuint>& TextureManager::item_textures() const {
return m_item_textures; return m_item_textures;
} }
@@ -45,8 +70,9 @@ void TextureManager::load_block_status(unsigned id) {
unsigned char* image_data = nullptr; unsigned char* image_data = nullptr;
image_data = (Tools::load_image_data(path)); image_data = (Tools::load_image_data(path));
glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, 16, 16, 1, GL_RGBA, glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, BLOCK_STATUS_SIZE,
GL_UNSIGNED_BYTE, image_data); BLOCK_STATUS_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE,
image_data);
Tools::delete_image_data(image_data); Tools::delete_image_data(image_data);
} }
@@ -76,8 +102,9 @@ void TextureManager::load_block_texture(unsigned id) {
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array);
Tools::check_opengl_error(); Tools::check_opengl_error();
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, 16, 16, 1, glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i, BLOCK_SIZE,
GL_RGBA, GL_UNSIGNED_BYTE, image_data[i]); BLOCK_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE,
image_data[i]);
Tools::check_opengl_error(); Tools::check_opengl_error();
Tools::delete_image_data(image_data[i]); Tools::delete_image_data(image_data[i]);
} }
@@ -94,8 +121,8 @@ void TextureManager::load_block_item_texture(unsigned id) {
GLuint texture; GLuint texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, BLOCK_ITEM_SIZE, BLOCK_ITEM_SIZE,
GL_UNSIGNED_BYTE, data); 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
@@ -115,8 +142,8 @@ void TextureManager::load_cross_plane_texture(unsigned id) {
unsigned char* image_data = Tools::load_image_data(path); unsigned char* image_data = Tools::load_image_data(path);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0,
BlockManager::cross_plane_index(id), 16, 16, 1, GL_RGBA, BlockManager::cross_plane_index(id), CROSS_PLANE_SIZE,
GL_UNSIGNED_BYTE, image_data); CROSS_PLANE_SIZE, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
Tools::delete_image_data(image_data); Tools::delete_image_data(image_data);
} }
@@ -127,27 +154,74 @@ void TextureManager::load_ui_texture(unsigned id) {
unsigned char* image_data = nullptr; unsigned char* image_data = nullptr;
image_data = (Tools::load_image_data(path)); image_data = (Tools::load_image_data(path));
glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, 16, 16, 1, GL_RGBA, glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id, UI_SIZE, UI_SIZE, 1,
GL_UNSIGNED_BYTE, image_data); GL_RGBA, GL_UNSIGNED_BYTE, image_data);
Tools::delete_image_data(image_data); Tools::delete_image_data(image_data);
} }
void TextureManager::load_pbr_texture(unsigned id) {
if (id == 0) {
return;
}
if (BlockManager::is_cross_plane(id)) {
return;
}
std::string path = "normal/block/" + BlockManager::name_form_id(id);
unsigned char* image_data[6];
image_data[0] = (Tools::load_image_data(path + "/front_n.png", false));
image_data[1] = (Tools::load_image_data(path + "/right_n.png", false));
image_data[2] = (Tools::load_image_data(path + "/back_n.png", false));
image_data[3] = (Tools::load_image_data(path + "/left_n.png", false));
image_data[4] = (Tools::load_image_data(path + "/top_n.png", false));
image_data[5] = (Tools::load_image_data(path + "/base_n.png", false));
glBindTexture(GL_TEXTURE_2D_ARRAY, m_pbr_texture_array);
for (int i = 0; i < 6; i++) {
unsigned char* data = image_data[i];
bool is_fallback = false;
if (!data) {
is_fallback = true;
data = generate_flat_normal_map();
}
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, id * 6 + i,
BLOCK_NORMAL_SIZE, BLOCK_NORMAL_SIZE, 1, GL_RGBA,
GL_UNSIGNED_BYTE, data);
if (is_fallback) {
delete[] data;
} else {
Tools::delete_image_data(image_data[i]);
}
}
}
void TextureManager::init_block() { void TextureManager::init_block() {
glGenTextures(1, &m_texture_array); glGenTextures(1, &m_texture_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, BLOCK_SIZE, BLOCK_SIZE,
BlockManager::sums() * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE, BlockManager::sums() * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr); nullptr);
glGenTextures(1, &m_cross_plane_array); glGenTextures(1, &m_cross_plane_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, CROSS_PLANE_SIZE,
BlockManager::cross_plane_sum(), 0, GL_RGBA, GL_UNSIGNED_BYTE, CROSS_PLANE_SIZE, BlockManager::cross_plane_sum(), 0, GL_RGBA,
nullptr); GL_UNSIGNED_BYTE, nullptr);
glGenTextures(1, &m_pbr_texture_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_pbr_texture_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, BLOCK_NORMAL_SIZE,
BLOCK_NORMAL_SIZE, BlockManager::sums() * 6, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
for (unsigned i = 0; i < BlockManager::sums(); i++) { for (unsigned i = 0; i < BlockManager::sums(); i++) {
load_block_texture(i); load_block_texture(i);
load_block_item_texture(i); load_block_item_texture(i);
load_pbr_texture(i);
} }
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array);
@@ -172,13 +246,25 @@ void TextureManager::init_block() {
static_cast<GLfloat>(m_aniso)); static_cast<GLfloat>(m_aniso));
} }
glBindTexture(GL_TEXTURE_2D_ARRAY, m_pbr_texture_array);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
if (m_aniso >= 1) {
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY,
static_cast<GLfloat>(m_aniso));
}
Logger::info("Block Texture Load Success"); Logger::info("Block Texture Load Success");
} }
void TextureManager::init_ui() { void TextureManager::init_ui() {
glGenTextures(1, &m_ui_array); glGenTextures(1, &m_ui_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_ui_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, MAX_UI_NUM, 0, glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, UI_SIZE, UI_SIZE, MAX_UI_NUM,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr); 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
for (int i = 0; i < MAX_UI_NUM; i++) { for (int i = 0; i < MAX_UI_NUM; i++) {
load_ui_texture(i); load_ui_texture(i);
} }
@@ -190,8 +276,9 @@ void TextureManager::init_ui() {
void TextureManager::init_block_status() { void TextureManager::init_block_status() {
glGenTextures(1, &m_block_status_array); glGenTextures(1, &m_block_status_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_block_status_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, MAX_BLOCK_STATUS, 0, glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, BLOCK_STATUS_SIZE,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr); BLOCK_STATUS_SIZE, MAX_BLOCK_STATUS, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
for (int i = 0; i < MAX_BLOCK_STATUS; i++) { for (int i = 0; i < MAX_BLOCK_STATUS; i++) {
load_block_status(i); load_block_status(i);
} }

View File

@@ -118,16 +118,31 @@ std::string read_shader_source(const std::string& file_path) {
return content; return content;
} }
void delete_image_data(unsigned char* data) { SOIL_free_image_data(data); } void delete_image_data(unsigned char* data) {
if (data == nullptr) {
return;
}
SOIL_free_image_data(data);
}
unsigned char* load_image_data(const std::string& tex_image_path) { unsigned char* load_image_data(const std::string& tex_image_path,
bool check_exist) {
fs::path path = ASSETS_PATH + tex_image_path; fs::path path = ASSETS_PATH + tex_image_path;
ASSERT_MSG(fs::is_regular_file(path), path.c_str()); if (check_exist) {
ASSERT_MSG(fs::is_regular_file(path), path.c_str());
}
unsigned char* data = nullptr; unsigned char* data = nullptr;
int width, height, channels; int width, height, channels;
data = SOIL_load_image(path.string().c_str(), &width, &height, &channels, data =
SOIL_LOAD_AUTO); SOIL_load_image(path.string().c_str(), &width, &height, &channels,
ASSERT_MSG(data, "Could not load texture" + path.string()); SOIL_LOAD_RGBA); // Materials are all RGBA; must force
// RGBA, otherwise sampling will fail.
if (check_exist) {
if (!data) {
ASSERT_MSG(data, "Could not load texture" + path.string());
std::abort();
}
}
return data; return data;
} }

35
uv.lock generated
View File

@@ -17,6 +17,7 @@ version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "loguru" }, { name = "loguru" },
{ name = "pillow" },
{ name = "pytomlpp" }, { name = "pytomlpp" },
] ]
@@ -28,6 +29,7 @@ dev = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "loguru", specifier = ">=0.7.3" }, { name = "loguru", specifier = ">=0.7.3" },
{ name = "pillow", specifier = ">=12.2.0" },
{ name = "pytomlpp", specifier = ">=1.1.0" }, { name = "pytomlpp", specifier = ">=1.1.0" },
] ]
@@ -47,6 +49,39 @@ wheels = [
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, { url = "https://mirrors.ustc.edu.cn/pypi/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" },
] ]
[[package]]
name = "pillow"
version = "12.2.0"
source = { registry = "https://mirrors.ustc.edu.cn/pypi/simple" }
sdist = { url = "https://mirrors.ustc.edu.cn/pypi/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" }
wheels = [
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/bf/98/4595daa2365416a86cb0d495248a393dfc84e96d62ad080c8546256cb9c0/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:3adc9215e8be0448ed6e814966ecf3d9952f0ea40eb14e89a102b87f450660d8", size = 4100848, upload-time = "2026-04-01T14:44:48.48Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/0b/79/40184d464cf89f6663e18dfcf7ca21aae2491fff1a16127681bf1fa9b8cf/pillow-12.2.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:6a9adfc6d24b10f89588096364cc726174118c62130c817c2837c60cf08a392b", size = 4176515, upload-time = "2026-04-01T14:44:51.353Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/b0/63/703f86fd4c422a9cf722833670f4f71418fb116b2853ff7da722ea43f184/pillow-12.2.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:6a6e67ea2e6feda684ed370f9a1c52e7a243631c025ba42149a2cc5934dec295", size = 3640159, upload-time = "2026-04-01T14:44:53.588Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/71/e0/fb22f797187d0be2270f83500aab851536101b254bfa1eae10795709d283/pillow-12.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2bb4a8d594eacdfc59d9e5ad972aa8afdd48d584ffd5f13a937a664c3e7db0ed", size = 5312185, upload-time = "2026-04-01T14:44:56.039Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ba/8c/1a9e46228571de18f8e28f16fabdfc20212a5d019f3e3303452b3f0a580d/pillow-12.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:80b2da48193b2f33ed0c32c38140f9d3186583ce7d516526d462645fd98660ae", size = 4695386, upload-time = "2026-04-01T14:44:58.663Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/70/62/98f6b7f0c88b9addd0e87c217ded307b36be024d4ff8869a812b241d1345/pillow-12.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22db17c68434de69d8ecfc2fe821569195c0c373b25cccb9cbdacf2c6e53c601", size = 6280384, upload-time = "2026-04-01T14:45:01.5Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/5e/03/688747d2e91cfbe0e64f316cd2e8005698f76ada3130d0194664174fa5de/pillow-12.2.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b14cc0106cd9aecda615dd6903840a058b4700fcb817687d0ee4fc8b6e389be", size = 8091599, upload-time = "2026-04-01T14:45:04.5Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/f6/35/577e22b936fcdd66537329b33af0b4ccfefaeabd8aec04b266528cddb33c/pillow-12.2.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cbeb542b2ebc6fcdacabf8aca8c1a97c9b3ad3927d46b8723f9d4f033288a0f", size = 6396021, upload-time = "2026-04-01T14:45:07.117Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/11/8d/d2532ad2a603ca2b93ad9f5135732124e57811d0168155852f37fbce2458/pillow-12.2.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfd07bc812fbd20395212969e41931001fd59eb55a60658b0e5710872e95286", size = 7083360, upload-time = "2026-04-01T14:45:09.763Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/5e/26/d325f9f56c7e039034897e7380e9cc202b1e368bfd04d4cbe6a441f02885/pillow-12.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9aba9a17b623ef750a4d11b742cbafffeb48a869821252b30ee21b5e91392c50", size = 6507628, upload-time = "2026-04-01T14:45:12.378Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/5f/f7/769d5632ffb0988f1c5e7660b3e731e30f7f8ec4318e94d0a5d674eb65a4/pillow-12.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:deede7c263feb25dba4e82ea23058a235dcc2fe1f6021025dc71f2b618e26104", size = 7209321, upload-time = "2026-04-01T14:45:15.122Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/6a/7a/c253e3c645cd47f1aceea6a8bacdba9991bf45bb7dfe927f7c893e89c93c/pillow-12.2.0-cp314-cp314-win32.whl", hash = "sha256:632ff19b2778e43162304d50da0181ce24ac5bb8180122cbe1bf4673428328c7", size = 6479723, upload-time = "2026-04-01T14:45:17.797Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/cd/8b/601e6566b957ca50e28725cb6c355c59c2c8609751efbecd980db44e0349/pillow-12.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e6c62e9d237e9b65fac06857d511e90d8461a32adcc1b9065ea0c0fa3a28150", size = 7217400, upload-time = "2026-04-01T14:45:20.529Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/d6/94/220e46c73065c3e2951bb91c11a1fb636c8c9ad427ac3ce7d7f3359b9b2f/pillow-12.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:b1c1fbd8a5a1af3412a0810d060a78b5136ec0836c8a4ef9aa11807f2a22f4e1", size = 2554835, upload-time = "2026-04-01T14:45:23.162Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/b6/ab/1b426a3974cb0e7da5c29ccff4807871d48110933a57207b5a676cccc155/pillow-12.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:57850958fe9c751670e49b2cecf6294acc99e562531f4bd317fa5ddee2068463", size = 5314225, upload-time = "2026-04-01T14:45:25.637Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/19/1e/dce46f371be2438eecfee2a1960ee2a243bbe5e961890146d2dee1ff0f12/pillow-12.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d5d38f1411c0ed9f97bcb49b7bd59b6b7c314e0e27420e34d99d844b9ce3b6f3", size = 4698541, upload-time = "2026-04-01T14:45:28.355Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/55/c3/7fbecf70adb3a0c33b77a300dc52e424dc22ad8cdc06557a2e49523b703d/pillow-12.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c0a9f29ca8e79f09de89293f82fc9b0270bb4af1d58bc98f540cc4aedf03166", size = 6322251, upload-time = "2026-04-01T14:45:30.924Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/1c/3c/7fbc17cfb7e4fe0ef1642e0abc17fc6c94c9f7a16be41498e12e2ba60408/pillow-12.2.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1610dd6c61621ae1cf811bef44d77e149ce3f7b95afe66a4512f8c59f25d9ebe", size = 8127807, upload-time = "2026-04-01T14:45:33.908Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ff/c3/a8ae14d6defd2e448493ff512fae903b1e9bd40b72efb6ec55ce0048c8ce/pillow-12.2.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a34329707af4f73cf1782a36cd2289c0368880654a2c11f027bcee9052d35dd", size = 6433935, upload-time = "2026-04-01T14:45:36.623Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/6e/32/2880fb3a074847ac159d8f902cb43278a61e85f681661e7419e6596803ed/pillow-12.2.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e9c4f5b3c546fa3458a29ab22646c1c6c787ea8f5ef51300e5a60300736905e", size = 7116720, upload-time = "2026-04-01T14:45:39.258Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/46/87/495cc9c30e0129501643f24d320076f4cc54f718341df18cc70ec94c44e1/pillow-12.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fb043ee2f06b41473269765c2feae53fc2e2fbf96e5e22ca94fb5ad677856f06", size = 6540498, upload-time = "2026-04-01T14:45:41.879Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/18/53/773f5edca692009d883a72211b60fdaf8871cbef075eaa9d577f0a2f989e/pillow-12.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f278f034eb75b4e8a13a54a876cc4a5ab39173d2cdd93a638e1b467fc545ac43", size = 7239413, upload-time = "2026-04-01T14:45:44.705Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/c9/e4/4b64a97d71b2a83158134abbb2f5bd3f8a2ea691361282f010998f339ec7/pillow-12.2.0-cp314-cp314t-win32.whl", hash = "sha256:6bb77b2dcb06b20f9f4b4a8454caa581cd4dd0643a08bacf821216a16d9c8354", size = 6482084, upload-time = "2026-04-01T14:45:47.568Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ba/13/306d275efd3a3453f72114b7431c877d10b1154014c1ebbedd067770d629/pillow-12.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:6562ace0d3fb5f20ed7290f1f929cae41b25ae29528f2af1722966a0a02e2aa1", size = 7225152, upload-time = "2026-04-01T14:45:50.032Z" },
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/ff/6e/cf826fae916b8658848d7b9f38d88da6396895c676e8086fc0988073aaf8/pillow-12.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aa88ccfe4e32d362816319ed727a004423aab09c5cea43c01a4b435643fa34eb", size = 2556579, upload-time = "2026-04-01T14:45:52.529Z" },
]
[[package]] [[package]]
name = "pytomlpp" name = "pytomlpp"
version = "1.1.0" version = "1.1.0"