[custom levels] A few bug fixes (#3736)
Some checks failed
Build / 🖥️ Windows (push) Waiting to run
Build / 🐧 Linux (push) Waiting to run
Build / 🍎 MacOS (push) Waiting to run
Lint / 📝 Formatting (push) Waiting to run
Lint / 📝 Required Checks (push) Waiting to run
Lint / 📝 Optional Checks (push) Waiting to run
Update Controller Database / update-controller-db (push) Has been cancelled

- 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>
This commit is contained in:
water111 2024-10-27 12:31:41 -04:00 committed by GitHub
parent 1962fbfb51
commit dff9ac163a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 195 additions and 9 deletions

View File

@ -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
)

View File

@ -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 {

View File

@ -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++;
}
}
}
}

View File

@ -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);

View File

@ -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];
@ -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++;
}
}

View File

@ -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 {
@ -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");
@ -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;
@ -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;
@ -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:

View File

@ -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()

View File

@ -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;
}