#wii #graphics #gamedev #3d-model

sys brres-sys

librii的.brres层的低级绑定

12个版本

0.1.12 2024年8月3日
0.1.10 2024年6月15日

#95 in 数据格式

Download history 602/week @ 2024-06-10 34/week @ 2024-06-17 27/week @ 2024-07-01 12/week @ 2024-07-22 125/week @ 2024-07-29

137 每月下载量
用于 brres

MITGPL-2.0-or-later

1MB
17K SLoC

C++ 14K SLoC // 0.1% comments Rust 2.5K SLoC // 0.0% comments Bitbake 715 SLoC

crates.io docs.rs

brres-sys

在librii的g3d的JSON导出导入层之上实现Rust层。重要的是,像纹理数据和顶点数据这样的大型缓冲区实际上并没有在JSON中编码,而是直接作为二进制块传递。这使得JSON文件保持轻量。

公开以下Rust接口

pub struct CBrresWrapper<'a> {
    pub json_metadata: &'a str,
    pub buffer_data: &'a [u8],
    // ...
}

impl<'a> CBrresWrapper<'a> {
	// .bin -> .json
    pub fn from_bytes(buf: &[u8]) -> anyhow::Result<Self>;
    // .json -> .bin
    pub fn write_bytes(json: &str, buffer: &[u8]) -> anyhow::Result<Self>;
}

这封装了以下C接口,仍然可以使用(例如,用于其他语言绑定)。

// include/brres_sys.h
struct CResult {
	const char* json_metadata;
	uint32_t len_json_metadata;
	const void* buffer_data;
	uint32_t len_buffer_data;
	void (*freeResult)(struct CResult* self);
	void* opaque;
};
uint32_t brres_read_from_bytes(CResult* result, const void* buf,
                               uint32_t len);
uint32_t brres_write_bytes(CResult* result, const char* json,
                          uint32_t json_len, const void* buffer,
                          uint32_t buffer_len);
void brres_free(CResult* result);

内部API文档

1. 测试

每个BRRES格式都经过验证,以产生1:1字节数据相同的输出,即使是中间表示(见下文)。测试可以在tests文件夹中找到。

2. BRRES子文件

文件类型 描述 源文件 二进制表示 中间表示
BRRES v0 3D资源 ArchiveIO.cpp BinaryArchive Archive
MDL0 v11 3D模型 ModelIO.cpp BinaryModel Model
TEX0 v1/v3 纹理 TextureIO.cpp - TextureData
CLR0 v4 着色器统一变量动画 AnimClrIO.cpp BinaryClr -
SHP0 骨骼/角色动画 - - -
SRT0 v5 纹理缩放/旋转/平移动画 AnimIO.cpp BinarySrt SrtAnim*
PAT0 v4 纹理图像动画 AnimTexPatIO.cpp BinaryTexPat -
VIS0 v4 骨骼可见性动画 AnimVisIO.cpp BinaryVis -

* SrtAnim 目前为只读;BinaryArchive 目前包含可读写的 BinarySrt

librii为每种文件类型提供两个结构

  1. 二进制表示:设计为与格式的1:1映射。直接编辑此内容可能需要大量记录并容易出错。然而,即使是损坏的模型通常也可以在这里解析。
  2. 中间表示:足够简单以涵盖所有官方生成的模型,但并非更简单。易于编辑;有很多验证。可能在用jank工具创建的社区模型上失败。

将BRRES文件解析为二进制表示形式(BinaryArchive

std::expected<librii::g3d::BinaryArchive, std::string> parsed_brres = librii::g3d::BinaryArchive::read(reader, transaction);
if (!parsed_brres) {
    return std::unexpected(std::format("Failed to parse BRRES with error: {}", parsed_brres.error()));
}

将BinaryArchive解包为中间表示形式(Archive

std::expected<librii::g3d::Archive, std::string> archive = librii::g3d::Archive::from(*parsed_brres);
if (!archive) {
    return std::unexpected(std::format("Failed to unpack BRRES file with error: {}", archive.error()));
}
librii::g3d::Archive data(std::move(*archive));
for (const auto &model : data.models) {
    printf("%s\n", std::format("Model: {} with {} materials", model.name, model.materials.size()).c_str());
}

3. MDL0子文件

特别提供MDL0(BinaryModelModel)的信息

文件类型 描述 源文件 二进制表示 中间表示
MDL0.ByteCode 绘制调用 + 骨骼 ModelIO.cpp ByteCodeMethod 合并到BoneDataDrawMatrix
MDL0.Bone 骨骼 BoneIO.cpp BinaryBoneData BoneData
MDL0.PositionBuf 包含顶点位置 ModelIO.cpp - PositionBuffer
MDL0.NormalBuf 包含顶点法线 ModelIO.cpp - NormalBuffer
MDL0.ColorBuf 包含顶点颜色 ModelIO.cpp - ColorBuffer
MDL0.UVBuf 包含UV贴图 ModelIO.cpp - TextureCoordinateBuffer
MDL0.FurVecBuf 与毛皮相关 - - -
MDL0.FurPosBuf 与毛皮相关 - - -
MDL0.Material 材质数据 MatIO.cpp BinaryMaterial G3dMaterialData
MDL0.TEV 着色器数据 TevIO.cpp BinaryTev ** 合并到G3dMaterialData
MDL0.Mesh 3D网格数据 ModelIO.cpp - PolygonData
MDL0.TextureLink 内部 ModelIO.cpp - - ***
MDL0.PaletteLink 内部 - - - ****
MDL0.UserData 元数据 - - -

*** BinaryTev通常被一个包含仅BinaryTev设置的TEV字段的gx::LowLevelGxMaterial实例所取代。

*** 纹理链接总是重新计算。如果一个模型有无效的纹理链接,只需通过BinaryModel传递数据即可纠正。

**** 不支持调色板。

建议您在可能的情况下始终使用中间表示形式。 G3dMaterialData直接满足LowLevelGxMaterial,这使得它与librii::gl的着色器编译器兼容。当然,一个BinaryMaterial + BinaryTev(以部分gx::LowLevelGxMaterial的形式)组合可以通过启用TEV合并的MatIO的函数fromBinMat(...) -> gx::LowLevelGxMaterial进行渲染。

示例用法(低级API)

例如以下惯用的C代码

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "include/brres_sys.h"

int main() {
	uint8_t my_brres_file[] = { /* ... */ }; // Initialize with actual .brres file data

	CResult result = {};
	uint32_t ok = brres_read_from_bytes(&result, my_brres_file, sizeof(my_brres_file));

	if (!ok) {
		printf("Failed to read .brres: %*.s\n", (size_t)result.len_json_metadata, result.json_metadata);
		brres_free(&result); // Free the error message
		return 1;
	}

	// Write JSON metadata to a file
	FILE *json_file = fopen("output.json", "wb");
	if (json_file == NULL) {
		perror("Failed to open output.json for writing");
		brres_free(&result);
		return 1;
	}

	size_t written = fwrite(result.json_metadata, 1, result.len_json_metadata, json_file);
	if (written != result.len_json_metadata) {
		perror("Failed to write all JSON metadata to output.json");
		fclose(json_file);
		brres_free(&result);
		return 1;
	}

	fclose(json_file);

	// Write binary buffer data to a file
	FILE *bin_file = fopen("output.bin", "wb");
	if (bin_file == NULL) {
		perror("Failed to open output.bin for writing");
		brres_free(&result);
		return 1;
	}

	written = fwrite(result.buffer_data, 1, result.len_buffer_data, bin_file);
	if (written != result.len_buffer_data) {
		perror("Failed to write all buffer data to output.bin");
		fclose(bin_file);
		brres_free(&result);
		return 1;
	}

	fclose(bin_file);
	brres_free(&result);

	return 0;
}

依赖关系

~0.5–2.6MB
~50K SLoC