mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-03-25 05:53:03 +00:00
Merge pull request #722 from Laupetin/chore/techset-tests
chore: add unit tests for techset dumping and compilation for all remaining games
This commit is contained in:
@@ -0,0 +1,338 @@
|
|||||||
|
#include "Game/IW3/Techset/TechniqueCompilerIW3.h"
|
||||||
|
|
||||||
|
#include "Game/IW3/IW3.h"
|
||||||
|
#include "Game/IW3/Techset/PixelShaderLoaderIW3.h"
|
||||||
|
#include "Game/IW3/Techset/VertexDeclCompilerIW3.h"
|
||||||
|
#include "Game/IW3/Techset/VertexShaderLoaderIW3.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Shader/ShaderCommon.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
#include "catch2/generators/catch_generators.hpp"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace IW3;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void GivenVertexShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/IW3/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForVertexShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GivenPixelShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/IW3/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForPixelShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechniqueCompilerIW3", "[iw3][techset][compiler]")
|
||||||
|
{
|
||||||
|
|
||||||
|
Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
|
||||||
|
creatorCollection.AddSubAssetCreator(techset::CreateVertexDeclCompilerIW3(memory));
|
||||||
|
creatorCollection.AddSubAssetCreator(techset::CreateVertexShaderLoaderIW3(memory, searchPath));
|
||||||
|
creatorCollection.AddSubAssetCreator(techset::CreatePixelShaderLoaderIW3(memory, searchPath));
|
||||||
|
|
||||||
|
auto loader = techset::CreateTechniqueCompilerIW3(memory, zone, searchPath);
|
||||||
|
|
||||||
|
SECTION("Can compile simple technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/zprepass.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("simple.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("simple.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("zprepass", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "zprepass"s);
|
||||||
|
|
||||||
|
CHECK(technique->flags == 0x4);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "simple.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "simple.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 1);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 1);
|
||||||
|
REQUIRE(pass.perObjArgCount == 1);
|
||||||
|
REQUIRE(pass.stableArgCount == 0);
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can compile advanced technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
normalMapSampler = material.normalMap;
|
||||||
|
colorMapSampler = material.colorMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
baseLightingCoords = constant.baseLightingCoords;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
fogConsts = constant.fogConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
attenuationSampler = sampler.attenuationSampler;
|
||||||
|
normalMapSampler = material.normalMap;
|
||||||
|
colorMapSampler = material.colorMap;
|
||||||
|
modelLightingSampler = sampler.modelLightingSampler;
|
||||||
|
fogColor = constant.fogColor;
|
||||||
|
lightingLookupScale = constant.lightingLookupScale;
|
||||||
|
lightPosition = constant.lightPosition;
|
||||||
|
lightDiffuse = constant.lightDiffuse;
|
||||||
|
lightSpotDir = constant.lightSpotDir;
|
||||||
|
lightSpotFactors = constant.lightSpotFactors;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/example_lit_spot.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("advanced.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("advanced.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("example_lit_spot", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "example_lit_spot"s);
|
||||||
|
|
||||||
|
CHECK(technique->flags == 0x10);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "advanced.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "advanced.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 5);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(vertexDecl.routing.data[1].dest == STREAM_DST_COLOR_0);
|
||||||
|
CHECK(vertexDecl.routing.data[2].source == STREAM_SRC_TEXCOORD_0);
|
||||||
|
CHECK(vertexDecl.routing.data[2].dest == STREAM_DST_TEXCOORD_0);
|
||||||
|
CHECK(vertexDecl.routing.data[3].source == STREAM_SRC_NORMAL);
|
||||||
|
CHECK(vertexDecl.routing.data[3].dest == STREAM_DST_NORMAL);
|
||||||
|
CHECK(vertexDecl.routing.data[4].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(vertexDecl.routing.data[4].dest == STREAM_DST_TEXCOORD_2);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 2);
|
||||||
|
REQUIRE(pass.perObjArgCount == 2);
|
||||||
|
REQUIRE(pass.stableArgCount == 10);
|
||||||
|
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 8);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_BASE_LIGHTING_COORDS);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[2].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[2].dest == 0);
|
||||||
|
CHECK(pass.args[2].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[2].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[2].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[3].type == MTL_ARG_CODE_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[3].dest == 6);
|
||||||
|
CHECK(pass.args[3].u.codeSampler == TEXTURE_SRC_CODE_LIGHT_ATTENUATION);
|
||||||
|
|
||||||
|
CHECK(pass.args[4].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[4].dest == 5);
|
||||||
|
CHECK(pass.args[4].u.nameHash == 0x59D30D0F);
|
||||||
|
|
||||||
|
CHECK(pass.args[5].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[5].dest == 0);
|
||||||
|
CHECK(pass.args[5].u.nameHash == 0xA0AB1041);
|
||||||
|
|
||||||
|
CHECK(pass.args[6].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[6].dest == 21);
|
||||||
|
CHECK(pass.args[6].u.codeConst.index == CONST_SRC_CODE_FOG);
|
||||||
|
CHECK(pass.args[6].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[6].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[7].type == MTL_ARG_CODE_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[7].dest == 4);
|
||||||
|
CHECK(pass.args[7].u.codeSampler == TEXTURE_SRC_CODE_MODEL_LIGHTING);
|
||||||
|
|
||||||
|
CHECK(pass.args[8].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[8].dest == 0);
|
||||||
|
CHECK(pass.args[8].u.codeConst.index == CONST_SRC_CODE_FOG_COLOR);
|
||||||
|
CHECK(pass.args[8].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[8].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[9].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[9].dest == 5);
|
||||||
|
CHECK(pass.args[9].u.codeConst.index == CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE);
|
||||||
|
CHECK(pass.args[9].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[9].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[10].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[10].dest == 6);
|
||||||
|
CHECK(pass.args[10].u.codeConst.index == CONST_SRC_CODE_LIGHT_POSITION);
|
||||||
|
CHECK(pass.args[10].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[10].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[11].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[11].dest == 7);
|
||||||
|
CHECK(pass.args[11].u.codeConst.index == CONST_SRC_CODE_LIGHT_DIFFUSE);
|
||||||
|
CHECK(pass.args[11].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[11].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[12].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[12].dest == 8);
|
||||||
|
CHECK(pass.args[12].u.codeConst.index == CONST_SRC_CODE_LIGHT_SPOTDIR);
|
||||||
|
CHECK(pass.args[12].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[12].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[13].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[13].dest == 9);
|
||||||
|
CHECK(pass.args[13].u.codeConst.index == CONST_SRC_CODE_LIGHT_SPOTFACTORS);
|
||||||
|
CHECK(pass.args[13].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[13].u.codeConst.rowCount == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#include "Game/IW3/Techset/TechsetCompilerIW3.h"
|
||||||
|
|
||||||
|
#include "Game/IW3/IW3.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Techset/TechsetCommon.h"
|
||||||
|
#include "Utils/TestMemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace IW3;
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
MaterialTechnique* GivenTechnique(const std::string& name, AssetCreationContext& context, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = memory.Dup(name.c_str());
|
||||||
|
|
||||||
|
context.AddSubAsset<SubAssetTechnique>(name, technique);
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetCompilerIW3", "[techset][iw3][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("test", 0, GameId::IW3, GamePlatform::PC);
|
||||||
|
AssetCreatorCollection creators(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssets;
|
||||||
|
AssetCreationContext context(zone, &creators, &ignoredAssets);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
TestMemoryManager memory;
|
||||||
|
const auto sut = techset::CreateTechsetCompilerIW3(memory, searchPath);
|
||||||
|
|
||||||
|
SECTION("Sets correct worldVertFormat")
|
||||||
|
{
|
||||||
|
const auto [techsetName, expectedWorldVertFormat] = GENERATE(Catch::Generators::table<const char*, MaterialWorldVertexFormat>({
|
||||||
|
{"default", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"effect_zeqqz943", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"lit_r0c0_t1c1n1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_b0c0_b1c1_b2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_b0c0_b1c1n1x1_b2c2n2v2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0_b1c1_b2c2_b3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0_b1c1_b2c2n2v2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0_b1c1n1s1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0s0_b1c1n1s1_b2c2n2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1s1v1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1v1_b2c2n2s2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0s0_b1c1n1s1_m2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1n1s1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0x0_m1c1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_t0c0n0_b1c1n1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_t0c0n0s0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"mc_lit_sm_r0c0d0_2213939z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_b0c0s0_3f3q946z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_r0c0n0s0o0_qj92q1f8", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_sw4_3d_burning_embers_nuketown_74j6971w", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_unlitdecalblend_add_j26wq580", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(techsetName);
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName(techsetName), "");
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset(techsetName, context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->worldVertFormat == expectedWorldVertFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse simple techset")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit spot":
|
||||||
|
example_lit_spot;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLit = GivenTechnique("example_lit_spot", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 2);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_SPOT] == exampleLit);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse techset with same technique used multiple times")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
"build shadowmap depth":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit":
|
||||||
|
"lit sun":
|
||||||
|
"lit instanced omni shadow":
|
||||||
|
example_lit_spot;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLitOmni = GivenTechnique("example_lit_spot", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 5);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_BUILD_SHADOWMAP_DEPTH] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT] == exampleLitOmni);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_SUN] == exampleLitOmni);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW] == exampleLitOmni);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#include "Game/IW3/Techset/VertexDeclCompilerIW3.h"
|
||||||
|
|
||||||
|
#include "Game/IW3/IW3.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
using namespace IW3;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
TEST_CASE("VertexDeclCompilerIW3", "[iw3][techset][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
|
||||||
|
auto loader = techset::CreateVertexDeclCompilerIW3(memory);
|
||||||
|
|
||||||
|
SECTION("Can create simple vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateSubAsset("pp", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->hasOptionalSource == false);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 1);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can create advanced vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateSubAsset("pt6cc1tt7t1t1n1n", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->hasOptionalSource == true);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 5);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_TEXCOORD_6);
|
||||||
|
CHECK(decl->routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(decl->routing.data[1].dest == STREAM_DST_COLOR_1);
|
||||||
|
CHECK(decl->routing.data[2].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(decl->routing.data[2].dest == STREAM_DST_TEXCOORD_7);
|
||||||
|
CHECK(decl->routing.data[3].source == STREAM_SRC_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[3].dest == STREAM_DST_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[4].source == STREAM_SRC_NORMAL_TRANSFORM_1);
|
||||||
|
CHECK(decl->routing.data[4].dest == STREAM_DST_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjCompilingTests/Game/IW3/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW3/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW3/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW3/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW3/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW3/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW3/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW3/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
@@ -0,0 +1,308 @@
|
|||||||
|
#include "Game/IW4/Techset/TechniqueCompilerIW4.h"
|
||||||
|
|
||||||
|
#include "Game/IW4/IW4.h"
|
||||||
|
#include "Game/IW4/Techset/PixelShaderLoaderIW4.h"
|
||||||
|
#include "Game/IW4/Techset/VertexDeclCompilerIW4.h"
|
||||||
|
#include "Game/IW4/Techset/VertexShaderLoaderIW4.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Shader/ShaderCommon.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
#include "catch2/generators/catch_generators.hpp"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace IW4;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void GivenVertexShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/IW4/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForVertexShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GivenPixelShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/IW4/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForPixelShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechniqueCompilerIW4", "[iw4][techset][compiler]")
|
||||||
|
{
|
||||||
|
|
||||||
|
Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
|
||||||
|
creatorCollection.AddAssetCreator(techset::CreateVertexDeclCompilerIW4(memory));
|
||||||
|
creatorCollection.AddAssetCreator(techset::CreateVertexShaderLoaderIW4(memory, searchPath));
|
||||||
|
creatorCollection.AddAssetCreator(techset::CreatePixelShaderLoaderIW4(memory, searchPath));
|
||||||
|
|
||||||
|
auto loader = techset::CreateTechniqueCompilerIW4(memory, zone, searchPath);
|
||||||
|
|
||||||
|
SECTION("Can compile simple technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/zprepass.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("simple.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("simple.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("zprepass", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "zprepass"s);
|
||||||
|
|
||||||
|
CHECK(technique->flags == 0x204);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "simple.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "simple.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.name == "pp"s);
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 1);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 1);
|
||||||
|
REQUIRE(pass.perObjArgCount == 1);
|
||||||
|
REQUIRE(pass.stableArgCount == 0);
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can compile advanced technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
normalMapSampler = material.normalMap;
|
||||||
|
colorMapSampler = material.colorMap;
|
||||||
|
fogColorLinear = float4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
fogConsts = constant.fogConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
attenuationSampler = sampler.attenuationSampler;
|
||||||
|
normalMapSampler = material.normalMap;
|
||||||
|
colorMapSampler = material.colorMap;
|
||||||
|
lightPosition = constant.lightPosition;
|
||||||
|
lightDiffuse = constant.lightDiffuse;
|
||||||
|
fogColorLinear = float4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/example_lit_omni.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("advanced.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("advanced.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("example_lit_omni", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "example_lit_omni"s);
|
||||||
|
|
||||||
|
CHECK(technique->flags == 0);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "advanced.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "advanced.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 5);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(vertexDecl.routing.data[1].dest == STREAM_DST_COLOR_0);
|
||||||
|
CHECK(vertexDecl.routing.data[2].source == STREAM_SRC_TEXCOORD_0);
|
||||||
|
CHECK(vertexDecl.routing.data[2].dest == STREAM_DST_TEXCOORD_0);
|
||||||
|
CHECK(vertexDecl.routing.data[3].source == STREAM_SRC_NORMAL);
|
||||||
|
CHECK(vertexDecl.routing.data[3].dest == STREAM_DST_NORMAL);
|
||||||
|
CHECK(vertexDecl.routing.data[4].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(vertexDecl.routing.data[4].dest == STREAM_DST_TEXCOORD_2);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 1);
|
||||||
|
REQUIRE(pass.perObjArgCount == 2);
|
||||||
|
REQUIRE(pass.stableArgCount == 6);
|
||||||
|
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[2].type == MTL_ARG_CODE_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[2].dest == 5);
|
||||||
|
CHECK(pass.args[2].u.codeSampler == TEXTURE_SRC_CODE_LIGHT_ATTENUATION);
|
||||||
|
|
||||||
|
CHECK(pass.args[3].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[3].dest == 4);
|
||||||
|
CHECK(pass.args[3].u.nameHash == 0x59D30D0F);
|
||||||
|
|
||||||
|
CHECK(pass.args[4].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[4].dest == 0);
|
||||||
|
CHECK(pass.args[4].u.nameHash == 0xA0AB1041);
|
||||||
|
|
||||||
|
CHECK(pass.args[5].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[5].dest == 21);
|
||||||
|
CHECK(pass.args[5].u.codeConst.index == CONST_SRC_CODE_FOG);
|
||||||
|
CHECK(pass.args[5].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[5].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[6].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[6].dest == 17);
|
||||||
|
CHECK(pass.args[6].u.codeConst.index == CONST_SRC_CODE_LIGHT_POSITION);
|
||||||
|
CHECK(pass.args[6].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[6].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[7].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[7].dest == 18);
|
||||||
|
CHECK(pass.args[7].u.codeConst.index == CONST_SRC_CODE_LIGHT_DIFFUSE);
|
||||||
|
CHECK(pass.args[7].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[7].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[8].type == MTL_ARG_LITERAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[8].dest == 0);
|
||||||
|
CHECK((*pass.args[8].u.literalConst)[0] == 0.0f);
|
||||||
|
CHECK((*pass.args[8].u.literalConst)[1] == 0.0f);
|
||||||
|
CHECK((*pass.args[8].u.literalConst)[2] == 0.0f);
|
||||||
|
CHECK((*pass.args[8].u.literalConst)[3] == 0.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#include "Game/IW4/Techset/TechsetCompilerIW4.h"
|
||||||
|
|
||||||
|
#include "Game/IW4/IW4.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Techset/TechsetCommon.h"
|
||||||
|
#include "Utils/TestMemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace IW4;
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
MaterialTechnique* GivenTechnique(const std::string& name, AssetCreationContext& context, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = memory.Dup(name.c_str());
|
||||||
|
|
||||||
|
context.AddSubAsset<SubAssetTechnique>(name, technique);
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetCompilerIW4", "[techset][iw4][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("test", 0, GameId::IW4, GamePlatform::PC);
|
||||||
|
AssetCreatorCollection creators(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssets;
|
||||||
|
AssetCreationContext context(zone, &creators, &ignoredAssets);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
TestMemoryManager memory;
|
||||||
|
const auto sut = techset::CreateTechsetCompilerIW4(memory, searchPath);
|
||||||
|
|
||||||
|
SECTION("Sets correct worldVertFormat")
|
||||||
|
{
|
||||||
|
const auto [techsetName, expectedWorldVertFormat] = GENERATE(Catch::Generators::table<const char*, MaterialWorldVertexFormat>({
|
||||||
|
{"default", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"effect_zeqqz943", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"lit_r0c0_t1c1n1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_b0c0_b1c1_b2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_b0c0_b1c1n1x1_b2c2n2v2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0_b1c1_b2c2_b3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0_b1c1_b2c2n2v2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0_b1c1n1s1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0s0_b1c1n1s1_b2c2n2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1s1v1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1v1_b2c2n2s2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0s0_b1c1n1s1_m2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1n1s1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0x0_m1c1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_t0c0n0_b1c1n1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_t0c0n0s0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"mc_lit_sm_r0c0d0_2213939z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_b0c0s0_3f3q946z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_r0c0n0s0o0_qj92q1f8", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_sw4_3d_burning_embers_nuketown_74j6971w", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_unlitdecalblend_add_j26wq580", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(techsetName);
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName(techsetName), "");
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset(techsetName, context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->worldVertFormat == expectedWorldVertFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse simple techset")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit omni":
|
||||||
|
example_lit_omni;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLit = GivenTechnique("example_lit_omni", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 2);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_OMNI] == exampleLit);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse techset with same technique used multiple times")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
"build shadowmap depth":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit":
|
||||||
|
"lit sun":
|
||||||
|
"lit instanced omni shadow dfog":
|
||||||
|
example_lit_omni;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLitOmni = GivenTechnique("example_lit_omni", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 5);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_BUILD_SHADOWMAP_DEPTH] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT] == exampleLitOmni);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_SUN] == exampleLitOmni);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW_DFOG] == exampleLitOmni);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "Game/IW4/Techset/VertexDeclCompilerIW4.h"
|
||||||
|
|
||||||
|
#include "Game/IW4/IW4.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
using namespace IW4;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
TEST_CASE("VertexDeclCompilerIW4", "[iw4][techset][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
|
||||||
|
auto loader = techset::CreateVertexDeclCompilerIW4(memory);
|
||||||
|
|
||||||
|
SECTION("Can create simple vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateAsset("pp", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->name == "pp"s);
|
||||||
|
CHECK(decl->hasOptionalSource == false);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 1);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can create advanced vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateAsset("pdcc1tt7t1t1n1n", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->name == "pdcc1tt7t1t1n1n"s);
|
||||||
|
CHECK(decl->hasOptionalSource == true);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 5);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_DEPTH);
|
||||||
|
CHECK(decl->routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(decl->routing.data[1].dest == STREAM_DST_COLOR_1);
|
||||||
|
CHECK(decl->routing.data[2].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(decl->routing.data[2].dest == STREAM_DST_TEXCOORD_7);
|
||||||
|
CHECK(decl->routing.data[3].source == STREAM_SRC_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[3].dest == STREAM_DST_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[4].source == STREAM_SRC_NORMAL_TRANSFORM_1);
|
||||||
|
CHECK(decl->routing.data[4].dest == STREAM_DST_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjCompilingTests/Game/IW4/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW4/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW4/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW4/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW4/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW4/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW4/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW4/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
@@ -0,0 +1,386 @@
|
|||||||
|
#include "Game/IW5/Techset/TechniqueCompilerIW5.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "Game/IW5/Techset/PixelShaderLoaderIW5.h"
|
||||||
|
#include "Game/IW5/Techset/VertexDeclCompilerIW5.h"
|
||||||
|
#include "Game/IW5/Techset/VertexShaderLoaderIW5.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Shader/ShaderCommon.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
#include "catch2/generators/catch_generators.hpp"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void GivenVertexShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/IW5/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForVertexShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GivenPixelShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/IW5/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForPixelShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechniqueCompilerIW5", "[iw5][techset][compiler]")
|
||||||
|
{
|
||||||
|
|
||||||
|
Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
|
||||||
|
creatorCollection.AddAssetCreator(techset::CreateVertexDeclCompilerIW5(memory));
|
||||||
|
creatorCollection.AddAssetCreator(techset::CreateVertexShaderLoaderIW5(memory, searchPath));
|
||||||
|
creatorCollection.AddAssetCreator(techset::CreatePixelShaderLoaderIW5(memory, searchPath));
|
||||||
|
|
||||||
|
auto loader = techset::CreateTechniqueCompilerIW5(memory, zone, searchPath);
|
||||||
|
|
||||||
|
SECTION("Can compile simple technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/zprepass.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("simple.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("simple.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("zprepass", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "zprepass"s);
|
||||||
|
|
||||||
|
CHECK(technique->flags == 0x204);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "simple.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "simple.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.name == "pp"s);
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 1);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 1);
|
||||||
|
REQUIRE(pass.perObjArgCount == 1);
|
||||||
|
REQUIRE(pass.stableArgCount == 0);
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can compile advanced technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
oceanUVAnimParmOctave0 = material.oceanUVAnimParmOctave0;
|
||||||
|
oceanUVAnimParmOctave1 = material.oceanUVAnimParmOctave1;
|
||||||
|
oceanAmplitude = material.oceanAmplitude;
|
||||||
|
oceanDisplacementSampler = material.oceanDisplacementMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
oceanEnvSampler = material.oceanEnvMap;
|
||||||
|
oceanDetailNormalSampler = material.oceanDetailNormalMap;
|
||||||
|
oceanHeightNormalSampler = material.oceanHeightNormalMap;
|
||||||
|
oceanFoamSampler = material.oceanFoamMap;
|
||||||
|
oceanUVAnimParmFoam = material.oceanUVAnimParmFoam;
|
||||||
|
envMapParms = material.envMapParms;
|
||||||
|
oceanFoamParms = material.oceanFoamParms;
|
||||||
|
oceanUVAnimParmOctave0 = material.oceanUVAnimParmOctave0;
|
||||||
|
oceanUVAnimParmOctave1 = material.oceanUVAnimParmOctave1;
|
||||||
|
oceanUVAnimParmDetail1 = material.oceanUVAnimParmDetail1;
|
||||||
|
oceanUVAnimParmDetail0 = material.oceanUVAnimParmDetail0;
|
||||||
|
oceanAmplitude = material.oceanAmplitude;
|
||||||
|
oceanMiscParms = material.oceanMiscParms;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
oceanUVAnimParmOctave0 = material.oceanUVAnimParmOctave0;
|
||||||
|
oceanUVAnimParmOctave1 = material.oceanUVAnimParmOctave1;
|
||||||
|
oceanAmplitude = material.oceanAmplitude;
|
||||||
|
oceanDisplacementSampler = material.oceanDisplacementMap;
|
||||||
|
eyeOffset = constant.eyeOffset;
|
||||||
|
fogConsts = constant.fogConsts;
|
||||||
|
gameTime = constant.gameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
oceanEnvSampler = material.oceanEnvMap;
|
||||||
|
oceanDetailNormalSampler = material.oceanDetailNormalMap;
|
||||||
|
oceanHeightNormalSampler = material.oceanHeightNormalMap;
|
||||||
|
oceanFoamSampler = material.oceanFoamMap;
|
||||||
|
fogColorLinear = constant.fogColorLinear;
|
||||||
|
gameTime = constant.gameTime;
|
||||||
|
oceanUVAnimParmFoam = material.oceanUVAnimParmFoam;
|
||||||
|
envMapParms = material.envMapParms;
|
||||||
|
oceanFoamParms = material.oceanFoamParms;
|
||||||
|
oceanUVAnimParmOctave0 = material.oceanUVAnimParmOctave0;
|
||||||
|
oceanUVAnimParmOctave1 = material.oceanUVAnimParmOctave1;
|
||||||
|
oceanUVAnimParmDetail1 = material.oceanUVAnimParmDetail1;
|
||||||
|
oceanUVAnimParmDetail0 = material.oceanUVAnimParmDetail0;
|
||||||
|
oceanAmplitude = material.oceanAmplitude;
|
||||||
|
oceanMiscParms = material.oceanMiscParms;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/example_lit_sun_shadow.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("advanced.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("advanced.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("example_lit_sun_shadow", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "example_lit_sun_shadow"s);
|
||||||
|
|
||||||
|
CHECK(technique->flags == 0);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "advanced.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "advanced.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 2);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(vertexDecl.routing.data[1].dest == STREAM_DST_COLOR_0);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 1);
|
||||||
|
REQUIRE(pass.perObjArgCount == 1);
|
||||||
|
REQUIRE(pass.stableArgCount == 22);
|
||||||
|
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[2].type == MTL_ARG_MATERIAL_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[2].dest == 59);
|
||||||
|
CHECK(pass.args[2].u.nameHash == 0x470F6C9A);
|
||||||
|
|
||||||
|
CHECK(pass.args[3].type == MTL_ARG_MATERIAL_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[3].dest == 60);
|
||||||
|
CHECK(pass.args[3].u.nameHash == 0x470F6C9B);
|
||||||
|
|
||||||
|
CHECK(pass.args[4].type == MTL_ARG_MATERIAL_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[4].dest == 58);
|
||||||
|
CHECK(pass.args[4].u.nameHash == 0x9D5408FF);
|
||||||
|
|
||||||
|
CHECK(pass.args[5].type == MTL_ARG_MATERIAL_VERTEX_SAMPLER);
|
||||||
|
CHECK(pass.args[5].dest == 2);
|
||||||
|
CHECK(pass.args[5].u.nameHash == 0x29F357AD);
|
||||||
|
|
||||||
|
CHECK(pass.args[6].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[6].dest == 5);
|
||||||
|
CHECK(pass.args[6].u.nameHash == 0x7D392967);
|
||||||
|
|
||||||
|
CHECK(pass.args[7].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[7].dest == 7);
|
||||||
|
CHECK(pass.args[7].u.nameHash == 0x88792E38);
|
||||||
|
|
||||||
|
CHECK(pass.args[8].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[8].dest == 4);
|
||||||
|
CHECK(pass.args[8].u.nameHash == 0x8CB95536);
|
||||||
|
|
||||||
|
CHECK(pass.args[9].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[9].dest == 6);
|
||||||
|
CHECK(pass.args[9].u.nameHash == 0xC096573F);
|
||||||
|
|
||||||
|
CHECK(pass.args[10].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[10].dest == 10);
|
||||||
|
CHECK(pass.args[10].u.codeConst.index == CONST_SRC_CODE_EYEOFFSET);
|
||||||
|
CHECK(pass.args[10].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[10].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[11].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[11].dest == 21);
|
||||||
|
CHECK(pass.args[11].u.codeConst.index == CONST_SRC_CODE_FOG);
|
||||||
|
CHECK(pass.args[11].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[11].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[12].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[12].dest == 22);
|
||||||
|
CHECK(pass.args[12].u.codeConst.index == CONST_SRC_CODE_GAMETIME);
|
||||||
|
CHECK(pass.args[12].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[12].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[13].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[13].dest == 0);
|
||||||
|
CHECK(pass.args[13].u.codeConst.index == CONST_SRC_CODE_FOG_COLOR_LINEAR);
|
||||||
|
CHECK(pass.args[13].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[13].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[14].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[14].dest == 3);
|
||||||
|
CHECK(pass.args[14].u.codeConst.index == CONST_SRC_CODE_GAMETIME);
|
||||||
|
CHECK(pass.args[14].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[14].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[15].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[15].dest == 9);
|
||||||
|
CHECK(pass.args[15].u.nameHash == 0x64E3AE5);
|
||||||
|
|
||||||
|
CHECK(pass.args[16].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[16].dest == 5);
|
||||||
|
CHECK(pass.args[16].u.nameHash == 0x3D9994DC);
|
||||||
|
|
||||||
|
CHECK(pass.args[17].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[17].dest == 22);
|
||||||
|
CHECK(pass.args[17].u.nameHash == 0x3FC0F1DE);
|
||||||
|
|
||||||
|
CHECK(pass.args[18].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[18].dest == 7);
|
||||||
|
CHECK(pass.args[18].u.nameHash == 0x470F6C9A);
|
||||||
|
|
||||||
|
CHECK(pass.args[19].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[19].dest == 8);
|
||||||
|
CHECK(pass.args[19].u.nameHash == 0x470F6C9B);
|
||||||
|
|
||||||
|
CHECK(pass.args[20].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[20].dest == 20);
|
||||||
|
CHECK(pass.args[20].u.nameHash == 0x6373ABA0);
|
||||||
|
|
||||||
|
CHECK(pass.args[21].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[21].dest == 11);
|
||||||
|
CHECK(pass.args[21].u.nameHash == 0x6373ABA1);
|
||||||
|
|
||||||
|
CHECK(pass.args[22].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[22].dest == 6);
|
||||||
|
CHECK(pass.args[22].u.nameHash == 0x9D5408FF);
|
||||||
|
|
||||||
|
CHECK(pass.args[23].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[23].dest == 21);
|
||||||
|
CHECK(pass.args[23].u.nameHash == 0xAA2E7C4F);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#include "Game/IW5/Techset/TechsetCompilerIW5.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Techset/TechsetCommon.h"
|
||||||
|
#include "Utils/TestMemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
MaterialTechnique* GivenTechnique(const std::string& name, AssetCreationContext& context, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = memory.Dup(name.c_str());
|
||||||
|
|
||||||
|
context.AddSubAsset<SubAssetTechnique>(name, technique);
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetCompilerIW5", "[techset][iw5][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("test", 0, GameId::IW5, GamePlatform::PC);
|
||||||
|
AssetCreatorCollection creators(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssets;
|
||||||
|
AssetCreationContext context(zone, &creators, &ignoredAssets);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
TestMemoryManager memory;
|
||||||
|
const auto sut = techset::CreateTechsetCompilerIW5(memory, searchPath);
|
||||||
|
|
||||||
|
SECTION("Sets correct worldVertFormat")
|
||||||
|
{
|
||||||
|
const auto [techsetName, expectedWorldVertFormat] = GENERATE(Catch::Generators::table<const char*, MaterialWorldVertexFormat>({
|
||||||
|
{"default", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"effect_zeqqz943", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"lit_r0c0_t1c1n1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_b0c0_b1c1_b2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_b0c0_b1c1n1x1_b2c2n2v2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0_b1c1_b2c2_b3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0_b1c1_b2c2n2v2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0_b1c1n1s1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0s0_b1c1n1s1_b2c2n2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1s1v1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1v1_b2c2n2s2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0s0_b1c1n1s1_m2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1n1s1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0x0_m1c1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_t0c0n0_b1c1n1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_t0c0n0s0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"mc_lit_sm_r0c0d0_2213939z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_b0c0s0_3f3q946z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_r0c0n0s0o0_qj92q1f8", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_sw4_3d_burning_embers_nuketown_74j6971w", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_unlitdecalblend_add_j26wq580", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(techsetName);
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName(techsetName), "");
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset(techsetName, context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->worldVertFormat == expectedWorldVertFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse simple techset")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit":
|
||||||
|
example_lit;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLit = GivenTechnique("example_lit", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 2);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT] == exampleLit);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse techset with same technique used multiple times")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
"build shadowmap depth":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit":
|
||||||
|
"lit sun":
|
||||||
|
"lit instanced spot shadow cucoloris dfog":
|
||||||
|
example_lit_sun;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLitSun = GivenTechnique("example_lit_sun", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 5);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_BUILD_SHADOWMAP_DEPTH] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT] == exampleLitSun);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_SUN] == exampleLitSun);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW_CUCOLORIS_DFOG] == exampleLitSun);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "Game/IW5/Techset/VertexDeclCompilerIW5.h"
|
||||||
|
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
TEST_CASE("VertexDeclCompilerIW5", "[iw5][techset][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
|
||||||
|
auto loader = techset::CreateVertexDeclCompilerIW5(memory);
|
||||||
|
|
||||||
|
SECTION("Can create simple vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateAsset("pp", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->name == "pp"s);
|
||||||
|
CHECK(decl->hasOptionalSource == false);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 1);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can create advanced vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateAsset("pdcc1tt7t1t1n1n", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->name == "pdcc1tt7t1t1n1n"s);
|
||||||
|
CHECK(decl->hasOptionalSource == true);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 5);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_DEPTH);
|
||||||
|
CHECK(decl->routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(decl->routing.data[1].dest == STREAM_DST_COLOR_1);
|
||||||
|
CHECK(decl->routing.data[2].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(decl->routing.data[2].dest == STREAM_DST_TEXCOORD_7);
|
||||||
|
CHECK(decl->routing.data[3].source == STREAM_SRC_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[3].dest == STREAM_DST_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[4].source == STREAM_SRC_NORMAL_TRANSFORM_1);
|
||||||
|
CHECK(decl->routing.data[4].dest == STREAM_DST_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjCompilingTests/Game/IW5/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW5/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW5/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW5/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW5/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW5/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/IW5/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/IW5/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
@@ -0,0 +1,558 @@
|
|||||||
|
#include "Game/T5/Techset/TechniqueCompilerT5.h"
|
||||||
|
|
||||||
|
#include "Game/T5/T5.h"
|
||||||
|
#include "Game/T5/Techset/PixelShaderLoaderT5.h"
|
||||||
|
#include "Game/T5/Techset/VertexDeclCompilerT5.h"
|
||||||
|
#include "Game/T5/Techset/VertexShaderLoaderT5.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Shader/ShaderCommon.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
#include "catch2/generators/catch_generators.hpp"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace T5;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
void GivenVertexShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/T5/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForVertexShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GivenPixelShaderFile(const std::string& name, MockSearchPath& searchPath)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjCompilingTests/Game/T5/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
std::string data(fileSize, '\0');
|
||||||
|
file.read(data.data(), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
searchPath.AddFileData(shader::GetFileNameForPixelShaderAssetName(name), std::move(data));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechniqueCompilerT5", "[t5][techset][compiler]")
|
||||||
|
{
|
||||||
|
|
||||||
|
Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
|
||||||
|
creatorCollection.AddSubAssetCreator(techset::CreateVertexDeclCompilerT5(memory));
|
||||||
|
creatorCollection.AddSubAssetCreator(techset::CreateVertexShaderLoaderT5(memory, searchPath));
|
||||||
|
creatorCollection.AddSubAssetCreator(techset::CreatePixelShaderLoaderT5(memory, searchPath));
|
||||||
|
|
||||||
|
auto loader = techset::CreateTechniqueCompilerT5(memory, zone, searchPath);
|
||||||
|
|
||||||
|
SECTION("Can compile simple technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/pimp_technique_zprepass_example.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("simple.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("simple.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("pimp_technique_zprepass_example", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "pimp_technique_zprepass_example"s);
|
||||||
|
|
||||||
|
// Usually would be 0x80 set as well, but that's only set when postprocessing with materials
|
||||||
|
CHECK(technique->flags == 0x04);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 0);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "simple.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "simple.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
CHECK(vertexDecl.isLoaded == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 1);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 1);
|
||||||
|
REQUIRE(pass.perObjArgCount == 1);
|
||||||
|
REQUIRE(pass.stableArgCount == 0);
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can compile advanced technique")
|
||||||
|
{
|
||||||
|
const auto [inputName, inputData] = GENERATE(Catch::Generators::table<const char*, const char*>({
|
||||||
|
{"auto-create args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
Flicker_Min = material.Flicker_Min;
|
||||||
|
Flicker_Max = material.Flicker_Max;
|
||||||
|
Seed_Value = material.Seed_Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
Diffuse_MapSampler = material.Diffuse_MapSampler;
|
||||||
|
Diffuse_Map_Damage = material.Diffuse_Map_Damage;
|
||||||
|
Normal_Map_Cracked = material.Normal_Map_Cracked;
|
||||||
|
Reveal_Map = material.Reveal_Map;
|
||||||
|
Specular_Map = material.Specular_Map;
|
||||||
|
Heat_Map = material.Heat_Map;
|
||||||
|
Ember_Scale = material.Ember_Scale;
|
||||||
|
Heat_Direction = material.Heat_Direction;
|
||||||
|
Ember_Direction = material.Ember_Direction;
|
||||||
|
Heat_Scale = material.Heat_Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
{"manual args", R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough";
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
worldMatrix = constant.worldMatrix;
|
||||||
|
baseLightingCoords = constant.baseLightingCoords;
|
||||||
|
viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
shadowLookupMatrix = constant.shadowLookupMatrix;
|
||||||
|
Flicker_Min = material.Flicker_Min;
|
||||||
|
Flicker_Max = material.Flicker_Max;
|
||||||
|
Seed_Value = material.Seed_Value;
|
||||||
|
fogConsts = constant.fogConsts;
|
||||||
|
fogConsts2 = constant.fogConsts2;
|
||||||
|
gameTime = constant.gameTime;
|
||||||
|
sunFogDir = constant.sunFogDir;
|
||||||
|
sunFogColor = constant.sunFogColor;
|
||||||
|
sunFog = constant.sunFog;
|
||||||
|
fogColor = constant.fogColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
Diffuse_MapSampler = material.Diffuse_MapSampler;
|
||||||
|
Diffuse_Map_Damage = material.Diffuse_Map_Damage;
|
||||||
|
Normal_Map_Cracked = material.Normal_Map_Cracked;
|
||||||
|
Reveal_Map = material.Reveal_Map;
|
||||||
|
Specular_Map = material.Specular_Map;
|
||||||
|
Heat_Map = material.Heat_Map;
|
||||||
|
shadowmapSamplerSpot = sampler.shadowmapSamplerSpot;
|
||||||
|
modelLightingSampler = sampler.modelLightingSampler;
|
||||||
|
lightingLookupScale = constant.lightingLookupScale;
|
||||||
|
glightPosXs = constant.glightPosXs;
|
||||||
|
glightPosYs = constant.glightPosYs;
|
||||||
|
glightPosZs = constant.glightPosZs;
|
||||||
|
glightFallOffs = constant.glightFallOffs;
|
||||||
|
glightReds = constant.glightReds;
|
||||||
|
glightGreens = constant.glightGreens;
|
||||||
|
glightBlues = constant.glightBlues;
|
||||||
|
lightPosition = constant.lightPosition;
|
||||||
|
lightDiffuse = constant.lightDiffuse;
|
||||||
|
lightHeroScale = constant.lightHeroScale;
|
||||||
|
lightSpotDir = constant.lightSpotDir;
|
||||||
|
lightSpotFactors = constant.lightSpotFactors;
|
||||||
|
lightAttenuation = constant.lightAttenuation;
|
||||||
|
lightFallOffA = constant.lightFallOffA;
|
||||||
|
spotShadowmapPixelAdjust = constant.spotShadowmapPixelAdjust;
|
||||||
|
gameTime = constant.gameTime;
|
||||||
|
hdrControl0 = constant.hdrControl0;
|
||||||
|
heroLightingR = constant.heroLightingR;
|
||||||
|
heroLightingG = constant.heroLightingG;
|
||||||
|
heroLightingB = constant.heroLightingB;
|
||||||
|
Ember_Scale = material.Ember_Scale;
|
||||||
|
Heat_Direction = material.Heat_Direction;
|
||||||
|
Ember_Direction = material.Ember_Direction;
|
||||||
|
Heat_Scale = material.Heat_Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE"},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(inputName);
|
||||||
|
searchPath.AddFileData("techniques/example_lit_omni_shadow_glight.tech", inputData);
|
||||||
|
|
||||||
|
GivenVertexShaderFile("advanced.hlsl", searchPath);
|
||||||
|
GivenPixelShaderFile("advanced.hlsl", searchPath);
|
||||||
|
|
||||||
|
auto result = loader->CreateSubAsset("example_lit_omni_shadow_glight", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialTechnique>*>(result.GetAssetInfo());
|
||||||
|
const auto* technique = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(technique->name == "example_lit_omni_shadow_glight"s);
|
||||||
|
|
||||||
|
// Usually would be 0x80 set as well, but that's only set when postprocessing with materials
|
||||||
|
CHECK(technique->flags == 0x10);
|
||||||
|
|
||||||
|
REQUIRE(technique->passCount == 1);
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
CHECK(pass.customSamplerFlags == 1);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexShader);
|
||||||
|
CHECK(pass.vertexShader->name == "advanced.hlsl"s);
|
||||||
|
REQUIRE(pass.pixelShader);
|
||||||
|
CHECK(pass.pixelShader->name == "advanced.hlsl"s);
|
||||||
|
|
||||||
|
REQUIRE(pass.vertexDecl);
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
CHECK(vertexDecl.hasOptionalSource == false);
|
||||||
|
CHECK(vertexDecl.isLoaded == false);
|
||||||
|
REQUIRE(vertexDecl.streamCount == 4);
|
||||||
|
CHECK(vertexDecl.routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
CHECK(vertexDecl.routing.data[1].source == STREAM_SRC_TEXCOORD_0);
|
||||||
|
CHECK(vertexDecl.routing.data[1].dest == STREAM_DST_TEXCOORD_0);
|
||||||
|
CHECK(vertexDecl.routing.data[2].source == STREAM_SRC_NORMAL);
|
||||||
|
CHECK(vertexDecl.routing.data[2].dest == STREAM_DST_NORMAL);
|
||||||
|
CHECK(vertexDecl.routing.data[3].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(vertexDecl.routing.data[3].dest == STREAM_DST_TEXCOORD_2);
|
||||||
|
|
||||||
|
REQUIRE(pass.perPrimArgCount == 2);
|
||||||
|
REQUIRE(pass.perObjArgCount == 2);
|
||||||
|
REQUIRE(pass.stableArgCount == 43);
|
||||||
|
|
||||||
|
CHECK(pass.args[0].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[0].dest == 4);
|
||||||
|
CHECK(pass.args[0].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX);
|
||||||
|
CHECK(pass.args[0].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[0].u.codeConst.rowCount == 3);
|
||||||
|
|
||||||
|
CHECK(pass.args[1].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[1].dest == 8);
|
||||||
|
CHECK(pass.args[1].u.codeConst.index == CONST_SRC_CODE_BASE_LIGHTING_COORDS);
|
||||||
|
CHECK(pass.args[1].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[1].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[2].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[2].dest == 0);
|
||||||
|
CHECK(pass.args[2].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX);
|
||||||
|
CHECK(pass.args[2].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[2].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[3].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[3].dest == 24);
|
||||||
|
CHECK(pass.args[3].u.codeConst.index == CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX);
|
||||||
|
CHECK(pass.args[3].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[3].u.codeConst.rowCount == 4);
|
||||||
|
|
||||||
|
CHECK(pass.args[4].type == MTL_ARG_MATERIAL_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[4].dest == 36);
|
||||||
|
CHECK(pass.args[4].u.nameHash == 0x2DDF50E9);
|
||||||
|
|
||||||
|
CHECK(pass.args[5].type == MTL_ARG_MATERIAL_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[5].dest == 23);
|
||||||
|
CHECK(pass.args[5].u.nameHash == 0x2DDF51F7);
|
||||||
|
|
||||||
|
CHECK(pass.args[6].type == MTL_ARG_MATERIAL_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[6].dest == 37);
|
||||||
|
CHECK(pass.args[6].u.nameHash == 0x9CC05A63);
|
||||||
|
|
||||||
|
CHECK(pass.args[7].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[7].dest == 5);
|
||||||
|
CHECK(pass.args[7].u.nameHash == 0x25D709F9);
|
||||||
|
|
||||||
|
CHECK(pass.args[8].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[8].dest == 7);
|
||||||
|
CHECK(pass.args[8].u.nameHash == 0x96F1B7B9);
|
||||||
|
|
||||||
|
CHECK(pass.args[9].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[9].dest == 3);
|
||||||
|
CHECK(pass.args[9].u.nameHash == 0xCFBF1DD6);
|
||||||
|
|
||||||
|
CHECK(pass.args[10].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[10].dest == 2);
|
||||||
|
CHECK(pass.args[10].u.nameHash == 0xCFED92EA);
|
||||||
|
|
||||||
|
CHECK(pass.args[11].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[11].dest == 4);
|
||||||
|
CHECK(pass.args[11].u.nameHash == 0xD28C20CC);
|
||||||
|
|
||||||
|
CHECK(pass.args[12].type == MTL_ARG_MATERIAL_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[12].dest == 6);
|
||||||
|
CHECK(pass.args[12].u.nameHash == 0xE4B9BF3B);
|
||||||
|
|
||||||
|
CHECK(pass.args[13].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[13].dest == 20);
|
||||||
|
CHECK(pass.args[13].u.codeConst.index == CONST_SRC_CODE_FOG);
|
||||||
|
CHECK(pass.args[13].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[13].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[14].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[14].dest == 21);
|
||||||
|
CHECK(pass.args[14].u.codeConst.index == CONST_SRC_CODE_FOG2);
|
||||||
|
CHECK(pass.args[14].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[14].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[15].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[15].dest == 22);
|
||||||
|
CHECK(pass.args[15].u.codeConst.index == CONST_SRC_CODE_GAMETIME);
|
||||||
|
CHECK(pass.args[15].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[15].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[16].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[16].dest == 64);
|
||||||
|
CHECK(pass.args[16].u.codeConst.index == CONST_SRC_CODE_SUN_FOG_DIR);
|
||||||
|
CHECK(pass.args[16].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[16].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[17].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[17].dest == 65);
|
||||||
|
CHECK(pass.args[17].u.codeConst.index == CONST_SRC_CODE_SUN_FOG_COLOR);
|
||||||
|
CHECK(pass.args[17].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[17].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[18].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[18].dest == 66);
|
||||||
|
CHECK(pass.args[18].u.codeConst.index == CONST_SRC_CODE_SUN_FOG);
|
||||||
|
CHECK(pass.args[18].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[18].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[19].type == MTL_ARG_CODE_VERTEX_CONST);
|
||||||
|
CHECK(pass.args[19].dest == 67);
|
||||||
|
CHECK(pass.args[19].u.codeConst.index == CONST_SRC_CODE_FOG_COLOR);
|
||||||
|
CHECK(pass.args[19].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[19].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[20].type == MTL_ARG_CODE_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[20].dest == 1);
|
||||||
|
CHECK(pass.args[20].u.codeSampler == TEXTURE_SRC_CODE_SHADOWMAP_SPOT);
|
||||||
|
|
||||||
|
CHECK(pass.args[21].type == MTL_ARG_CODE_PIXEL_SAMPLER);
|
||||||
|
CHECK(pass.args[21].dest == 11);
|
||||||
|
CHECK(pass.args[21].u.codeSampler == TEXTURE_SRC_CODE_MODEL_LIGHTING);
|
||||||
|
|
||||||
|
CHECK(pass.args[22].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[22].dest == 5);
|
||||||
|
CHECK(pass.args[22].u.codeConst.index == CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE);
|
||||||
|
CHECK(pass.args[22].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[22].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[23].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[23].dest == 6);
|
||||||
|
CHECK(pass.args[23].u.codeConst.index == CONST_SRC_CODE_GLIGHT_POSXS);
|
||||||
|
CHECK(pass.args[23].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[23].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[24].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[24].dest == 7);
|
||||||
|
CHECK(pass.args[24].u.codeConst.index == CONST_SRC_CODE_GLIGHT_POSYS);
|
||||||
|
CHECK(pass.args[24].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[24].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[25].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[25].dest == 8);
|
||||||
|
CHECK(pass.args[25].u.codeConst.index == CONST_SRC_CODE_GLIGHT_POSZS);
|
||||||
|
CHECK(pass.args[25].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[25].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[26].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[26].dest == 9);
|
||||||
|
CHECK(pass.args[26].u.codeConst.index == CONST_SRC_CODE_GLIGHT_FALLOFFS);
|
||||||
|
CHECK(pass.args[26].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[26].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[27].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[27].dest == 10);
|
||||||
|
CHECK(pass.args[27].u.codeConst.index == CONST_SRC_CODE_GLIGHT_REDS);
|
||||||
|
CHECK(pass.args[27].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[27].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[28].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[28].dest == 11);
|
||||||
|
CHECK(pass.args[28].u.codeConst.index == CONST_SRC_CODE_GLIGHT_GREENS);
|
||||||
|
CHECK(pass.args[28].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[28].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[29].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[29].dest == 20);
|
||||||
|
CHECK(pass.args[29].u.codeConst.index == CONST_SRC_CODE_GLIGHT_BLUES);
|
||||||
|
CHECK(pass.args[29].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[29].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[30].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[30].dest == 21);
|
||||||
|
CHECK(pass.args[30].u.codeConst.index == CONST_SRC_CODE_LIGHT_POSITION);
|
||||||
|
CHECK(pass.args[30].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[30].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[31].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[31].dest == 22);
|
||||||
|
CHECK(pass.args[31].u.codeConst.index == CONST_SRC_CODE_LIGHT_DIFFUSE);
|
||||||
|
CHECK(pass.args[31].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[31].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[32].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[32].dest == 23);
|
||||||
|
CHECK(pass.args[32].u.codeConst.index == CONST_SRC_CODE_LIGHT_HERO_SCALE);
|
||||||
|
CHECK(pass.args[32].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[32].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[33].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[33].dest == 24);
|
||||||
|
CHECK(pass.args[33].u.codeConst.index == CONST_SRC_CODE_LIGHT_SPOTDIR);
|
||||||
|
CHECK(pass.args[33].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[33].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[34].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[34].dest == 25);
|
||||||
|
CHECK(pass.args[34].u.codeConst.index == CONST_SRC_CODE_LIGHT_SPOTFACTORS);
|
||||||
|
CHECK(pass.args[34].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[34].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[35].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[35].dest == 26);
|
||||||
|
CHECK(pass.args[35].u.codeConst.index == CONST_SRC_CODE_LIGHT_ATTENUATION);
|
||||||
|
CHECK(pass.args[35].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[35].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[36].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[36].dest == 27);
|
||||||
|
CHECK(pass.args[36].u.codeConst.index == CONST_SRC_CODE_LIGHT_FALLOFF_A);
|
||||||
|
CHECK(pass.args[36].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[36].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[37].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[37].dest == 28);
|
||||||
|
CHECK(pass.args[37].u.codeConst.index == CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST);
|
||||||
|
CHECK(pass.args[37].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[37].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[38].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[38].dest == 29);
|
||||||
|
CHECK(pass.args[38].u.codeConst.index == CONST_SRC_CODE_GAMETIME);
|
||||||
|
CHECK(pass.args[38].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[38].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[39].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[39].dest == 30);
|
||||||
|
CHECK(pass.args[39].u.codeConst.index == CONST_SRC_CODE_HDRCONTROL_0);
|
||||||
|
CHECK(pass.args[39].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[39].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[40].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[40].dest == 31);
|
||||||
|
CHECK(pass.args[40].u.codeConst.index == CONST_SRC_CODE_HERO_LIGHTING_R);
|
||||||
|
CHECK(pass.args[40].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[40].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[41].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[41].dest == 32);
|
||||||
|
CHECK(pass.args[41].u.codeConst.index == CONST_SRC_CODE_HERO_LIGHTING_G);
|
||||||
|
CHECK(pass.args[41].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[41].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[42].type == MTL_ARG_CODE_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[42].dest == 33);
|
||||||
|
CHECK(pass.args[42].u.codeConst.index == CONST_SRC_CODE_HERO_LIGHTING_B);
|
||||||
|
CHECK(pass.args[42].u.codeConst.firstRow == 0);
|
||||||
|
CHECK(pass.args[42].u.codeConst.rowCount == 1);
|
||||||
|
|
||||||
|
CHECK(pass.args[43].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[43].dest == 34);
|
||||||
|
CHECK(pass.args[43].u.nameHash == 0x349EB03A);
|
||||||
|
|
||||||
|
CHECK(pass.args[44].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[44].dest == 36);
|
||||||
|
CHECK(pass.args[44].u.nameHash == 0x978B6822);
|
||||||
|
|
||||||
|
CHECK(pass.args[45].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[45].dest == 35);
|
||||||
|
CHECK(pass.args[45].u.nameHash == 0xF6410F67);
|
||||||
|
|
||||||
|
CHECK(pass.args[46].type == MTL_ARG_MATERIAL_PIXEL_CONST);
|
||||||
|
CHECK(pass.args[46].dest == 37);
|
||||||
|
CHECK(pass.args[46].u.nameHash == 0xFA98347F);
|
||||||
|
}
|
||||||
|
}
|
||||||
149
test/ObjCompilingTests/Game/T5/Techset/TechsetCompilerT5Test.cpp
Normal file
149
test/ObjCompilingTests/Game/T5/Techset/TechsetCompilerT5Test.cpp
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
#include "Game/T5/Techset/TechsetCompilerT5.h"
|
||||||
|
|
||||||
|
#include "Game/T5/T5.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Techset/TechsetCommon.h"
|
||||||
|
#include "Utils/TestMemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <catch2/generators/catch_generators.hpp>
|
||||||
|
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace T5;
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
MaterialTechnique* GivenTechnique(const std::string& name, AssetCreationContext& context, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = memory.Dup(name.c_str());
|
||||||
|
|
||||||
|
context.AddSubAsset<SubAssetTechnique>(name, technique);
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetCompilerT5", "[techset][t5][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("test", 0, GameId::T5, GamePlatform::PC);
|
||||||
|
AssetCreatorCollection creators(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssets;
|
||||||
|
AssetCreationContext context(zone, &creators, &ignoredAssets);
|
||||||
|
MockSearchPath searchPath;
|
||||||
|
TestMemoryManager memory;
|
||||||
|
const auto sut = techset::CreateTechsetCompilerT5(memory, searchPath);
|
||||||
|
|
||||||
|
SECTION("Sets correct worldVertFormat")
|
||||||
|
{
|
||||||
|
const auto [techsetName, expectedWorldVertFormat] = GENERATE(Catch::Generators::table<const char*, MaterialWorldVertexFormat>({
|
||||||
|
{"default", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"effect_zeqqz943", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"lit_r0c0_t1c1n1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_r0c0n0x0_b1c1n1s1v1_b2c2n2x2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_b0c0_b1c1_b2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_b0c0_b1c1n1x1_b2c2n2v2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0_b1c1_b2c2_b3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0_b1c1_b2c2n2v2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0_b1c1n1s1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0s0_b1c1n1s1_b2c2n2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1s1v1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_r0c0n0x0_b1c1v1_b2c2n2s2_m3c3", MTL_WORLDVERT_TEX_4_NRM_2},
|
||||||
|
{"lit_sm_r0c0s0_b1c1n1s1_m2c2", MTL_WORLDVERT_TEX_3_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1", MTL_WORLDVERT_TEX_2_NRM_1},
|
||||||
|
{"lit_sm_r0c0x0_b1c1n1s1_b2c2n2s2", MTL_WORLDVERT_TEX_3_NRM_2},
|
||||||
|
{"lit_sm_r0c0x0_m1c1_m2c2_m3c3", MTL_WORLDVERT_TEX_4_NRM_1},
|
||||||
|
{"lit_sm_t0c0n0_b1c1n1v1", MTL_WORLDVERT_TEX_2_NRM_2},
|
||||||
|
{"lit_sm_t0c0n0s0_b1c1n1_b2c2n2s2v2", MTL_WORLDVERT_TEX_3_NRM_3},
|
||||||
|
{"mc_lit_sm_r0c0d0_2213939z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_b0c0s0_3f3q946z", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_lit_sm_r0c0n0s0o0_qj92q1f8", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_sw4_3d_burning_embers_nuketown_74j6971w", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
{"wpc_unlitdecalblend_add_j26wq580", MTL_WORLDVERT_TEX_1_NRM_1},
|
||||||
|
}));
|
||||||
|
|
||||||
|
CAPTURE(techsetName);
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName(techsetName), "");
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset(techsetName, context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->worldVertFormat == expectedWorldVertFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse simple techset")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit omni shadow glight":
|
||||||
|
example_lit_omni_shadow_glight;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLLitOmniShadowGlight = GivenTechnique("example_lit_omni_shadow_glight", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 2);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_OMNI_SHADOW_GLIGHT] == exampleLLitOmniShadowGlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can parse techset with same technique used multiple times")
|
||||||
|
{
|
||||||
|
searchPath.AddFileData(techset::GetFileNameForTechsetName("simple"), R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
"build shadowmap depth":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit":
|
||||||
|
"lit sun":
|
||||||
|
"lit sun shadow":
|
||||||
|
example_lit_sun_shadow;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
auto* exampleZPrepass = GivenTechnique("example_zprepass", context, memory);
|
||||||
|
auto* exampleLitSunShadow = GivenTechnique("example_lit_sun_shadow", context, memory);
|
||||||
|
|
||||||
|
const auto result = sut->CreateAsset("simple", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* techset = static_cast<MaterialTechniqueSet*>(result.GetAssetInfo()->m_ptr);
|
||||||
|
CHECK(techset->name == "simple"s);
|
||||||
|
CHECK(techset->worldVertFormat == MTL_WORLDVERT_TEX_1_NRM_1);
|
||||||
|
|
||||||
|
size_t techniqueCount = 0;
|
||||||
|
for (auto* technique : techset->techniques)
|
||||||
|
{
|
||||||
|
if (technique)
|
||||||
|
techniqueCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(techniqueCount == 5);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_DEPTH_PREPASS] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_BUILD_SHADOWMAP_DEPTH] == exampleZPrepass);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT] == exampleLitSunShadow);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_SUN] == exampleLitSunShadow);
|
||||||
|
CHECK(techset->techniques[TECHNIQUE_LIT_SUN_SHADOW] == exampleLitSunShadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "Game/T5/Techset/VertexDeclCompilerT5.h"
|
||||||
|
|
||||||
|
#include "Game/T5/T5.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
using namespace T5;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
TEST_CASE("VertexDeclCompilerT5", "[t5][techset][compiler]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::T5, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
AssetCreatorCollection creatorCollection(zone);
|
||||||
|
IgnoredAssetLookup ignoredAssetLookup;
|
||||||
|
AssetCreationContext context(zone, &creatorCollection, &ignoredAssetLookup);
|
||||||
|
|
||||||
|
auto loader = techset::CreateVertexDeclCompilerT5(memory);
|
||||||
|
|
||||||
|
SECTION("Can create simple vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateSubAsset("pp", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->hasOptionalSource == false);
|
||||||
|
CHECK(decl->isLoaded == false);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 1);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can create advanced vertex decl")
|
||||||
|
{
|
||||||
|
auto result = loader->CreateSubAsset("pbcc1tt10t1t1n1n", context);
|
||||||
|
REQUIRE(result.HasBeenSuccessful());
|
||||||
|
|
||||||
|
const auto* assetInfo = reinterpret_cast<XAssetInfo<MaterialVertexDeclaration>*>(result.GetAssetInfo());
|
||||||
|
const auto* decl = assetInfo->Asset();
|
||||||
|
|
||||||
|
CHECK(decl->hasOptionalSource == true);
|
||||||
|
CHECK(decl->isLoaded == false);
|
||||||
|
|
||||||
|
REQUIRE(decl->streamCount == 5);
|
||||||
|
CHECK(decl->routing.data[0].source == STREAM_SRC_POSITION);
|
||||||
|
CHECK(decl->routing.data[0].dest == STREAM_DST_BLENDWEIGHT);
|
||||||
|
CHECK(decl->routing.data[1].source == STREAM_SRC_COLOR);
|
||||||
|
CHECK(decl->routing.data[1].dest == STREAM_DST_COLOR_1);
|
||||||
|
CHECK(decl->routing.data[2].source == STREAM_SRC_TANGENT);
|
||||||
|
CHECK(decl->routing.data[2].dest == STREAM_DST_TEXCOORD_10);
|
||||||
|
CHECK(decl->routing.data[3].source == STREAM_SRC_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[3].dest == STREAM_DST_TEXCOORD_1);
|
||||||
|
CHECK(decl->routing.data[4].source == STREAM_SRC_NORMAL_TRANSFORM_1);
|
||||||
|
CHECK(decl->routing.data[4].dest == STREAM_DST_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjCompilingTests/Game/T5/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/T5/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/T5/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/T5/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/T5/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/T5/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjCompilingTests/Game/T5/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjCompilingTests/Game/T5/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
338
test/ObjWritingTests/Game/IW3/Techset/TechsetDumperIW3Test.cpp
Normal file
338
test/ObjWritingTests/Game/IW3/Techset/TechsetDumperIW3Test.cpp
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
#include "Game/IW3/Techset/TechsetDumperIW3.h"
|
||||||
|
|
||||||
|
#include "Asset/AssetRegistration.h"
|
||||||
|
#include "Game/IW3/IW3.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockOutputPath.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace IW3;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string Trimmed(const std::string& input)
|
||||||
|
{
|
||||||
|
auto start = input.find_first_not_of(" \r\n");
|
||||||
|
if (start == std::string::npos)
|
||||||
|
start = 0;
|
||||||
|
|
||||||
|
auto end = input.find_last_not_of(" \r\n");
|
||||||
|
if (end == std::string::npos)
|
||||||
|
end = input.length();
|
||||||
|
else
|
||||||
|
end = end + 1;
|
||||||
|
|
||||||
|
return input.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialVertexShader* GivenVertexShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/IW3/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialVertexShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4u);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialPixelShader* GivenPixelShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/IW3/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialPixelShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4u);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenDepthPrepassTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_zprepass";
|
||||||
|
technique->flags = 0x4;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 1;
|
||||||
|
pass.perObjArgCount = 1;
|
||||||
|
pass.stableArgCount = 0;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("simple.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("simple.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.streamCount = 1;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(2);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 0;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenLitSpotTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_lit_spot";
|
||||||
|
technique->flags = 0x10;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 2;
|
||||||
|
pass.perObjArgCount = 2;
|
||||||
|
pass.stableArgCount = 10;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("advanced.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("advanced.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.streamCount = 5;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
vertexDecl.routing.data[1].source = STREAM_SRC_COLOR;
|
||||||
|
vertexDecl.routing.data[1].dest = STREAM_DST_COLOR_0;
|
||||||
|
vertexDecl.routing.data[2].source = STREAM_SRC_TEXCOORD_0;
|
||||||
|
vertexDecl.routing.data[2].dest = STREAM_DST_TEXCOORD_0;
|
||||||
|
vertexDecl.routing.data[3].source = STREAM_SRC_NORMAL;
|
||||||
|
vertexDecl.routing.data[3].dest = STREAM_DST_NORMAL;
|
||||||
|
vertexDecl.routing.data[4].source = STREAM_SRC_TANGENT;
|
||||||
|
vertexDecl.routing.data[4].dest = STREAM_DST_TEXCOORD_2;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(14);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 8;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_BASE_LIGHTING_COORDS;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[2].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[2].dest = 0;
|
||||||
|
pass.args[2].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[2].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[2].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[3].type = MTL_ARG_CODE_PIXEL_SAMPLER;
|
||||||
|
pass.args[3].dest = 6;
|
||||||
|
pass.args[3].u.codeSampler = TEXTURE_SRC_CODE_LIGHT_ATTENUATION;
|
||||||
|
|
||||||
|
pass.args[4].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[4].dest = 5;
|
||||||
|
pass.args[4].u.nameHash = 0x59D30D0F;
|
||||||
|
|
||||||
|
pass.args[5].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[5].dest = 0;
|
||||||
|
pass.args[5].u.nameHash = 0xA0AB1041;
|
||||||
|
|
||||||
|
pass.args[6].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[6].dest = 21;
|
||||||
|
pass.args[6].u.codeConst.index = CONST_SRC_CODE_FOG;
|
||||||
|
pass.args[6].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[6].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[7].type = MTL_ARG_CODE_PIXEL_SAMPLER;
|
||||||
|
pass.args[7].dest = 4;
|
||||||
|
pass.args[7].u.codeSampler = TEXTURE_SRC_CODE_MODEL_LIGHTING;
|
||||||
|
|
||||||
|
pass.args[8].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[8].dest = 0;
|
||||||
|
pass.args[8].u.codeConst.index = CONST_SRC_CODE_FOG_COLOR;
|
||||||
|
pass.args[8].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[8].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[9].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[9].dest = 5;
|
||||||
|
pass.args[9].u.codeConst.index = CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE;
|
||||||
|
pass.args[9].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[9].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[10].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[10].dest = 6;
|
||||||
|
pass.args[10].u.codeConst.index = CONST_SRC_CODE_LIGHT_POSITION;
|
||||||
|
pass.args[10].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[10].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[11].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[11].dest = 7;
|
||||||
|
pass.args[11].u.codeConst.index = CONST_SRC_CODE_LIGHT_DIFFUSE;
|
||||||
|
pass.args[11].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[11].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[12].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[12].dest = 8;
|
||||||
|
pass.args[12].u.codeConst.index = CONST_SRC_CODE_LIGHT_SPOTDIR;
|
||||||
|
pass.args[12].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[12].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[13].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[13].dest = 9;
|
||||||
|
pass.args[13].u.codeConst.index = CONST_SRC_CODE_LIGHT_SPOTFACTORS;
|
||||||
|
pass.args[13].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[13].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechniqueSet* GivenTechset(Zone& zone, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* techset = memory.Alloc<MaterialTechniqueSet>();
|
||||||
|
techset->name = "example_techset";
|
||||||
|
techset->worldVertFormat = MTL_WORLDVERT_TEX_4_NRM_2;
|
||||||
|
|
||||||
|
techset->techniques[TECHNIQUE_DEPTH_PREPASS] = GivenDepthPrepassTechnique(memory);
|
||||||
|
techset->techniques[TECHNIQUE_LIT_SPOT] = GivenLitSpotTechnique(memory);
|
||||||
|
|
||||||
|
zone.m_pools.AddAsset(std::make_unique<XAssetInfo<MaterialTechniqueSet>>(ASSET_TYPE_TECHNIQUE_SET, techset->name, techset));
|
||||||
|
return techset;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetDumperIW3", "[iw3][techset][dumper]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::IW3, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
MockSearchPath mockObjPath;
|
||||||
|
MockOutputPath mockOutput;
|
||||||
|
AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt);
|
||||||
|
|
||||||
|
GivenTechset(zone, memory);
|
||||||
|
|
||||||
|
techset::DumperIW3 dumper(true);
|
||||||
|
|
||||||
|
SECTION("Can dump techset")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit spot":
|
||||||
|
example_lit_spot;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techsets/example_techset.techset");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump simple technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
// TECHNIQUE FLAGS: 0x4
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_zprepass.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump advanced technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
// TECHNIQUE FLAGS: 0x10
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: baseLightingCoords = constant.baseLightingCoords;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
// Omitted due to matching accessors: fogConsts = constant.fogConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: attenuationSampler = sampler.attenuationSampler;
|
||||||
|
normalMapSampler = material.normalMap;
|
||||||
|
colorMapSampler = material.colorMap;
|
||||||
|
// Omitted due to matching accessors: modelLightingSampler = sampler.modelLightingSampler;
|
||||||
|
// Omitted due to matching accessors: fogColor = constant.fogColor;
|
||||||
|
// Omitted due to matching accessors: lightingLookupScale = constant.lightingLookupScale;
|
||||||
|
// Omitted due to matching accessors: lightPosition = constant.lightPosition;
|
||||||
|
// Omitted due to matching accessors: lightDiffuse = constant.lightDiffuse;
|
||||||
|
// Omitted due to matching accessors: lightSpotDir = constant.lightSpotDir;
|
||||||
|
// Omitted due to matching accessors: lightSpotFactors = constant.lightSpotFactors;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_lit_spot.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjWritingTests/Game/IW3/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW3/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW3/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW3/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW3/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW3/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW3/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW3/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
310
test/ObjWritingTests/Game/IW4/Techset/TechsetDumperIW4Test.cpp
Normal file
310
test/ObjWritingTests/Game/IW4/Techset/TechsetDumperIW4Test.cpp
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
#include "Game/IW4/Techset/TechsetDumperIW4.h"
|
||||||
|
|
||||||
|
#include "Asset/AssetRegistration.h"
|
||||||
|
#include "Game/IW4/IW4.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockOutputPath.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace IW4;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string Trimmed(const std::string& input)
|
||||||
|
{
|
||||||
|
auto start = input.find_first_not_of(" \r\n");
|
||||||
|
if (start == std::string::npos)
|
||||||
|
start = 0;
|
||||||
|
|
||||||
|
auto end = input.find_last_not_of(" \r\n");
|
||||||
|
if (end == std::string::npos)
|
||||||
|
end = input.length();
|
||||||
|
else
|
||||||
|
end = end + 1;
|
||||||
|
|
||||||
|
return input.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialVertexShader* GivenVertexShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/IW4/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialVertexShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4u);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialPixelShader* GivenPixelShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/IW4/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialPixelShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4u);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenDepthPrepassTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_zprepass";
|
||||||
|
technique->flags = 0x204;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 1;
|
||||||
|
pass.perObjArgCount = 1;
|
||||||
|
pass.stableArgCount = 0;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("simple.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("simple.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.name = "pp";
|
||||||
|
vertexDecl.streamCount = 1;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(2);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 0;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenLitOmniTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_lit_omni";
|
||||||
|
technique->flags = 0;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 1;
|
||||||
|
pass.perObjArgCount = 2;
|
||||||
|
pass.stableArgCount = 6;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("advanced.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("advanced.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.name = "ppcc0t0t0nntt2";
|
||||||
|
vertexDecl.streamCount = 5;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
vertexDecl.routing.data[1].source = STREAM_SRC_COLOR;
|
||||||
|
vertexDecl.routing.data[1].dest = STREAM_DST_COLOR_0;
|
||||||
|
vertexDecl.routing.data[2].source = STREAM_SRC_TEXCOORD_0;
|
||||||
|
vertexDecl.routing.data[2].dest = STREAM_DST_TEXCOORD_0;
|
||||||
|
vertexDecl.routing.data[3].source = STREAM_SRC_NORMAL;
|
||||||
|
vertexDecl.routing.data[3].dest = STREAM_DST_NORMAL;
|
||||||
|
vertexDecl.routing.data[4].source = STREAM_SRC_TANGENT;
|
||||||
|
vertexDecl.routing.data[4].dest = STREAM_DST_TEXCOORD_2;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(9);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 0;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[2].type = MTL_ARG_CODE_PIXEL_SAMPLER;
|
||||||
|
pass.args[2].dest = 5;
|
||||||
|
pass.args[2].u.codeSampler = TEXTURE_SRC_CODE_LIGHT_ATTENUATION;
|
||||||
|
|
||||||
|
pass.args[3].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[3].dest = 4;
|
||||||
|
pass.args[3].u.nameHash = 0x59D30D0F;
|
||||||
|
|
||||||
|
pass.args[4].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[4].dest = 0;
|
||||||
|
pass.args[4].u.nameHash = 0xA0AB1041;
|
||||||
|
|
||||||
|
pass.args[5].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[5].dest = 21;
|
||||||
|
pass.args[5].u.codeConst.index = CONST_SRC_CODE_FOG;
|
||||||
|
pass.args[5].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[5].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[6].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[6].dest = 17;
|
||||||
|
pass.args[6].u.codeConst.index = CONST_SRC_CODE_LIGHT_POSITION;
|
||||||
|
pass.args[6].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[6].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[7].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[7].dest = 18;
|
||||||
|
pass.args[7].u.codeConst.index = CONST_SRC_CODE_LIGHT_DIFFUSE;
|
||||||
|
pass.args[7].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[7].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[8].type = MTL_ARG_LITERAL_PIXEL_CONST;
|
||||||
|
pass.args[8].dest = 0;
|
||||||
|
auto* literalConst = memory.Alloc<float[4]>();
|
||||||
|
(*literalConst)[0] = 0.0f;
|
||||||
|
(*literalConst)[1] = 0.0f;
|
||||||
|
(*literalConst)[2] = 0.0f;
|
||||||
|
(*literalConst)[3] = 0.0f;
|
||||||
|
pass.args[8].u.literalConst = literalConst;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechniqueSet* GivenTechset(Zone& zone, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* techset = memory.Alloc<MaterialTechniqueSet>();
|
||||||
|
techset->name = "example_techset";
|
||||||
|
techset->worldVertFormat = MTL_WORLDVERT_TEX_4_NRM_2;
|
||||||
|
|
||||||
|
techset->techniques[TECHNIQUE_DEPTH_PREPASS] = GivenDepthPrepassTechnique(memory);
|
||||||
|
techset->techniques[TECHNIQUE_LIT_OMNI] = GivenLitOmniTechnique(memory);
|
||||||
|
|
||||||
|
zone.m_pools.AddAsset(std::make_unique<XAssetInfo<MaterialTechniqueSet>>(ASSET_TYPE_TECHNIQUE_SET, techset->name, techset));
|
||||||
|
return techset;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetDumperIW4", "[iw4][techset][dumper]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::IW4, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
MockSearchPath mockObjPath;
|
||||||
|
MockOutputPath mockOutput;
|
||||||
|
AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt);
|
||||||
|
|
||||||
|
GivenTechset(zone, memory);
|
||||||
|
|
||||||
|
techset::DumperIW4 dumper(true);
|
||||||
|
|
||||||
|
SECTION("Can dump techset")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit omni":
|
||||||
|
example_lit_omni;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techsets/example_techset.techset");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump simple technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
// TECHNIQUE FLAGS: 0x4
|
||||||
|
// TECHNIQUE FLAGS: 0x200
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_zprepass.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump advanced technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
// Omitted due to matching accessors: fogConsts = constant.fogConsts;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: attenuationSampler = sampler.attenuationSampler;
|
||||||
|
normalMapSampler = material.normalMap;
|
||||||
|
colorMapSampler = material.colorMap;
|
||||||
|
// Omitted due to matching accessors: lightPosition = constant.lightPosition;
|
||||||
|
// Omitted due to matching accessors: lightDiffuse = constant.lightDiffuse;
|
||||||
|
fogColorLinear = float4(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_lit_omni.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjWritingTests/Game/IW4/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW4/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW4/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW4/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW4/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW4/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW4/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW4/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
375
test/ObjWritingTests/Game/IW5/Techset/TechsetDumperIW5Test.cpp
Normal file
375
test/ObjWritingTests/Game/IW5/Techset/TechsetDumperIW5Test.cpp
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
#include "Game/IW5/Techset/TechsetDumperIW5.h"
|
||||||
|
|
||||||
|
#include "Asset/AssetRegistration.h"
|
||||||
|
#include "Game/IW5/IW5.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockOutputPath.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace IW5;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string Trimmed(const std::string& input)
|
||||||
|
{
|
||||||
|
auto start = input.find_first_not_of(" \r\n");
|
||||||
|
if (start == std::string::npos)
|
||||||
|
start = 0;
|
||||||
|
|
||||||
|
auto end = input.find_last_not_of(" \r\n");
|
||||||
|
if (end == std::string::npos)
|
||||||
|
end = input.length();
|
||||||
|
else
|
||||||
|
end = end + 1;
|
||||||
|
|
||||||
|
return input.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialVertexShader* GivenVertexShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/IW5/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialVertexShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4u);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialPixelShader* GivenPixelShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/IW5/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialPixelShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4u);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenDepthPrepassTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_zprepass";
|
||||||
|
technique->flags = 0x204;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 1;
|
||||||
|
pass.perObjArgCount = 1;
|
||||||
|
pass.stableArgCount = 0;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("simple.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("simple.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.name = "pp";
|
||||||
|
vertexDecl.streamCount = 1;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(2);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 0;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenLitTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_lit";
|
||||||
|
technique->flags = 0;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 1;
|
||||||
|
pass.perObjArgCount = 1;
|
||||||
|
pass.stableArgCount = 22;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("advanced.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("advanced.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.name = "ppcc0";
|
||||||
|
vertexDecl.streamCount = 2;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
vertexDecl.routing.data[1].source = STREAM_SRC_COLOR;
|
||||||
|
vertexDecl.routing.data[1].dest = STREAM_DST_COLOR_0;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(24);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX0;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 0;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[2].type = MTL_ARG_MATERIAL_VERTEX_CONST;
|
||||||
|
pass.args[2].dest = 59;
|
||||||
|
pass.args[2].u.nameHash = 0x470F6C9A;
|
||||||
|
|
||||||
|
pass.args[3].type = MTL_ARG_MATERIAL_VERTEX_CONST;
|
||||||
|
pass.args[3].dest = 60;
|
||||||
|
pass.args[3].u.nameHash = 0x470F6C9B;
|
||||||
|
|
||||||
|
pass.args[4].type = MTL_ARG_MATERIAL_VERTEX_CONST;
|
||||||
|
pass.args[4].dest = 58;
|
||||||
|
pass.args[4].u.nameHash = 0x9D5408FF;
|
||||||
|
|
||||||
|
pass.args[5].type = MTL_ARG_MATERIAL_VERTEX_SAMPLER;
|
||||||
|
pass.args[5].dest = 2;
|
||||||
|
pass.args[5].u.nameHash = 0x29F357AD;
|
||||||
|
|
||||||
|
pass.args[6].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[6].dest = 5;
|
||||||
|
pass.args[6].u.nameHash = 0x7D392967;
|
||||||
|
|
||||||
|
pass.args[7].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[7].dest = 7;
|
||||||
|
pass.args[7].u.nameHash = 0x88792E38;
|
||||||
|
|
||||||
|
pass.args[8].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[8].dest = 4;
|
||||||
|
pass.args[8].u.nameHash = 0x8CB95536;
|
||||||
|
|
||||||
|
pass.args[9].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[9].dest = 6;
|
||||||
|
pass.args[9].u.nameHash = 0xC096573F;
|
||||||
|
|
||||||
|
pass.args[10].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[10].dest = 10;
|
||||||
|
pass.args[10].u.codeConst.index = CONST_SRC_CODE_EYEOFFSET;
|
||||||
|
pass.args[10].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[10].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[11].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[11].dest = 21;
|
||||||
|
pass.args[11].u.codeConst.index = CONST_SRC_CODE_FOG;
|
||||||
|
pass.args[11].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[11].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[12].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[12].dest = 22;
|
||||||
|
pass.args[12].u.codeConst.index = CONST_SRC_CODE_GAMETIME;
|
||||||
|
pass.args[12].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[12].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[13].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[13].dest = 0;
|
||||||
|
pass.args[13].u.codeConst.index = CONST_SRC_CODE_FOG_COLOR_LINEAR;
|
||||||
|
pass.args[13].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[13].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[14].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[14].dest = 3;
|
||||||
|
pass.args[14].u.codeConst.index = CONST_SRC_CODE_GAMETIME;
|
||||||
|
pass.args[14].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[14].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[15].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[15].dest = 9;
|
||||||
|
pass.args[15].u.nameHash = 0x64E3AE5;
|
||||||
|
|
||||||
|
pass.args[16].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[16].dest = 5;
|
||||||
|
pass.args[16].u.nameHash = 0x3D9994DC;
|
||||||
|
|
||||||
|
pass.args[17].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[17].dest = 22;
|
||||||
|
pass.args[17].u.nameHash = 0x3FC0F1DE;
|
||||||
|
|
||||||
|
pass.args[18].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[18].dest = 7;
|
||||||
|
pass.args[18].u.nameHash = 0x470F6C9A;
|
||||||
|
|
||||||
|
pass.args[19].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[19].dest = 8;
|
||||||
|
pass.args[19].u.nameHash = 0x470F6C9B;
|
||||||
|
|
||||||
|
pass.args[20].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[20].dest = 20;
|
||||||
|
pass.args[20].u.nameHash = 0x6373ABA0;
|
||||||
|
|
||||||
|
pass.args[21].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[21].dest = 11;
|
||||||
|
pass.args[21].u.nameHash = 0x6373ABA1;
|
||||||
|
|
||||||
|
pass.args[22].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[22].dest = 6;
|
||||||
|
pass.args[22].u.nameHash = 0x9D5408FF;
|
||||||
|
|
||||||
|
pass.args[23].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[23].dest = 21;
|
||||||
|
pass.args[23].u.nameHash = 0xAA2E7C4F;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechniqueSet* GivenTechset(Zone& zone, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* techset = memory.Alloc<MaterialTechniqueSet>();
|
||||||
|
techset->name = "example_techset";
|
||||||
|
techset->worldVertFormat = MTL_WORLDVERT_TEX_4_NRM_2;
|
||||||
|
|
||||||
|
techset->techniques[TECHNIQUE_DEPTH_PREPASS] = GivenDepthPrepassTechnique(memory);
|
||||||
|
techset->techniques[TECHNIQUE_LIT] = GivenLitTechnique(memory);
|
||||||
|
|
||||||
|
zone.m_pools.AddAsset(std::make_unique<XAssetInfo<MaterialTechniqueSet>>(ASSET_TYPE_TECHNIQUE_SET, techset->name, techset));
|
||||||
|
return techset;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetDumperIW5", "[iw5][techset][dumper]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::IW5, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
MockSearchPath mockObjPath;
|
||||||
|
MockOutputPath mockOutput;
|
||||||
|
AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt);
|
||||||
|
|
||||||
|
GivenTechset(zone, memory);
|
||||||
|
|
||||||
|
techset::DumperIW5 dumper(true);
|
||||||
|
|
||||||
|
SECTION("Can dump techset")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit":
|
||||||
|
example_lit;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techsets/example_techset.techset");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump simple technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
// TECHNIQUE FLAGS: 0x4
|
||||||
|
// TECHNIQUE FLAGS: 0x200
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_zprepass.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump advanced technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
oceanUVAnimParmOctave0 = material.oceanUVAnimParmOctave0;
|
||||||
|
oceanUVAnimParmOctave1 = material.oceanUVAnimParmOctave1;
|
||||||
|
oceanAmplitude = material.oceanAmplitude;
|
||||||
|
oceanDisplacementSampler = material.oceanDisplacementMap;
|
||||||
|
// Omitted due to matching accessors: eyeOffset = constant.eyeOffset;
|
||||||
|
// Omitted due to matching accessors: fogConsts = constant.fogConsts;
|
||||||
|
// Omitted due to matching accessors: gameTime = constant.gameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
oceanEnvSampler = material.oceanEnvMap;
|
||||||
|
oceanDetailNormalSampler = material.oceanDetailNormalMap;
|
||||||
|
oceanHeightNormalSampler = material.oceanHeightNormalMap;
|
||||||
|
oceanFoamSampler = material.oceanFoamMap;
|
||||||
|
// Omitted due to matching accessors: fogColorLinear = constant.fogColorLinear;
|
||||||
|
// Omitted due to matching accessors: gameTime = constant.gameTime;
|
||||||
|
oceanUVAnimParmFoam = material.oceanUVAnimParmFoam;
|
||||||
|
envMapParms = material.envMapParms;
|
||||||
|
oceanFoamParms = material.oceanFoamParms;
|
||||||
|
oceanUVAnimParmOctave0 = material.oceanUVAnimParmOctave0;
|
||||||
|
oceanUVAnimParmOctave1 = material.oceanUVAnimParmOctave1;
|
||||||
|
oceanUVAnimParmDetail1 = material.oceanUVAnimParmDetail1;
|
||||||
|
oceanUVAnimParmDetail0 = material.oceanUVAnimParmDetail0;
|
||||||
|
oceanAmplitude = material.oceanAmplitude;
|
||||||
|
oceanMiscParms = material.oceanMiscParms;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.color[0] = code.color;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_lit.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjWritingTests/Game/IW5/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW5/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW5/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW5/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW5/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW5/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/IW5/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/IW5/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
549
test/ObjWritingTests/Game/T5/Techset/TechsetDumperT5Test.cpp
Normal file
549
test/ObjWritingTests/Game/T5/Techset/TechsetDumperT5Test.cpp
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
#include "Game/T5/Techset/TechsetDumperT5.h"
|
||||||
|
|
||||||
|
#include "Asset/AssetRegistration.h"
|
||||||
|
#include "Game/T5/GameT5.h"
|
||||||
|
#include "OatTestPaths.h"
|
||||||
|
#include "SearchPath/MockOutputPath.h"
|
||||||
|
#include "SearchPath/MockSearchPath.h"
|
||||||
|
#include "Utils/MemoryManager.h"
|
||||||
|
|
||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace T5;
|
||||||
|
using namespace Catch;
|
||||||
|
using namespace std::literals;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string Trimmed(const std::string& input)
|
||||||
|
{
|
||||||
|
auto start = input.find_first_not_of(" \r\n");
|
||||||
|
if (start == std::string::npos)
|
||||||
|
start = 0;
|
||||||
|
|
||||||
|
auto end = input.find_last_not_of(" \r\n");
|
||||||
|
if (end == std::string::npos)
|
||||||
|
end = input.length();
|
||||||
|
else
|
||||||
|
end = end + 1;
|
||||||
|
|
||||||
|
return input.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialVertexShader* GivenVertexShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/T5/Techset" / std::format("vs_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialVertexShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxVertexShaderLoadDef::programSize)>(fileSize / 4);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialPixelShader* GivenPixelShader(const std::string& name, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
const auto filePath = oat::paths::GetTestDirectory() / "ObjWritingTests/Game/T5/Techset" / std::format("ps_{}.cso", name);
|
||||||
|
const auto fileSize = static_cast<size_t>(fs::file_size(filePath));
|
||||||
|
|
||||||
|
std::ifstream file(filePath, std::ios::binary);
|
||||||
|
REQUIRE(file.is_open());
|
||||||
|
|
||||||
|
auto* shader = memory.Alloc<MaterialPixelShader>();
|
||||||
|
shader->name = memory.Dup(name.c_str());
|
||||||
|
shader->prog.loadDef.program = reinterpret_cast<unsigned*>(memory.Alloc<char>(fileSize));
|
||||||
|
shader->prog.loadDef.programSize = static_cast<decltype(GfxPixelShaderLoadDef::programSize)>(fileSize / 4);
|
||||||
|
file.read(reinterpret_cast<char*>(shader->prog.loadDef.program), fileSize);
|
||||||
|
REQUIRE(file.gcount() == static_cast<std::streamsize>(fileSize));
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenDepthPrepassTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_zprepass";
|
||||||
|
technique->flags = 0x84;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 1;
|
||||||
|
pass.perObjArgCount = 1;
|
||||||
|
pass.stableArgCount = 0;
|
||||||
|
pass.customSamplerFlags = 0;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("simple.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("simple.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.streamCount = 1;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.isLoaded = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(2);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 0;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechnique* GivenLitOmniShadowGlightTechnique(MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* technique = memory.Alloc<MaterialTechnique>();
|
||||||
|
technique->name = "example_lit_omni_shadow_glight";
|
||||||
|
technique->flags = 0x90;
|
||||||
|
technique->passCount = 1;
|
||||||
|
|
||||||
|
auto& pass = technique->passArray[0];
|
||||||
|
pass.perPrimArgCount = 2;
|
||||||
|
pass.perObjArgCount = 2;
|
||||||
|
pass.stableArgCount = 43;
|
||||||
|
pass.customSamplerFlags = 1;
|
||||||
|
|
||||||
|
pass.vertexShader = GivenVertexShader("advanced.hlsl", memory);
|
||||||
|
pass.pixelShader = GivenPixelShader("advanced.hlsl", memory);
|
||||||
|
|
||||||
|
pass.vertexDecl = memory.Alloc<MaterialVertexDeclaration>();
|
||||||
|
auto& vertexDecl = *pass.vertexDecl;
|
||||||
|
vertexDecl.streamCount = 4;
|
||||||
|
vertexDecl.hasOptionalSource = false;
|
||||||
|
vertexDecl.isLoaded = false;
|
||||||
|
vertexDecl.routing.data[0].source = STREAM_SRC_POSITION;
|
||||||
|
vertexDecl.routing.data[0].dest = STREAM_DST_POSITION;
|
||||||
|
vertexDecl.routing.data[1].source = STREAM_SRC_TEXCOORD_0;
|
||||||
|
vertexDecl.routing.data[1].dest = STREAM_DST_TEXCOORD_0;
|
||||||
|
vertexDecl.routing.data[2].source = STREAM_SRC_NORMAL;
|
||||||
|
vertexDecl.routing.data[2].dest = STREAM_DST_NORMAL;
|
||||||
|
vertexDecl.routing.data[3].source = STREAM_SRC_TANGENT;
|
||||||
|
vertexDecl.routing.data[3].dest = STREAM_DST_TEXCOORD_2;
|
||||||
|
|
||||||
|
pass.args = memory.Alloc<MaterialShaderArgument>(47);
|
||||||
|
pass.args[0].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[0].dest = 4;
|
||||||
|
pass.args[0].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_WORLD_MATRIX;
|
||||||
|
pass.args[0].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[0].u.codeConst.rowCount = 3;
|
||||||
|
|
||||||
|
pass.args[1].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[1].dest = 8;
|
||||||
|
pass.args[1].u.codeConst.index = CONST_SRC_CODE_BASE_LIGHTING_COORDS;
|
||||||
|
pass.args[1].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[1].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[2].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[2].dest = 0;
|
||||||
|
pass.args[2].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_VIEW_PROJECTION_MATRIX;
|
||||||
|
pass.args[2].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[2].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[3].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[3].dest = 24;
|
||||||
|
pass.args[3].u.codeConst.index = CONST_SRC_CODE_TRANSPOSE_SHADOW_LOOKUP_MATRIX;
|
||||||
|
pass.args[3].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[3].u.codeConst.rowCount = 4;
|
||||||
|
|
||||||
|
pass.args[4].type = MTL_ARG_MATERIAL_VERTEX_CONST;
|
||||||
|
pass.args[4].dest = 36;
|
||||||
|
pass.args[4].u.nameHash = 0x2DDF50E9;
|
||||||
|
|
||||||
|
pass.args[5].type = MTL_ARG_MATERIAL_VERTEX_CONST;
|
||||||
|
pass.args[5].dest = 23;
|
||||||
|
pass.args[5].u.nameHash = 0x2DDF51F7;
|
||||||
|
|
||||||
|
pass.args[6].type = MTL_ARG_MATERIAL_VERTEX_CONST;
|
||||||
|
pass.args[6].dest = 37;
|
||||||
|
pass.args[6].u.nameHash = 0x9CC05A63;
|
||||||
|
|
||||||
|
pass.args[7].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[7].dest = 5;
|
||||||
|
pass.args[7].u.nameHash = 0x25D709F9;
|
||||||
|
|
||||||
|
pass.args[8].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[8].dest = 7;
|
||||||
|
pass.args[8].u.nameHash = 0x96F1B7B9;
|
||||||
|
|
||||||
|
pass.args[9].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[9].dest = 3;
|
||||||
|
pass.args[9].u.nameHash = 0xCFBF1DD6;
|
||||||
|
|
||||||
|
pass.args[10].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[10].dest = 2;
|
||||||
|
pass.args[10].u.nameHash = 0xCFED92EA;
|
||||||
|
|
||||||
|
pass.args[11].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[11].dest = 4;
|
||||||
|
pass.args[11].u.nameHash = 0xD28C20CC;
|
||||||
|
|
||||||
|
pass.args[12].type = MTL_ARG_MATERIAL_PIXEL_SAMPLER;
|
||||||
|
pass.args[12].dest = 6;
|
||||||
|
pass.args[12].u.nameHash = 0xE4B9BF3B;
|
||||||
|
|
||||||
|
pass.args[13].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[13].dest = 20;
|
||||||
|
pass.args[13].u.codeConst.index = CONST_SRC_CODE_FOG;
|
||||||
|
pass.args[13].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[13].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[14].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[14].dest = 21;
|
||||||
|
pass.args[14].u.codeConst.index = CONST_SRC_CODE_FOG2;
|
||||||
|
pass.args[14].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[14].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[15].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[15].dest = 22;
|
||||||
|
pass.args[15].u.codeConst.index = CONST_SRC_CODE_GAMETIME;
|
||||||
|
pass.args[15].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[15].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[16].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[16].dest = 64;
|
||||||
|
pass.args[16].u.codeConst.index = CONST_SRC_CODE_SUN_FOG_DIR;
|
||||||
|
pass.args[16].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[16].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[17].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[17].dest = 65;
|
||||||
|
pass.args[17].u.codeConst.index = CONST_SRC_CODE_SUN_FOG_COLOR;
|
||||||
|
pass.args[17].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[17].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[18].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[18].dest = 66;
|
||||||
|
pass.args[18].u.codeConst.index = CONST_SRC_CODE_SUN_FOG;
|
||||||
|
pass.args[18].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[18].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[19].type = MTL_ARG_CODE_VERTEX_CONST;
|
||||||
|
pass.args[19].dest = 67;
|
||||||
|
pass.args[19].u.codeConst.index = CONST_SRC_CODE_FOG_COLOR;
|
||||||
|
pass.args[19].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[19].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[20].type = MTL_ARG_CODE_PIXEL_SAMPLER;
|
||||||
|
pass.args[20].dest = 1;
|
||||||
|
pass.args[20].u.codeSampler = TEXTURE_SRC_CODE_SHADOWMAP_SPOT;
|
||||||
|
|
||||||
|
pass.args[21].type = MTL_ARG_CODE_PIXEL_SAMPLER;
|
||||||
|
pass.args[21].dest = 11;
|
||||||
|
pass.args[21].u.codeSampler = TEXTURE_SRC_CODE_MODEL_LIGHTING;
|
||||||
|
|
||||||
|
pass.args[22].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[22].dest = 5;
|
||||||
|
pass.args[22].u.codeConst.index = CONST_SRC_CODE_LIGHTING_LOOKUP_SCALE;
|
||||||
|
pass.args[22].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[22].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[23].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[23].dest = 6;
|
||||||
|
pass.args[23].u.codeConst.index = CONST_SRC_CODE_GLIGHT_POSXS;
|
||||||
|
pass.args[23].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[23].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[24].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[24].dest = 7;
|
||||||
|
pass.args[24].u.codeConst.index = CONST_SRC_CODE_GLIGHT_POSYS;
|
||||||
|
pass.args[24].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[24].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[25].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[25].dest = 8;
|
||||||
|
pass.args[25].u.codeConst.index = CONST_SRC_CODE_GLIGHT_POSZS;
|
||||||
|
pass.args[25].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[25].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[26].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[26].dest = 9;
|
||||||
|
pass.args[26].u.codeConst.index = CONST_SRC_CODE_GLIGHT_FALLOFFS;
|
||||||
|
pass.args[26].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[26].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[27].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[27].dest = 10;
|
||||||
|
pass.args[27].u.codeConst.index = CONST_SRC_CODE_GLIGHT_REDS;
|
||||||
|
pass.args[27].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[27].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[28].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[28].dest = 11;
|
||||||
|
pass.args[28].u.codeConst.index = CONST_SRC_CODE_GLIGHT_GREENS;
|
||||||
|
pass.args[28].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[28].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[29].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[29].dest = 20;
|
||||||
|
pass.args[29].u.codeConst.index = CONST_SRC_CODE_GLIGHT_BLUES;
|
||||||
|
pass.args[29].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[29].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[30].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[30].dest = 21;
|
||||||
|
pass.args[30].u.codeConst.index = CONST_SRC_CODE_LIGHT_POSITION;
|
||||||
|
pass.args[30].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[30].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[31].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[31].dest = 22;
|
||||||
|
pass.args[31].u.codeConst.index = CONST_SRC_CODE_LIGHT_DIFFUSE;
|
||||||
|
pass.args[31].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[31].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[32].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[32].dest = 23;
|
||||||
|
pass.args[32].u.codeConst.index = CONST_SRC_CODE_LIGHT_HERO_SCALE;
|
||||||
|
pass.args[32].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[32].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[33].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[33].dest = 24;
|
||||||
|
pass.args[33].u.codeConst.index = CONST_SRC_CODE_LIGHT_SPOTDIR;
|
||||||
|
pass.args[33].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[33].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[34].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[34].dest = 25;
|
||||||
|
pass.args[34].u.codeConst.index = CONST_SRC_CODE_LIGHT_SPOTFACTORS;
|
||||||
|
pass.args[34].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[34].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[35].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[35].dest = 26;
|
||||||
|
pass.args[35].u.codeConst.index = CONST_SRC_CODE_LIGHT_ATTENUATION;
|
||||||
|
pass.args[35].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[35].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[36].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[36].dest = 27;
|
||||||
|
pass.args[36].u.codeConst.index = CONST_SRC_CODE_LIGHT_FALLOFF_A;
|
||||||
|
pass.args[36].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[36].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[37].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[37].dest = 28;
|
||||||
|
pass.args[37].u.codeConst.index = CONST_SRC_CODE_SPOT_SHADOWMAP_PIXEL_ADJUST;
|
||||||
|
pass.args[37].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[37].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[38].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[38].dest = 29;
|
||||||
|
pass.args[38].u.codeConst.index = CONST_SRC_CODE_GAMETIME;
|
||||||
|
pass.args[38].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[38].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[39].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[39].dest = 30;
|
||||||
|
pass.args[39].u.codeConst.index = CONST_SRC_CODE_HDRCONTROL_0;
|
||||||
|
pass.args[39].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[39].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[40].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[40].dest = 31;
|
||||||
|
pass.args[40].u.codeConst.index = CONST_SRC_CODE_HERO_LIGHTING_R;
|
||||||
|
pass.args[40].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[40].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[41].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[41].dest = 32;
|
||||||
|
pass.args[41].u.codeConst.index = CONST_SRC_CODE_HERO_LIGHTING_G;
|
||||||
|
pass.args[41].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[41].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[42].type = MTL_ARG_CODE_PIXEL_CONST;
|
||||||
|
pass.args[42].dest = 33;
|
||||||
|
pass.args[42].u.codeConst.index = CONST_SRC_CODE_HERO_LIGHTING_B;
|
||||||
|
pass.args[42].u.codeConst.firstRow = 0;
|
||||||
|
pass.args[42].u.codeConst.rowCount = 1;
|
||||||
|
|
||||||
|
pass.args[43].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[43].dest = 34;
|
||||||
|
pass.args[43].u.nameHash = 0x349EB03A;
|
||||||
|
|
||||||
|
pass.args[44].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[44].dest = 36;
|
||||||
|
pass.args[44].u.nameHash = 0x978B6822;
|
||||||
|
|
||||||
|
pass.args[45].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[45].dest = 35;
|
||||||
|
pass.args[45].u.nameHash = 0xF6410F67;
|
||||||
|
|
||||||
|
pass.args[46].type = MTL_ARG_MATERIAL_PIXEL_CONST;
|
||||||
|
pass.args[46].dest = 37;
|
||||||
|
pass.args[46].u.nameHash = 0xFA98347F;
|
||||||
|
|
||||||
|
return technique;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTechniqueSet* GivenTechset(Zone& zone, MemoryManager& memory)
|
||||||
|
{
|
||||||
|
auto* techset = memory.Alloc<MaterialTechniqueSet>();
|
||||||
|
techset->name = "example_techset";
|
||||||
|
techset->worldVertFormat = MTL_WORLDVERT_TEX_4_NRM_2;
|
||||||
|
|
||||||
|
techset->techniques[TECHNIQUE_DEPTH_PREPASS] = GivenDepthPrepassTechnique(memory);
|
||||||
|
techset->techniques[TECHNIQUE_LIT_OMNI_SHADOW_GLIGHT] = GivenLitOmniShadowGlightTechnique(memory);
|
||||||
|
|
||||||
|
zone.m_pools.AddAsset(std::make_unique<XAssetInfo<MaterialTechniqueSet>>(ASSET_TYPE_TECHNIQUE_SET, techset->name, techset));
|
||||||
|
return techset;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST_CASE("TechsetDumperT5", "[t5][techset][dumper]")
|
||||||
|
{
|
||||||
|
Zone zone("MockZone", 0, GameId::T6, GamePlatform::PC);
|
||||||
|
zone.Register();
|
||||||
|
|
||||||
|
MemoryManager memory;
|
||||||
|
MockSearchPath mockObjPath;
|
||||||
|
MockOutputPath mockOutput;
|
||||||
|
AssetDumpingContext context(zone, "", mockOutput, mockObjPath, std::nullopt);
|
||||||
|
|
||||||
|
GivenTechset(zone, memory);
|
||||||
|
|
||||||
|
techset::DumperT5 dumper(true);
|
||||||
|
|
||||||
|
SECTION("Can dump techset")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHSET(
|
||||||
|
"depth prepass":
|
||||||
|
example_zprepass;
|
||||||
|
|
||||||
|
"lit omni shadow glight":
|
||||||
|
example_lit_omni_shadow_glight;
|
||||||
|
)TECHSET");
|
||||||
|
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techsets/example_techset.techset");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump simple technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
// TECHNIQUE FLAGS: 0x4
|
||||||
|
// TECHNIQUE FLAGS: 0x80
|
||||||
|
{
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "simple.hlsl"
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_zprepass.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Can dump advanced technique")
|
||||||
|
{
|
||||||
|
std::string expected(R"TECHNIQUE(
|
||||||
|
// TECHNIQUE FLAGS: 0x10
|
||||||
|
// TECHNIQUE FLAGS: 0x80
|
||||||
|
{
|
||||||
|
// CUSTOM SAMPLER FLAGS: 0x1
|
||||||
|
stateMap "passthrough"; // TODO
|
||||||
|
|
||||||
|
vertexShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
// Omitted due to matching accessors: worldMatrix = constant.worldMatrix;
|
||||||
|
// Omitted due to matching accessors: baseLightingCoords = constant.baseLightingCoords;
|
||||||
|
// Omitted due to matching accessors: viewProjectionMatrix = constant.viewProjectionMatrix;
|
||||||
|
// Omitted due to matching accessors: shadowLookupMatrix = constant.shadowLookupMatrix;
|
||||||
|
Flicker_Min = material.Flicker_Min;
|
||||||
|
Flicker_Max = material.Flicker_Max;
|
||||||
|
Seed_Value = material.Seed_Value;
|
||||||
|
// Omitted due to matching accessors: fogConsts = constant.fogConsts;
|
||||||
|
// Omitted due to matching accessors: fogConsts2 = constant.fogConsts2;
|
||||||
|
// Omitted due to matching accessors: gameTime = constant.gameTime;
|
||||||
|
// Omitted due to matching accessors: sunFogDir = constant.sunFogDir;
|
||||||
|
// Omitted due to matching accessors: sunFogColor = constant.sunFogColor;
|
||||||
|
// Omitted due to matching accessors: sunFog = constant.sunFog;
|
||||||
|
// Omitted due to matching accessors: fogColor = constant.fogColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixelShader 3.0 "advanced.hlsl"
|
||||||
|
{
|
||||||
|
Diffuse_MapSampler = material.Diffuse_MapSampler;
|
||||||
|
Diffuse_Map_Damage = material.Diffuse_Map_Damage;
|
||||||
|
Normal_Map_Cracked = material.Normal_Map_Cracked;
|
||||||
|
Reveal_Map = material.Reveal_Map;
|
||||||
|
Specular_Map = material.Specular_Map;
|
||||||
|
Heat_Map = material.Heat_Map;
|
||||||
|
// Omitted due to matching accessors: shadowmapSamplerSpot = sampler.shadowmapSamplerSpot;
|
||||||
|
// Omitted due to matching accessors: modelLightingSampler = sampler.modelLightingSampler;
|
||||||
|
// Omitted due to matching accessors: lightingLookupScale = constant.lightingLookupScale;
|
||||||
|
// Omitted due to matching accessors: glightPosXs = constant.glightPosXs;
|
||||||
|
// Omitted due to matching accessors: glightPosYs = constant.glightPosYs;
|
||||||
|
// Omitted due to matching accessors: glightPosZs = constant.glightPosZs;
|
||||||
|
// Omitted due to matching accessors: glightFallOffs = constant.glightFallOffs;
|
||||||
|
// Omitted due to matching accessors: glightReds = constant.glightReds;
|
||||||
|
// Omitted due to matching accessors: glightGreens = constant.glightGreens;
|
||||||
|
// Omitted due to matching accessors: glightBlues = constant.glightBlues;
|
||||||
|
// Omitted due to matching accessors: lightPosition = constant.lightPosition;
|
||||||
|
// Omitted due to matching accessors: lightDiffuse = constant.lightDiffuse;
|
||||||
|
// Omitted due to matching accessors: lightHeroScale = constant.lightHeroScale;
|
||||||
|
// Omitted due to matching accessors: lightSpotDir = constant.lightSpotDir;
|
||||||
|
// Omitted due to matching accessors: lightSpotFactors = constant.lightSpotFactors;
|
||||||
|
// Omitted due to matching accessors: lightAttenuation = constant.lightAttenuation;
|
||||||
|
// Omitted due to matching accessors: lightFallOffA = constant.lightFallOffA;
|
||||||
|
// Omitted due to matching accessors: spotShadowmapPixelAdjust = constant.spotShadowmapPixelAdjust;
|
||||||
|
// Omitted due to matching accessors: gameTime = constant.gameTime;
|
||||||
|
// Omitted due to matching accessors: hdrControl0 = constant.hdrControl0;
|
||||||
|
// Omitted due to matching accessors: heroLightingR = constant.heroLightingR;
|
||||||
|
// Omitted due to matching accessors: heroLightingG = constant.heroLightingG;
|
||||||
|
// Omitted due to matching accessors: heroLightingB = constant.heroLightingB;
|
||||||
|
Ember_Scale = material.Ember_Scale;
|
||||||
|
Heat_Direction = material.Heat_Direction;
|
||||||
|
Ember_Direction = material.Ember_Direction;
|
||||||
|
Heat_Scale = material.Heat_Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex.position = code.position;
|
||||||
|
vertex.texcoord[0] = code.texcoord[0];
|
||||||
|
vertex.normal = code.normal;
|
||||||
|
vertex.texcoord[2] = code.tangent;
|
||||||
|
}
|
||||||
|
)TECHNIQUE");
|
||||||
|
dumper.Dump(context);
|
||||||
|
|
||||||
|
const auto* file = mockOutput.GetMockedFile("techniques/example_lit_omni_shadow_glight.tech");
|
||||||
|
REQUIRE(file);
|
||||||
|
REQUIRE(Trimmed(file->AsString()) == Trimmed(expected));
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test/ObjWritingTests/Game/T5/Techset/ps_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/T5/Techset/ps_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/T5/Techset/ps_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/T5/Techset/ps_simple.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/T5/Techset/vs_advanced.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/T5/Techset/vs_advanced.hlsl.cso
Normal file
Binary file not shown.
BIN
test/ObjWritingTests/Game/T5/Techset/vs_simple.hlsl.cso
Normal file
BIN
test/ObjWritingTests/Game/T5/Techset/vs_simple.hlsl.cso
Normal file
Binary file not shown.
Reference in New Issue
Block a user