Skip to content

Commit

Permalink
[custom levels] A few bug fixes (#3736)
Browse files Browse the repository at this point in the history
- Bug fix to KD tree splitting, should fix cases with bad vertex
colors/alphas.
- Normalize normals instead of asserting if they are the wrong length.
**the fact that blender exports normals incorrectly is a bug and I doubt
their implementation is correct if you've scaled things on only on
axis.**
- Automatically resize metallic texture (envmap strength) if it doesn't
match the size of the rgb texture instead of asserting

Co-authored-by: water111 <awaterford1111445@gmail.com>
  • Loading branch information
water111 and water111 authored Oct 27, 2024
1 parent 1962fbf commit dff9ac1
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 9 deletions.
1 change: 1 addition & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ add_library(common
util/Timer.cpp
util/unicode_util.cpp
util/gltf_util.cpp
util/image_resize.cpp
versions/versions.cpp
)

Expand Down
3 changes: 2 additions & 1 deletion common/util/gltf_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ ExtractedVertices gltf_vertices(const tinygltf::Model& model,
normals = extract_vec3f(data_ptr, count, byte_stride);
for (auto& nrm : normals) {
math::Vector4f nrm4(nrm.x(), nrm.y(), nrm.z(), 0.f);
nrm = (w_T_local * nrm4).xyz();
// we found that normals aren't normalized if an object is scaled in blender.
nrm = (w_T_local * nrm4).xyz().normalized();
}
ASSERT(normals.size() == result.size());
} else {
Expand Down
101 changes: 101 additions & 0 deletions common/util/image_resize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include "image_resize.h"

#include <cmath>

#include "common/math/Vector.h"

namespace {
struct BilinearSample {
int i0, i1;
double w0, w1;
};

BilinearSample bilinear(int src_i, double sample, bool wrap) {
BilinearSample result;

const double px_size = 1. / double(src_i);
const double px_center_f = (sample - 0.5 * px_size) / px_size;
const int px_floor = std::floor(px_center_f);
const double frac = px_center_f - double(px_floor);

if (px_center_f < 0) { // off the bottom
if (wrap) {
// wrap around!
result.i0 = 0;
result.i1 = src_i - 1;
result.w1 = -px_center_f;
result.w0 = 1. - result.w1;
} else {
// clamp to edge
result.i0 = 0;
result.i1 = 0;
result.w0 = 1;
result.w1 = 0;
}
} else if (px_floor >= (src_i - 1)) { // off the top
if (wrap) {
result.i0 = src_i - 1;
result.i1 = 0;
result.w0 = 1. - frac;
result.w1 = frac;
} else {
// clamp
result.i0 = src_i - 1;
result.i1 = src_i - 1;
result.w0 = 1;
result.w1 = 0;
}
} else {
result.i0 = px_floor;
result.i1 = px_floor + 1;
result.w0 = 1. - frac;
result.w1 = frac;
}

return result;
}

math::Vector4<u8> sample1(const u8* src, int w, int h, int x, int y) {
(void)h;
int offset = 4 * (y * w + x);
return math::Vector4<u8>(src[offset], src[offset + 1], src[offset + 2], src[offset + 3]);
}

math::Vector4<u8> sample_bilinear(const u8* src,
int w,
int h,
const BilinearSample& x,
const BilinearSample& y) {
auto p00 = sample1(src, w, h, x.i0, y.i0).cast<double>();
auto p01 = sample1(src, w, h, x.i0, y.i1).cast<double>();
auto p10 = sample1(src, w, h, x.i1, y.i0).cast<double>();
auto p11 = sample1(src, w, h, x.i1, y.i1).cast<double>();
auto p_interp = (p00 * x.w0 * y.w0 + p01 * x.w0 * y.w1 + p10 * x.w1 * y.w0 + p11 * x.w1 * y.w1);
return p_interp.cast<u8>();
}
} // namespace

void resize_rgba_image(u8* dst,
int dst_w,
int dst_h,
const u8* src,
int src_w,
int src_h,
bool wrap_w,
bool wrap_h) {
const double dst_px_h = 1. / dst_h;
const double dst_px_w = 1. / dst_w;
for (int h = 0; h < dst_h; h++) {
const float h_pos = (double(h) + 0.5) * dst_px_h;
const auto h_sample = bilinear(src_h, h_pos, wrap_h);
for (int w = 0; w < dst_w; w++) {
const float w_pos = (double(w) + 0.5) * dst_px_w;
const auto w_sample = bilinear(src_w, w_pos, wrap_w);
auto result = sample_bilinear(src, src_w, src_h, w_sample, h_sample);
for (int i = 0; i < 4; i++) {
*dst = result[i];
dst++;
}
}
}
}
12 changes: 12 additions & 0 deletions common/util/image_resize.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "common/common_types.h"

void resize_rgba_image(u8* dst,
int dst_w,
int dst_h,
const u8* src,
int src_w,
int src_h,
bool wrap_w,
bool wrap_h);
23 changes: 21 additions & 2 deletions goalc/build_level/common/color_quantization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,24 @@ void split_kd(KdNode* in, u32 depth, int next_split_dim) {
return;
}

if (!in->colors.empty()) {
for (int i = 0; i < 4; i++) {
bool all_same = true;
u8 same = in->colors[0][next_split_dim];
for (auto& color : in->colors) {
if (color[next_split_dim] != same) {
all_same = false;
break;
}
}
if (all_same) {
next_split_dim = (next_split_dim + 1) % 4;
} else {
break;
}
}
}

// sort by split dimension
std::stable_sort(in->colors.begin(), in->colors.end(), [=](const Color& a, const Color& b) {
return a[next_split_dim] < b[next_split_dim];
Expand All @@ -248,11 +266,12 @@ void split_kd(KdNode* in, u32 depth, int next_split_dim) {
size_t i = 0;
size_t mid = in->colors.size() / 2;
if (depth & 1) {
while (mid > 1 && in->colors[mid] == in->colors[mid - 1]) {
while (mid > 1 && in->colors[mid][next_split_dim] == in->colors[mid - 1][next_split_dim]) {
mid--;
}
} else {
while (mid + 2 < in->colors.size() && in->colors[mid] == in->colors[mid + 1]) {
while (mid + 2 < in->colors.size() &&
in->colors[mid][next_split_dim] == in->colors[mid + 1][next_split_dim]) {
mid++;
}
}
Expand Down
24 changes: 18 additions & 6 deletions goalc/build_level/common/gltf_mesh_extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "common/math/geometry.h"
#include "common/util/Timer.h"
#include "common/util/gltf_util.h"
#include <common/util/image_resize.h>

using namespace gltf_util;
namespace gltf_mesh_extract {
Expand Down Expand Up @@ -262,7 +263,9 @@ void add_to_packed_verts(std::vector<tfrag3::PackedTieVertices::Vertex>* out,
int texture_pool_add_envmap_control_texture(TexturePool* pool,
const tinygltf::Model& model,
int rgb_image_id,
int mr_image_id) {
int mr_image_id,
bool wrap_w,
bool wrap_h) {
const auto& existing = pool->envmap_textures_by_gltf_id.find({rgb_image_id, mr_image_id});
if (existing != pool->envmap_textures_by_gltf_id.end()) {
lg::info("Reusing envmap textures");
Expand All @@ -279,8 +282,16 @@ int texture_pool_add_envmap_control_texture(TexturePool* pool,
ASSERT(mr_tex.pixel_type == TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE);
ASSERT(mr_tex.component == 4);

ASSERT(rgb_tex.width == mr_tex.width);
ASSERT(rgb_tex.height == mr_tex.height);
std::vector<u8> resized_mr_tex;
const u8* mr_src;
if (rgb_tex.width == mr_tex.width && rgb_tex.height == mr_tex.height) {
mr_src = mr_tex.image.data();
} else {
resized_mr_tex.resize(rgb_tex.width * rgb_tex.height * 4);
resize_rgba_image(resized_mr_tex.data(), rgb_tex.width, rgb_tex.height, mr_tex.image.data(),
mr_tex.width, mr_tex.height, wrap_w, wrap_h);
mr_src = resized_mr_tex.data();
}

size_t idx = pool->textures_by_idx.size();
pool->envmap_textures_by_gltf_id[{rgb_image_id, mr_image_id}] = idx;
Expand All @@ -298,7 +309,7 @@ int texture_pool_add_envmap_control_texture(TexturePool* pool,
// adjust alpha from metallic channel
for (size_t i = 0; i < tt.data.size(); i++) {
u32 rgb = tt.data[i];
u32 metal = mr_tex.image[4 * i + 2] / 4;
u32 metal = mr_src[4 * i + 2] / 4;
rgb &= 0xff'ff'ff;
rgb |= (metal << 24);
tt.data[i] = rgb;
Expand Down Expand Up @@ -404,8 +415,9 @@ void extract(const Input& in,
ASSERT(roughness_tex.source >= 0);

// draw.tree_tex_id = texture_pool_add_texture(in.tex_pool, model.images[base_tex.source]);
draw.tree_tex_id = texture_pool_add_envmap_control_texture(in.tex_pool, model, base_tex.source,
roughness_tex.source);
draw.tree_tex_id = texture_pool_add_envmap_control_texture(
in.tex_pool, model, base_tex.source, roughness_tex.source, !draw.mode.get_clamp_s_enable(),
!draw.mode.get_clamp_t_enable());
out.base_draws.push_back(draw);

// now, setup envmap draw:
Expand Down
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ add_executable(goalc-test

target_link_libraries(goalc-test common runtime compiler gtest decomp Zydis libzstd_static tree-sitter)

add_executable(test_image_resize ${CMAKE_CURRENT_LIST_DIR}/common/test_image_resize.cpp)
target_link_libraries(test_image_resize common)

if(WIN32)
target_link_libraries(goalc-test mman)
endif()
Expand Down
37 changes: 37 additions & 0 deletions test/common/test_image_resize.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include <cstdio>
#include <vector>

#include "common/common_types.h"
#include "common/util/FileUtil.h"
#include "common/util/Timer.h"
#include "common/util/image_resize.h"

int main() {
int src_h = 30;
int src_w = 30;
int check_divide = 3;
int dst_sz = 300;

std::vector<u8> src;
for (int h = 0; h < src_h; h++) {
for (int w = 0; w < src_w; w++) {
u8 color = (((h / check_divide) & 1) ^ ((w / check_divide) & 1)) ? 0x10 : 0xd0;
src.push_back(color);
src.push_back(color);
src.push_back(color);
src.push_back(255);
}
}

std::vector<u8> dst(dst_sz * dst_sz * 4);
Timer timer;
resize_rgba_image(dst.data(), dst_sz, dst_sz, src.data(), src_w, src_h, true, true);
printf("resized in %.3f ms\n", timer.getMs());
file_util::write_rgba_png("test_wrap.png", dst.data(), dst_sz, dst_sz);
resize_rgba_image(dst.data(), dst_sz, dst_sz, src.data(), src_w, src_h, false, false);
printf("resized in %.3f ms\n", timer.getMs());
file_util::write_rgba_png("test_unwrap.png", dst.data(), dst_sz, dst_sz);
file_util::write_rgba_png("src.png", src.data(), src_w, src_h);

return 0;
}

0 comments on commit dff9ac1

Please sign in to comment.