This commit is contained in:
Dmitry 2021-07-04 13:18:29 +03:00
parent 9e0b83d85d
commit 3a8e683f01
30 changed files with 488 additions and 204 deletions

View File

@ -3,21 +3,17 @@
# Verus Engine
## What is Verus Engine?
Verus Engine is a modern, platform-agnostic 3D game engine. It is developed using **C++** and **HLSL**. It is based on **Direct3D 12** and **Vulkan** graphics APIs. The code is user friendly and well optimized. The engine is intended to be a full-featured solution for making games. It includes modules to handle input, audio, networking and other things. Hence there is no need to search for third-party libraries or write custom code.
## Supported systems
* **Windows 10**, 8, 8.1 *(via Vulkan renderer)*
* **64-bit only**, 32-bit is not supported
## Supported graphics libraries
* Vulkan
* Direct3D 12
## Features
* Native cross-platform code
* Same HLSL shader code for all APIs
* Deferred shading
@ -31,11 +27,18 @@ Verus Engine is a modern, platform-agnostic 3D game engine. It is developed usin
* Filesystem abstraction
* Other things
## VerusEdit
VerusEdit is an official editor for Verus Engine.
You can download the latest version of VerusEdit here: https://verushub.com/page/verusedit.
![VerusEdit](https://en.verushub.com/img/verusedit/screenshot0_tn.jpg)
![VerusEdit](https://en.verushub.com/img/verusedit/screenshot1_tn.jpg)
## Roadmap
These features should be implemented in version **1.x**:
These features should be implemented in version 1.x:
- [ ] glTF file format support
- [x] glTF file format support
- [ ] Shadow maps for all lights
- [ ] Triggers
- [ ] Quest system, dialogue system
@ -45,7 +48,7 @@ These features should be implemented in version 1.x:
- [ ] Lua scripting
- [ ] Linux support
These features should be implemented in version 2.x:
These features should be implemented in version **2.x**:
- [ ] Signed distance field (SDF) fonts
- [ ] Materials v2.0: separate textures for gloss, emission, etc.
@ -56,5 +59,4 @@ These features should be implemented in version 2.x:
- [ ] Android and iOS support
## License
Verus Engine is free for non-commercial use

View File

@ -303,6 +303,20 @@ void CommandBufferD3D12::Dispatch(int groupCountX, int groupCountY, int groupCou
GetD3DGraphicsCommandList()->Dispatch(groupCountX, groupCountY, groupCountZ);
}
void CommandBufferD3D12::DispatchMesh(int groupCountX, int groupCountY, int groupCountZ)
{
//GetD3DGraphicsCommandList()->DispatchMesh(groupCountX, groupCountY, groupCountZ);
}
void CommandBufferD3D12::TraceRays(int width, int height, int depth)
{
//D3D12_DISPATCH_RAYS_DESC desc = {};
//desc.Width = width;
//desc.Height = height;
//desc.Depth = depth;
//GetD3DGraphicsCommandList()->DispatchRays(&desc);
}
ID3D12GraphicsCommandList3* CommandBufferD3D12::GetD3DGraphicsCommandList() const
{
VERUS_QREF_RENDERER;

View File

@ -47,8 +47,9 @@ namespace verus
virtual void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) override;
virtual void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int vertexOffset, int firstInstance) override;
virtual void Dispatch(int groupCountX, int groupCountY, int groupCountZ) override;
virtual void DispatchMesh(int groupCountX, int groupCountY, int groupCountZ) override;
virtual void TraceRays(int width, int height, int depth) override;
//
// D3D12

View File

@ -26,11 +26,7 @@ void PipelineD3D12::Init(RcPipelineDesc desc)
return;
}
_vertexInputBindingsFilter = desc._vertexInputBindingsFilter;
RcGeometryD3D12 geo = static_cast<RcGeometryD3D12>(*desc._geometry);
RcShaderD3D12 shader = static_cast<RcShaderD3D12>(*desc._shader);
// Attachment count according to blend equations:
int attachmentCount = 0;
for (const auto& x : desc._colorAttachBlendEqs)
{
@ -39,8 +35,12 @@ void PipelineD3D12::Init(RcPipelineDesc desc)
attachmentCount++;
}
RcGeometryD3D12 geo = static_cast<RcGeometryD3D12>(*desc._geometry);
RcShaderD3D12 shader = static_cast<RcShaderD3D12>(*desc._shader);
_pRootSignature = shader.GetD3DRootSignature();
_topology = ToNativePrimitiveTopology(desc._topology);
_vertexInputBindingsFilter = desc._vertexInputBindingsFilter;
RP::RcD3DRenderPass renderPass = pRendererD3D12->GetRenderPass(desc._renderPassHandle);
RP::RcD3DSubpass subpass = renderPass._vSubpasses[desc._subpass];
@ -60,78 +60,9 @@ void PipelineD3D12::Init(RcPipelineDesc desc)
gpsDesc.GS = ToBytecode(compiled._pBlobs[+BaseShader::Stage::gs].Get());
gpsDesc.StreamOutput = {};
gpsDesc.BlendState = {};
gpsDesc.BlendState.AlphaToCoverageEnable = FALSE;
gpsDesc.BlendState.IndependentBlendEnable = TRUE;
VERUS_FOR(i, attachmentCount)
{
CSZ p = _C(desc._colorAttachWriteMasks[i]);
while (*p)
{
switch (*p)
{
case 'r': gpsDesc.BlendState.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_RED; break;
case 'g': gpsDesc.BlendState.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_GREEN; break;
case 'b': gpsDesc.BlendState.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_BLUE; break;
case 'a': gpsDesc.BlendState.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_ALPHA; break;
}
p++;
}
if (desc._colorAttachBlendEqs[i] != "off")
{
gpsDesc.BlendState.RenderTarget[i].BlendEnable = TRUE;
static const D3D12_BLEND bfs[] =
{
D3D12_BLEND_ZERO,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_DEST_ALPHA,
D3D12_BLEND_INV_DEST_COLOR,
D3D12_BLEND_INV_BLEND_FACTOR,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_INV_SRC_COLOR,
D3D12_BLEND_DEST_ALPHA,
D3D12_BLEND_DEST_COLOR,
D3D12_BLEND_BLEND_FACTOR,
D3D12_BLEND_SRC_ALPHA,
D3D12_BLEND_SRC_ALPHA_SAT,
D3D12_BLEND_SRC_COLOR
};
static const D3D12_BLEND_OP ops[] =
{
D3D12_BLEND_OP_ADD,
D3D12_BLEND_OP_SUBTRACT,
D3D12_BLEND_OP_REV_SUBTRACT,
D3D12_BLEND_OP_MIN,
D3D12_BLEND_OP_MAX
};
int colorBlendOp = -1;
int alphaBlendOp = -1;
int srcColorBlendFactor = -1;
int dstColorBlendFactor = -1;
int srcAlphaBlendFactor = -1;
int dstAlphaBlendFactor = -1;
BaseRenderer::SetAlphaBlendHelper(
_C(desc._colorAttachBlendEqs[i]),
colorBlendOp,
alphaBlendOp,
srcColorBlendFactor,
dstColorBlendFactor,
srcAlphaBlendFactor,
dstAlphaBlendFactor);
gpsDesc.BlendState.RenderTarget[i].SrcBlend = bfs[srcColorBlendFactor];
gpsDesc.BlendState.RenderTarget[i].DestBlend = bfs[dstColorBlendFactor];
gpsDesc.BlendState.RenderTarget[i].BlendOp = ops[colorBlendOp];
gpsDesc.BlendState.RenderTarget[i].SrcBlendAlpha = bfs[srcAlphaBlendFactor];
gpsDesc.BlendState.RenderTarget[i].DestBlendAlpha = bfs[dstAlphaBlendFactor];
gpsDesc.BlendState.RenderTarget[i].BlendOpAlpha = ops[alphaBlendOp];
gpsDesc.BlendState.RenderTarget[i].LogicOp = D3D12_LOGIC_OP_CLEAR;
}
}
FillBlendStateRenderTargets(desc, attachmentCount, gpsDesc.BlendState);
gpsDesc.SampleMask = UINT_MAX;
@ -160,12 +91,11 @@ void PipelineD3D12::Init(RcPipelineDesc desc)
gpsDesc.IBStripCutValue = desc._primitiveRestartEnable ? GetStripCutValue() : D3D12_INDEX_BUFFER_STRIP_CUT_VALUE_DISABLED;
gpsDesc.PrimitiveTopologyType = ToNativePrimitiveTopologyType(desc._topology);
gpsDesc.NumRenderTargets = Utils::Cast32(subpass._vColor.size());
VERUS_RT_ASSERT(gpsDesc.NumRenderTargets == attachmentCount);
gpsDesc.NumRenderTargets = Utils::Cast32(subpass._vColor.size()); // According to subpass.
VERUS_RT_ASSERT(gpsDesc.NumRenderTargets == attachmentCount); // These two must match.
VERUS_U_FOR(i, gpsDesc.NumRenderTargets)
{
const int index = subpass._vColor[i]._index;
RP::RcD3DAttachment attachment = renderPass._vAttachments[index];
RP::RcD3DAttachment attachment = renderPass._vAttachments[subpass._vColor[i]._index];
gpsDesc.RTVFormats[i] = ToNativeFormat(attachment._format, false);
}
if (subpass._depthStencil._index >= 0)
@ -214,6 +144,88 @@ void PipelineD3D12::InitCompute(RcPipelineDesc desc)
_pPipelineState->SetName(_C(Str::Utf8ToWide(String("PipelineState (") + _C(shader.GetSourceName()) + ", " + desc._shaderBranch + ")")));
}
void PipelineD3D12::InitMeshShading(RcPipelineDesc desc)
{
VERUS_QREF_RENDERER_D3D12;
HRESULT hr = 0;
// Attachment count according to blend equations:
int attachmentCount = 0;
for (const auto& x : desc._colorAttachBlendEqs)
{
if (x.empty())
break;
attachmentCount++;
}
RcShaderD3D12 shader = static_cast<RcShaderD3D12>(*desc._shader);
_pRootSignature = shader.GetD3DRootSignature();
_topology = ToNativePrimitiveTopology(desc._topology);
RP::RcD3DRenderPass renderPass = pRendererD3D12->GetRenderPass(desc._renderPassHandle);
RP::RcD3DSubpass subpass = renderPass._vSubpasses[desc._subpass];
D3DX12_MESH_SHADER_PIPELINE_STATE_DESC mspsDesc = {};
mspsDesc.pRootSignature = _pRootSignature;
mspsDesc.AS = ToBytecode(nullptr);
mspsDesc.MS = ToBytecode(nullptr);
mspsDesc.PS = ToBytecode(nullptr);
mspsDesc.BlendState.AlphaToCoverageEnable = FALSE;
mspsDesc.BlendState.IndependentBlendEnable = TRUE;
FillBlendStateRenderTargets(desc, attachmentCount, mspsDesc.BlendState);
mspsDesc.SampleMask = UINT_MAX;
mspsDesc.RasterizerState.FillMode = ToNativePolygonMode(desc._rasterizationState._polygonMode);
mspsDesc.RasterizerState.CullMode = ToNativeCullMode(desc._rasterizationState._cullMode);
mspsDesc.RasterizerState.FrontCounterClockwise = TRUE;
mspsDesc.RasterizerState.DepthBias = static_cast<INT>(desc._rasterizationState._depthBiasConstantFactor);
mspsDesc.RasterizerState.DepthBiasClamp = desc._rasterizationState._depthBiasClamp;
mspsDesc.RasterizerState.SlopeScaledDepthBias = desc._rasterizationState._depthBiasSlopeFactor;
mspsDesc.RasterizerState.DepthClipEnable = desc._rasterizationState._depthClampEnable;
mspsDesc.RasterizerState.MultisampleEnable = FALSE;
mspsDesc.RasterizerState.AntialiasedLineEnable = (desc._colorAttachBlendEqs[0] == VERUS_COLOR_BLEND_ALPHA) ? TRUE : FALSE;
mspsDesc.RasterizerState.ForcedSampleCount = 0;
mspsDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
if (subpass._depthStencil._index >= 0)
{
mspsDesc.DepthStencilState.DepthEnable = desc._depthTestEnable;
mspsDesc.DepthStencilState.DepthWriteMask = desc._depthWriteEnable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
mspsDesc.DepthStencilState.DepthFunc = ToNativeCompareOp(desc._depthCompareOp);
mspsDesc.DepthStencilState.StencilEnable = desc._stencilTestEnable;
}
mspsDesc.PrimitiveTopologyType = ToNativePrimitiveTopologyType(desc._topology);
mspsDesc.NumRenderTargets = Utils::Cast32(subpass._vColor.size()); // According to subpass.
VERUS_RT_ASSERT(mspsDesc.NumRenderTargets == attachmentCount); // These two must match.
VERUS_U_FOR(i, mspsDesc.NumRenderTargets)
{
RP::RcD3DAttachment attachment = renderPass._vAttachments[subpass._vColor[i]._index];
mspsDesc.RTVFormats[i] = ToNativeFormat(attachment._format, false);
}
if (subpass._depthStencil._index >= 0)
{
RP::RcD3DAttachment attachment = renderPass._vAttachments[subpass._depthStencil._index];
mspsDesc.DSVFormat = ToNativeFormat(attachment._format, false);
}
mspsDesc.SampleDesc.Count = desc._sampleCount;
mspsDesc.NodeMask = 0;
mspsDesc.CachedPSO = { nullptr, 0 };
mspsDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE;
auto meshStateStream = CD3DX12_PIPELINE_MESH_STATE_STREAM(mspsDesc);
D3D12_PIPELINE_STATE_STREAM_DESC streamDesc = {};
streamDesc.SizeInBytes = sizeof(meshStateStream);
streamDesc.pPipelineStateSubobjectStream = &meshStateStream;
if (FAILED(hr = pRendererD3D12->GetD3DDevice()->CreatePipelineState(&streamDesc, IID_PPV_ARGS(&_pPipelineState))))
throw VERUS_RUNTIME_ERROR << "CreatePipelineState(), hr=" << VERUS_HR(hr);
}
D3D12_SHADER_BYTECODE PipelineD3D12::ToBytecode(ID3DBlob* pBlob)
{
D3D12_SHADER_BYTECODE bytecode = {};
@ -224,3 +236,76 @@ D3D12_SHADER_BYTECODE PipelineD3D12::ToBytecode(ID3DBlob* pBlob)
}
return bytecode;
}
void PipelineD3D12::FillBlendStateRenderTargets(RcPipelineDesc desc, int attachmentCount, D3D12_BLEND_DESC& blendDesc)
{
VERUS_FOR(i, attachmentCount)
{
CSZ p = _C(desc._colorAttachWriteMasks[i]);
while (*p)
{
switch (*p)
{
case 'r': blendDesc.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_RED; break;
case 'g': blendDesc.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_GREEN; break;
case 'b': blendDesc.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_BLUE; break;
case 'a': blendDesc.RenderTarget[i].RenderTargetWriteMask |= D3D12_COLOR_WRITE_ENABLE_ALPHA; break;
}
p++;
}
if (desc._colorAttachBlendEqs[i] != "off")
{
blendDesc.RenderTarget[i].BlendEnable = TRUE;
static const D3D12_BLEND bfs[] =
{
D3D12_BLEND_ZERO,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_DEST_ALPHA,
D3D12_BLEND_INV_DEST_COLOR,
D3D12_BLEND_INV_BLEND_FACTOR,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_INV_SRC_COLOR,
D3D12_BLEND_DEST_ALPHA,
D3D12_BLEND_DEST_COLOR,
D3D12_BLEND_BLEND_FACTOR,
D3D12_BLEND_SRC_ALPHA,
D3D12_BLEND_SRC_ALPHA_SAT,
D3D12_BLEND_SRC_COLOR
};
static const D3D12_BLEND_OP ops[] =
{
D3D12_BLEND_OP_ADD,
D3D12_BLEND_OP_SUBTRACT,
D3D12_BLEND_OP_REV_SUBTRACT,
D3D12_BLEND_OP_MIN,
D3D12_BLEND_OP_MAX
};
int colorBlendOp = -1;
int alphaBlendOp = -1;
int srcColorBlendFactor = -1;
int dstColorBlendFactor = -1;
int srcAlphaBlendFactor = -1;
int dstAlphaBlendFactor = -1;
BaseRenderer::SetAlphaBlendHelper(
_C(desc._colorAttachBlendEqs[i]),
colorBlendOp,
alphaBlendOp,
srcColorBlendFactor,
dstColorBlendFactor,
srcAlphaBlendFactor,
dstAlphaBlendFactor);
blendDesc.RenderTarget[i].SrcBlend = bfs[srcColorBlendFactor];
blendDesc.RenderTarget[i].DestBlend = bfs[dstColorBlendFactor];
blendDesc.RenderTarget[i].BlendOp = ops[colorBlendOp];
blendDesc.RenderTarget[i].SrcBlendAlpha = bfs[srcAlphaBlendFactor];
blendDesc.RenderTarget[i].DestBlendAlpha = bfs[dstAlphaBlendFactor];
blendDesc.RenderTarget[i].BlendOpAlpha = ops[alphaBlendOp];
blendDesc.RenderTarget[i].LogicOp = D3D12_LOGIC_OP_CLEAR;
}
}
}

View File

@ -24,11 +24,13 @@ namespace verus
//
VERUS_P(void InitCompute(RcPipelineDesc desc));
VERUS_P(void InitMeshShading(RcPipelineDesc desc));
D3D12_SHADER_BYTECODE ToBytecode(ID3DBlob* pBlob);
bool IsCompute() const { return _compute; }
ID3D12PipelineState* GetD3DPipelineState() const { return _pPipelineState.Get(); }
ID3D12RootSignature* GetD3DRootSignature() const { return _pRootSignature; }
D3D_PRIMITIVE_TOPOLOGY GetPrimitiveTopology() const { return _topology; }
void FillBlendStateRenderTargets(RcPipelineDesc desc, int attachmentCount, D3D12_BLEND_DESC& blendDesc);
};
VERUS_TYPEDEFS(PipelineD3D12);
}

View File

@ -301,6 +301,8 @@ void ShaderD3D12::CreatePipelineLayout()
case ShaderStageFlags::ds: visibility = D3D12_SHADER_VISIBILITY_DOMAIN; break;
case ShaderStageFlags::gs: visibility = D3D12_SHADER_VISIBILITY_GEOMETRY; break;
case ShaderStageFlags::fs: visibility = D3D12_SHADER_VISIBILITY_PIXEL; break;
case ShaderStageFlags::ts: visibility = D3D12_SHADER_VISIBILITY_AMPLIFICATION; break;
case ShaderStageFlags::ms: visibility = D3D12_SHADER_VISIBILITY_MESH; break;
}
CD3DX12_DESCRIPTOR_RANGE1 descRange;
@ -373,6 +375,10 @@ void ShaderD3D12::CreatePipelineLayout()
rootSignatureFlags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
if (!(stageFlags & ShaderStageFlags::fs))
rootSignatureFlags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
if (!(stageFlags & ShaderStageFlags::ts))
rootSignatureFlags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS;
if (!(stageFlags & ShaderStageFlags::ms))
rootSignatureFlags |= D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS;
}
UpdateDebugInfo(vRootParams, vStaticSamplers, rootSignatureFlags);
@ -633,6 +639,8 @@ void ShaderD3D12::UpdateDebugInfo(
case D3D12_SHADER_VISIBILITY_DOMAIN: ss << "D"; break;
case D3D12_SHADER_VISIBILITY_GEOMETRY: ss << "G"; break;
case D3D12_SHADER_VISIBILITY_PIXEL: ss << "P"; break;
case D3D12_SHADER_VISIBILITY_AMPLIFICATION: ss << "A"; break;
case D3D12_SHADER_VISIBILITY_MESH: ss << "M"; break;
}
};
@ -721,6 +729,10 @@ void ShaderD3D12::UpdateDebugInfo(
ss << "[ASO]";
if (rootSignatureFlags & D3D12_ROOT_SIGNATURE_FLAG_LOCAL_ROOT_SIGNATURE)
ss << "[LRS]";
if (rootSignatureFlags & D3D12_ROOT_SIGNATURE_FLAG_DENY_AMPLIFICATION_SHADER_ROOT_ACCESS)
ss << "[DASRA]";
if (rootSignatureFlags & D3D12_ROOT_SIGNATURE_FLAG_DENY_MESH_SHADER_ROOT_ACCESS)
ss << "[DMSRA]";
_debugInfo = ss.str();
}

View File

@ -352,6 +352,11 @@ void CommandBufferVulkan::Dispatch(int groupCountX, int groupCountY, int groupCo
vkCmdDispatch(GetVkCommandBuffer(), groupCountX, groupCountY, groupCountZ);
}
void CommandBufferVulkan::TraceRays(int width, int height, int depth)
{
//vkCmdTraceRaysKHR(GetVkCommandBuffer(), nullptr, nullptr, nullptr, nullptr, width, height, depth);
}
VkCommandBuffer CommandBufferVulkan::GetVkCommandBuffer() const
{
VERUS_QREF_RENDERER;

View File

@ -42,8 +42,8 @@ namespace verus
virtual void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) override;
virtual void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int vertexOffset, int firstInstance) override;
virtual void Dispatch(int groupCountX, int groupCountY, int groupCountZ) override;
virtual void TraceRays(int width, int height, int depth) override;
//
// Vulkan

View File

@ -143,6 +143,18 @@ VkShaderStageFlags CGI::ToNativeStageFlags(ShaderStageFlags stageFlags)
ret |= VK_SHADER_STAGE_FRAGMENT_BIT;
if (stageFlags & ShaderStageFlags::cs)
ret |= VK_SHADER_STAGE_COMPUTE_BIT;
if (stageFlags & ShaderStageFlags::rtrg)
ret |= VK_SHADER_STAGE_RAYGEN_BIT_KHR;
if (stageFlags & ShaderStageFlags::rtah)
ret |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR;
if (stageFlags & ShaderStageFlags::rtch)
ret |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR;
if (stageFlags & ShaderStageFlags::rtm)
ret |= VK_SHADER_STAGE_MISS_BIT_KHR;
if (stageFlags & ShaderStageFlags::rti)
ret |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR;
if (stageFlags & ShaderStageFlags::rtc)
ret |= VK_SHADER_STAGE_CALLABLE_BIT_KHR;
return ret;
}

View File

@ -26,11 +26,7 @@ void PipelineVulkan::Init(RcPipelineDesc desc)
return;
}
_vertexInputBindingsFilter = desc._vertexInputBindingsFilter;
RcGeometryVulkan geo = static_cast<RcGeometryVulkan>(*desc._geometry);
RcShaderVulkan shader = static_cast<RcShaderVulkan>(*desc._shader);
// Attachment count according to blend equations:
int attachmentCount = 0;
for (const auto& x : desc._colorAttachBlendEqs)
{
@ -39,6 +35,10 @@ void PipelineVulkan::Init(RcPipelineDesc desc)
attachmentCount++;
}
RcGeometryVulkan geo = static_cast<RcGeometryVulkan>(*desc._geometry);
RcShaderVulkan shader = static_cast<RcShaderVulkan>(*desc._shader);
_vertexInputBindingsFilter = desc._vertexInputBindingsFilter;
const bool tess = desc._topology >= PrimitiveTopology::patchList3 && desc._topology <= PrimitiveTopology::patchList4;
ShaderVulkan::RcCompiled compiled = shader.GetCompiled(desc._shaderBranch);
@ -97,7 +97,6 @@ void PipelineVulkan::Init(RcPipelineDesc desc)
rasterizationState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationState.pNext = &rasterizationLineState;
rasterizationState.depthClampEnable = desc._rasterizationState._depthClampEnable;
rasterizationState.rasterizerDiscardEnable = desc._rasterizationState._rasterizerDiscardEnable;
rasterizationState.polygonMode = ToNativePolygonMode(desc._rasterizationState._polygonMode);
rasterizationState.cullMode = ToNativeCullMode(desc._rasterizationState._cullMode);
rasterizationState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
@ -119,74 +118,7 @@ void PipelineVulkan::Init(RcPipelineDesc desc)
depthStencilState.stencilTestEnable = desc._stencilTestEnable;
VkPipelineColorBlendAttachmentState vkpcbas[VERUS_MAX_RT] = {};
VERUS_FOR(i, attachmentCount)
{
CSZ p = _C(desc._colorAttachWriteMasks[i]);
while (*p)
{
switch (*p)
{
case 'r': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; break;
case 'g': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; break;
case 'b': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; break;
case 'a': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; break;
}
p++;
}
if (desc._colorAttachBlendEqs[i] != "off")
{
vkpcbas[i].blendEnable = VK_TRUE;
static const VkBlendFactor bfs[] =
{
VK_BLEND_FACTOR_ZERO,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_DST_ALPHA,
VK_BLEND_FACTOR_DST_COLOR,
VK_BLEND_FACTOR_CONSTANT_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_SRC_ALPHA_SATURATE,
VK_BLEND_FACTOR_SRC_COLOR
};
static const VkBlendOp ops[] =
{
VK_BLEND_OP_ADD,
VK_BLEND_OP_SUBTRACT,
VK_BLEND_OP_REVERSE_SUBTRACT,
VK_BLEND_OP_MIN,
VK_BLEND_OP_MAX
};
int colorBlendOp = -1;
int alphaBlendOp = -1;
int srcColorBlendFactor = -1;
int dstColorBlendFactor = -1;
int srcAlphaBlendFactor = -1;
int dstAlphaBlendFactor = -1;
BaseRenderer::SetAlphaBlendHelper(
_C(desc._colorAttachBlendEqs[i]),
colorBlendOp,
alphaBlendOp,
srcColorBlendFactor,
dstColorBlendFactor,
srcAlphaBlendFactor,
dstAlphaBlendFactor);
vkpcbas[i].srcColorBlendFactor = bfs[srcColorBlendFactor];
vkpcbas[i].dstColorBlendFactor = bfs[dstColorBlendFactor];
vkpcbas[i].colorBlendOp = ops[colorBlendOp];
vkpcbas[i].srcAlphaBlendFactor = bfs[srcAlphaBlendFactor];
vkpcbas[i].dstAlphaBlendFactor = bfs[dstAlphaBlendFactor];
vkpcbas[i].alphaBlendOp = ops[alphaBlendOp];
}
}
FillColorBlendAttachmentStates(desc, attachmentCount, vkpcbas);
VkPipelineColorBlendStateCreateInfo colorBlendState = {};
colorBlendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendState.logicOpEnable = VK_FALSE;
@ -255,3 +187,75 @@ void PipelineVulkan::InitCompute(RcPipelineDesc desc)
if (VK_SUCCESS != (res = vkCreateComputePipelines(pRendererVulkan->GetVkDevice(), VK_NULL_HANDLE, 1, &vkcpci, pRendererVulkan->GetAllocator(), &_pipeline)))
throw VERUS_RUNTIME_ERROR << "vkCreateComputePipelines(), res=" << res;
}
void PipelineVulkan::FillColorBlendAttachmentStates(RcPipelineDesc desc, int attachmentCount, VkPipelineColorBlendAttachmentState vkpcbas[])
{
VERUS_FOR(i, attachmentCount)
{
CSZ p = _C(desc._colorAttachWriteMasks[i]);
while (*p)
{
switch (*p)
{
case 'r': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; break;
case 'g': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; break;
case 'b': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; break;
case 'a': vkpcbas[i].colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; break;
}
p++;
}
if (desc._colorAttachBlendEqs[i] != "off")
{
vkpcbas[i].blendEnable = VK_TRUE;
static const VkBlendFactor bfs[] =
{
VK_BLEND_FACTOR_ZERO,
VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR,
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR,
VK_BLEND_FACTOR_DST_ALPHA,
VK_BLEND_FACTOR_DST_COLOR,
VK_BLEND_FACTOR_CONSTANT_COLOR,
VK_BLEND_FACTOR_SRC_ALPHA,
VK_BLEND_FACTOR_SRC_ALPHA_SATURATE,
VK_BLEND_FACTOR_SRC_COLOR
};
static const VkBlendOp ops[] =
{
VK_BLEND_OP_ADD,
VK_BLEND_OP_SUBTRACT,
VK_BLEND_OP_REVERSE_SUBTRACT,
VK_BLEND_OP_MIN,
VK_BLEND_OP_MAX
};
int colorBlendOp = -1;
int alphaBlendOp = -1;
int srcColorBlendFactor = -1;
int dstColorBlendFactor = -1;
int srcAlphaBlendFactor = -1;
int dstAlphaBlendFactor = -1;
BaseRenderer::SetAlphaBlendHelper(
_C(desc._colorAttachBlendEqs[i]),
colorBlendOp,
alphaBlendOp,
srcColorBlendFactor,
dstColorBlendFactor,
srcAlphaBlendFactor,
dstAlphaBlendFactor);
vkpcbas[i].srcColorBlendFactor = bfs[srcColorBlendFactor];
vkpcbas[i].dstColorBlendFactor = bfs[dstColorBlendFactor];
vkpcbas[i].colorBlendOp = ops[colorBlendOp];
vkpcbas[i].srcAlphaBlendFactor = bfs[srcAlphaBlendFactor];
vkpcbas[i].dstAlphaBlendFactor = bfs[dstAlphaBlendFactor];
vkpcbas[i].alphaBlendOp = ops[alphaBlendOp];
}
}
}

View File

@ -24,6 +24,7 @@ namespace verus
VERUS_P(void InitCompute(RcPipelineDesc desc));
bool IsCompute() const { return _compute; }
VkPipeline GetVkPipeline() const { return _pipeline; }
void FillColorBlendAttachmentStates(RcPipelineDesc desc, int attachmentCount, VkPipelineColorBlendAttachmentState vkpcbas[]);
};
VERUS_TYPEDEFS(PipelineVulkan);
}

View File

@ -3,8 +3,8 @@
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<IncludePath>C:\Compressonator_3.2.4691\include;C:\Home\Projects\Verus\verus\Verus\src;C:\Home\Middleware\AMD Tootle 2.3\include;C:\Home\Middleware\bullet3-2.89\src;C:\Home\Middleware\bullet3-2.89\Extras;C:\Home\Middleware\libogg-1.3.4\include;C:\Home\Middleware\libvorbis-1.3.7\include;C:\Home\Middleware\openal-soft-1.20.1-bin\include;C:\Home\Middleware\SDL2-2.0.14\include;C:\VulkanSDK\1.2.176.1\Include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Compressonator_3.2.4691\lib\VS2017\x64;C:\Home\Middleware\bullet3-2.89\bin;C:\Home\Middleware\AMD Tootle 2.3\lib;C:\Home\Middleware\libogg-1.3.4\lib;C:\Home\Middleware\libvorbis-1.3.7\lib2;C:\Home\Middleware\openal-soft-1.20.1-bin\libs\Win64;C:\Home\Middleware\SDL2-2.0.14\lib\x64;C:\VulkanSDK\1.2.176.1\Lib;$(LibraryPath)</LibraryPath>
<IncludePath>C:\Compressonator_3.2.4691\include;C:\Home\Projects\Verus\verus\Verus\src;C:\Home\Middleware\AMD Tootle 2.3\include;C:\Home\Middleware\bullet3-2.89\src;C:\Home\Middleware\bullet3-2.89\Extras;C:\Home\Middleware\libogg-1.3.4\include;C:\Home\Middleware\libvorbis-1.3.7\include;C:\Home\Middleware\openal-soft-1.20.1-bin\include;C:\Home\Middleware\SDL2-2.0.14\include;C:\VulkanSDK\1.2.182.0\Include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Compressonator_3.2.4691\lib\VS2017\x64;C:\Home\Middleware\bullet3-2.89\bin;C:\Home\Middleware\AMD Tootle 2.3\lib;C:\Home\Middleware\libogg-1.3.4\lib;C:\Home\Middleware\libvorbis-1.3.7\lib2;C:\Home\Middleware\openal-soft-1.20.1-bin\libs\Win64;C:\Home\Middleware\SDL2-2.0.14\lib\x64;C:\VulkanSDK\1.2.182.0\Lib;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup />

View File

@ -29,6 +29,11 @@ bool QualitySettings::operator==(RcQualitySettings that) const
_sceneWaterQuality == that._sceneWaterQuality;
}
bool QualitySettings::operator!=(RcQualitySettings that) const
{
return !(*this == that);
}
void QualitySettings::SetQuality(OverallQuality q)
{
if (OverallQuality::custom == q)

View File

@ -62,6 +62,7 @@ namespace verus
WaterQuality _sceneWaterQuality = WaterQuality::solidColor;
bool operator==(const QualitySettings& that) const;
bool operator!=(const QualitySettings& that) const;
void SetQuality(OverallQuality q);
OverallQuality DetectQuality() const;

View File

@ -43,8 +43,10 @@ namespace verus
virtual void Draw(int vertexCount, int instanceCount = 1, int firstVertex = 0, int firstInstance = 0) = 0;
virtual void DrawIndexed(int indexCount, int instanceCount = 1, int firstIndex = 0, int vertexOffset = 0, int firstInstance = 0) = 0;
virtual void Dispatch(int groupCountX, int groupCountY, int groupCountZ = 1) = 0;
virtual void DispatchIndirect() {} // WIP.
virtual void DispatchMesh(int groupCountX, int groupCountY, int groupCountZ) {} // WIP.
virtual void TraceRays(int width, int height, int depth) {} // WIP.
RcVector4 GetViewportSize() const { return _viewportSize; }
};

View File

@ -52,7 +52,6 @@ namespace verus
float _depthBiasSlopeFactor = 0;
bool _depthClampEnable = false;
bool _depthBiasEnable = false;
bool _rasterizerDiscardEnable = false;
};
enum class CompareOp : int
@ -69,12 +68,27 @@ namespace verus
enum class ShaderStageFlags : UINT32
{
vs = (1 << 0),
hs = (1 << 1),
ds = (1 << 2),
gs = (1 << 3),
fs = (1 << 4),
cs = (1 << 5),
// Classic pipeline:
vs = (1 << 0), // Vertex shader.
hs = (1 << 1), // Hull shader. Also known as tessellation control shader.
ds = (1 << 2), // Domain shader. Also known as tessellation evaluation shader.
gs = (1 << 3), // Geometry shader.
fs = (1 << 4), // Fragment shader. Also known as pixel shader.
cs = (1 << 5), // Compute shader. Also known as kernel shader.
// Raytracing:
rtrg = (1 << 6), // Ray generation shader.
rtah = (1 << 7), // Any hit shader.
rtch = (1 << 8), // Closest hit shader.
rtm = (1 << 9), // Miss shader.
rti = (1 << 10), // Intersection shader.
rtc = (1 << 11), // Callable shader.
// Mesh shading pipeline:
ts = (1 << 12), // Task shader. Also known as amplification shader.
ms = (1 << 13), // Mesh shader.
vs_fs = vs | fs,
vs_hs_ds = vs | hs | ds,
vs_hs_ds_fs = vs | hs | ds | fs

View File

@ -47,8 +47,9 @@ void ConvertGLTF::ParseData(CSZ pathname)
LoadFromFile(pathname);
const String baseDir = IO::FileSystem::ReplaceFilename(pathname, "");
std::string err, warn;
if (!_context.LoadASCIIFromString(&_model, &err, &warn, _vData.data(), Utils::Cast32(_vData.size()), ""))
if (!_context.LoadASCIIFromString(&_model, &err, &warn, _vData.data(), Utils::Cast32(_vData.size()), _C(baseDir)))
throw VERUS_RECOVERABLE << "LoadASCIIFromString() failed, err=" << err << ", warn=" << warn;
_vNodeExtraData.resize(_model.nodes.size());
@ -265,7 +266,7 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
OnProgressText(_C(ss.str()));
}
const UINT16* p = reinterpret_cast<const UINT16*>(&buffer.data[bufferView.byteOffset]);
const BYTE* p = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const int faceCount = static_cast<int>(accessor.count / 3);
_pCurrentMesh->SetFaceCount(faceCount);
@ -274,7 +275,12 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
{
Mesh::Face face;
VERUS_FOR(i, 3)
face._indices[i] = p[faceIndex * 3 + i];
{
const UINT16* pSrc = reinterpret_cast<const UINT16*>(p + byteStride * (faceIndex * 3 + i));
face._indices[i] = *pSrc;
}
if (_desc._flipFaces)
std::swap(face._indices[1], face._indices[2]);
_pCurrentMesh->GetSetFaceAt(faceIndex) = face;
}
_pCurrentMesh->AddFoundFlag(Mesh::Found::faces);
@ -301,13 +307,14 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
OnProgressText(_C(ss.str()));
}
const float* p = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset]);
const BYTE* p = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const int vertCount = Utils::Cast32(accessor.count);
TryResizeVertsArray(vertCount);
VERUS_FOR(vertIndex, vertCount)
{
glm::vec3 pos = glm::make_vec3(p + vertIndex * 3);
const float* pSrc = reinterpret_cast<const float*>(p + byteStride * vertIndex);
glm::vec3 pos = glm::make_vec3(pSrc);
pos = glm::vec3(_pCurrentMesh->GetGlobalMatrix() * glm::vec4(pos, 1));
_pCurrentMesh->GetSetVertexAt(vertIndex)._pos = pos;
}
@ -327,15 +334,18 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
OnProgressText(_C(ss.str()));
}
const float* p = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset]);
const BYTE* p = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const int vertCount = Utils::Cast32(accessor.count);
TryResizeVertsArray(vertCount);
VERUS_FOR(vertIndex, vertCount)
{
glm::vec3 nrm = glm::make_vec3(p + vertIndex * 3);
const float* pSrc = reinterpret_cast<const float*>(p + byteStride * vertIndex);
glm::vec3 nrm = glm::make_vec3(pSrc);
nrm = glm::vec3(_pCurrentMesh->GetGlobalMatrix() * glm::vec4(nrm, 0));
nrm = glm::normalize(nrm);
if (_desc._flipNormals)
nrm = -nrm;
_pCurrentMesh->GetSetVertexAt(vertIndex)._nrm = nrm;
}
_pCurrentMesh->AddFoundFlag(Mesh::Found::normals);
@ -355,13 +365,14 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
OnProgressText(_C(ss.str()));
}
const float* p = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset]);
const BYTE* p = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const int vertCount = Utils::Cast32(accessor.count);
TryResizeVertsArray(vertCount);
VERUS_FOR(vertIndex, vertCount)
{
const glm::vec2 tc = glm::make_vec2(p + vertIndex * 2);
const float* pSrc = reinterpret_cast<const float*>(p + byteStride * vertIndex);
const glm::vec2 tc = glm::make_vec2(pSrc);
_pCurrentMesh->GetSetVertexAt(vertIndex)._tc0 = tc;
}
_pCurrentMesh->AddFoundFlag(Mesh::Found::posAndTexCoord0);
@ -381,7 +392,7 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
OnProgressText(_C(ss.str()));
}
const BYTE* p = reinterpret_cast<const BYTE*>(&buffer.data[bufferView.byteOffset]);
const BYTE* p = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const int vertCount = Utils::Cast32(accessor.count);
TryResizeVertsArray(vertCount);
@ -389,7 +400,7 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
VERUS_FOR(vertIndex, vertCount)
{
VERUS_FOR(i, 4)
vBlendIndices[vertIndex][i] = p[vertIndex * 4 + i] + _pCurrentMesh->GetBoneCount();
vBlendIndices[vertIndex][i] = p[byteStride * vertIndex + i] + _pCurrentMesh->GetBoneCount();
}
}
if (attr.first == "WEIGHTS_0")
@ -407,13 +418,16 @@ void ConvertGLTF::ProcessMesh(const tinygltf::Mesh& mesh, int nodeIndex, int ski
OnProgressText(_C(ss.str()));
}
const float* p = reinterpret_cast<const float*>(&buffer.data[bufferView.byteOffset]);
const BYTE* p = &buffer.data[accessor.byteOffset + bufferView.byteOffset];
const int vertCount = Utils::Cast32(accessor.count);
TryResizeVertsArray(vertCount);
vBlendWeights.resize(vertCount);
VERUS_FOR(vertIndex, vertCount)
vBlendWeights[vertIndex] = glm::make_vec4(p + vertIndex * 4);
{
const float* pSrc = reinterpret_cast<const float*>(p + byteStride * vertIndex);
vBlendWeights[vertIndex] = glm::make_vec4(pSrc);
}
}
if (!vBlendIndices.empty() && !vBlendWeights.empty())
@ -566,6 +580,16 @@ void ConvertGLTF::ProcessAnimation(const tinygltf::Animation& an)
progress++;
}
// Remove empty:
_vAnimSets[animSetIndex]._vAnimations.erase(std::remove_if(
_vAnimSets[animSetIndex]._vAnimations.begin(),
_vAnimSets[animSetIndex]._vAnimations.end(),
[](const Animation& anim)
{
return anim._vAnimKeys.empty();
}),
_vAnimSets[animSetIndex]._vAnimations.end());
// Convert data to bone space:
progress = 0;
for (Animation& animWithKeys : _vAnimSets[animSetIndex]._vAnimations)

View File

@ -14,6 +14,8 @@ namespace verus
float _scaleFactor = 1;
float _angle = 0;
float _areaBasedNormals = 0;
bool _flipNormals = false;
bool _flipFaces = false;
bool _useRigidBones = false;
bool _convertAsScene = false;
};

View File

@ -323,7 +323,7 @@ void BaseGame::Run(bool relativeMouseMode)
case CGI::Gapi::vulkan: gapi = "Vulkan"; break;
case CGI::Gapi::direct3D12: gapi = "Direct3D 12"; break;
}
sprintf_s(title, "[%s] - %.1f FPS", gapi, renderer.GetFps());
sprintf_s(title, "GAPI: %s, FPS: %.1f", gapi, renderer.GetFps());
SDL_SetWindowTitle(renderer.GetMainWindow()->GetSDL(), title);
}
} while (!quit); // The Game Loop.

View File

@ -69,6 +69,7 @@ namespace verus
static float GetMouseScale();
bool IsRestartAppRequested() const { return _restartApp; }
void RestartApp();
void RequestAppRestart();
};

View File

@ -38,7 +38,7 @@ void Utils::FreeEx(PBaseAllocator pAlloc)
void Utils::InitPaths()
{
if (_companyFolderName.empty())
_companyFolderName = "VerusEngine";
_companyFolderName = "Verus Engine";
wchar_t pathname[MAX_PATH] = {};
if (_modulePath.empty())

View File

@ -622,8 +622,8 @@ void Math::Test()
Scene::Camera camera;
camera.MoveEyeTo(Point3(0, 0, 3));
camera.MoveAtTo(Point3(0, 0, 4));
camera.SetAspectRatio(1);
camera.SetYFov(VERUS_PI * 0.25f);
camera.SetAspectRatio(1);
camera.SetZNear(0.5f);
camera.SetZFar(100);
camera.Update();

View File

@ -27,8 +27,8 @@ namespace verus
VERUS_PD(Point3 _atPos = Point3(0, 0, -1));
VERUS_PD(Vector3 _upDir = Vector3(0, 1, 0));
VERUS_PD(Vector3 _frontDir = Vector3(0, 0, -1));
float _aspectRatio = 1;
float _yFov = VERUS_PI / 4; // Zero FOV means ortho.
float _aspectRatio = 1;
float _zNear = 0.1f; // 10 cm.
float _zFar = 10000; // 10 km.
float _xMag = 80;
@ -70,11 +70,11 @@ namespace verus
RcMatrix4 GetMatrixVP() const { return _matVP; }
// Perspective:
float GetAspectRatio() const { return _aspectRatio; }
void SetAspectRatio(float x) { _update |= Update::p; _aspectRatio = x; }
float GetYFov() const { return _yFov; }
void SetYFov(float x) { _update |= Update::p; _yFov = x; }
void SetXFov(float x);
void SetXFov(float x); // Set correct aspect ratio before calling this method.
float GetAspectRatio() const { return _aspectRatio; }
void SetAspectRatio(float x) { _update |= Update::p; _aspectRatio = x; }
float GetZNear() const { return _zNear; }
void SetZNear(float x) { _update |= Update::p; _zNear = x; }
float GetZFar() const { return _zFar; }

View File

@ -90,8 +90,8 @@ void CubeMap::BeginEnvMap(CGI::CubeMapFace cubeMapFace, RcPoint3 center)
_camera.MoveEyeTo(center);
_camera.MoveAtTo(center - facePos); // Looking at center.
_camera.SetUpDirection(up);
_camera.SetAspectRatio(1);
_camera.SetYFov(VERUS_PI * -0.5f); // Using left-handed coordinate system!
_camera.SetAspectRatio(1);
_camera.SetZNear(1);
_camera.SetZFar(200);
_camera.Update();

View File

@ -58,7 +58,7 @@ void Forest::DoneStatic()
s_shader.Done();
}
void Forest::Init(PTerrain pTerrain)
void Forest::Init(PTerrain pTerrain, CSZ url)
{
VERUS_INIT();
@ -86,6 +86,52 @@ void Forest::Init(PTerrain pTerrain)
_vDrawPlants.resize(_capacity);
_vCollisionPool.Resize(1000);
if (url)
{
Vector<BYTE> vData;
IO::FileSystem::LoadResource(url, vData, IO::FileSystem::LoadDesc(true));
pugi::xml_document doc;
const pugi::xml_parse_result result = doc.load_buffer_inplace(vData.data(), vData.size());
if (!result)
throw VERUS_RECOVERABLE << "load_buffer_inplace(), " << result.description();
pugi::xml_node root = doc.first_child();
for (auto layerNode : root.children("layer"))
{
const int id = layerNode.attribute("id").as_int();
LayerDesc layerDesc;
for (auto typeNode : layerNode.children("type"))
{
const int typeID = typeNode.attribute("id").as_int();
SCATTER_TYPE scatterType = SCATTER_TYPE_COUNT;
switch (typeID)
{
case 100: scatterType = SCATTER_TYPE_100; break;
case 50: scatterType = SCATTER_TYPE_50; break;
case 25: scatterType = SCATTER_TYPE_25; break;
case 20: scatterType = SCATTER_TYPE_20; break;
case 15: scatterType = SCATTER_TYPE_15; break;
case 10: scatterType = SCATTER_TYPE_10; break;
case 5: scatterType = SCATTER_TYPE_5; break;
case 3: scatterType = SCATTER_TYPE_3; break;
}
if (SCATTER_TYPE_COUNT == scatterType)
throw VERUS_RECOVERABLE << "Invalid scatterType";
layerDesc._forType[scatterType].Set(
typeNode.text().as_string(),
typeNode.attribute("alignToNormal").as_float(1),
typeNode.attribute("minScale").as_float(0.5f),
typeNode.attribute("maxScale").as_float(1.5f),
typeNode.attribute("windBending").as_float(1),
typeNode.attribute("allowedNormal").as_int(116));
}
SetLayer(id, layerDesc);
}
}
}
void Forest::Done()
@ -982,8 +1028,8 @@ void Forest::BakeSprite(RPlant plant, CSZ url)
Camera cam;
cam.MoveEyeTo(Vector3(0, size * 0.5f / _margin, 0) + offset);
cam.MoveAtTo(Vector3(0, size * 0.5f / _margin, 0));
cam.SetAspectRatio(1);
cam.SetYFov(0);
cam.SetAspectRatio(1);
cam.SetZNear(0);
cam.SetZFar(size * 2);
cam.SetXMag(size * _margin);
@ -1041,7 +1087,7 @@ void Forest::BakeSprite(RPlant plant, CSZ url)
String pathname = _C(plant._mesh.GetUrl());
char filename[20];
sprintf_s(filename, "GB%d%s.psd", i, ext[i]);
sprintf_s(filename, "GB%d%s.tga", i, ext[i]);
Str::ReplaceFilename(pathname, filename);
pathname = Str::ToPakFriendlyUrl(_C(pathname));
pathname = String(url) + '/' + pathname;

View File

@ -214,7 +214,7 @@ namespace verus
static void InitStatic();
static void DoneStatic();
void Init(PTerrain pTerrain);
void Init(PTerrain pTerrain, CSZ url = nullptr);
void Done();
void ResetInstanceCount();

View File

@ -442,6 +442,24 @@ void Grass::ResetAllTextures()
_bushMask = 0;
}
void Grass::LoadLayersFromFile(CSZ url)
{
if (!url)
url = "[Misc]:TerrainLayers.xml";
Vector<BYTE> vData;
IO::FileSystem::LoadResource(url, vData);
pugi::xml_document doc;
const pugi::xml_parse_result result = doc.load_buffer_inplace(vData.data(), vData.size());
if (!result)
throw VERUS_RECOVERABLE << "load_buffer_inplace(), " << result.description();
pugi::xml_node root = doc.first_child();
for (auto node : root.children("grass"))
{
const int id = node.attribute("id").as_int();
SetTexture(id, node.attribute("url").value(), node.attribute("urlEx").value());
}
}
void Grass::SetTexture(int layer, CSZ url, CSZ url2)
{
if (_vTextureSubresData.empty()) // Using DDS?

View File

@ -104,6 +104,7 @@ namespace verus
VERUS_P(void CreateBuffers());
void ResetAllTextures();
void LoadLayersFromFile(CSZ url = nullptr);
void SetTexture(int layer, CSZ url, CSZ url2 = nullptr);
VERUS_P(void OnTextureLoaded(int layer));
void SaveTexture(CSZ url);

View File

@ -423,11 +423,17 @@ void Terrain::Init(RcDesc desc)
VERUS_QREF_CONST_SETTINGS;
VERUS_QREF_RENDERER;
if (!Math::IsPowerOfTwo(desc._mapSide))
throw VERUS_RECOVERABLE << "Init(), mapSide must be power of two";
_mapSide = desc._mapSide;
_mapShift = Math::HighestBit(desc._mapSide);
IO::Image image;
if (desc._heightmapUrl)
{
image.Init(desc._heightmapUrl);
_mapSide = image._width;
}
_mapShift = Math::HighestBit(_mapSide);
if (!Math::IsPowerOfTwo(_mapSide))
throw VERUS_RECOVERABLE << "Init(), mapSide must be power of two";
const int patchSide = _mapSide >> 4;
const int patchShift = _mapShift - 4;
@ -449,7 +455,30 @@ void Terrain::Init(RcDesc desc)
patch._layerForChannel[0] = desc._layer;
}
});
if (desc._debugHills)
if (desc._heightmapUrl)
{
VERUS_P_FOR(i, _mapSide)
{
VERUS_FOR(j, _mapSide)
{
const BYTE pix = image._p[((i << _mapShift) + j) * image._bytesPerPixel];
const float h = Convert::Uint8ToUnorm(pix) * desc._heightmapScale + desc._heightmapBias;
const short hs = Math::Clamp(ConvertHeight(h), -SHRT_MAX, SHRT_MAX);
const int ij[] = { i, j };
SetHeightAt(ij, hs);
}
});
VERUS_FOR(lod, 5)
{
VERUS_P_FOR(i, Utils::Cast32(_vPatches.size()))
{
_vPatches[i].UpdateNormals(this, lod);
});
}
}
else if (desc._debugHills)
{
if (desc._debugHills < 0)
{

View File

@ -181,6 +181,9 @@ namespace verus
struct Desc
{
CSZ _heightmapUrl = nullptr;
float _heightmapScale = 1;
float _heightmapBias = 0;
int _mapSide = 256;
int _layer = 0;
short _height = 0;