feat: add simple transparency
This commit is contained in:
@@ -29,6 +29,11 @@ enum class TextureFilteringMode : std::uint32_t {
|
||||
Anisotropic,
|
||||
};
|
||||
|
||||
enum class BlendMode : std::uint32_t {
|
||||
Opaque,
|
||||
Transparent,
|
||||
};
|
||||
|
||||
// Metallic / roughness workflow, matching glTF:
|
||||
// metallic is read from the blue channel, roughness from green and occlusion from red,
|
||||
// so a packed (occlusion)metallicRoughness texture can be used for all three slots.
|
||||
@@ -59,6 +64,9 @@ struct Material {
|
||||
float normalScale{1.f};
|
||||
float occlusionStrength{1.f};
|
||||
|
||||
BlendMode blendMode{BlendMode::Opaque};
|
||||
float opacity{1.f};
|
||||
|
||||
TextureFilteringMode textureFilteringMode{TextureFilteringMode::Linear};
|
||||
|
||||
ImageID diffuseTexture{NULL_IMAGE_ID};
|
||||
|
||||
@@ -44,6 +44,14 @@ public:
|
||||
const IndirectDrawList& drawList,
|
||||
const IndirectDrawList::View& view);
|
||||
|
||||
void drawTransparent(
|
||||
VkCommandBuffer cmd,
|
||||
VkExtent2D renderExtent,
|
||||
const RenderResources& resources,
|
||||
const GPUBuffer& sceneDataBuffer,
|
||||
const IndirectDrawList& drawList,
|
||||
const IndirectDrawList::View& view);
|
||||
|
||||
void cleanup(VkDevice device);
|
||||
|
||||
void setRenderWireframe(bool wireframe) {
|
||||
@@ -72,6 +80,7 @@ private:
|
||||
private:
|
||||
VkPipelineLayout m_pipelineLayout{VK_NULL_HANDLE};
|
||||
std::unique_ptr<Pipeline> m_pipeline;
|
||||
std::unique_ptr<Pipeline> m_transparentPipeline;
|
||||
std::unique_ptr<Pipeline> m_depthPrepassPipeline;
|
||||
std::unique_ptr<Pipeline> m_selectionPrepassPipeline;
|
||||
|
||||
|
||||
@@ -183,6 +183,7 @@ private:
|
||||
|
||||
std::vector<MeshDrawCommand> meshDrawCommands;
|
||||
std::vector<std::size_t> sortedMeshDrawCommands;
|
||||
std::vector<std::size_t> transparentMeshDrawCommands;
|
||||
IndirectDrawList drawList;
|
||||
DrawStats lastDrawStats;
|
||||
|
||||
|
||||
@@ -414,6 +414,16 @@ void MeshRendererComponent::DrawMaterialEditor(RenderResources& resources, GameR
|
||||
changed = finished = true;
|
||||
}
|
||||
|
||||
constexpr std::array<const char*, 2> blendModeNames{"Opaque", "Transparent"};
|
||||
int blendMode = static_cast<int>(material.blendMode);
|
||||
if (ImGui::Combo("Blend", &blendMode, blendModeNames.data(), static_cast<int>(blendModeNames.size()))) {
|
||||
material.blendMode = static_cast<BlendMode>(blendMode);
|
||||
changed = finished = true;
|
||||
}
|
||||
if (material.blendMode == BlendMode::Transparent) {
|
||||
track(ImGui::SliderFloat("Opacity", &material.opacity, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Textures");
|
||||
for (const TextureSlot& slot : TextureSlots) {
|
||||
ImGui::PushID(slot.label);
|
||||
|
||||
@@ -184,7 +184,7 @@ MaterialData MaterialCache::toMaterialData(const Material& material) const
|
||||
};
|
||||
|
||||
return MaterialData{
|
||||
.baseColor = glm::vec4(material.baseColor, 1.0f),
|
||||
.baseColor = glm::vec4(material.baseColor, material.blendMode == BlendMode::Transparent ? material.opacity : 1.0f),
|
||||
.emissiveColor = glm::vec4(material.emissiveColor, 0.0f),
|
||||
|
||||
.metallicRoughnessNormalOcclusion = glm::vec4{
|
||||
|
||||
@@ -97,16 +97,10 @@ IndirectDrawList::View IndirectDrawList::addView(
|
||||
++view.instanceCount;
|
||||
};
|
||||
|
||||
if (!sortedDrawCommands.empty()) {
|
||||
for (std::size_t drawIndex : sortedDrawCommands) {
|
||||
assert(drawIndex < drawCommands.size());
|
||||
addDraw(drawCommands[drawIndex]);
|
||||
}
|
||||
} else {
|
||||
for (const MeshDrawCommand& dc : drawCommands) {
|
||||
addDraw(dc);
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,36 @@ void MeshPipeline::init(
|
||||
pipelineConfig
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
PipelineConfigInfo pipelineConfig{};
|
||||
Pipeline::DefaultPipelineConfigInfo(pipelineConfig);
|
||||
|
||||
pipelineConfig.name = "Mesh Transparent Pipeline";
|
||||
pipelineConfig.pipelineLayout = m_pipelineLayout;
|
||||
|
||||
pipelineConfig.vertexAttributeDescriptions = {};
|
||||
pipelineConfig.vertexBindingDescriptions = {};
|
||||
|
||||
pipelineConfig.colorAttachments = {drawImageFormat};
|
||||
pipelineConfig.depthAttachment = depthImageFormat;
|
||||
|
||||
pipelineConfig.depthStencilInfo.depthWriteEnable = VK_FALSE;
|
||||
pipelineConfig.depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
|
||||
pipelineConfig.colorBlendAttachment.blendEnable = VK_TRUE;
|
||||
pipelineConfig.colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
pipelineConfig.colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
pipelineConfig.colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
pipelineConfig.colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
|
||||
m_transparentPipeline = std::make_unique<Pipeline>(
|
||||
gfxDevice,
|
||||
vertexShader.string(),
|
||||
fragShader.string(),
|
||||
pipelineConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -187,6 +217,29 @@ void MeshPipeline::draw(
|
||||
view);
|
||||
}
|
||||
|
||||
void MeshPipeline::drawTransparent(
|
||||
VkCommandBuffer cmd,
|
||||
VkExtent2D renderExtent,
|
||||
const RenderResources& resources,
|
||||
const GPUBuffer& sceneDataBuffer,
|
||||
const IndirectDrawList& drawList,
|
||||
const IndirectDrawList::View& view)
|
||||
{
|
||||
if (!m_transparentPipeline || view.commandCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
drawMeshes(
|
||||
cmd,
|
||||
*m_transparentPipeline,
|
||||
renderExtent,
|
||||
resources,
|
||||
sceneDataBuffer,
|
||||
drawList,
|
||||
view);
|
||||
}
|
||||
|
||||
void MeshPipeline::drawMeshes(
|
||||
VkCommandBuffer cmd,
|
||||
const Pipeline& pipeline,
|
||||
@@ -254,6 +307,7 @@ void MeshPipeline::drawMeshes(
|
||||
void MeshPipeline::cleanup(VkDevice device)
|
||||
{
|
||||
m_pipeline.reset();
|
||||
m_transparentPipeline.reset();
|
||||
m_depthPrepassPipeline.reset();
|
||||
m_selectionPrepassPipeline.reset();
|
||||
|
||||
|
||||
@@ -101,13 +101,24 @@ void GameRenderer::endDrawing()
|
||||
|
||||
// Grouping by mesh lets IndirectDrawList merge each run into one instanced draw.
|
||||
// Material and vertex buffer are per instance, so they don't break a batch.
|
||||
std::stable_sort(
|
||||
sortedMeshDrawCommands.begin(),
|
||||
sortedMeshDrawCommands.end(),
|
||||
std::ranges::stable_sort(sortedMeshDrawCommands
|
||||
,
|
||||
[this](std::size_t left, std::size_t right)
|
||||
{
|
||||
return meshDrawCommands[left].meshId < meshDrawCommands[right].meshId;
|
||||
});
|
||||
|
||||
const auto& materials = resources->materials();
|
||||
transparentMeshDrawCommands.clear();
|
||||
std::erase_if(sortedMeshDrawCommands, [&](std::size_t index)
|
||||
{
|
||||
if (materials.getMaterial(meshDrawCommands[index].materialId).blendMode != BlendMode::Transparent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
transparentMeshDrawCommands.push_back(index);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData)
|
||||
@@ -161,7 +172,23 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
|
||||
// Culling stays on the CPU: every view (camera, directional light, each spot light)
|
||||
// gets its own range of indirect commands in this frame's draw list.
|
||||
{
|
||||
ZoneScopedN("Sort Transparent Draws");
|
||||
|
||||
const glm::vec3 cameraPos = camera.GetPosition();
|
||||
const auto distanceSq = [&](std::size_t index)
|
||||
{
|
||||
const glm::vec3 offset = meshDrawCommands[index].worldBoundingSphere.center - cameraPos;
|
||||
return glm::dot(offset, offset);
|
||||
};
|
||||
std::ranges::sort(transparentMeshDrawCommands, [&](std::size_t left, std::size_t right)
|
||||
{
|
||||
return distanceSq(left) > distanceSq(right);
|
||||
});
|
||||
}
|
||||
|
||||
IndirectDrawList::View mainView;
|
||||
IndirectDrawList::View transparentView;
|
||||
IndirectDrawList::View directionalShadowView;
|
||||
std::vector<IndirectDrawList::View> spotShadowViews;
|
||||
{
|
||||
@@ -177,6 +204,13 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
edge::createFrustumFromCamera(camera),
|
||||
IndirectDrawList::Filter::All);
|
||||
|
||||
transparentView = drawList.addView(
|
||||
meshes,
|
||||
meshDrawCommands,
|
||||
transparentMeshDrawCommands,
|
||||
edge::createFrustumFromCamera(camera),
|
||||
IndirectDrawList::Filter::All);
|
||||
|
||||
if (shadowLightDirection)
|
||||
{
|
||||
directionalShadowView = drawList.addView(
|
||||
@@ -416,6 +450,19 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
skyboxPipeline->draw(cmd, *resources, camera, sceneData.skyboxRotation);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::drawTransparent");
|
||||
vkutil::ScopedCmdLabel label(cmd, "MeshPipeline::drawTransparent", vkutil::labelColors::opaque);
|
||||
|
||||
meshPipeline->drawTransparent(
|
||||
cmd,
|
||||
drawImage.getExtent2D(),
|
||||
*resources,
|
||||
sceneDataBuffer.getBuffer(),
|
||||
drawList,
|
||||
transparentView);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdEndRendering");
|
||||
|
||||
@@ -600,6 +647,7 @@ void GameRenderer::cleanup(GfxDevice& gfxDevice)
|
||||
pendingMaterialUploads.clear();
|
||||
meshDrawCommands.clear();
|
||||
sortedMeshDrawCommands.clear();
|
||||
transparentMeshDrawCommands.clear();
|
||||
initialized = false;
|
||||
GameState::GetInstance().SetRenderer(nullptr);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ namespace {
|
||||
return TextureFilteringMode::Linear;
|
||||
}
|
||||
|
||||
constexpr std::array<std::string_view, 2> BlendModeNames{"opaque", "transparent"};
|
||||
|
||||
[[nodiscard]] BlendMode ParseBlendMode(std::string_view name)
|
||||
{
|
||||
for (std::size_t i = 0; i < BlendModeNames.size(); ++i) {
|
||||
if (BlendModeNames[i] == name) {
|
||||
return static_cast<BlendMode>(i);
|
||||
}
|
||||
}
|
||||
return BlendMode::Opaque;
|
||||
}
|
||||
|
||||
[[nodiscard]] RenderResources* GetResources()
|
||||
{
|
||||
auto& state = GameState::GetInstance();
|
||||
@@ -110,6 +122,8 @@ namespace MaterialSerialization {
|
||||
{"normalScale", material.normalScale},
|
||||
{"occlusionStrength", material.occlusionStrength},
|
||||
{"filtering", FilteringNames.at(static_cast<std::size_t>(material.textureFilteringMode))},
|
||||
{"blend", BlendModeNames.at(static_cast<std::size_t>(material.blendMode))},
|
||||
{"opacity", material.opacity},
|
||||
};
|
||||
|
||||
json textures = json::object();
|
||||
@@ -138,6 +152,8 @@ namespace MaterialSerialization {
|
||||
material.normalScale = data.value("normalScale", material.normalScale);
|
||||
material.occlusionStrength = data.value("occlusionStrength", material.occlusionStrength);
|
||||
material.textureFilteringMode = ParseFiltering(data.value("filtering", std::string{"linear"}));
|
||||
material.blendMode = ParseBlendMode(data.value("blend", std::string{"opaque"}));
|
||||
material.opacity = data.value("opacity", material.opacity);
|
||||
|
||||
if (data.contains("textures")) {
|
||||
const json& textures = data.at("textures");
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"guid": "a6c64648ac15e99110de13faea6b80e1"
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"blend": "opaque",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -34,6 +35,7 @@
|
||||
"name": "material_0",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 0.699999988079071,
|
||||
"textures": {
|
||||
"diffuse": {
|
||||
@@ -43,12 +45,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"10": {
|
||||
"baseColor": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"blend": "transparent",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"filtering": "linear",
|
||||
"metallic": 0.0,
|
||||
"name": "Material",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 1.0,
|
||||
"textures": {
|
||||
"diffuse": {
|
||||
"guid": "a6c64648ac15e99110de13faea6b80e1",
|
||||
"path": "game://kobe.png",
|
||||
"srgb": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"baseColor": [
|
||||
0.8999999761581421,
|
||||
0.10000000149011612,
|
||||
0.10000000149011612
|
||||
],
|
||||
"blend": "opaque",
|
||||
"emissiveColor": [
|
||||
0.35999998450279236,
|
||||
0.04000000283122063,
|
||||
@@ -59,6 +89,7 @@
|
||||
"name": "ButtonMaterial",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 0.4000000059604645
|
||||
},
|
||||
"3": {
|
||||
@@ -67,6 +98,7 @@
|
||||
0.949999988079071,
|
||||
0.8500000238418579
|
||||
],
|
||||
"blend": "opaque",
|
||||
"emissiveColor": [
|
||||
1.0,
|
||||
0.8199999928474426,
|
||||
@@ -77,6 +109,7 @@
|
||||
"name": "BulbMaterial",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 1.0
|
||||
},
|
||||
"4": {
|
||||
@@ -85,6 +118,7 @@
|
||||
0.8999999761581421,
|
||||
0.8999999761581421
|
||||
],
|
||||
"blend": "opaque",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
@@ -95,6 +129,7 @@
|
||||
"name": "LightSwitchMaterial",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 0.5
|
||||
},
|
||||
"5": {
|
||||
@@ -103,6 +138,7 @@
|
||||
0.10000000149011612,
|
||||
0.10000000149011612
|
||||
],
|
||||
"blend": "opaque",
|
||||
"emissiveColor": [
|
||||
0.35999998450279236,
|
||||
0.04000000283122063,
|
||||
@@ -113,7 +149,116 @@
|
||||
"name": "WinButtonMaterial",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 0.4000000059604645
|
||||
},
|
||||
"6": {
|
||||
"baseColor": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"blend": "transparent",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"filtering": "linear",
|
||||
"metallic": 0.0,
|
||||
"name": "Material",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 1.0,
|
||||
"textures": {
|
||||
"diffuse": {
|
||||
"guid": "a6c64648ac15e99110de13faea6b80e1",
|
||||
"path": "game://kobe.png",
|
||||
"srgb": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"7": {
|
||||
"baseColor": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"blend": "transparent",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"filtering": "linear",
|
||||
"metallic": 0.0,
|
||||
"name": "Material",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 1.0,
|
||||
"textures": {
|
||||
"diffuse": {
|
||||
"guid": "a6c64648ac15e99110de13faea6b80e1",
|
||||
"path": "game://kobe.png",
|
||||
"srgb": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"8": {
|
||||
"baseColor": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"blend": "transparent",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"filtering": "linear",
|
||||
"metallic": 0.0,
|
||||
"name": "Material",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 1.0,
|
||||
"textures": {
|
||||
"diffuse": {
|
||||
"guid": "a6c64648ac15e99110de13faea6b80e1",
|
||||
"path": "game://kobe.png",
|
||||
"srgb": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"9": {
|
||||
"baseColor": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"blend": "transparent",
|
||||
"emissiveColor": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"filtering": "linear",
|
||||
"metallic": 0.0,
|
||||
"name": "Material",
|
||||
"normalScale": 1.0,
|
||||
"occlusionStrength": 1.0,
|
||||
"opacity": 1.0,
|
||||
"roughness": 1.0,
|
||||
"textures": {
|
||||
"diffuse": {
|
||||
"guid": "a6c64648ac15e99110de13faea6b80e1",
|
||||
"path": "game://kobe.png",
|
||||
"srgb": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Main",
|
||||
@@ -7571,7 +7716,6 @@
|
||||
{
|
||||
"data": {
|
||||
"clip": {
|
||||
"guid": "b21bcb92ef324239e14c407ad8b9ff39",
|
||||
"path": "game://MontyOnTheRun.wav"
|
||||
},
|
||||
"loop": false,
|
||||
@@ -7607,6 +7751,186 @@
|
||||
1.0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"components": [
|
||||
{
|
||||
"data": {
|
||||
"material": {
|
||||
"localMaterial": "6"
|
||||
},
|
||||
"mesh": "prim://plane?size=1,1"
|
||||
},
|
||||
"enabled": true,
|
||||
"type": "MeshRendererComponent"
|
||||
}
|
||||
],
|
||||
"id": 121,
|
||||
"name": "Decal",
|
||||
"parent": 0,
|
||||
"transform": {
|
||||
"position": [
|
||||
-4.743542194366455,
|
||||
0.3649294674396515,
|
||||
0.07782143354415894
|
||||
],
|
||||
"rotation": [
|
||||
0.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
0.5
|
||||
],
|
||||
"scale": [
|
||||
1.0,
|
||||
0.9999995231628418,
|
||||
0.9999995827674866
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"components": [
|
||||
{
|
||||
"data": {
|
||||
"material": {
|
||||
"localMaterial": "7"
|
||||
},
|
||||
"mesh": "prim://plane?size=1,1"
|
||||
},
|
||||
"enabled": true,
|
||||
"type": "MeshRendererComponent"
|
||||
}
|
||||
],
|
||||
"id": 122,
|
||||
"name": "Decal",
|
||||
"parent": 0,
|
||||
"transform": {
|
||||
"position": [
|
||||
-4.743542194366455,
|
||||
1.1351869106292725,
|
||||
2.188525676727295
|
||||
],
|
||||
"rotation": [
|
||||
0.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
0.5
|
||||
],
|
||||
"scale": [
|
||||
1.0,
|
||||
0.9999995231628418,
|
||||
0.9999995827674866
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"components": [
|
||||
{
|
||||
"data": {
|
||||
"material": {
|
||||
"localMaterial": "8"
|
||||
},
|
||||
"mesh": "prim://plane?size=1,1"
|
||||
},
|
||||
"enabled": true,
|
||||
"type": "MeshRendererComponent"
|
||||
}
|
||||
],
|
||||
"id": 123,
|
||||
"name": "Decal",
|
||||
"parent": 0,
|
||||
"transform": {
|
||||
"position": [
|
||||
-4.743542194366455,
|
||||
1.5165855884552002,
|
||||
0.601291835308075
|
||||
],
|
||||
"rotation": [
|
||||
0.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
0.5
|
||||
],
|
||||
"scale": [
|
||||
1.0,
|
||||
0.9999995231628418,
|
||||
0.9999995827674866
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"components": [
|
||||
{
|
||||
"data": {
|
||||
"material": {
|
||||
"localMaterial": "9"
|
||||
},
|
||||
"mesh": "prim://plane?size=1,1"
|
||||
},
|
||||
"enabled": true,
|
||||
"type": "MeshRendererComponent"
|
||||
}
|
||||
],
|
||||
"id": 124,
|
||||
"name": "Decal",
|
||||
"parent": 0,
|
||||
"transform": {
|
||||
"position": [
|
||||
-4.743542194366455,
|
||||
0.5876348614692688,
|
||||
-1.545495867729187
|
||||
],
|
||||
"rotation": [
|
||||
0.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
0.5
|
||||
],
|
||||
"scale": [
|
||||
1.0,
|
||||
0.9999995231628418,
|
||||
0.9999995827674866
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"components": [
|
||||
{
|
||||
"data": {
|
||||
"material": {
|
||||
"localMaterial": "10"
|
||||
},
|
||||
"mesh": "prim://plane?size=1,1"
|
||||
},
|
||||
"enabled": true,
|
||||
"type": "MeshRendererComponent"
|
||||
}
|
||||
],
|
||||
"id": 125,
|
||||
"name": "Decal",
|
||||
"parent": 0,
|
||||
"transform": {
|
||||
"position": [
|
||||
-4.743542194366455,
|
||||
1.3765578269958496,
|
||||
-2.816664457321167
|
||||
],
|
||||
"rotation": [
|
||||
0.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
0.5
|
||||
],
|
||||
"scale": [
|
||||
1.0,
|
||||
0.9999995231628418,
|
||||
0.9999995827674866
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 3
|
||||
|
||||
@@ -293,6 +293,8 @@ namespace {
|
||||
material.roughnessFactor = 0.55f;
|
||||
material.emissiveColor = {1.0f, 0.5f, 0.0f};
|
||||
material.textureFilteringMode = TextureFilteringMode::Anisotropic;
|
||||
material.blendMode = BlendMode::Transparent;
|
||||
material.opacity = 0.25f;
|
||||
material.diffuseTexture = 3;
|
||||
material.emissiveTexture = 7;
|
||||
material.normalTexture = 9;
|
||||
@@ -315,7 +317,8 @@ namespace {
|
||||
check(loaded.name == "Crate" && loaded.baseColor == material.baseColor &&
|
||||
close(loaded.metallicFactor, 0.3f) && close(loaded.roughnessFactor, 0.55f) &&
|
||||
loaded.emissiveColor == material.emissiveColor &&
|
||||
loaded.textureFilteringMode == TextureFilteringMode::Anisotropic,
|
||||
loaded.textureFilteringMode == TextureFilteringMode::Anisotropic &&
|
||||
loaded.blendMode == BlendMode::Transparent && close(loaded.opacity, 0.25f),
|
||||
"material factors must round-trip through json");
|
||||
check(loaded.diffuseTexture == 30 && loaded.emissiveTexture == 70 &&
|
||||
loaded.normalTexture == NULL_IMAGE_ID && loaded.metallicTexture == NULL_IMAGE_ID,
|
||||
|
||||
Reference in New Issue
Block a user