Skip to content

Commit

Permalink
Load textures into the game level
Browse files Browse the repository at this point in the history
  • Loading branch information
Lyuu17 committed Jun 13, 2024
1 parent 655f261 commit dcf6f1f
Show file tree
Hide file tree
Showing 22 changed files with 443 additions and 13 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "extern/godot-cpp"]
path = extern/godot-cpp
url = https://github.com/godotengine/godot-cpp.git
[submodule "extern/DirectXTex"]
path = extern/DirectXTex
url = https://github.com/microsoft/DirectXTex.git
6 changes: 1 addition & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,13 @@ project(swbf2
VERSION
0.1.0
)
set(CMAKE_CXX_STANDARD 23)

set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)

# Create our library
add_library( ${PROJECT_NAME} SHARED )

target_compile_features( ${PROJECT_NAME}
PRIVATE
cxx_std_23
)

# LIB_ARCH is the architecture being built. It is set to the build system's architecture.
# For macOS, we build a universal library (both arm64 and x86_64).
set( LIB_ARCH ${CMAKE_SYSTEM_PROCESSOR} )
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ target_sources(${PROJECT_NAME}
"SWBF2/Native/Chunks/ModelChunk.cpp"
"SWBF2/Native/Chunks/ModelSegmentChunk.cpp"
"SWBF2/Native/Chunks/StreamReader.cpp"
"SWBF2/Native/Chunks/TextureChunk.cpp"
"SWBF2/Native/Chunks/UcfbChunk.cpp"
"SWBF2/Native/Chunks/WorldChunk.cpp"
"SWBF2/Native/Models/Model.cpp"
"SWBF2/Native/Texture/Texture.cpp"
"SWBF2/Native/Texture/TextureUtils.cpp"
"SWBF2/Native/Level.cpp"
"SWBF2/Core.cpp"
"SWBF2/Level.cpp"
Expand Down
4 changes: 2 additions & 2 deletions src/SWBF2/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ namespace SWBF2
{
godot::UtilityFunctions::print("hello world!");

SWBF2::Native::UcfbChunk::ReadUcfbFile("data/_lvl_pc/common.lvl");
SWBF2::Native::UcfbChunk::ReadUcfbFile("data/_lvl_pc/core.lvl");
// SWBF2::Native::UcfbChunk::ReadUcfbFile("data/_lvl_pc/common.lvl");
// SWBF2::Native::UcfbChunk::ReadUcfbFile("data/_lvl_pc/core.lvl");

add_child(memnew(Level));
}
Expand Down
31 changes: 31 additions & 0 deletions src/SWBF2/FNVHash.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

namespace SWBF2
{
typedef uint32_t FNVHash;

constexpr FNVHash FNVGenerateHash(const std::string &str)
{
constexpr uint32_t FNV_prime = 16777619;
constexpr uint32_t offset_basis = 2166136261;

uint32_t hash = offset_basis;

const char *buffer = str.data();
for (size_t i = 0; i < str.length(); ++i)
{
char c = buffer[i];
c |= 0x20;

hash ^= c;
hash *= FNV_prime;
}

return hash;
}

constexpr FNVHash operator""_fnv(const char *str, const std::size_t length)
{
return FNVGenerateHash({ str, length });
}
}
36 changes: 36 additions & 0 deletions src/SWBF2/Level.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/classes/standard_material3d.hpp>
#include <godot_cpp/variant/color.hpp>

#include "Native/Chunks/ChunkProcessor.hpp"
Expand All @@ -14,9 +16,28 @@ namespace SWBF2
{
SWBF2::Native::UcfbChunk::ReadUcfbFile("data/_lvl_pc/cor/cor1.lvl");

LoadTextures();
LoadMeshes();
}

void Level::LoadTextures()
{
for (auto const &[id, tex] : Native::Level::m_tex)
{
for (auto const format : tex->m_formats)
{
for (auto const faceLevel : format.m_faceLevels)
{
godot::Ref<godot::StandardMaterial3D> material;
material.instantiate();
material->set_texture(godot::StandardMaterial3D::TEXTURE_ALBEDO, faceLevel.m_gdImageTexture);

m_textureMaterials.insert_or_assign(id, material);
}
}
}
}

void Level::LoadMeshes()
{
for (auto const &[id, model] : Native::Level::m_models)
Expand All @@ -37,6 +58,7 @@ namespace SWBF2

for (const auto &position : segment.m_verticesBuf.m_positions)
{
const auto &position = segment.m_verticesBuf.m_positions[i];
vertices.push_back({ position.x, position.y, position.z });
}

Expand Down Expand Up @@ -78,6 +100,20 @@ namespace SWBF2
godot::ArrayMesh *arrMesh = memnew(godot::ArrayMesh);
arrMesh->add_surface_from_arrays(godot::Mesh::PRIMITIVE_TRIANGLE_STRIP, arrays);

auto tex_id = 0;
for (const auto &texName : segment.m_textureNames)
{
if (!m_textureMaterials.contains(texName))
{
godot::UtilityFunctions::printerr(__FILE__, ":", __LINE__, ": No texture found for ", texName.c_str());
continue;
}

meshInstance->set_material_override(m_textureMaterials[texName]);

tex_id++;
}

meshInstance->set_mesh(arrMesh);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/SWBF2/Level.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@
#pragma once

#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/standard_material3d.hpp>

namespace SWBF2
{
class Level : public godot::Node3D {
GDCLASS(Level, godot::Node3D)

private:
std::unordered_map<std::string, godot::Ref<godot::StandardMaterial3D>> m_textureMaterials;
public:
Level() {}
~Level() = default;

virtual void _ready() override;

void LoadTextures();
void LoadMeshes();

void _process(double delta_time) override;
Expand Down
4 changes: 3 additions & 1 deletion src/SWBF2/Native/Chunks/ChunkProcessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "UcfbChunk.hpp"
#include "WorldChunk.hpp"
#include "LoclChunk.hpp"
#include "TextureChunk.hpp"

namespace SWBF2::Native
{
Expand All @@ -21,7 +22,8 @@ namespace SWBF2::Native
{ "ucfb"_m, UcfbChunk::ProcessChunk },
{ "wrld"_m, WorldChunk::ProcessChunk },
{ "modl"_m, ModelChunk::ProcessChunk },
{ "Locl"_m, LoclChunk::ProcessChunk }
{ "Locl"_m, LoclChunk::ProcessChunk },
{ "tex_"_m, TextureChunk::ProcessChunk }
};

static void ProcessChunk(StreamReader &streamReader, StreamReader &parentReader);
Expand Down
17 changes: 16 additions & 1 deletion src/SWBF2/Native/Chunks/ModelSegmentChunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "StreamReader.hpp"

#include "FNVHash.hpp"

#include "ModelSegmentChunk.hpp"

namespace SWBF2::Native
Expand Down Expand Up @@ -60,10 +62,23 @@ namespace SWBF2::Native

break;
}
case "TNAM"_m:
{
uint32_t index;
*readerChild >> index;

std::string texName;
*readerChild >> texName;

string_tolower(texName);

segment.m_textureNames.push_back(texName);

break;
}
case "BNAM"_m:
case "SKIN"_m:
case "BMAP"_m:
case "TNAM"_m:
case "MNAM"_m:
default:
godot::UtilityFunctions::printerr(__FILE__, ":", __LINE__, ": ", readerChild->GetHeader().ToString().c_str(), " not implemented");
Expand Down
2 changes: 1 addition & 1 deletion src/SWBF2/Native/Chunks/StreamReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ namespace SWBF2::Native
StreamReader &operator>>(std::string &value)
{
const char *str = reinterpret_cast<const char *>(&m_data[m_head]);
std::size_t len = GetHeader().size;
std::size_t len = std::distance(str, std::find(str, str + GetHeader().size, '\0'));

value = std::string_view(str, len);

Expand Down
113 changes: 113 additions & 0 deletions src/SWBF2/Native/Chunks/TextureChunk.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include <godot_cpp/variant/utility_functions.hpp>

#include "Native/Chunks/StreamReader.hpp"
#include "Native/Chunks/TextureChunk.hpp"

#include "Native/Texture/TextureUtils.hpp"
#include "Native/Level.hpp"

namespace SWBF2::Native
{
void TextureChunk::ProcessChunk(StreamReader &streamReader)
{
std::unique_ptr<Texture> tex = std::make_unique<Texture>();

auto texNameReaderChild = streamReader.ReadChildWithHeader<"NAME"_m>();
{
*texNameReaderChild >> tex->m_name;
}

auto infoReaderChild = streamReader.ReadChildWithHeader<"INFO"_m>();
{
*infoReaderChild >> tex->m_formatCount;

tex->m_d3dFormats.resize(tex->m_formatCount);
*infoReaderChild >> tex->m_d3dFormats;

std::optional<StreamReader> fmtReaderChild;
while ((fmtReaderChild = streamReader.ReadChild()).has_value())
{
switch (fmtReaderChild->GetHeader().m_Magic)
{
case "FMT_"_m: {
StreamReader r{ *fmtReaderChild };
TextureChunk::ProcessFMTChunk(r, tex.get());
break;
}

default:
godot::UtilityFunctions::printerr(__FILE__, ":", __LINE__, ": ", fmtReaderChild->GetHeader().ToString().c_str(), " not implemented");
break;
}
}
}

Level::m_tex.try_emplace(tex->m_name, std::move(tex));
}

void TextureChunk::ProcessFMTChunk(StreamReader &streamReader, Texture *tex)
{
auto infoReaderChild = streamReader.ReadChildWithHeader<"INFO"_m>();

auto &fmt = tex->m_formats.emplace_back(TextureFormat());
{
*infoReaderChild >> fmt.m_format;
*infoReaderChild >> fmt.m_width;
*infoReaderChild >> fmt.m_height;
*infoReaderChild >> fmt.m_depth;
*infoReaderChild >> fmt.m_mipmapCount;
*infoReaderChild >> fmt.m_typeDetailBias;
}

auto faceReaderChild = streamReader.ReadChildWithHeader<"FACE"_m>();
{
std::optional<StreamReader> lvlReaderChild;
while ((lvlReaderChild = faceReaderChild->ReadChild()).has_value())
{
switch (lvlReaderChild->GetHeader().m_Magic)
{
case "LVL_"_m: {
StreamReader r{ *lvlReaderChild };
TextureChunk::ProcessTextureLevelChunk(r, fmt);
break;
}

default:
godot::UtilityFunctions::printerr(__FILE__, ":", __LINE__, ": ", lvlReaderChild->GetHeader().ToString().c_str(), " not implemented");
break;
}
}
}
}

void TextureChunk::ProcessTextureLevelChunk(StreamReader &streamReader, TextureFormat &fmt)
{
TextureFormatFaceLevel lvl;

auto infoReaderChild = streamReader.ReadChildWithHeader<"INFO"_m>();
{
*infoReaderChild >> lvl.m_mipLevel;
*infoReaderChild >> lvl.m_bodySize;
}

auto bodyReaderChild = streamReader.ReadChildWithHeader<"BODY"_m>();
{
lvl.m_imageInBytes.resize(lvl.m_bodySize);

*bodyReaderChild >> lvl.m_imageInBytes;
}

if (TextureUtils::IsD3DFormatSupported(fmt.m_format))
{
godot::PackedByteArray imageBuf;
imageBuf.resize(lvl.m_bodySize);
std::copy(lvl.m_imageInBytes.begin(), lvl.m_imageInBytes.end(), (std::byte *)imageBuf.ptrw());
godot::Ref<godot::Image> image{ godot::Image::create_from_data(fmt.m_width, fmt.m_height, fmt.m_mipmapCount > 1, TextureUtils::D3DToGLFormat(fmt.m_format), imageBuf) };

lvl.m_gdImageTexture.instantiate();
lvl.m_gdImageTexture->create_from_image(image);
}

fmt.m_faceLevels.push_back(lvl);
}
}
16 changes: 16 additions & 0 deletions src/SWBF2/Native/Chunks/TextureChunk.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "Native/Chunks/StreamReader.hpp"

#include "Native/Texture/Texture.hpp"

namespace SWBF2::Native
{
class TextureChunk {
public:
static void ProcessChunk(StreamReader &streamReader);
static void ProcessFMTChunk(StreamReader &streamReader, Texture *tex);
static void ProcessTextureLevelChunk(StreamReader &streamReader, TextureFormat &fmt);
};

}
Loading

0 comments on commit dcf6f1f

Please sign in to comment.