This commit is contained in:
Dmitry 2021-04-02 21:31:01 +03:00
parent 28c4c6fb3a
commit 806249048b
82 changed files with 2789 additions and 440 deletions

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\HelloTexturedCubeGame.h" />
<ClInclude Include="src\pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\HelloTexturedCubeGame.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="src\HelloTexturedCubeShader.hlsl">
<FileType>Document</FileType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RendererDirect3D12\RendererDirect3D12.vcxproj">
<Project>{53923514-84b2-4b78-889a-8709c6bfa3a5}</Project>
</ProjectReference>
<ProjectReference Include="..\RendererVulkan\RendererVulkan.vcxproj">
<Project>{c9195a1c-9224-4b40-bbbc-aa90ef3be3e0}</Project>
</ProjectReference>
<ProjectReference Include="..\Verus\Verus.vcxproj">
<Project>{b154d670-e4b1-4d8a-885c-69546a5bd833}</Project>
</ProjectReference>
<ProjectReference Include="..\VulkanShaderCompiler\VulkanShaderCompiler.vcxproj">
<Project>{1ea5f5d1-9138-406d-871b-3cd75343e14c}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{26bd6e61-e36d-464a-a312-4110adf10083}</ProjectGuid>
<RootNamespace>HelloTexturedCube</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\Verus\Verus.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\Verus\Verus.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y $(ProjectDir)src\HelloTexturedCubeShader.hlsl $(OutDir)Data\Shaders\
xcopy /y $(ProjectDir)src\HelloTexturedCube.dds $(OutDir)Data\Textures\</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<PostBuildEvent>
<Command>xcopy /y $(ProjectDir)src\HelloTexturedCubeShader.hlsl $(OutDir)Data\Shaders\
xcopy /y $(ProjectDir)src\HelloTexturedCube.dds $(OutDir)Data\Textures\</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="src">
<UniqueIdentifier>{15ea43db-379c-4b0c-9691-a1b7e080aefb}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\HelloTexturedCubeGame.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="src\pch.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\HelloTexturedCubeGame.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\main.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\pch.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="src\HelloTexturedCubeShader.hlsl">
<Filter>src</Filter>
</None>
</ItemGroup>
</Project>

Binary file not shown.

View File

@ -0,0 +1,168 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "pch.h"
using namespace verus;
using namespace verus::Game;
HelloTexturedCubeGame::HelloTexturedCubeGame()
{
Utils::I().SetWritableFolderName("HelloTexturedCube");
}
HelloTexturedCubeGame::~HelloTexturedCubeGame()
{
CGI::Renderer::I()->WaitIdle();
}
void HelloTexturedCubeGame::BaseGame_UpdateSettings()
{
VERUS_QREF_SETTINGS;
settings._displayOffscreenDraw = false; // Draw directly to swap chain.
GetEngineInit().ReducedFeatureSet(); // Don't try to load any resources.
}
void HelloTexturedCubeGame::BaseGame_LoadContent()
{
VERUS_QREF_RENDERER;
GetCameraSpirit().MoveTo(Point3::Replicate(3));
GetCameraSpirit().LookAt(Point3(0), true);
// Create geometry:
{
const Vertex cubeVerts[] =
{
{-1, -1, -1, VERUS_COLOR_RGBA(0, 0, 0, 255)},
{+1, -1, -1, VERUS_COLOR_RGBA(255, 0, 0, 255)},
{-1, +1, -1, VERUS_COLOR_RGBA(0, 255, 0, 255)},
{+1, +1, -1, VERUS_COLOR_RGBA(255, 255, 0, 255)},
{-1, -1, +1, VERUS_COLOR_RGBA(0, 0, 255, 255)},
{+1, -1, +1, VERUS_COLOR_RGBA(255, 0, 255, 255)},
{-1, +1, +1, VERUS_COLOR_RGBA(0, 255, 255, 255)},
{+1, +1, +1, VERUS_COLOR_RGBA(255, 255, 255, 255)}
};
_vertCount = VERUS_COUNT_OF(cubeVerts);
const UINT16 cubeIndices[] =
{
// -X
2, 0, 6,
6, 0, 4,
// +X
1, 3, 5,
5, 3, 7,
// -Y
0, 1, 4,
4, 1, 5,
// +Y
2, 6, 3,
3, 6, 7,
// -Z
5, 7, 4,
4, 7, 6,
// +Z
1, 0, 3,
3, 0, 2
};
_indexCount = VERUS_COUNT_OF(cubeIndices);
CGI::GeometryDesc geoDesc;
geoDesc._name = "HelloTexturedCube.Geo"; // Optional name, for RenderDoc, etc.
const CGI::VertexInputAttrDesc viaDesc[] =
{
{0, offsetof(Vertex, _x), CGI::ViaType::floats, 3, CGI::ViaUsage::position, 0}, // 3 floats for position.
{0, offsetof(Vertex, _color), CGI::ViaType::ubytes, 4, CGI::ViaUsage::color, 0}, // Color can be stored as 32-bit unsigned integer.
CGI::VertexInputAttrDesc::End()
};
geoDesc._pVertexInputAttrDesc = viaDesc;
const int strides[] = { sizeof(Vertex), 0 };
geoDesc._pStrides = strides;
_geo.Init(geoDesc);
_geo->CreateVertexBuffer(_vertCount, 0);
_geo->UpdateVertexBuffer(cubeVerts, 0); // Copy command. When BaseGame_LoadContent is called, the command buffer is already recording commands.
_geo->CreateIndexBuffer(_indexCount);
_geo->UpdateIndexBuffer(cubeIndices); // Copy command.
}
// Create shader:
{
_shader.Init("[Shaders]:HelloTexturedCubeShader.hlsl"); // HLSL file was copied by Post-Build Event.
_shader->CreateDescriptorSet(0, &_ubShaderVS, sizeof(_ubShaderVS), 100, {}, CGI::ShaderStageFlags::vs); // Uniform buffer used only by vertex shader.
_shader->CreateDescriptorSet(1, &_ubShaderFS, sizeof(_ubShaderFS), 100,
{
CGI::Sampler::aniso // Immutable sampler for this set.
}, CGI::ShaderStageFlags::fs); // Uniform buffer used only by fragment shader.
_shader->CreatePipelineLayout();
}
// Create pipeline:
{
// What to draw? How to draw? Where to draw?
CGI::PipelineDesc pipeDesc(_geo, _shader, "#", renderer.GetRenderPassHandle_AutoWithDepth()); // Reuse existing render pass.
_pipe.Init(pipeDesc);
}
_tex.Init("[Textures]:HelloTexturedCube.dds"); // Texture file was copied by Post-Build Event.
}
void HelloTexturedCubeGame::BaseGame_UnloadContent()
{
}
void HelloTexturedCubeGame::BaseGame_HandleInput()
{
}
void HelloTexturedCubeGame::BaseGame_Update()
{
VERUS_QREF_TIMER;
_rotation += Vector3(dt * 0.03f, dt * 0.05f, dt * 0.07f);
}
void HelloTexturedCubeGame::BaseGame_Draw()
{
VERUS_QREF_RENDERER;
VERUS_QREF_TIMER;
if (!_csh.IsSet() && _tex->IsLoaded())
_csh = _shader->BindDescriptorSetTextures(1, { _tex });
ImGui::Text("HelloTexturedCube");
ImGui::Text("Use Mouse and WASD keys");
Scene::RCamera camera = GetCamera();
Transform3 matW = Transform3::rotationZYX(_rotation);
auto cb = renderer.GetCommandBuffer(); // Default command buffer for this frame.
_ubShaderVS._matW = matW.UniformBufferFormat();
_ubShaderVS._matVP = camera.GetMatrixVP().UniformBufferFormat();
_ubShaderFS._phase = pow(abs(0.5f - glm::fract(timer.GetTime() * 0.25f)) * 2, 4.f); // Blink.
cb->BeginRenderPass(
renderer.GetRenderPassHandle_AutoWithDepth(),
renderer.GetFramebufferHandle_AutoWithDepth(renderer->GetSwapChainBufferIndex()),
{ Vector4(0), Vector4(1) });
if (_csh.IsSet()) // Texture is loaded asynchronously. Don't draw the cube, while the texture is loading.
{
cb->BindPipeline(_pipe);
cb->BindVertexBuffers(_geo);
cb->BindIndexBuffer(_geo);
_shader->BeginBindDescriptors(); // Map.
cb->BindDescriptors(_shader, 0); // Update uniform buffer for set=0. (Copy from _ubShaderVS)
cb->BindDescriptors(_shader, 1, _csh); // Update uniform buffer for set=1. (Copy from _ubShaderFS). Also set textures.
_shader->EndBindDescriptors(); // Unmap.
cb->DrawIndexed(_indexCount);
}
renderer->ImGuiRenderDrawData(); // Also draw Dear ImGui, please.
cb->EndRenderPass();
}

View File

@ -0,0 +1,55 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#pragma once
namespace verus
{
namespace Game
{
class HelloTexturedCubeGame : public Singleton<HelloTexturedCubeGame>, public BaseGame
{
struct Vertex
{
float _x, _y, _z;
UINT32 _color;
};
VERUS_TYPEDEFS(Vertex);
VERUS_UBUFFER UB_ShaderVS
{
mataff _matW;
matrix _matVP;
};
VERUS_UBUFFER UB_ShaderFS
{
float _phase;
};
Vector3 _rotation = Vector3(0);
CGI::GeometryPwn _geo;
CGI::ShaderPwn _shader;
CGI::PipelinePwn _pipe;
CGI::TexturePwn _tex;
CSZ _shaderCode;
CGI::CSHandle _csh; // Complex set handle. Simple set has one uniform buffer. Complex set additionally has textures.
UB_ShaderVS _ubShaderVS;
UB_ShaderFS _ubShaderFS;
int _vertCount = 0;
int _indexCount = 0;
public:
HelloTexturedCubeGame();
~HelloTexturedCubeGame();
virtual void BaseGame_UpdateSettings() override;
virtual void BaseGame_LoadContent() override;
virtual void BaseGame_UnloadContent() override;
virtual void BaseGame_HandleInput() override;
virtual void BaseGame_Update() override;
virtual void BaseGame_Draw() override;
};
VERUS_TYPEDEFS(HelloTexturedCubeGame);
}
}
#define VERUS_QREF_GAME Game::RHelloTexturedCubeGame game = Game::HelloTexturedCubeGame::I()

View File

@ -0,0 +1,86 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
// Copied from Lib.hlsl:
#define VERUS_UBUFFER struct
#define mataff float4x3
#ifdef _VULKAN
# define VK_LOCATION_POSITION [[vk::location(0)]]
# define VK_LOCATION_COLOR0 [[vk::location(3)]]
#else
# define VK_LOCATION_POSITION
# define VK_LOCATION_COLOR0
#endif
VERUS_UBUFFER UB_ShaderVS
{
mataff _matW;
matrix _matVP;
};
VERUS_UBUFFER UB_ShaderFS
{
float _phase;
};
ConstantBuffer<UB_ShaderVS> g_ubShaderVS : register(b0, space0);
ConstantBuffer<UB_ShaderFS> g_ubShaderFS : register(b0, space1);
Texture2D g_tex : register(t1, space1);
SamplerState g_sam : register(s1, space1);
struct VSI
{
VK_LOCATION_POSITION float3 pos : POSITION;
VK_LOCATION_COLOR0 float4 color : COLOR0;
};
struct VSO
{
float4 pos : SV_Position;
float3 tcCube : TEXCOORD0;
float4 color : COLOR0;
};
struct FSO
{
float4 color : SV_Target0;
};
#ifdef _VS
VSO mainVS(VSI si)
{
VSO so;
const float3 posW = mul(float4(si.pos, 1), g_ubShaderVS._matW);
so.pos = mul(float4(posW, 1), g_ubShaderVS._matVP);
so.tcCube = si.pos.xyz * float3(1, -1, 1);
so.color = si.color;
return so;
}
#endif
#ifdef _FS
FSO mainFS(VSO si)
{
FSO so;
// Generate texture coordinates using CubeMap method:
float2 tc = 0.0;
const float3 mag = abs(si.tcCube);
if (mag.x >= mag.y && mag.x >= mag.z)
tc = 0.5 * (si.tcCube.zy / mag.x + 1.0);
else if (mag.y >= mag.x && mag.y >= mag.z)
tc = 0.5 * (si.tcCube.xz / mag.y + 1.0);
else
tc = 0.5 * (si.tcCube.xy / mag.z + 1.0);
so.color = g_tex.Sample(g_sam, tc);
so.color = lerp(so.color, si.color, g_ubShaderFS._phase);
return so;
}
#endif
//@main:#

View File

@ -0,0 +1,25 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "pch.h"
using namespace verus;
int main(VERUS_MAIN_DEFAULT_ARGS)
{
Game::HelloTexturedCubeGame game;
try
{
game.Initialize(argc, argv);
game.Run();
}
catch (D::RcRuntimeError e)
{
D::Log::I().Write(e.what(), e.GetThreadID(), e.GetFile(), e.GetLine(), D::Log::Severity::error);
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
VERUS_LOG_ERROR(e.what());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,2 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "pch.h"

View File

@ -0,0 +1,6 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#pragma once
#include <verus.h>
#include "HelloTexturedCubeGame.h"

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\HelloTriangleGame.h" />
<ClInclude Include="src\pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\HelloTriangleGame.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RendererDirect3D12\RendererDirect3D12.vcxproj">
<Project>{53923514-84b2-4b78-889a-8709c6bfa3a5}</Project>
</ProjectReference>
<ProjectReference Include="..\RendererVulkan\RendererVulkan.vcxproj">
<Project>{c9195a1c-9224-4b40-bbbc-aa90ef3be3e0}</Project>
</ProjectReference>
<ProjectReference Include="..\Verus\Verus.vcxproj">
<Project>{b154d670-e4b1-4d8a-885c-69546a5bd833}</Project>
</ProjectReference>
<ProjectReference Include="..\VulkanShaderCompiler\VulkanShaderCompiler.vcxproj">
<Project>{1ea5f5d1-9138-406d-871b-3cd75343e14c}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="src\HelloTriangleShader.hlsl">
<FileType>Document</FileType>
</None>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{bc17acd3-97eb-4d5c-a2c9-574cdaa7576b}</ProjectGuid>
<RootNamespace>HelloTriangle</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\Verus\Verus.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\Verus\Verus.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="src">
<UniqueIdentifier>{b500ecb1-338a-4b8b-ad40-389727e8a8cf}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\HelloTriangleGame.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="src\pch.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\HelloTriangleGame.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\main.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="src\pch.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="src\HelloTriangleShader.hlsl">
<Filter>src</Filter>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,131 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "pch.h"
using namespace verus;
using namespace verus::Game;
HelloTriangleGame::HelloTriangleGame()
{
Utils::I().SetWritableFolderName("HelloTriangle");
}
HelloTriangleGame::~HelloTriangleGame()
{
CGI::Renderer::I()->WaitIdle();
}
void HelloTriangleGame::BaseGame_UpdateSettings()
{
VERUS_QREF_SETTINGS;
settings._displayOffscreenDraw = false; // Draw directly to swap chain.
GetEngineInit().ReducedFeatureSet(); // Don't try to load any resources.
}
void HelloTriangleGame::BaseGame_LoadContent()
{
VERUS_QREF_RENDERER;
// Create geometry:
{
const Vertex triangle[] =
{
{ 0, 0.5f, 0, VERUS_COLOR_RGBA(255, 0, 0, 255)}, // Red vertex (on top).
{-0.5f, -0.5f, 0, VERUS_COLOR_RGBA(0, 255, 0, 255)}, // Green vertex.
{ 0.5f, -0.5f, 0, VERUS_COLOR_RGBA(0, 0, 255, 255)}, // Blue vertex.
};
_vertCount = VERUS_COUNT_OF(triangle);
CGI::GeometryDesc geoDesc;
geoDesc._name = "HelloTriangle.Geo"; // Optional name, for RenderDoc, etc.
const CGI::VertexInputAttrDesc viaDesc[] =
{
{0, offsetof(Vertex, _x), CGI::ViaType::floats, 3, CGI::ViaUsage::position, 0}, // 3 floats for position.
{0, offsetof(Vertex, _color), CGI::ViaType::ubytes, 4, CGI::ViaUsage::color, 0}, // Color can be stored as 32-bit unsigned integer.
CGI::VertexInputAttrDesc::End()
};
geoDesc._pVertexInputAttrDesc = viaDesc;
const int strides[] = { sizeof(Vertex), 0 };
geoDesc._pStrides = strides;
_geo.Init(geoDesc);
_geo->CreateVertexBuffer(_vertCount, 0);
_geo->UpdateVertexBuffer(triangle, 0); // Copy command. When BaseGame_LoadContent is called, the command buffer is already recording commands.
}
// Create shader:
{
// Shader code can be provided as a string.
_shaderCode =
#include "HelloTriangleShader.hlsl"
;
CSZ branches[] =
{
"main:#",
nullptr
};
CGI::ShaderDesc shaderDesc;
shaderDesc._source = _shaderCode;
shaderDesc._branches = branches;
_shader.Init(shaderDesc);
_shader->CreateDescriptorSet(0, &_ubShaderVS, sizeof(_ubShaderVS), 100, {}, CGI::ShaderStageFlags::vs); // Uniform buffer used only by vertex shader.
_shader->CreateDescriptorSet(1, &_ubShaderFS, sizeof(_ubShaderFS), 100, {}, CGI::ShaderStageFlags::fs); // Uniform buffer used only by fragment shader.
_shader->CreatePipelineLayout();
}
// Create pipeline:
{
// What to draw? How to draw? Where to draw?
CGI::PipelineDesc pipeDesc(_geo, _shader, "#", renderer.GetRenderPassHandle_AutoWithDepth()); // Reuse existing render pass.
pipeDesc.DisableDepthTest();
_pipe.Init(pipeDesc);
}
}
void HelloTriangleGame::BaseGame_UnloadContent()
{
}
void HelloTriangleGame::BaseGame_HandleInput()
{
//_stateMachine.HandleInput();
}
void HelloTriangleGame::BaseGame_Update()
{
//_stateMachine.Update();
}
void HelloTriangleGame::BaseGame_Draw()
{
VERUS_QREF_RENDERER;
VERUS_QREF_TIMER;
//ImGui::Text("HelloTriangle");
auto cb = renderer.GetCommandBuffer(); // Default command buffer for this frame.
const Matrix4 matWVP = Matrix4::scale(
Vector3(
0.9f + 0.2f * sin(timer.GetTime() * VERUS_PI * 0.41f), // Wobble.
0.9f + 0.2f * sin(timer.GetTime() * VERUS_PI * 0.47f),
1));
_ubShaderVS._matWVP = matWVP.UniformBufferFormat();
_ubShaderFS._phase = pow(abs(0.5f - glm::fract(timer.GetTime())) * 2, 4.f); // Blink.
cb->BeginRenderPass(
renderer.GetRenderPassHandle_AutoWithDepth(),
renderer.GetFramebufferHandle_AutoWithDepth(renderer->GetSwapChainBufferIndex()),
{ Vector4(0), Vector4(1) });
cb->BindPipeline(_pipe);
cb->BindVertexBuffers(_geo);
_shader->BeginBindDescriptors(); // Map.
cb->BindDescriptors(_shader, 0); // Update uniform buffer for set=0. (Copy from _ubShaderVS)
cb->BindDescriptors(_shader, 1); // Update uniform buffer for set=1. (Copy from _ubShaderFS)
_shader->EndBindDescriptors(); // Unmap.
cb->Draw(_vertCount);
renderer->ImGuiRenderDrawData(); // Also draw Dear ImGui, please.
cb->EndRenderPass();
}

View File

@ -0,0 +1,55 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#pragma once
namespace verus
{
namespace Game
{
// Epigraph
// "Vulkan (it takes) 1000 lines to draw a triangle"
// "- I thought you only needed 3 lines to draw a triangle"
// https://www.reddit.com/r/linux/comments/58fyqn/vulkan_1000_lines_to_draw_a_triangle/
class HelloTriangleGame : public Singleton<HelloTriangleGame>, public BaseGame
{
struct Vertex
{
float _x, _y, _z;
UINT32 _color;
};
VERUS_TYPEDEFS(Vertex);
VERUS_UBUFFER UB_ShaderVS
{
matrix _matWVP;
};
VERUS_UBUFFER UB_ShaderFS
{
float _phase;
};
CGI::GeometryPwn _geo;
CGI::ShaderPwn _shader;
CGI::PipelinePwn _pipe;
CSZ _shaderCode;
UB_ShaderVS _ubShaderVS;
UB_ShaderFS _ubShaderFS;
int _vertCount = 0;
public:
HelloTriangleGame();
~HelloTriangleGame();
virtual void BaseGame_UpdateSettings() override;
virtual void BaseGame_LoadContent() override;
virtual void BaseGame_UnloadContent() override;
virtual void BaseGame_HandleInput() override;
virtual void BaseGame_Update() override;
virtual void BaseGame_Draw() override;
};
VERUS_TYPEDEFS(HelloTriangleGame);
}
}
#define VERUS_QREF_GAME Game::RHelloTriangleGame game = Game::HelloTriangleGame::I()

View File

@ -0,0 +1,70 @@
R"(
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
// Copied from Lib.hlsl:
#define VERUS_UBUFFER struct
#ifdef _VULKAN
# define VK_LOCATION_POSITION [[vk::location(0)]]
# define VK_LOCATION_COLOR0 [[vk::location(3)]]
#else
# define VK_LOCATION_POSITION
# define VK_LOCATION_COLOR0
#endif
VERUS_UBUFFER UB_ShaderVS
{
matrix _matWVP;
};
VERUS_UBUFFER UB_ShaderFS
{
float _phase;
};
ConstantBuffer<UB_ShaderVS> g_ubShaderVS : register(b0, space0);
ConstantBuffer<UB_ShaderFS> g_ubShaderFS : register(b0, space1);
struct VSI
{
VK_LOCATION_POSITION float4 pos : POSITION;
VK_LOCATION_COLOR0 float4 color : COLOR0;
};
struct VSO
{
float4 pos : SV_Position;
float4 color : COLOR0;
};
struct FSO
{
float4 color : SV_Target0;
};
#ifdef _VS
VSO mainVS(VSI si)
{
VSO so;
so.pos = mul(si.pos, g_ubShaderVS._matWVP);
so.color = si.color;
return so;
}
#endif
#ifdef _FS
FSO mainFS(VSO si)
{
FSO so;
so.color = saturate(si.color + g_ubShaderFS._phase);
return so;
}
#endif
//@main:#
)"

View File

@ -0,0 +1,25 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "pch.h"
using namespace verus;
int main(VERUS_MAIN_DEFAULT_ARGS)
{
Game::HelloTriangleGame game;
try
{
game.Initialize(argc, argv);
game.Run(false);
}
catch (D::RcRuntimeError e)
{
D::Log::I().Write(e.what(), e.GetThreadID(), e.GetFile(), e.GetLine(), D::Log::Severity::error);
return EXIT_FAILURE;
}
catch (const std::exception& e)
{
VERUS_LOG_ERROR(e.what());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,2 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "pch.h"

6
HelloTriangle/src/pch.h Normal file
View File

@ -0,0 +1,6 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#pragma once
#include <verus.h>
#include "HelloTriangleGame.h"

View File

@ -1,8 +1,29 @@
# verus
Game Engine
# Verus Engine
# Release
undef VERUS_RELEASE_DEBUG
UseFullPaths=false
DebugInformationFormat=None
GenerateDebugInformation=false (Link)
## 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
* Efficient textures and streaming
* Large terrain support
* Built-in audio system
* Multiplayer support
* Custom user interface module
* Collision and physics support
* A bunch of post-processing effects
* Filesystem abstraction
* Other things
## License
Verus Engine is free for non-commercial use

View File

@ -374,6 +374,12 @@ void RendererD3D12::CreateSamplers()
init.MinLOD = 0;
init.MaxLOD = D3D12_FLOAT32_MAX;
desc = init;
desc.Filter = D3D12_FILTER_ANISOTROPIC;
desc.MipLODBias = -2;
desc.MaxAnisotropy = settings._gpuAnisotropyLevel;
_vSamplers[+Sampler::lodBias] = desc;
desc = init;
desc.Filter = (settings._sceneShadowQuality <= App::Settings::Quality::low) ?
D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT : D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
@ -888,6 +894,10 @@ void RendererD3D12::SetDescriptorHeaps(PBaseCommandBuffer p)
void RendererD3D12::UpdateUtilization()
{
VERUS_QREF_RENDERER;
renderer.AddUtilization("D3D12 Views", _dhViews.GetOffset(), _dhViews.GetCapacity());
renderer.AddUtilization("D3D12 Samplers", _dhSamplers.GetOffset(), _dhSamplers.GetCapacity());
for (auto& x : TStoreGeometry::_list)
x.UpdateUtilization();
for (auto& x : TStoreShaders::_list)

View File

@ -629,6 +629,12 @@ void RendererVulkan::CreateSamplers()
return sampler;
};
vksci = init;
vksci.mipLodBias = -2;
vksci.anisotropyEnable = (settings._gpuAnisotropyLevel > 0) ? VK_TRUE : VK_FALSE;
vksci.maxAnisotropy = static_cast<float>(settings._gpuAnisotropyLevel);
_vSamplers[+Sampler::lodBias] = Create(vksci);
vksci = init;
if (settings._sceneShadowQuality <= App::Settings::Quality::low)
{

View File

@ -15,6 +15,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PAKBuilder", "PAKBuilder\PA
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TextureTool", "TextureTool\TextureTool.vcxproj", "{5A1A3E76-7F69-48B6-B1E3-F6BB281B7E73}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloTriangle", "HelloTriangle\HelloTriangle.vcxproj", "{BC17ACD3-97EB-4D5C-A2C9-574CDAA7576B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloTexturedCube", "HelloTexturedCube\HelloTexturedCube.vcxproj", "{26BD6E61-E36D-464A-A312-4110ADF10083}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -45,6 +49,14 @@ Global
{5A1A3E76-7F69-48B6-B1E3-F6BB281B7E73}.Debug|x64.Build.0 = Debug|x64
{5A1A3E76-7F69-48B6-B1E3-F6BB281B7E73}.Release|x64.ActiveCfg = Release|x64
{5A1A3E76-7F69-48B6-B1E3-F6BB281B7E73}.Release|x64.Build.0 = Release|x64
{BC17ACD3-97EB-4D5C-A2C9-574CDAA7576B}.Debug|x64.ActiveCfg = Debug|x64
{BC17ACD3-97EB-4D5C-A2C9-574CDAA7576B}.Debug|x64.Build.0 = Debug|x64
{BC17ACD3-97EB-4D5C-A2C9-574CDAA7576B}.Release|x64.ActiveCfg = Release|x64
{BC17ACD3-97EB-4D5C-A2C9-574CDAA7576B}.Release|x64.Build.0 = Release|x64
{26BD6E61-E36D-464A-A312-4110ADF10083}.Debug|x64.ActiveCfg = Debug|x64
{26BD6E61-E36D-464A-A312-4110ADF10083}.Debug|x64.Build.0 = Debug|x64
{26BD6E61-E36D-464A-A312-4110ADF10083}.Release|x64.ActiveCfg = Release|x64
{26BD6E61-E36D-464A-A312-4110ADF10083}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

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.170.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.170.0\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.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>
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup />

View File

@ -181,6 +181,7 @@
<ClInclude Include="src\Global\Timer.h" />
<ClInclude Include="src\Global\Utils.h" />
<ClInclude Include="src\GUI\Animator.h" />
<ClInclude Include="src\GUI\Bars.h" />
<ClInclude Include="src\GUI\Button.h" />
<ClInclude Include="src\GUI\Chat.h" />
<ClInclude Include="src\GUI\Container.h" />
@ -378,6 +379,7 @@
<ClCompile Include="src\Global\Timer.cpp" />
<ClCompile Include="src\Global\Utils.cpp" />
<ClCompile Include="src\GUI\Animator.cpp" />
<ClCompile Include="src\GUI\Bars.cpp" />
<ClCompile Include="src\GUI\Button.cpp" />
<ClCompile Include="src\GUI\Chat.cpp" />
<ClCompile Include="src\GUI\Container.cpp" />

View File

@ -756,6 +756,9 @@
<ClInclude Include="src\Game\ActiveMechanics.h">
<Filter>src\Game</Filter>
</ClInclude>
<ClInclude Include="src\GUI\Bars.h">
<Filter>src\GUI</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\CGI\BaseGeometry.cpp">
@ -1292,6 +1295,9 @@
<ClCompile Include="src\Game\ActiveMechanics.cpp">
<Filter>src\Game</Filter>
</ClCompile>
<ClCompile Include="src\GUI\Bars.cpp">
<Filter>src\GUI</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="src\Shaders\Lib.hlsl">

View File

@ -80,39 +80,46 @@ void Animation::Update(int alphaTrackCount, PAlphaTrack pAlphaTracks)
if (_playing)
{
if (_blending)
float dt2 = dt;
if (_blending) // Still blending?
{
_blendTimer += dt;
if (_blendTimer >= _blendDuration)
_blendTime += dt2;
if (_blendTime >= _blendDuration)
{
_blending = false;
dt2 -= _blendTime - _blendDuration;
}
}
else if (!_currentMotion.empty())
if (!_blending && !_currentMotion.empty()) // Done blending?
{
RMotionData md = *_pCollection->Find(_C(_currentMotion));
const float len = md._motion.GetDuration();
if (_time < len + 0.5f)
const float duration = md._motion.GetDuration();
_time += dt2;
md._motion.ProcessTriggers(_time, this, GetTriggerStatesArray());
if (_time >= duration)
{
_time += dt;
md._motion.ProcessTriggers(_time, this, GetTriggerStatesArray());
if (_time >= len)
{
if (_pDelegate)
_pDelegate->Animation_OnEnd(_C(_currentMotion));
_time = md._loop ? fmod(_time, len) : len + 1;
md._motion.ResetTriggers(GetTriggerStatesArray()); // New loop, reset triggers.
}
_playing = md._loop; // Continue?
_time = md._loop ? fmod(_time, duration) : duration;
md._motion.ResetTriggers(GetTriggerStatesArray()); // New loop, reset triggers.
if (_pDelegate)
_pDelegate->Animation_OnEnd(_C(_currentMotion)); // This can call BlendTo and change everything.
}
}
}
UpdateSkeleton(alphaTrackCount, pAlphaTracks);
}
void Animation::UpdateSkeleton(int alphaTrackCount, PAlphaTrack pAlphaTracks)
{
if (!_currentMotion.empty() && alphaTrackCount >= 0) // Alpha track (-1) should not modify the skeleton.
{
RMotionData md = *_pCollection->Find(_C(_currentMotion));
int alphaMotionCount = 0;
Skeleton::AlphaMotion alphaMotions[4];
Skeleton::AlphaMotion alphaMotions[s_maxAlphaTracks];
for (int i = 0; i < alphaTrackCount && i < 4; ++i)
for (int i = 0; i < alphaTrackCount && i < s_maxAlphaTracks; ++i)
{
alphaMotions[i]._pMotion = pAlphaTracks[i]._pAnimation->GetMotion(); // Can be in blend state.
alphaMotions[i]._rootBone = pAlphaTracks[i]._rootBone;
@ -123,7 +130,7 @@ void Animation::Update(int alphaTrackCount, PAlphaTrack pAlphaTracks)
if (_blending)
{
md._motion.BindBlendMotion(&_blendMotion, _blendTimer / _blendDuration);
md._motion.BindBlendMotion(&_blendMotion, Math::ApplyEasing(_easing, _blendTime / _blendDuration));
_pSkeleton->ApplyMotion(md._motion, _time, alphaMotionCount, alphaMotions);
}
else
@ -147,20 +154,6 @@ void Animation::BindSkeleton(PSkeleton p)
_pSkeleton = p;
}
void Animation::SetCurrentMotion(CSZ name)
{
// Reset triggers:
if (!_currentMotion.empty())
_pCollection->Find(_C(_currentMotion))->_motion.ResetTriggers(GetTriggerStatesArray());
if (name)
{
_pCollection->Find(name)->_motion.ResetTriggers(GetTriggerStatesArray());
_currentMotion = name;
}
else
_currentMotion.clear();
}
void Animation::Play()
{
_playing = true;
@ -177,22 +170,22 @@ void Animation::Pause()
_playing = false;
}
void Animation::BlendTo(CSZ name, Range<float> duration, int randTime, PMotion pMotionFrom)
void Animation::BlendTo(CSZ name, Range<float> duration, int randTime, PMotion pFromMotion)
{
VERUS_QREF_UTILS;
PMotion pMotion = pMotionFrom;
if (!_currentMotion.empty() || pMotionFrom)
PMotion pMotion = pFromMotion;
if (!_currentMotion.empty() || pFromMotion)
{
if (!pMotionFrom)
if (!pFromMotion)
pMotion = &_pCollection->Find(_C(_currentMotion))->_motion;
if (_blending) // Already blending?
pMotion->BindBlendMotion(&_blendMotion, _blendTimer / _blendDuration);
pMotion->BindBlendMotion(&_blendMotion, Math::ApplyEasing(_easing, _blendTime / _blendDuration));
pMotion->BakeMotionAt(_time, _blendMotion); // Capture current pose.
pMotion->BindBlendMotion(nullptr, 0);
}
if ((0 == duration._max) || (_currentMotion.empty() && name && _prevMotion == name && _blendTimer / _blendDuration < 0.5f))
if ((0 == duration._max) || (_currentMotion.empty() && name && _prevMotion == name && _blendTime / _blendDuration < 0.5f))
{
_blending = false; // Special case for alpha tracks.
}
@ -200,7 +193,7 @@ void Animation::BlendTo(CSZ name, Range<float> duration, int randTime, PMotion p
{
_blending = true;
_blendDuration = (_currentMotion == (name ? name : "")) ? duration._min : duration._max;
_blendTimer = 0;
_blendTime = 0;
}
_prevMotion = _currentMotion;
@ -219,16 +212,18 @@ void Animation::BlendTo(CSZ name, Range<float> duration, int randTime, PMotion p
_time = (randTime >= 0 && pMotion) ? (randTime / 255.f) * pMotion->GetDuration() : 0;
if (_time)
pMotion->SkipTriggers(_time, GetTriggerStatesArray());
_playing = true;
}
bool Animation::BlendToNew(std::initializer_list<CSZ> names, Range<float> duration, int randTime, PMotion pMotionFrom)
bool Animation::BlendToNew(std::initializer_list<CSZ> names, Range<float> duration, int randTime, PMotion pFromMotion)
{
for (auto name : names)
{
if (_currentMotion == name)
return false;
}
BlendTo(*names.begin(), duration, randTime, pMotionFrom);
BlendTo(*names.begin(), duration, randTime, pFromMotion);
return true;
}
@ -251,7 +246,7 @@ PMotion Animation::GetMotion()
p = &_pCollection->Find(_C(_currentMotion))->_motion;
}
if (p && _blending)
p->BindBlendMotion(&_blendMotion, _blendTimer / _blendDuration);
p->BindBlendMotion(&_blendMotion, Math::ApplyEasing(_easing, _blendTime / _blendDuration));
return p;
}
@ -275,9 +270,9 @@ float Animation::GetAlpha(CSZ name) const
fadeOut = matchPrevMotion && !matchCurrentMotion;
}
if (fadeIn)
return Math::SmoothStep(0, 1, _blendTimer / _blendDuration);
return Math::ApplyEasing(_easing, _blendTime / _blendDuration);
if (fadeOut)
return Math::SmoothStep(0, 1, 1 - _blendTimer / _blendDuration);
return Math::ApplyEasing(_easing, 1 - _blendTime / _blendDuration);
}
if (name)
return matchCurrentMotion ? 1.f : 0.f;

View File

@ -46,6 +46,8 @@ namespace verus
class Animation : public MotionDelegate
{
public:
static const int s_maxAlphaTracks = 4;
enum class Edge : int
{
begin = (1 << 0),
@ -67,9 +69,10 @@ namespace verus
String _currentMotion;
String _prevMotion;
Vector<int> _vTriggerStates;
Easing _easing = Easing::quadInOut;
float _time = 0;
float _blendDuration = 0;
float _blendTimer = 0;
float _blendTime = 0;
bool _blending = false;
bool _playing = false;
@ -78,12 +81,13 @@ namespace verus
~Animation();
void Update(int alphaTrackCount = 0, PAlphaTrack pAlphaTracks = nullptr);
void UpdateSkeleton(int alphaTrackCount = 0, PAlphaTrack pAlphaTracks = nullptr);
void BindCollection(PCollection p);
void BindSkeleton(PSkeleton p);
void SetCurrentMotion(CSZ name);
PAnimationDelegate SetDelegate(PAnimationDelegate p) { return Utils::Swap(_pDelegate, p); }
bool IsPlaying() const { return _playing; }
void Play();
void Stop();
void Pause();
@ -91,11 +95,13 @@ namespace verus
Str GetCurrentMotionName() const { return _C(_currentMotion); }
void BlendTo(CSZ name,
Range<float> duration = 0.5f, int randTime = -1, PMotion pMotionFrom = nullptr);
Range<float> duration = 0.5f, int randTime = -1, PMotion pFromMotion = nullptr);
bool BlendToNew(std::initializer_list<CSZ> names,
Range<float> duration = 0.5f, int randTime = -1, PMotion pMotionFrom = nullptr);
Range<float> duration = 0.5f, int randTime = -1, PMotion pFromMotion = nullptr);
bool IsBlending() const { return _blending; }
void SetEasing(Easing easing) { _easing = easing; }
virtual void Motion_OnTrigger(CSZ name, int state) override;
PMotion GetMotion();

View File

@ -4,6 +4,8 @@
using namespace verus;
using namespace verus::Anim;
const float Motion::Bone::s_magicValueForCircle = 0.825f * 0.7071f * 4; // Octagon vertices will form a perfect circle.
// Motion::Bone::Rotation:
Motion::Bone::Rotation::Rotation()
@ -130,9 +132,24 @@ bool Motion::Bone::FindKeyframeTrigger(int frame, int& state) const
void Motion::Bone::ComputeRotationAt(float time, RVector3 euler, RQuat q) const
{
Rotation prev, next, null(Quat(0));
const float alpha = GetAlpha(_mapRot, prev, next, null, time);
q = VMath::slerp(alpha, prev._q, next._q);
int frames[4];
Rotation keys[4];
const float alpha = FindControlPoints(_mapRot, frames, keys, time);
if (_flags & Flags::slerpRot)
{
const Rotation null(Quat(0));
RcRotation prev = (frames[1] == -1) ? null : keys[1];
RcRotation next = (frames[2] == -1) ? null : keys[2];
q = VMath::slerp(alpha, prev._q, next._q);
}
else
{
const Rotation null(Quat(0));
RcRotation prev = (frames[1] == -1) ? null : keys[1];
RcRotation next = (frames[2] == -1) ? null : keys[2];
q = Math::NLerp(alpha, prev._q, next._q);
}
PMotion pBlendMotion = _pMotion->GetBlendMotion();
if (pBlendMotion)
@ -143,7 +160,12 @@ void Motion::Bone::ComputeRotationAt(float time, RVector3 euler, RQuat q) const
Vector3 eulerBlend;
Quat qBlend;
if (pBone->FindKeyframeRotation(0, eulerBlend, qBlend))
q = VMath::slerp(_pMotion->GetBlendAlpha(), qBlend, q);
{
if (_flags & Flags::slerpRot)
q = VMath::slerp(_pMotion->GetBlendAlpha(), qBlend, q);
else
q = Math::NLerp(_pMotion->GetBlendAlpha(), qBlend, q);
}
}
}
@ -152,9 +174,38 @@ void Motion::Bone::ComputeRotationAt(float time, RVector3 euler, RQuat q) const
void Motion::Bone::ComputePositionAt(float time, RVector3 pos) const
{
Vector3 prev, next, null(0);
const float alpha = GetAlpha(_mapPos, prev, next, null, time);
pos = VMath::lerp(alpha, prev, next);
int frames[4];
Vector3 keys[4];
const float alpha = FindControlPoints(_mapPos, frames, keys, time);
if (_flags & Flags::splinePos)
{
const Vector3 null(0);
if (frames[1] == -1) { frames[1] = 0; keys[1] = null; }
if (frames[2] == -1) { frames[2] = 0; keys[2] = null; }
// Extrapolate:
if (frames[0] == -1) { frames[0] = frames[1] * 2 - frames[2]; keys[0] = keys[1] * 2 - keys[2]; }
if (frames[3] == -1) { frames[3] = frames[2] * 2 - frames[1]; keys[3] = keys[2] * 2 - keys[1]; }
// Ratio controls the direction and length of tangents. One keyframe can have different tangent lengths to match speed.
const int intervals[3] =
{
frames[1] - frames[0],
frames[2] - frames[1],
frames[3] - frames[2]
};
const float ratioA = static_cast<float>(intervals[0]) / (intervals[0] + intervals[1]);
const float ratioB = static_cast<float>(intervals[1]) / (intervals[1] + intervals[2]);
const Vector3 tanA = VMath::lerp(ratioA, keys[1] - keys[0], keys[2] - keys[1]) * s_magicValueForCircle * (1 - ratioA);
const Vector3 tanB = VMath::lerp(ratioB, keys[2] - keys[1], keys[3] - keys[2]) * s_magicValueForCircle * ratioB;
pos = glm::hermite(keys[1].GLM(), tanA.GLM(), keys[2].GLM(), tanB.GLM(), alpha);
}
else
{
const Vector3 null(0);
RcVector3 prev = (frames[1] == -1) ? null : keys[1];
RcVector3 next = (frames[2] == -1) ? null : keys[2];
pos = VMath::lerp(alpha, prev, next);
}
PMotion pBlendMotion = _pMotion->GetBlendMotion();
if (pBlendMotion)
@ -171,9 +222,38 @@ void Motion::Bone::ComputePositionAt(float time, RVector3 pos) const
void Motion::Bone::ComputeScaleAt(float time, RVector3 scale) const
{
Vector3 prev, next, null(1, 1, 1);
const float alpha = GetAlpha(_mapScale, prev, next, null, time);
scale = VMath::lerp(alpha, prev, next);
int frames[4];
Vector3 keys[4];
const float alpha = FindControlPoints(_mapScale, frames, keys, time);
if (_flags & Flags::splineScale)
{
const Vector3 null(1, 1, 1);
if (frames[1] == -1) { frames[1] = 0; keys[1] = null; }
if (frames[2] == -1) { frames[2] = 0; keys[2] = null; }
// Extrapolate:
if (frames[0] == -1) { frames[0] = frames[1] * 2 - frames[2]; keys[0] = keys[1] * 2 - keys[2]; }
if (frames[3] == -1) { frames[3] = frames[2] * 2 - frames[1]; keys[3] = keys[2] * 2 - keys[1]; }
// Ratio controls the direction and length of tangents. One keyframe can have different tangent lengths to match speed.
const int intervals[3] =
{
frames[1] - frames[0],
frames[2] - frames[1],
frames[3] - frames[2]
};
const float ratioA = static_cast<float>(intervals[0]) / (intervals[0] + intervals[1]);
const float ratioB = static_cast<float>(intervals[1]) / (intervals[1] + intervals[2]);
const Vector3 tanA = VMath::lerp(ratioA, keys[1] - keys[0], keys[2] - keys[1]) * s_magicValueForCircle * (1 - ratioA);
const Vector3 tanB = VMath::lerp(ratioB, keys[2] - keys[1], keys[3] - keys[2]) * s_magicValueForCircle * ratioB;
scale = glm::hermite(keys[1].GLM(), tanA.GLM(), keys[2].GLM(), tanB.GLM(), alpha);
}
else
{
const Vector3 null(1, 1, 1);
RcVector3 prev = (frames[1] == -1) ? null : keys[1];
RcVector3 next = (frames[2] == -1) ? null : keys[2];
scale = VMath::lerp(alpha, prev, next);
}
PMotion pBlendMotion = _pMotion->GetBlendMotion();
if (pBlendMotion)
@ -195,7 +275,7 @@ void Motion::Bone::ComputeTriggerAt(float time, int& state) const
state = 0;
return;
}
const int frame = int(_pMotion->GetFps() * time);
const int frame = static_cast<int>(_pMotion->GetFps() * time);
TMapTrigger::const_iterator it = _mapTrigger.upper_bound(frame); // Find frame after 'time'.
if (it != _mapTrigger.begin())
{
@ -211,22 +291,22 @@ void Motion::Bone::ComputeTriggerAt(float time, int& state) const
void Motion::Bone::ComputeMatrixAt(float time, RTransform3 mat)
{
Quat q;
Vector3 scale, euler, pos;
Vector3 euler, pos, scale;
ComputeRotationAt(time, euler, q);
ComputePositionAt(time, pos);
ComputeScaleAt(time, scale);
mat = VMath::appendScale(Transform3(q, pos), scale);
}
void Motion::Bone::MoveKeyframe(int direction, Type type, int frame)
void Motion::Bone::MoveKeyframe(int direction, Channel channel, int frame)
{
const int frameDest = (direction >= 0) ? frame + 1 : frame - 1;
if (frameDest < 0 || frameDest >= _pMotion->GetFrameCount())
return;
switch (type)
switch (channel)
{
case Type::rotation:
case Channel::rotation:
{
VERUS_IF_FOUND_IN(TMapRot, _mapRot, frame, it)
{
@ -238,7 +318,7 @@ void Motion::Bone::MoveKeyframe(int direction, Type type, int frame)
}
}
break;
case Type::position:
case Channel::position:
{
VERUS_IF_FOUND_IN(TMapPos, _mapPos, frame, it)
{
@ -250,7 +330,7 @@ void Motion::Bone::MoveKeyframe(int direction, Type type, int frame)
}
}
break;
case Type::scale:
case Channel::scale:
{
VERUS_IF_FOUND_IN(TMapScale, _mapScale, frame, it)
{
@ -262,7 +342,7 @@ void Motion::Bone::MoveKeyframe(int direction, Type type, int frame)
}
}
break;
case Type::trigger:
case Channel::trigger:
{
VERUS_IF_FOUND_IN(TMapTrigger, _mapTrigger, frame, it)
{
@ -277,8 +357,10 @@ void Motion::Bone::MoveKeyframe(int direction, Type type, int frame)
}
}
void Motion::Bone::Serialize(IO::RStream stream)
void Motion::Bone::Serialize(IO::RStream stream, UINT16 version)
{
stream << _flags;
const int rotKeyframeCount = Utils::Cast32(_mapRot.size());
const int posKeyframeCount = Utils::Cast32(_mapPos.size());
const int scaleKeyframeCount = Utils::Cast32(_mapScale.size());
@ -313,8 +395,12 @@ void Motion::Bone::Serialize(IO::RStream stream)
}
}
void Motion::Bone::Deserialize(IO::RStream stream)
void Motion::Bone::Deserialize(IO::RStream stream, UINT16 version)
{
_flags = Flags::none;
if (version >= 0x0102)
stream >> _flags;
int rotKeyframeCount, posKeyframeCount, scaleKeyframeCount, triggerKeyframeCount, frame, state;
Vector3 temp;
Quat q;
@ -603,6 +689,18 @@ void Motion::Bone::Scatter(int srcFrom, int srcTo, int dMin, int dMax)
}
}
bool Motion::Bone::SpaceTimeSync(Channel channel, int fromFrame, int toFrame)
{
if (fromFrame == toFrame) // Endpoints must not match.
return false;
switch (channel)
{
case Channel::position: return SpaceTimeSyncTemplate(_mapPos, fromFrame, toFrame);
case Channel::scale: return SpaceTimeSyncTemplate(_mapScale, fromFrame, toFrame);
}
return false;
}
int Motion::Bone::GetLastKeyframe() const
{
int frame = -1;
@ -708,7 +806,7 @@ void Motion::Serialize(IO::RStream stream)
{
RBone bone = kv.second;
stream.WriteString(_C(bone.GetName()));
bone.Serialize(stream);
bone.Serialize(stream, version);
}
}
@ -721,7 +819,7 @@ void Motion::Deserialize(IO::RStream stream)
UINT16 version = 0;
stream >> version;
if (s_xanVersion != version)
if (s_xanVersion < version)
throw VERUS_RECOVERABLE << "Deserialize(), invalid XAN version";
stream >> _frameCount;
@ -743,7 +841,7 @@ void Motion::Deserialize(IO::RStream stream)
{
stream.ReadString(buffer);
PBone pBone = InsertBone(buffer);
pBone->Deserialize(stream);
pBone->Deserialize(stream, version);
}
}
@ -902,7 +1000,7 @@ void Motion::ComputePlaybackSpeed(float duration)
_playbackSpeedInv = 1 / _playbackSpeed;
}
void Motion::Exec(CSZ code)
void Motion::Exec(CSZ code, PBone pBone, Bone::Channel channel)
{
if (Str::StartsWith(code, "copy "))
{
@ -962,6 +1060,12 @@ void Motion::Exec(CSZ code)
}
}
}
if (pBone && Str::StartsWith(code, "sts "))
{
int from = 0, to = 0;
sscanf(code, "%*s %d %d", &from, &to);
pBone->SpaceTimeSync(channel, from, to);
}
}
int Motion::GetLastKeyframe() const

View File

@ -22,8 +22,28 @@ namespace verus
public:
class Bone : public AllocatorAware
{
public:
enum class Channel : int
{
rotation,
position,
scale,
trigger
};
enum class Flags : UINT32
{
none = 0,
slerpRot = (1 << 0),
splinePos = (1 << 1),
splineScale = (1 << 2)
};
private:
friend class Motion;
static const float s_magicValueForCircle;
class Rotation
{
public:
@ -47,63 +67,124 @@ namespace verus
TMapScale _mapScale; //!< Scaling keyframes.
TMapTrigger _mapTrigger; //!< Trigger keyframes.
int _lastTriggerState = 0;
Flags _flags = Flags::none;
template<typename TMap, typename T>
float GetAlpha(const TMap& m, T& prev, T& next, const T& null, float time) const
float FindControlPoints(const TMap& m, int frames[4], T keys[4], float time) const
{
frames[0] = frames[1] = frames[2] = frames[3] = -1;
if (m.empty()) // No frames at all, so return null.
{
prev = null;
next = null;
return 0;
}
time = Math::Max(0.f, time); // Negative time is not allowed.
float alpha;
const int frame = static_cast<int>(_pMotion->GetFps() * time); // Frame is before or at 'time'.
typename TMap::const_iterator it = m.upper_bound(frame); // Find frame after 'time'.
if (it != m.end()) // There are frames greater (after 'time'):
if (it != m.cend()) // There are frames greater (after 'time'):
{
if (it != m.begin()) // And there are less than (before 'time'), full interpolation:
if (it != m.cbegin()) // And there are less than (before 'time'), full interpolation:
{
typename TMap::const_iterator itPrev = it;
itPrev--;
const float prevTime = itPrev->first * _pMotion->GetFpsInv();
const float nextTime = it->first * _pMotion->GetFpsInv();
const float delta = nextTime - prevTime;
const float offset = nextTime - time;
alpha = 1 - offset / delta;
prev = itPrev->second;
next = it->second;
frames[1] = itPrev->first;
keys[1] = itPrev->second;
if (itPrev != m.cbegin())
{
itPrev--;
frames[0] = itPrev->first;
keys[0] = itPrev->second;
}
frames[2] = it->first;
keys[2] = it->second;
it++;
if (it != m.cend())
{
frames[3] = it->first;
keys[3] = it->second;
}
alpha = (time - (frames[1] * _pMotion->GetFpsInv())) / ((frames[2] - frames[1]) * _pMotion->GetFpsInv());
}
else // But there are no less than:
{
const float nextTime = it->first * _pMotion->GetFpsInv();
const float delta = nextTime;
const float offset = time;
alpha = offset / delta;
prev = null;
next = it->second;
frames[2] = it->first;
keys[2] = it->second;
it++;
if (it != m.cend())
{
frames[3] = it->first;
keys[3] = it->second;
}
alpha = time / (frames[2] * _pMotion->GetFpsInv());
}
}
else // There are no frames greater, but there are less than:
{
it--;
frames[1] = it->first;
keys[1] = it->second;
if (it != m.cbegin())
{
it--;
frames[0] = it->first;
keys[0] = it->second;
}
alpha = 0;
prev = it->second;
next = null;
}
return alpha;
}
public:
enum class Type : int
template<typename TMap>
static bool SpaceTimeSyncTemplate(TMap& m, int fromFrame, int toFrame)
{
rotation,
position,
scale,
trigger
};
const auto itFrom = m.find(fromFrame);
const auto itTo = m.find(toFrame);
if (m.end() == itFrom || m.end() == itTo) // Endpoints must exist.
return false;
const int totalFrames = toFrame - fromFrame;
auto it = itFrom;
Vector<float> vSegments;
vSegments.reserve(8);
float totalDist = 0;
while (it != itTo)
{
const auto& posA = it->second;
it++;
const auto& posB = it->second;
const float d = VMath::dist(Point3(posA), Point3(posB));
vSegments.push_back(d);
totalDist += d;
}
if (totalDist < 1e-4f) // Distance is too small.
return false;
const float invTotalDist = 1.f / totalDist;
TMap tempMap;
it = itFrom;
it++;
while (it != itTo) // Copy all keys in-between, delete original keys.
{
tempMap[it->first] = std::move(it->second);
it = m.erase(it);
}
it = tempMap.begin();
float distAcc = 0;
int i = 0;
while (it != tempMap.end())
{
distAcc += vSegments[i++];
int syncedFrame = fromFrame + static_cast<int>(totalFrames * (distAcc * invTotalDist) + 0.5f);
while (m.end() != m.find(syncedFrame)) // Frame already occupied?
syncedFrame++;
m[syncedFrame] = it->second;
it++;
}
return true;
}
public:
Bone(Motion* pMotion = nullptr);
~Bone();
@ -113,39 +194,46 @@ namespace verus
int GetLastTriggerState() const { return _lastTriggerState; }
void SetLastTriggerState(int state) { _lastTriggerState = state; }
Flags GetFlags() const { return _flags; }
void SetFlags(Flags flags) { _flags = flags; }
void DeleteAll();
// Insert:
void InsertKeyframeRotation(int frame, RcQuat q);
void InsertKeyframeRotation(int frame, RcVector3 euler);
void InsertKeyframePosition(int frame, RcVector3 pos);
void InsertKeyframeScale(int frame, RcVector3 scale);
void InsertKeyframeTrigger(int frame, int state);
// Delete:
void DeleteKeyframeRotation(int frame);
void DeleteKeyframePosition(int frame);
void DeleteKeyframeScale(int frame);
void DeleteKeyframeTrigger(int frame);
// Find:
bool FindKeyframeRotation(int frame, RVector3 euler, RQuat q) const;
bool FindKeyframePosition(int frame, RVector3 pos) const;
bool FindKeyframeScale(int frame, RVector3 scale) const;
bool FindKeyframeTrigger(int frame, int& state) const;
// Compute:
void ComputeRotationAt(float time, RVector3 euler, RQuat q) const;
void ComputePositionAt(float time, RVector3 pos) const;
void ComputeScaleAt(float time, RVector3 scale) const;
void ComputeTriggerAt(float time, int& state) const;
void ComputeMatrixAt(float time, RTransform3 mat);
void MoveKeyframe(int direction, Type type, int frame);
void MoveKeyframe(int direction, Channel channel, int frame);
int GetRotationKeyCount() const { return Utils::Cast32(_mapRot.size()); }
int GetPositionKeyCount() const { return Utils::Cast32(_mapPos.size()); }
int GetScaleKeyCount() const { return Utils::Cast32(_mapScale.size()); }
int GetTriggerKeyCount() const { return Utils::Cast32(_mapTrigger.size()); }
VERUS_P(void Serialize(IO::RStream stream));
VERUS_P(void Deserialize(IO::RStream stream));
VERUS_P(void Serialize(IO::RStream stream, UINT16 version));
VERUS_P(void Deserialize(IO::RStream stream, UINT16 version));
void DeleteRedundantKeyframes();
void DeleteOddKeyframes();
@ -157,12 +245,14 @@ namespace verus
void Scatter(int srcFrom, int srcTo, int dMin, int dMax);
bool SpaceTimeSync(Channel channel, int fromFrame, int toFrame);
int GetLastKeyframe() const;
};
VERUS_TYPEDEFS(Bone);
private:
static const int s_xanVersion = 0x0101;
static const int s_xanVersion = 0x0102;
static const int s_maxFps = 10000;
static const int s_maxBones = 10000;
static const int s_maxFrames = 32 * 1024 * 1024;
@ -171,9 +261,9 @@ namespace verus
TMapBones _mapBones;
Motion* _pBlendMotion = nullptr;
int _frameCount = 50;
int _fps = 10;
float _fpsInv = 0.1f;
int _frameCount = 60;
int _fps = 12;
float _fpsInv = 1 / 12.f;
float _blendAlpha = 0;
float _playbackSpeed = 1;
float _playbackSpeedInv = 1;
@ -206,6 +296,16 @@ namespace verus
void DeleteAllBones();
PBone FindBone(CSZ name);
template<typename T>
void ForEachBone(const T& fn)
{
for (auto& kv : _mapBones)
{
if (Continue::no == fn(kv.second))
break;
}
}
void Serialize(IO::RStream stream);
void Deserialize(IO::RStream stream);
@ -233,7 +333,7 @@ namespace verus
void ComputePlaybackSpeed(float duration);
bool IsReversed() const { return _reversed; }
void Exec(CSZ code);
void Exec(CSZ code, PBone pBone = nullptr, Bone::Channel channel = Bone::Channel::rotation);
int GetLastKeyframe() const;
};

View File

@ -117,14 +117,14 @@ namespace verus
void ForEachBone(const F& fn)
{
for (auto& kv : _mapBones)
if (Continue::yes != fn(kv.second))
if (Continue::no == fn(kv.second))
return;
}
template<typename F>
void ForEachBone(const F& fn) const
{
for (const auto& kv : _mapBones)
if (Continue::yes != fn(kv.second))
if (Continue::no == fn(kv.second))
return;
}

View File

@ -4,13 +4,108 @@
using namespace verus;
using namespace verus::App;
// QualitySettings:
bool QualitySettings::operator==(RcQualitySettings that) const
{
return
_displayOffscreenDraw == that._displayOffscreenDraw &&
_displayOffscreenScale == that._displayOffscreenScale &&
_gpuAnisotropyLevel == that._gpuAnisotropyLevel &&
_gpuAntialiasingLevel == that._gpuAntialiasingLevel &&
_gpuShaderQuality == that._gpuShaderQuality &&
_gpuTessellation == that._gpuTessellation &&
_gpuTextureLodLevel == that._gpuTextureLodLevel &&
_gpuTrilinearFilter == that._gpuTrilinearFilter &&
_postProcessBloom == that._postProcessBloom &&
_postProcessCinema == that._postProcessCinema &&
_postProcessLightShafts == that._postProcessLightShafts &&
_postProcessMotionBlur == that._postProcessMotionBlur &&
_postProcessSSAO == that._postProcessSSAO &&
_postProcessSSR == that._postProcessSSR &&
_sceneAmbientOcclusion == that._sceneAmbientOcclusion &&
_sceneGrassDensity == that._sceneGrassDensity &&
_sceneShadowQuality == that._sceneShadowQuality &&
_sceneWaterQuality == that._sceneWaterQuality;
}
void QualitySettings::SetQuality(OverallQuality q)
{
if (OverallQuality::custom == q)
return;
*this = QualitySettings(); // Start with medium.
switch (q)
{
case OverallQuality::low:
_gpuAnisotropyLevel = 2;
_gpuShaderQuality = Quality::low;
_gpuTrilinearFilter = false;
_postProcessBloom = false;
_postProcessLightShafts = false;
_postProcessSSAO = false;
_sceneGrassDensity = 600;
_sceneShadowQuality = Quality::low;
_sceneWaterQuality = WaterQuality::solidColor;
break;
case OverallQuality::medium:
break;
case OverallQuality::high:
_gpuAnisotropyLevel = 8;
_gpuShaderQuality = Quality::high;
_postProcessCinema = true;
_sceneAmbientOcclusion = true;
_sceneGrassDensity = 900;
_sceneShadowQuality = Quality::high;
_sceneWaterQuality = WaterQuality::trueWavesReflection;
break;
case OverallQuality::ultra:
_gpuAnisotropyLevel = 16;
_gpuShaderQuality = Quality::ultra;
_postProcessCinema = true;
_postProcessMotionBlur = true;
_postProcessSSR = true;
_sceneAmbientOcclusion = true;
_sceneGrassDensity = 1000;
_sceneShadowQuality = Quality::ultra;
_sceneWaterQuality = WaterQuality::trueWavesRefraction;
break;
}
}
QualitySettings::OverallQuality QualitySettings::DetectQuality() const
{
QualitySettings reference;
reference.SetQuality(OverallQuality::low);
if (*this == reference)
return OverallQuality::low;
reference.SetQuality(OverallQuality::medium);
if (*this == reference)
return OverallQuality::medium;
reference.SetQuality(OverallQuality::high);
if (*this == reference)
return OverallQuality::high;
reference.SetQuality(OverallQuality::ultra);
if (*this == reference)
return OverallQuality::ultra;
return OverallQuality::custom;
}
// Settings:
Settings::Settings()
{
#ifdef _WIN32
if (0x419 == GetUserDefaultUILanguage())
_uiLang = "RU";
#endif
SetQuality(Quality::medium);
SetQuality(OverallQuality::medium);
}
Settings::~Settings()
@ -47,6 +142,8 @@ void Settings::ParseCommandLineArgs(int argc, char* argv[])
_commandLine._windowed = true;
if (IsArg(i, "--bwnd"))
_commandLine._borderlessWindowed = true;
if (IsArg(i, "--restarted"))
_commandLine._restarted = true;
}
SetFilename("Settings.json");
@ -57,73 +154,30 @@ void Settings::ParseCommandLineArgs(int argc, char* argv[])
if (IsArg(i, "--q-low"))
{
const bool ret = IO::FileSystem::Delete(_C(pathname));
SetQuality(Quality::low);
SetQuality(OverallQuality::low);
}
if (IsArg(i, "--q-medium"))
{
const bool ret = IO::FileSystem::Delete(_C(pathname));
SetQuality(Quality::medium);
SetQuality(OverallQuality::medium);
}
if (IsArg(i, "--q-high"))
{
const bool ret = IO::FileSystem::Delete(_C(pathname));
SetQuality(Quality::high);
SetQuality(OverallQuality::high);
}
if (IsArg(i, "--q-ultra"))
{
const bool ret = IO::FileSystem::Delete(_C(pathname));
SetQuality(Quality::ultra);
SetQuality(OverallQuality::ultra);
}
}
}
void Settings::SetQuality(Quality q)
{
_quality = q;
switch (q)
{
case Quality::low:
_gpuAnisotropyLevel = 2;
_gpuShaderQuality = Quality::low;
_gpuTrilinearFilter = false;
_postProcessBloom = false;
_postProcessGodRays = false;
_postProcessSSAO = false;
_sceneGrassDensity = 600;
_sceneShadowQuality = Quality::low;
_sceneWaterQuality = WaterQuality::solidColor;
break;
case Quality::medium:
break;
case Quality::high:
_gpuAnisotropyLevel = 8;
_gpuShaderQuality = Quality::high;
_postProcessCinema = true;
_sceneAmbientOcclusion = true;
_sceneGrassDensity = 900;
_sceneShadowQuality = Quality::high;
_sceneWaterQuality = WaterQuality::trueWavesReflection;
break;
case Quality::ultra:
_gpuAnisotropyLevel = 16;
_gpuShaderQuality = Quality::ultra;
_postProcessCinema = true;
_postProcessMotionBlur = true;
_postProcessSSR = true;
_sceneAmbientOcclusion = true;
_sceneGrassDensity = 1000;
_sceneShadowQuality = Quality::ultra;
_sceneWaterQuality = WaterQuality::trueWavesRefraction;
break;
}
}
void Settings::Load()
{
Json::Load();
_quality = static_cast<Quality>(GetI("quality", +_quality));
_displayAllowHighDPI = GetB("displayAllowHighDPI", _displayAllowHighDPI);
_displayFOV = GetF("displayFOV", _displayFOV);
_displayMode = static_cast<DisplayMode>(GetI("displayMode", +_displayMode));
@ -142,7 +196,7 @@ void Settings::Load()
_inputMouseSensitivity = GetF("inputMouseSensitivity", _inputMouseSensitivity);
_postProcessBloom = GetB("postProcessBloom", _postProcessBloom);
_postProcessCinema = GetB("postProcessCinema", _postProcessCinema);
_postProcessGodRays = GetB("postProcessGodRays", _postProcessGodRays);
_postProcessLightShafts = GetB("postProcessLightShafts", _postProcessLightShafts);
_postProcessMotionBlur = GetB("postProcessMotionBlur", _postProcessMotionBlur);
_postProcessSSAO = GetB("postProcessSSAO", _postProcessSSAO);
_postProcessSSR = GetB("postProcessSSR", _postProcessSSR);
@ -173,7 +227,7 @@ void Settings::HandleCommandLineArgs()
{
switch (_commandLine._gapi)
{
case 0: _gapi = 0; break;
case 0: _gapi = 0; break;
case 12: _gapi = 12; break;
}
@ -185,8 +239,8 @@ void Settings::HandleCommandLineArgs()
if (_commandLine._windowed)
{
_displayMode = DisplayMode::windowed;
_displaySizeHeight = 720;
_displaySizeWidth = 1280;
_displaySizeHeight = 720;
}
if (_commandLine._borderlessWindowed)
{
@ -197,10 +251,9 @@ void Settings::HandleCommandLineArgs()
void Settings::Validate()
{
_quality = Math::Clamp(_quality, Quality::low, Quality::ultra);
_displayFOV = Math::Clamp<float>(_displayFOV, 60, 90);
_displaySizeHeight = Math::Clamp(_displaySizeHeight, 270, 0x2000);
_displaySizeWidth = Math::Clamp(_displaySizeWidth, 480, 0x2000);
_displaySizeHeight = Math::Clamp(_displaySizeHeight, 270, 0x2000);
_gpuAnisotropyLevel = Math::Clamp(_gpuAnisotropyLevel, 0, 16);
_gpuAntialiasingLevel = Math::Clamp(_gpuAntialiasingLevel, 0, 16);
_gpuShaderQuality = Math::Clamp(_gpuShaderQuality, Quality::low, Quality::ultra);
@ -217,7 +270,6 @@ void Settings::Save()
{
Clear();
Set("quality", +_quality);
Set("displayAllowHighDPI", _displayAllowHighDPI);
Set("displayFOV", _displayFOV);
Set("displayMode", +_displayMode);
@ -236,7 +288,7 @@ void Settings::Save()
Set("inputMouseSensitivity", _inputMouseSensitivity);
Set("postProcessBloom", _postProcessBloom);
Set("postProcessCinema", _postProcessCinema);
Set("postProcessGodRays", _postProcessGodRays);
Set("postProcessLightShafts", _postProcessLightShafts);
Set("postProcessMotionBlur", _postProcessMotionBlur);
Set("postProcessSSAO", _postProcessSSAO);
Set("postProcessSSR", _postProcessSSR);

View File

@ -12,6 +12,62 @@ namespace verus
borderlessWindowed
};
class QualitySettings
{
public:
enum class OverallQuality : int
{
custom,
low,
medium,
high,
ultra
};
enum class Quality : int
{
low,
medium,
high,
ultra
};
enum class WaterQuality : int
{
solidColor,
simpleReflection,
distortedReflection,
trueWavesReflection,
trueWavesRefraction
};
public:
bool _displayOffscreenDraw = true;
float _displayOffscreenScale = 1;
int _gpuAnisotropyLevel = 4;
int _gpuAntialiasingLevel = 0;
Quality _gpuShaderQuality = Quality::medium;
bool _gpuTessellation = false;
int _gpuTextureLodLevel = 0;
bool _gpuTrilinearFilter = true;
bool _postProcessBloom = true;
bool _postProcessCinema = false;
bool _postProcessLightShafts = true;
bool _postProcessMotionBlur = false;
bool _postProcessSSAO = true;
bool _postProcessSSR = false;
bool _sceneAmbientOcclusion = false;
int _sceneGrassDensity = 800;
Quality _sceneShadowQuality = Quality::medium;
WaterQuality _sceneWaterQuality = WaterQuality::solidColor;
bool operator==(const QualitySettings& that) const;
void SetQuality(OverallQuality q);
OverallQuality DetectQuality() const;
};
VERUS_TYPEDEFS(QualitySettings);
// Capacity is per frame. 3 frames are buffered.
class Limits
{
@ -49,7 +105,7 @@ namespace verus
};
VERUS_TYPEDEFS(Limits);
class Settings : public Singleton<Settings>, IO::Json
class Settings : public QualitySettings, public Singleton<Settings>, IO::Json
{
typedef Map<String, String> TMapLocalizedStrings;
@ -62,23 +118,7 @@ namespace verus
bool _exclusiveFullscreen = false;
bool _windowed = false;
bool _borderlessWindowed = false;
};
enum class Quality : int
{
low,
medium,
high,
ultra
};
enum class WaterQuality : int
{
solidColor,
simpleReflection,
distortedReflection,
trueWavesReflection,
trueWavesRefraction
bool _restarted = false;
};
enum Platform : int
@ -87,47 +127,27 @@ namespace verus
uwp
};
Quality _quality = Quality::medium;
bool _displayAllowHighDPI = true;
float _displayFOV = 70;
DisplayMode _displayMode = DisplayMode::windowed;
bool _displayOffscreenDraw = true;
float _displayOffscreenScale = 1;
int _displaySizeHeight = 720;
int _displaySizeWidth = 1280;
bool _displayVSync = true;
int _gapi = 0;
int _gpuAnisotropyLevel = 4;
int _gpuAntialiasingLevel = 0;
Quality _gpuShaderQuality = Quality::medium;
bool _gpuTessellation = false;
int _gpuTextureLodLevel = 0;
bool _gpuTrilinearFilter = true;
float _inputMouseSensitivity = 1;
bool _physicsSupportDebugDraw = false;
bool _postProcessBloom = true;
bool _postProcessCinema = false;
bool _postProcessGodRays = true;
bool _postProcessMotionBlur = false;
bool _postProcessSSAO = true;
bool _postProcessSSR = false;
bool _sceneAmbientOcclusion = false;
int _sceneGrassDensity = 800;
Quality _sceneShadowQuality = Quality::medium;
WaterQuality _sceneWaterQuality = WaterQuality::solidColor;
String _uiLang = "EN";
CommandLine _commandLine;
Limits _limits;
String _imguiFont;
float _highDpiScale = 1;
Platform _platform = Platform::classic;
bool _displayAllowHighDPI = true;
float _displayFOV = 70;
DisplayMode _displayMode = DisplayMode::windowed;
int _displaySizeHeight = 720;
int _displaySizeWidth = 1280;
bool _displayVSync = true;
int _gapi = 0;
float _inputMouseSensitivity = 1;
bool _physicsSupportDebugDraw = false;
String _uiLang = "EN";
CommandLine _commandLine;
Limits _limits;
String _imguiFont;
float _highDpiScale = 1;
Platform _platform = Platform::classic;
Settings();
~Settings();
void ParseCommandLineArgs(int argc, wchar_t* argv[]);
void ParseCommandLineArgs(int argc, char* argv[]);
void SetQuality(Quality q);
void Load();
void HandleHighDpi();

View File

@ -58,7 +58,7 @@ namespace verus
int i = 0;
for (auto& x : _vCommands)
{
if (Continue::yes != fn(x, i < _nextUndo))
if (Continue::no == fn(x, i < _nextUndo))
return;
i++;
}

View File

@ -112,6 +112,7 @@ void StreamPlayer::Update()
void StreamPlayer::AddTrack(PTrack pTrack)
{
VERUS_RT_ASSERT(IsInitialized());
{
VERUS_LOCK(*this);
_vTracks.push_back(pTrack);

View File

@ -139,15 +139,18 @@ bool DebugDraw::AddPoint(
bool DebugDraw::AddLine(
RcPoint3 posA,
RcPoint3 posB,
UINT32 color)
UINT32 colorA,
UINT32 colorB)
{
if (!colorB)
colorB = colorA;
const int at = _offset + _vertCount;
if (at + 2 > _maxVerts)
return false;
posA.ToArray3(_vDynamicBuffer[at]._pos);
Utils::CopyColor(_vDynamicBuffer[at]._color, color);
Utils::CopyColor(_vDynamicBuffer[at]._color, colorA);
posB.ToArray3(_vDynamicBuffer[at + 1]._pos);
Utils::CopyColor(_vDynamicBuffer[at + 1]._color, color);
Utils::CopyColor(_vDynamicBuffer[at + 1]._color, colorB);
_vertCount += 2;
_peakLoad = Math::Max(_peakLoad, at + 2);
return true;

View File

@ -64,7 +64,8 @@ namespace verus
bool AddLine(
RcPoint3 posA,
RcPoint3 posB,
UINT32 color);
UINT32 colorA,
UINT32 colorB = 0);
bool AddTriangle(
RcPoint3 posA,
RcPoint3 posB,

View File

@ -24,7 +24,7 @@ bool Renderer::IsLoaded()
return IsValidSingleton() && !!I()._pBaseRenderer;
}
void Renderer::Init(PRendererDelegate pDelegate)
void Renderer::Init(PRendererDelegate pDelegate, bool allowInitShaders)
{
VERUS_INIT();
VERUS_QREF_CONST_SETTINGS;
@ -34,6 +34,10 @@ void Renderer::Init(PRendererDelegate pDelegate)
_pRendererDelegate = pDelegate;
_allowInitShaders = allowInitShaders;
VERUS_RT_ASSERT(_allowInitShaders || !settings._displayOffscreenDraw);
CSZ dll = "RendererVulkan.dll";
switch (settings._gapi)
{
@ -54,10 +58,11 @@ void Renderer::Init(PRendererDelegate pDelegate)
_commandBuffer.Init();
// Draw directly to swap chain buffer:
const RP::Attachment::LoadOp loadOp = settings._displayOffscreenDraw ? RP::Attachment::LoadOp::dontCare : RP::Attachment::LoadOp::clear;
const RP::Attachment::LoadOp colorLoadOp = settings._displayOffscreenDraw ? RP::Attachment::LoadOp::dontCare : RP::Attachment::LoadOp::clear;
const RP::Attachment::LoadOp depthLoadOp = settings._displayOffscreenDraw ? RP::Attachment::LoadOp::load : RP::Attachment::LoadOp::clear;
_rphSwapChain = _pBaseRenderer->CreateRenderPass(
{
RP::Attachment("Color", Format::srgbB8G8R8A8).SetLoadOp(loadOp).Layout(ImageLayout::undefined, ImageLayout::presentSrc)
RP::Attachment("Color", Format::srgbB8G8R8A8).SetLoadOp(colorLoadOp).Layout(ImageLayout::undefined, ImageLayout::presentSrc)
},
{
RP::Subpass("Sp0").Color({RP::Ref("Color", ImageLayout::colorAttachment)})
@ -65,8 +70,8 @@ void Renderer::Init(PRendererDelegate pDelegate)
{});
_rphSwapChainWithDepth = _pBaseRenderer->CreateRenderPass(
{
RP::Attachment("Color", Format::srgbB8G8R8A8).SetLoadOp(loadOp).Layout(ImageLayout::undefined, ImageLayout::presentSrc),
RP::Attachment("Depth", Format::unormD24uintS8).Layout(ImageLayout::depthStencilAttachment),
RP::Attachment("Color", Format::srgbB8G8R8A8).SetLoadOp(colorLoadOp).Layout(ImageLayout::undefined, ImageLayout::presentSrc),
RP::Attachment("Depth", Format::unormD24uintS8).SetLoadOp(depthLoadOp).Layout(ImageLayout::depthStencilAttachment),
},
{
RP::Subpass("Sp0").Color(
@ -109,32 +114,37 @@ void Renderer::Init(PRendererDelegate pDelegate)
geoDesc._pStrides = strides;
_geoQuad.Init(geoDesc);
_shader[SHADER_GENERATE_MIPS].Init("[Shaders]:GenerateMips.hlsl");
_shader[SHADER_GENERATE_MIPS]->CreateDescriptorSet(0, &_ubGenerateMips, sizeof(_ubGenerateMips), settings.GetLimits()._generateMips_ubCapacity,
{
Sampler::linearClampMipN,
Sampler::storage,
Sampler::storage,
Sampler::storage,
Sampler::storage
},
ShaderStageFlags::cs);
_shader[SHADER_GENERATE_MIPS]->CreatePipelineLayout();
if (_allowInitShaders)
{
_shader[SHADER_GENERATE_MIPS].Init("[Shaders]:GenerateMips.hlsl");
_shader[SHADER_GENERATE_MIPS]->CreateDescriptorSet(0, &_ubGenerateMips, sizeof(_ubGenerateMips), settings.GetLimits()._generateMips_ubCapacity,
{
Sampler::linearClampMipN,
Sampler::storage,
Sampler::storage,
Sampler::storage,
Sampler::storage
},
ShaderStageFlags::cs);
_shader[SHADER_GENERATE_MIPS]->CreatePipelineLayout();
_shader[SHADER_QUAD].Init("[Shaders]:Quad.hlsl");
_shader[SHADER_QUAD]->CreateDescriptorSet(0, &_ubQuadVS, sizeof(_ubQuadVS), settings.GetLimits()._quad_ubVSCapacity, {}, ShaderStageFlags::vs);
_shader[SHADER_QUAD]->CreateDescriptorSet(1, &_ubQuadFS, sizeof(_ubQuadFS), settings.GetLimits()._quad_ubFSCapacity, { Sampler::linearClampMipN }, ShaderStageFlags::fs);
_shader[SHADER_QUAD]->CreatePipelineLayout();
_shader[SHADER_QUAD].Init("[Shaders]:Quad.hlsl");
_shader[SHADER_QUAD]->CreateDescriptorSet(0, &_ubQuadVS, sizeof(_ubQuadVS), settings.GetLimits()._quad_ubVSCapacity, {}, ShaderStageFlags::vs);
_shader[SHADER_QUAD]->CreateDescriptorSet(1, &_ubQuadFS, sizeof(_ubQuadFS), settings.GetLimits()._quad_ubFSCapacity, { Sampler::linearClampMipN }, ShaderStageFlags::fs);
_shader[SHADER_QUAD]->CreatePipelineLayout();
}
if (_allowInitShaders)
{
PipelineDesc pipeDesc(_shader[SHADER_GENERATE_MIPS], "#");
_pipe[PIPE_GENERATE_MIPS].Init(pipeDesc);
}
if (_allowInitShaders)
{
PipelineDesc pipeDesc(_shader[SHADER_GENERATE_MIPS], "#Exposure");
_pipe[PIPE_GENERATE_MIPS_EXPOSURE].Init(pipeDesc);
}
if (App::Settings::I()._displayOffscreenDraw)
if (settings._displayOffscreenDraw)
{
PipelineDesc pipeDesc(_geoQuad, _shader[SHADER_QUAD], "#", _rphSwapChainWithDepth);
pipeDesc._topology = PrimitiveTopology::triangleStrip;
@ -147,7 +157,8 @@ void Renderer::Init(PRendererDelegate pDelegate)
_pBaseRenderer->ImGuiInit(_rphSwapChainWithDepth);
ImGuiUpdateStyle();
_ds.Init();
if (_allowInitShaders)
_ds.Init();
SetExposureValue(15);
}
@ -258,12 +269,18 @@ bool Renderer::OnWindowSizeChanged(int w, int h)
_pBaseRenderer->ResizeSwapChain();
OnSwapChainResized(true, true);
_ds.OnSwapChainResized(true, true);
Scene::Water::I().OnSwapChainResized();
Effects::Bloom::I().OnSwapChainResized();
Effects::Ssao::I().OnSwapChainResized();
Effects::Ssr::I().OnSwapChainResized();
Effects::Blur::I().OnSwapChainResized();
if (_ds.IsInitialized())
_ds.OnSwapChainResized(true, true);
if (Scene::Water::IsValidSingleton())
Scene::Water::I().OnSwapChainResized();
if (Effects::Bloom::IsValidSingleton())
Effects::Bloom::I().OnSwapChainResized();
if (Effects::Ssao::IsValidSingleton())
Effects::Ssao::I().OnSwapChainResized();
if (Effects::Ssr::IsValidSingleton())
Effects::Ssr::I().OnSwapChainResized();
if (Effects::Blur::IsValidSingleton())
Effects::Blur::I().OnSwapChainResized();
return true;
}
@ -272,7 +289,8 @@ void Renderer::OnSwapChainResized(bool init, bool done)
{
if (done)
{
_shader[SHADER_QUAD]->FreeDescriptorSet(_cshOffscreenColor);
if (_shader[SHADER_QUAD])
_shader[SHADER_QUAD]->FreeDescriptorSet(_cshOffscreenColor);
_pBaseRenderer->DeleteFramebuffer(_fbhOffscreenWithDepth);
_pBaseRenderer->DeleteFramebuffer(_fbhOffscreen);

View File

@ -84,6 +84,7 @@ namespace verus
UB_QuadVS _ubQuadVS;
UB_QuadFS _ubQuadFS;
bool _autoExposure = true;
bool _allowInitShaders = true;
bool _showUtilization = false;
public:
@ -94,7 +95,7 @@ namespace verus
PBaseRenderer operator->();
static bool IsLoaded();
void Init(PRendererDelegate pDelegate);
void Init(PRendererDelegate pDelegate, bool allowInitShaders = true);
void InitCmd();
void Done();

View File

@ -120,8 +120,9 @@ namespace verus
enum class Sampler : int
{
custom, // Not immutable, not static sampler.
storage, // Also known as UAV.
input,
storage, // Also known as UAV.
lodBias,
shadow,
aniso, // Most common sampler for 3D.
anisoClamp,

View File

@ -6,6 +6,8 @@ using namespace verus::D;
void Log::Write(CSZ txt, std::thread::id tid, CSZ filename, UINT32 line, Severity severity)
{
if (strstr(txt, "VUID-VkMappedMemoryRange-size-01389"))
return;
if (strstr(txt, "VUID-StandaloneSpirv-Offset-04663"))
return;
if (strstr(txt, "UNASSIGNED-BestPractices-vkAllocateMemory-small-allocation"))

View File

@ -4,9 +4,9 @@
using namespace verus;
using namespace verus::Effects;
Bloom::UB_BloomVS Bloom::s_ubBloomVS;
Bloom::UB_BloomFS Bloom::s_ubBloomFS;
Bloom::UB_BloomGodRaysFS Bloom::s_ubBloomGodRaysFS;
Bloom::UB_BloomVS Bloom::s_ubBloomVS;
Bloom::UB_BloomFS Bloom::s_ubBloomFS;
Bloom::UB_BloomLightShaftsFS Bloom::s_ubBloomLightShaftsFS;
Bloom::Bloom()
{
@ -30,7 +30,7 @@ void Bloom::Init()
}
_rph = renderer->CreateSimpleRenderPass(CGI::Format::srgbR8G8B8A8);
_rphGodRays = renderer->CreateSimpleRenderPass(CGI::Format::srgbR8G8B8A8, CGI::RP::Attachment::LoadOp::load);
_rphLightShafts = renderer->CreateSimpleRenderPass(CGI::Format::srgbR8G8B8A8, CGI::RP::Attachment::LoadOp::load);
_shader.Init("[Shaders]:Bloom.hlsl");
_shader->CreateDescriptorSet(0, &s_ubBloomVS, sizeof(s_ubBloomVS), 4, {}, CGI::ShaderStageFlags::vs);
@ -38,7 +38,7 @@ void Bloom::Init()
{
CGI::Sampler::linearClampMipN
}, CGI::ShaderStageFlags::fs);
_shader->CreateDescriptorSet(2, &s_ubBloomGodRaysFS, sizeof(s_ubBloomGodRaysFS), 4,
_shader->CreateDescriptorSet(2, &s_ubBloomLightShaftsFS, sizeof(s_ubBloomLightShaftsFS), 4,
{
CGI::Sampler::linearClampMipN,
CGI::Sampler::shadow
@ -51,13 +51,13 @@ void Bloom::Init()
pipeDesc.DisableDepthTest();
_pipe[PIPE_MAIN].Init(pipeDesc);
}
if (settings._postProcessGodRays)
if (settings._postProcessLightShafts)
{
CGI::PipelineDesc pipeDesc(renderer.GetGeoQuad(), _shader, "#GodRays", _rphGodRays);
CGI::PipelineDesc pipeDesc(renderer.GetGeoQuad(), _shader, "#LightShafts", _rphLightShafts);
pipeDesc._colorAttachBlendEqs[0] = VERUS_COLOR_BLEND_ADD;
pipeDesc._topology = CGI::PrimitiveTopology::triangleStrip;
pipeDesc.DisableDepthTest();
_pipe[PIPE_GOD_RAYS].Init(pipeDesc);
_pipe[PIPE_LIGHT_SHAFTS].Init(pipeDesc);
}
OnSwapChainResized();
@ -73,14 +73,14 @@ void Bloom::InitByAtmosphere(CGI::TexturePtr texShadow)
_texAtmoShadow = texShadow;
_cshGodRays = _shader->BindDescriptorSetTextures(2, { renderer.GetTexDepthStencil(), _texAtmoShadow });
_cshLightShafts = _shader->BindDescriptorSetTextures(2, { renderer.GetTexDepthStencil(), _texAtmoShadow });
}
void Bloom::Done()
{
VERUS_QREF_RENDERER;
renderer->DeleteFramebuffer(_fbh);
renderer->DeleteRenderPass(_rphGodRays);
renderer->DeleteRenderPass(_rphLightShafts);
renderer->DeleteRenderPass(_rph);
VERUS_DONE(Bloom);
}
@ -97,7 +97,7 @@ void Bloom::OnSwapChainResized()
return;
{
_shader->FreeDescriptorSet(_cshGodRays);
_shader->FreeDescriptorSet(_cshLightShafts);
_shader->FreeDescriptorSet(_csh);
renderer->DeleteFramebuffer(_fbh);
_tex.Done();
@ -141,7 +141,7 @@ void Bloom::Generate()
ImGui::DragFloat("Bloom (god rays) wideStrength", &_wideStrength, 0.01f);
ImGui::DragFloat("Bloom (god rays) sunStrength", &_sunStrength, 0.01f);
ImGui::Checkbox("Bloom blur", &_blur);
ImGui::Checkbox("Bloom (god rays) blur", &_blurGodRays);
ImGui::Checkbox("Bloom (light shafts) blur", &_blurLightShafts);
}
auto cb = renderer.GetCommandBuffer();
@ -166,39 +166,39 @@ void Bloom::Generate()
if (_blur)
Blur::I().GenerateForBloom(false);
if (settings._postProcessGodRays)
if (settings._postProcessLightShafts)
{
s_ubBloomGodRaysFS._matInvVP = Matrix4(VMath::inverse(sm.GetMainCamera()->GetMatrixVP())).UniformBufferFormat();
s_ubBloomGodRaysFS._dirToSun = float4(atmo.GetDirToSun().GLM(), 0);
s_ubBloomGodRaysFS._sunColor = float4(atmo.GetSunColor().GLM(), 0);
s_ubBloomGodRaysFS._eyePos = float4(sm.GetMainCamera()->GetEyePosition().GLM(), 0);
s_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.x = _maxDist;
s_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.y = _sunGloss;
s_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.z = _wideStrength;
s_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.w = _sunStrength;
s_ubBloomGodRaysFS._matShadow = atmo.GetShadowMap().GetShadowMatrix(0).UniformBufferFormat();
s_ubBloomGodRaysFS._matShadowCSM1 = atmo.GetShadowMap().GetShadowMatrix(1).UniformBufferFormat();
s_ubBloomGodRaysFS._matShadowCSM2 = atmo.GetShadowMap().GetShadowMatrix(2).UniformBufferFormat();
s_ubBloomGodRaysFS._matShadowCSM3 = atmo.GetShadowMap().GetShadowMatrix(3).UniformBufferFormat();
s_ubBloomGodRaysFS._matScreenCSM = atmo.GetShadowMap().GetScreenMatrixVP().UniformBufferFormat();
s_ubBloomGodRaysFS._csmSplitRanges = atmo.GetShadowMap().GetSplitRanges().GLM();
memcpy(&s_ubBloomGodRaysFS._shadowConfig, &atmo.GetShadowMap().GetConfig(), sizeof(s_ubBloomGodRaysFS._shadowConfig));
s_ubBloomLightShaftsFS._matInvVP = Matrix4(VMath::inverse(sm.GetMainCamera()->GetMatrixVP())).UniformBufferFormat();
s_ubBloomLightShaftsFS._dirToSun = float4(atmo.GetDirToSun().GLM(), 0);
s_ubBloomLightShaftsFS._sunColor = float4(atmo.GetSunColor().GLM(), 0);
s_ubBloomLightShaftsFS._eyePos = float4(sm.GetMainCamera()->GetEyePosition().GLM(), 0);
s_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.x = _maxDist;
s_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.y = _sunGloss;
s_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.z = _wideStrength;
s_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.w = _sunStrength;
s_ubBloomLightShaftsFS._matShadow = atmo.GetShadowMap().GetShadowMatrix(0).UniformBufferFormat();
s_ubBloomLightShaftsFS._matShadowCSM1 = atmo.GetShadowMap().GetShadowMatrix(1).UniformBufferFormat();
s_ubBloomLightShaftsFS._matShadowCSM2 = atmo.GetShadowMap().GetShadowMatrix(2).UniformBufferFormat();
s_ubBloomLightShaftsFS._matShadowCSM3 = atmo.GetShadowMap().GetShadowMatrix(3).UniformBufferFormat();
s_ubBloomLightShaftsFS._matScreenCSM = atmo.GetShadowMap().GetScreenMatrixVP().UniformBufferFormat();
s_ubBloomLightShaftsFS._csmSplitRanges = atmo.GetShadowMap().GetSplitRanges().GLM();
memcpy(&s_ubBloomLightShaftsFS._shadowConfig, &atmo.GetShadowMap().GetConfig(), sizeof(s_ubBloomLightShaftsFS._shadowConfig));
cb->PipelineImageMemoryBarrier(renderer.GetTexDepthStencil(), CGI::ImageLayout::depthStencilAttachment, CGI::ImageLayout::depthStencilReadOnly, 0);
cb->BeginRenderPass(_rphGodRays, _fbh, { _tex[TEX_PING]->GetClearValue() });
cb->BeginRenderPass(_rphLightShafts, _fbh, { _tex[TEX_PING]->GetClearValue() });
cb->BindPipeline(_pipe[PIPE_GOD_RAYS]);
cb->BindPipeline(_pipe[PIPE_LIGHT_SHAFTS]);
_shader->BeginBindDescriptors();
cb->BindDescriptors(_shader, 0);
cb->BindDescriptors(_shader, 1, _csh);
cb->BindDescriptors(_shader, 2, _cshGodRays);
cb->BindDescriptors(_shader, 2, _cshLightShafts);
_shader->EndBindDescriptors();
renderer.DrawQuad(cb.Get());
cb->EndRenderPass();
cb->PipelineImageMemoryBarrier(renderer.GetTexDepthStencil(), CGI::ImageLayout::depthStencilReadOnly, CGI::ImageLayout::depthStencilAttachment, 0);
if (_blurGodRays)
if (_blurLightShafts)
Blur::I().GenerateForBloom(true);
}
}

View File

@ -12,7 +12,7 @@ namespace verus
enum PIPE
{
PIPE_MAIN,
PIPE_GOD_RAYS,
PIPE_LIGHT_SHAFTS,
PIPE_COUNT
};
@ -23,19 +23,19 @@ namespace verus
TEX_COUNT
};
static UB_BloomVS s_ubBloomVS;
static UB_BloomFS s_ubBloomFS;
static UB_BloomGodRaysFS s_ubBloomGodRaysFS;
static UB_BloomVS s_ubBloomVS;
static UB_BloomFS s_ubBloomFS;
static UB_BloomLightShaftsFS s_ubBloomLightShaftsFS;
CGI::ShaderPwn _shader;
CGI::PipelinePwns<PIPE_COUNT> _pipe;
CGI::TexturePwns<TEX_COUNT> _tex;
CGI::TexturePtr _texAtmoShadow;
CGI::RPHandle _rph;
CGI::RPHandle _rphGodRays;
CGI::RPHandle _rphLightShafts;
CGI::FBHandle _fbh;
CGI::CSHandle _csh;
CGI::CSHandle _cshGodRays;
CGI::CSHandle _cshLightShafts;
float _colorScale = 0.8f;
float _colorBias = 1.1f;
float _maxDist = 20;
@ -43,7 +43,7 @@ namespace verus
float _wideStrength = 0.2f;
float _sunStrength = 0.3f;
bool _blur = true;
bool _blurGodRays = true;
bool _blurLightShafts = true;
bool _editMode = false;
public:

View File

@ -274,7 +274,7 @@ void Blur::Generate()
{
}
void Blur::GenerateForBloom(bool forGodRays)
void Blur::GenerateForBloom(bool forLightShafts)
{
VERUS_QREF_BLOOM;
VERUS_QREF_CONST_SETTINGS;
@ -283,10 +283,10 @@ void Blur::GenerateForBloom(bool forGodRays)
if (bloom.IsEditMode())
{
ImGui::DragFloat("Bloom blur radius", &_bloomRadius, 0.001f);
ImGui::DragFloat("Bloom (god rays) blur radius", &_bloomGodRaysRadius, 0.001f);
ImGui::DragFloat("Bloom (god rays) blur radius", &_bloomLightShaftsRadius, 0.001f);
}
const float radius = forGodRays ? _bloomGodRaysRadius : _bloomRadius;
const float radius = forLightShafts ? _bloomLightShaftsRadius : _bloomRadius;
float samplesPerPixel = 1;
int maxSamples = 1;
switch (settings._gpuShaderQuality)

View File

@ -62,7 +62,7 @@ namespace verus
CGI::CSHandle _cshMotionBlur;
CGI::CSHandle _cshMotionBlurExtra;
float _bloomRadius = 0.015f;
float _bloomGodRaysRadius = 0.004f;
float _bloomLightShaftsRadius = 0.004f;
float _dofFocusDist = 10;
float _dofBlurStrength = 0.2f;
float _ssrRadius = 0.025f;
@ -78,7 +78,7 @@ namespace verus
void OnSwapChainResized();
void Generate();
void GenerateForBloom(bool forGodRays);
void GenerateForBloom(bool forLightShafts);
void GenerateForDepthOfField();
void GenerateForSsao();
void GenerateForSsr();

65
Verus/src/GUI/Bars.cpp Normal file
View File

@ -0,0 +1,65 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#include "verus.h"
using namespace verus;
using namespace verus::GUI;
Bars::Bars()
{
}
Bars::~Bars()
{
}
PWidget Bars::Make()
{
return new Bars;
}
void Bars::Update()
{
Widget::Update();
}
void Bars::Draw()
{
VERUS_QREF_RENDERER;
VERUS_QREF_VM;
const float currentAspectRatio = renderer.GetSwapChainAspectRatio();
const float barsRatio = Math::Max(0.f, 1 - (1 / _aspectRatio * currentAspectRatio));
if (!barsRatio)
return;
const float barRatio = barsRatio * 0.5f;
float x, y;
GetAbsolutePosition(x, y);
auto cb = renderer.GetCommandBuffer();
auto shader = vm.GetShader();
auto& ubGui = vm.GetUbGui();
auto& ubGuiFS = vm.GetUbGuiFS();
ubGuiFS._color = GetColor().GLM();
vm.BindPipeline(ViewManager::PIPE_SOLID_COLOR, cb);
shader->BeginBindDescriptors();
ubGui._matW = Math::QuadMatrix(x, y, GetW(), GetH() * barRatio).UniformBufferFormat();
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, vm.GetDefaultComplexSetHandle());
renderer.DrawQuad(cb.Get());
ubGui._matW = Math::QuadMatrix(x, y + GetH() * (1 - barRatio), GetW(), GetH() * barRatio).UniformBufferFormat();
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, vm.GetDefaultComplexSetHandle());
renderer.DrawQuad(cb.Get());
shader->EndBindDescriptors();
}
void Bars::Parse(pugi::xml_node node)
{
Widget::Parse(node);
}

24
Verus/src/GUI/Bars.h Normal file
View File

@ -0,0 +1,24 @@
// Copyright (C) 2021, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
#pragma once
namespace verus
{
namespace GUI
{
class Bars : public Widget
{
float _aspectRatio = 21 / 9.f;
public:
Bars();
virtual ~Bars();
static PWidget Make();
virtual void Update() override;
virtual void Draw() override;
virtual void Parse(pugi::xml_node node) override;
};
VERUS_TYPEDEFS(Bars);
}
}

View File

@ -18,12 +18,13 @@ Container::~Container()
void Container::RegisterAll()
{
RegisterWidget("button", Button::Make);
RegisterWidget("image", Image::Make);
RegisterWidget("label", Label::Make);
RegisterWidget("sizer", Sizer::Make);
RegisterWidget("table", Table::Make);
RegisterWidget("textBox", TextBox::Make);
RegisterWidget("bars", &Bars::Make);
RegisterWidget("button", &Button::Make);
RegisterWidget("image", &Image::Make);
RegisterWidget("label", &Label::Make);
RegisterWidget("sizer", &Sizer::Make);
RegisterWidget("table", &Table::Make);
RegisterWidget("textBox", &TextBox::Make);
}
void Container::RegisterWidget(CSZ type, PFNCREATOR pCreator)

View File

@ -12,6 +12,7 @@
#include "Image.h"
#include "Button.h"
#include "Table.h"
#include "Bars.h"
#include "Cursor.h"
#include "ViewManager.h"
#include "ViewController.h"

View File

@ -99,7 +99,7 @@ void Image::Draw()
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, _solidColor ? vm.GetDefaultComplexSetHandle() : _csh);
shader->EndBindDescriptors();
renderer.DrawQuad();
renderer.DrawQuad(cb.Get());
}
void Image::Parse(pugi::xml_node node)

View File

@ -70,7 +70,7 @@ void Table::Draw()
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, vm.GetDefaultComplexSetHandle());
shader->EndBindDescriptors();
renderer.DrawQuad();
renderer.DrawQuad(cb.Get());
}
VERUS_FOR(i, _cols)

View File

@ -57,7 +57,7 @@ void TextBox::Draw()
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, vm.GetDefaultComplexSetHandle());
shader->EndBindDescriptors();
renderer.DrawQuad();
renderer.DrawQuad(cb.Get());
}
}

View File

@ -140,6 +140,7 @@ void View::Parse(pugi::xml_node node)
_cursor = node.attribute("cursor").as_bool(_cursor);
_debug = node.attribute("debug").as_bool(_debug);
_fadeSpeed = node.attribute("fadeSpeed").as_float(_fadeSpeed);
_locale = node.attribute("forceLocale").as_string(_C(_locale));
CSZ bg = node.attribute("bg").value();
if (strlen(bg) > 0)
@ -223,7 +224,7 @@ void View::OnKey(int scancode)
return;
const bool handled = InvokeOnKey(scancode);
#ifdef _DEBUG
#if defined(_DEBUG) || defined(VERUS_RELEASE_DEBUG)
if (handled && scancode == SDL_SCANCODE_F5)
return; // Reloaded, this pointer is invalid.
#endif

View File

@ -20,7 +20,7 @@ void ViewController::Init(CSZ url)
_pView = ViewManager::I().ParseView(url);
_pView->SetDelegate(this);
#ifdef _DEBUG
#if defined(_DEBUG) || defined(VERUS_RELEASE_DEBUG)
ConnectOnKey(VERUS_EVENT_HANDLER(&ViewController::OnKey));
#endif
}
@ -90,8 +90,20 @@ void ViewController::BeginFadeTo()
ViewManager::I().BeginFadeTo(_C(_pView->GetName()));
}
void ViewController::Activate()
{
ViewManager::I().Activate(_C(_pView->GetName()));
}
void ViewController::Deactivate()
{
ViewManager::I().Deactivate(_C(_pView->GetName()));
}
void ViewController::OnKey(RcEventArgs args)
{
#if defined(_DEBUG) || defined(VERUS_RELEASE_DEBUG)
if (args._char == SDL_SCANCODE_F5)
Reload();
#endif
}

View File

@ -38,6 +38,8 @@ namespace verus
void ConnectOnTimeout /**/(TFnEvent fn, CSZ id = nullptr);
void BeginFadeTo();
void Activate();
void Deactivate();
virtual void View_SetViewData(PView pView) override {}
virtual void View_GetViewData(PView pView) override {}

View File

@ -213,23 +213,13 @@ void ViewManager::MsgBox(CSZ txt, int data)
{
if (txt) // Display MessageBox:
{
PView pView = GetViewByName("UI/MsgBox.xml");
pView->Disable(false);
pView->Show();
pView->SetData();
pView->ResetAnimators();
pView->SetState(View::State::active);
MoveToFront("UI/MsgBox.xml");
PView pView = Activate("UI/MsgBox.xml");
PLabel pLabel = static_cast<PLabel>(pView->GetWidgetById("MsgBoxText"));
pLabel->SetText(txt);
}
else // Restore original view:
{
PView pView = GetViewByName("UI/MsgBox.xml");
pView->Disable();
pView->Hide();
pView->SetState(View::State::done);
MoveToBack("UI/MsgBox.xml");
PView pView = Deactivate("UI/MsgBox.xml");
}
}
@ -323,6 +313,28 @@ void ViewManager::BeginFadeOut()
}
}
PView ViewManager::Activate(CSZ viewName)
{
PView pView = GetViewByName(viewName);
pView->Disable(false);
pView->Show();
pView->SetData();
pView->ResetAnimators();
pView->SetState(View::State::active);
MoveToFront(viewName);
return pView;
}
PView ViewManager::Deactivate(CSZ viewName)
{
PView pView = GetViewByName(viewName);
pView->Disable();
pView->Hide();
pView->SetState(View::State::done);
MoveToBack(viewName);
return pView;
}
bool ViewManager::HasAllViewsInDoneState()
{
for (const auto& pView : _vViews)

View File

@ -77,6 +77,8 @@ namespace verus
PView MoveToBack /**/(CSZ viewName);
PView BeginFadeTo /**/(CSZ viewName = ":VOID:");
void BeginFadeOut();
PView Activate(CSZ viewName);
PView Deactivate(CSZ viewName);
bool HasAllViewsInDoneState();
bool HasSomeViewsInFadeState();
VERUS_P(bool SwitchView());

View File

@ -83,7 +83,7 @@ void Widget::DrawInputStyle()
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, vm.GetDefaultComplexSetHandle());
shader->EndBindDescriptors();
renderer.DrawQuad();
renderer.DrawQuad(cb.Get());
}
void Widget::Update()

View File

@ -36,6 +36,23 @@ bool ActiveMechanics::HandleInput()
bool ActiveMechanics::Update()
{
bool popped = false;
VERUS_WHILE(Vector<PMechanics>, _vStack, it)
{
if ((*it)->CanAutoPop())
{
popped = true;
(*it)->OnEnd();
it = _vStack.erase(it);
}
else
it++;
}
if (popped)
{
std::sort(_vStack.begin(), _vStack.end());
ActiveMechanics_OnChanged();
}
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
{
if (Continue::no == (*it)->Update())
@ -54,6 +71,16 @@ bool ActiveMechanics::Draw()
return false;
}
bool ActiveMechanics::DrawOverlay()
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
{
if (Continue::no == (*it)->DrawOverlay())
return true;
}
return false;
}
void ActiveMechanics::ApplyReport(const void* pReport)
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
@ -70,6 +97,36 @@ bool ActiveMechanics::GetBotDomainCenter(int id, RPoint3 center)
return false;
}
bool ActiveMechanics::GetSpawnPosition(int id, RPoint3 pos)
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
{
if (Continue::no == (*it)->GetSpawnPosition(id, pos))
return true;
}
return false;
}
bool ActiveMechanics::IsInputEnabled()
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
{
if (!(*it)->IsInputEnabled())
return false;
}
return true;
}
bool ActiveMechanics::OnDie(int id)
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
{
if (Continue::no == (*it)->OnDie(id))
return true;
}
return false;
}
bool ActiveMechanics::OnMouseMove(float x, float y)
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
@ -80,6 +137,16 @@ bool ActiveMechanics::OnMouseMove(float x, float y)
return false;
}
bool ActiveMechanics::OnTakeDamage(int id, float amount)
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)
{
if (Continue::no == (*it)->OnTakeDamage(id, amount))
return true;
}
return false;
}
bool ActiveMechanics::UpdateMultiplayer()
{
for (auto it = _vStack.begin(); it != _vStack.end(); ++it)

View File

@ -19,10 +19,15 @@ namespace verus
bool HandleInput();
bool Update();
bool Draw();
bool DrawOverlay();
void ApplyReport(const void* pReport);
bool GetBotDomainCenter(int id, RPoint3 center);
bool GetSpawnPosition(int id, RPoint3 pos);
bool IsInputEnabled();
bool OnDie(int id);
bool OnMouseMove(float x, float y);
bool OnTakeDamage(int id, float amount);
bool UpdateMultiplayer();
Scene::PMainCamera GetMainCamera();

View File

@ -69,6 +69,9 @@ BaseGame::~BaseGame()
Free_D();
Utils::FreeEx(&_alloc);
SDL_Quit();
if (_restartApp)
RestartApp();
}
void BaseGame::Initialize(VERUS_MAIN_DEFAULT_ARGS, App::Window::RcDesc desc)
@ -110,12 +113,14 @@ void BaseGame::Initialize(VERUS_MAIN_DEFAULT_ARGS, App::Window::RcDesc desc)
VERUS_QREF_MM;
_p->_camera.SetAspectRatio(renderer.GetSwapChainAspectRatio());
_p->_camera.Update();
sm.SetCamera(&_p->_camera);
if (Scene::SceneManager::IsValidSingleton())
Scene::SceneManager::I().SetCamera(&_p->_camera);
renderer->BeginFrame(false); // Begin recording a command buffer.
renderer.InitCmd();
_engineInit.InitCmd();
mm.InitCmd();
if (Scene::MaterialManager::IsValidSingleton())
Scene::MaterialManager::I().InitCmd();
BaseGame_LoadContent();
renderer->EndFrame(false); // End recording a command buffer.
renderer->Sync(false);
@ -124,11 +129,8 @@ void BaseGame::Initialize(VERUS_MAIN_DEFAULT_ARGS, App::Window::RcDesc desc)
void BaseGame::Run(bool relativeMouseMode)
{
VERUS_QREF_ASYNC;
VERUS_QREF_ASYS;
VERUS_QREF_BULLET;
VERUS_QREF_KM;
VERUS_QREF_RENDERER;
VERUS_QREF_SM;
VERUS_QREF_TIMER;
if (relativeMouseMode)
@ -207,7 +209,7 @@ void BaseGame::Run(bool relativeMouseMode)
{
if (renderer.OnWindowSizeChanged(event.window.data1, event.window.data2))
{
Scene::PCamera pCamera = sm.GetCamera();
Scene::PCamera pCamera = Scene::SceneManager::IsValidSingleton() ? Scene::SceneManager::I().GetCamera() : nullptr;
if (pCamera)
{
pCamera->SetAspectRatio(renderer.GetSwapChainAspectRatio());
@ -247,7 +249,7 @@ void BaseGame::Run(bool relativeMouseMode)
if (_p->_escapeKeyExitGame && km.IsKeyDownEvent(SDL_SCANCODE_ESCAPE))
Exit();
if (_p->_minimized)
if (_p->_minimized || _restartApp)
continue;
//
@ -272,9 +274,13 @@ void BaseGame::Run(bool relativeMouseMode)
if (km.IsKeyPressed(SDL_SCANCODE_D))
_p->_cameraSpirit.MoveSide(speed);
}
BaseGame_HandleInput();
bullet.Simulate();
BaseGame_HandleInput();
if (_restartApp)
continue;
if (Physics::Bullet::IsValidSingleton())
Physics::Bullet::I().Simulate();
if (_p->_defaultCameraMovement)
{
@ -289,12 +295,16 @@ void BaseGame::Run(bool relativeMouseMode)
_p->_camera.ExcludeWaterLine();
}
_p->_camera.Update();
sm.SetCamera(&_p->_camera);
if (Scene::SceneManager::IsValidSingleton())
Scene::SceneManager::I().SetCamera(&_p->_camera);
}
BaseGame_Update();
if (_restartApp)
continue;
asys.Update();
if (Audio::AudioSystem::IsValidSingleton())
Audio::AudioSystem::I().Update();
// Draw current frame:
renderer.Draw();
@ -415,3 +425,39 @@ float BaseGame::GetMouseScale()
const float rad = (VERUS_2PI / 360.f) / 3.f; // 3 pixels = 1 degree.
return rad * settings._inputMouseSensitivity;
}
void BaseGame::RestartApp()
{
#ifdef _WIN32
wchar_t pathname[MAX_PATH] = {};
GetModuleFileName(nullptr, pathname, MAX_PATH);
wchar_t commandLine[MAX_PATH] = {};
wcscpy_s(commandLine, L"--restarted");
STARTUPINFO si = {};
PROCESS_INFORMATION pi = {};
si.cb = sizeof(si);
CreateProcess(
pathname,
commandLine,
nullptr,
nullptr,
FALSE,
CREATE_NEW_PROCESS_GROUP,
nullptr,
nullptr,
&si,
&pi);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
#endif
}
void BaseGame::RequestAppRestart()
{
_restartApp = true;
Exit();
}

View File

@ -12,6 +12,7 @@ namespace verus
AlignedAllocator _alloc;
EngineInit _engineInit;
App::Window _window;
bool _restartApp = false;
public:
BaseGame();
@ -27,7 +28,7 @@ namespace verus
virtual void BaseGame_HandleInput() = 0;
virtual void BaseGame_Update() = 0;
virtual void BaseGame_Draw() = 0;
virtual void BaseGame_DrawOverlay() = 0;
virtual void BaseGame_DrawOverlay() {}
virtual void BaseGame_OnWindowSizeChanged() {}
virtual void BaseGame_OnActivated() {}
virtual void BaseGame_OnDeactivated() {}
@ -67,6 +68,9 @@ namespace verus
void BulletDebugDraw();
static float GetMouseScale();
void RestartApp();
void RequestAppRestart();
};
VERUS_TYPEDEFS(BaseGame);
}

View File

@ -4,12 +4,546 @@
using namespace verus;
using namespace verus::Game;
// Cutscene::Command:
void Cutscene::Command::Parse(pugi::xml_node node)
{
_url = node.attribute("url").value();
CSZ delay = node.attribute("delay").value();
if ('^' == *delay)
{
_prevDelay = static_cast<float>(atof(delay + 1));
_delay = FLT_MAX;
}
else if (']' == *delay)
{
_endDelay = static_cast<float>(atof(delay + 1));
_delay = FLT_MAX;
}
else
{
_delay = static_cast<float>(atof(delay));
}
_duration = node.attribute("duration").as_float(_duration);
}
bool Cutscene::Command::Update()
{
const float time = GetTime();
if (time >= _duration)
{
OnEnd();
return false;
}
return true;
}
void Cutscene::Command::UpdatePrevDelay(float newActiveDuration)
{
if (_prevDelay != FLT_MAX)
{
const float delay = newActiveDuration + _prevDelay;
_delay = (FLT_MAX == _delay) ? delay : Math::Max(_delay, delay);
}
}
void Cutscene::Command::UpdateEndDelay(float newActiveDuration)
{
if (_endDelay != FLT_MAX)
{
const float delay = newActiveDuration + _endDelay;
_delay = (FLT_MAX == _delay) ? delay : Math::Max(_delay, delay);
}
}
void Cutscene::Command::LimitActiveDuration(float limit)
{
VERUS_RT_ASSERT(_delay != FLT_MAX && _duration != FLT_MAX);
const float activeDuration = _delay + _duration;
const float diff = activeDuration - limit;
if (diff > 0)
{
const float newDuration = _duration - diff;
_duration = Math::Max(0.f, newDuration);
if (newDuration < 0)
_delay += newDuration;
}
}
// Cutscene::BarrierCommand:
Cutscene::PCommand Cutscene::BarrierCommand::Make()
{
return new BarrierCommand;
}
// Cutscene::CameraCommand:
Cutscene::CameraCommand::~CameraCommand()
{
IO::Async::Cancel(this);
}
Cutscene::PCommand Cutscene::CameraCommand::Make()
{
return new CameraCommand;
}
void Cutscene::CameraCommand::Parse(pugi::xml_node node)
{
Command::Parse(node);
_duration = FLT_MAX;
IO::Async::I().Load(_C(_url), this);
}
bool Cutscene::CameraCommand::Update()
{
if (IsDelayed() || FLT_MAX == _duration)
return true;
const float time = GetTime();
if (time >= _duration)
{
OnEnd();
return false;
}
_motion.ProcessTriggers(time, this);
_pCutscene->_camera.ApplyMotion(_C(_cameraName), _motion, time);
if (!_pCutscene->_pCurrentCameraCommand)
_pCutscene->_pCurrentCameraCommand = this;
return true;
}
void Cutscene::CameraCommand::OnBegin()
{
Command::OnBegin();
if (!_pCutscene->_pCurrentCameraCommand)
_pCutscene->_pCurrentCameraCommand = this;
}
void Cutscene::CameraCommand::OnEnd()
{
Command::OnEnd();
if (this == _pCutscene->_pCurrentCameraCommand)
_pCutscene->_pCurrentCameraCommand = nullptr;
}
void Cutscene::CameraCommand::Async_Run(CSZ url, RcBlob blob)
{
IO::StreamPtr sp(blob);
_motion.Init();
_motion.Deserialize(sp);
_duration = _motion.GetDuration();
if (_active)
_pCutscene->OnNewActiveDuration(_delay + _duration);
}
void Cutscene::CameraCommand::Motion_OnTrigger(CSZ name, int state)
{
if (!Str::StartsWith(name, "Camera."))
return;
if (state & 0x1)
{
_cameraName = name;
_pCutscene->_camera.CutMotionBlur();
}
}
// Cutscene::ConfigCommand:
Cutscene::PCommand Cutscene::ConfigCommand::Make()
{
return new ConfigCommand;
}
void Cutscene::ConfigCommand::Parse(pugi::xml_node node)
{
Command::Parse(node);
_tag = node.attribute("tag").as_int(_tag);
_callOnEnd = node.attribute("callOnEnd").as_bool(_callOnEnd);
_skipHere = node.attribute("skipHere").as_bool(_skipHere);
}
void Cutscene::ConfigCommand::OnBegin()
{
if (_callOnEnd)
{
if (_pCutscene->_fnOnEnd)
_pCutscene->_fnOnEnd(_tag); // Tag 1 and above means called from config command.
}
}
// Cutscene::FadeCommand:
Cutscene::PCommand Cutscene::FadeCommand::Make()
{
return new FadeCommand;
}
void Cutscene::FadeCommand::Parse(pugi::xml_node node)
{
Command::Parse(node);
_from.FromColorString(node.attribute("from").value());
_to.FromColorString(node.attribute("to").value());
_easing = Math::EasingFromString(node.attribute("easing").value());
_skipHere = node.attribute("skipHere").as_bool(_skipHere);
}
bool Cutscene::FadeCommand::DrawOverlay()
{
if (IsDelayed())
return false;
VERUS_QREF_RENDERER;
VERUS_QREF_VM;
const float ratio = Math::Clamp<float>(GetTime() / _duration, 0, 1);
const Vector4 color = VMath::lerp(Math::ApplyEasing(_easing, ratio), _from, _to);
auto cb = renderer.GetCommandBuffer();
auto shader = vm.GetShader();
vm.GetUbGui()._matW = Transform3::UniformBufferFormatIdentity();
vm.GetUbGuiFS()._color = color.GLM();
vm.BindPipeline(GUI::ViewManager::PIPE_SOLID_COLOR, cb);
shader->BeginBindDescriptors();
cb->BindDescriptors(shader, 0);
cb->BindDescriptors(shader, 1, vm.GetDefaultComplexSetHandle());
shader->EndBindDescriptors();
renderer.DrawQuad(cb.Get());
return true;
}
// Cutscene::MotionCommand:
Cutscene::PCommand Cutscene::MotionCommand::Make()
{
return new MotionCommand;
}
bool Cutscene::MotionCommand::Update()
{
if (IsDelayed() || FLT_MAX == _duration)
return true;
return true;
}
void Cutscene::MotionCommand::Motion_OnTrigger(CSZ name, int state)
{
}
// Cutscene::SoundCommand:
Cutscene::PCommand Cutscene::SoundCommand::Make()
{
return new SoundCommand;
}
void Cutscene::SoundCommand::Parse(pugi::xml_node node)
{
Command::Parse(node);
_gain = node.attribute("gain").as_float(_gain);
_pitch = node.attribute("pitch").as_float(_pitch);
_duration = FLT_MAX;
_sound.Init(_C(_url));
}
bool Cutscene::SoundCommand::Update()
{
if (IsDelayed())
return true;
if (FLT_MAX == _duration && _sound->IsLoaded())
{
_duration = _sound->GetLength();
_pCutscene->OnNewActiveDuration(_delay + _duration);
}
const float time = GetTime();
if (time >= _duration)
{
OnEnd();
return false;
}
if (!_source)
{
_sound->NewSource(&_source)->Play();
_source->SetGain(_gain);
_source->SetPitch(_pitch);
}
return true;
}
void Cutscene::SoundCommand::OnEnd()
{
Command::OnEnd();
if (_source)
_source->Stop();
}
// Cutscene:
Cutscene::Cutscene()
{
}
Cutscene::~Cutscene()
{
Done();
}
void Cutscene::Init()
{
VERUS_INIT();
RegisterCommand("barrier", &BarrierCommand::Make);
RegisterCommand("camera", &CameraCommand::Make);
RegisterCommand("config", &ConfigCommand::Make);
RegisterCommand("fade", &FadeCommand::Make);
RegisterCommand("motion", &MotionCommand::Make);
RegisterCommand("sound", &SoundCommand::Make);
OnWindowSizeChanged();
}
void Cutscene::Done()
{
DeleteCommands();
VERUS_DONE(Cutscene);
}
void Cutscene::Next(CSZ url, bool preload)
{
_url = url;
if (preload)
Load(_C(_url));
}
void Cutscene::Load(CSZ url)
{
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();
_interactive = root.attribute("interactive").as_bool(_interactive);
DeleteCommands();
_vCommands.reserve(16);
for (auto node : root.children())
{
CSZ commandType = node.name();
PCommand pCommand = CreateCommand(commandType);
if (pCommand)
{
pCommand->SetCutscene(this);
pCommand->Parse(node);
_vCommands.push_back(pCommand);
}
else
throw VERUS_RECOVERABLE << "CreateCommand(), type=" << commandType;
}
}
void Cutscene::OnBegin()
{
if (_vCommands.empty())
Load(_C(_url));
}
void Cutscene::OnEnd()
{
if (_fnOnEnd)
_fnOnEnd(0); // Tag 0 means whole cutscene.
}
bool Cutscene::CanAutoPop()
{
return _beginIndex >= _vCommands.size(); // No more commands.
}
void Cutscene::RegisterCommand(CSZ type, PFNCREATOR pCreator)
{
_mapCreators[type] = pCreator;
}
Cutscene::PCommand Cutscene::CreateCommand(CSZ type)
{
VERUS_IF_FOUND_IN(TMapCreators, _mapCreators, type, it)
return it->second();
return nullptr;
}
void Cutscene::DeleteCommands()
{
for (auto& p : _vCommands)
delete p;
_vCommands.clear();
}
void Cutscene::Skip()
{
PCommand pSkipToDelayed = nullptr;
for (int i = _beginIndex; i < _endIndex; ++i)
{
if (_vCommands[i]->IsActive() &&
_vCommands[i]->SkipHere() &&
_vCommands[i]->IsDelayed() &&
_vCommands[i]->GetDelay() != FLT_MAX)
{
pSkipToDelayed = _vCommands[i];
break;
}
}
if (pSkipToDelayed) // Soft skip using fade command:
{
const float adjustDelayBy = pSkipToDelayed->GetTime();
pSkipToDelayed->AdjustDelayBy(adjustDelayBy);
const float delay = pSkipToDelayed->GetDelay();
const float duration = pSkipToDelayed->GetDuration();
const float maxActiveDuration = delay + duration;
for (int i = _beginIndex; i < _endIndex; ++i)
{
if (_vCommands[i] != pSkipToDelayed && _vCommands[i]->IsActive())
_vCommands[i]->LimitActiveDuration(maxActiveDuration);
}
}
else // Hard skip using config command:
{
// Stop all current commands:
for (int i = _beginIndex; i < _endIndex; ++i)
{
if (_vCommands[i]->IsActive())
_vCommands[i]->OnEnd();
}
// Find where to skip:
while (_endIndex < _vCommands.size())
{
if (_vCommands[_endIndex]->SkipHere())
break;
_endIndex++;
}
_beginIndex = _endIndex;
_timeSinceBarrier = 0;
}
}
bool Cutscene::SkipBarrier()
{
VERUS_RT_ASSERT(_beginIndex < _vCommands.size());
if (!_vCommands[_beginIndex]->IsBarrier())
return false;
_beginIndex++;
_endIndex++;
_timeSinceBarrier = 0;
return true;
}
bool Cutscene::RunParallelCommands()
{
VERUS_RT_ASSERT(_beginIndex < _vCommands.size());
if (_vCommands[_beginIndex]->IsBarrier())
return false;
while (_endIndex < _vCommands.size())
{
if (_vCommands[_endIndex]->IsBarrier())
break;
_vCommands[_endIndex++]->OnBegin();
}
return _beginIndex != _endIndex;
}
Continue Cutscene::Update()
{
VERUS_QREF_TIMER;
_timeSinceBarrier += dt;
bool hasActiveCommands = false;
do
{
if (_beginIndex >= _vCommands.size()) // No more commands?
break;
if (_beginIndex == _endIndex) // At barrier?
{
SkipBarrier();
RunParallelCommands();
// Compute duration for this group:
float maxActiveDuration = 0;
for (int i = _beginIndex; i < _endIndex; ++i)
{
const float delay = _vCommands[i]->GetDelay();
const float duration = _vCommands[i]->GetDuration();
if (delay != FLT_MAX && duration != FLT_MAX)
{
const float activeDuration = delay + duration;
if (i + 1 < _vCommands.size())
_vCommands[i + 1]->UpdatePrevDelay(activeDuration);
maxActiveDuration = Math::Max(maxActiveDuration, activeDuration);
}
}
if (maxActiveDuration > 0)
{
for (int i = _beginIndex; i < _endIndex; ++i)
_vCommands[i]->UpdateEndDelay(maxActiveDuration);
}
}
for (int i = _beginIndex; i < _endIndex; ++i)
{
if (_vCommands[i]->IsActive() && _vCommands[i]->Update())
hasActiveCommands = true;
}
if (!hasActiveCommands)
_beginIndex = _endIndex; // Go to next barrier.
} while (!hasActiveCommands); // Don't return while at barrier.
return Continue::yes;
}
Continue Cutscene::DrawOverlay()
{
for (int i = _beginIndex; i < _endIndex; ++i)
{
if (_vCommands[i]->IsActive())
_vCommands[i]->DrawOverlay();
}
return Continue::yes;
}
bool Cutscene::IsInputEnabled()
{
return _interactive;
}
Continue Cutscene::OnMouseMove(float x, float y)
@ -17,17 +551,25 @@ Continue Cutscene::OnMouseMove(float x, float y)
return _interactive ? Continue::yes : Continue::no;
}
Continue Cutscene::HandleInput()
{
return _interactive ? Continue::yes : Continue::no;
}
Continue Cutscene::Update()
{
return Continue::yes;
}
Scene::PMainCamera Cutscene::GetMainCamera()
{
return _interactive ? nullptr : &_camera;
return _pCurrentCameraCommand ? &_camera : nullptr;
}
void Cutscene::OnWindowSizeChanged()
{
VERUS_QREF_RENDERER;
_camera.SetAspectRatio(renderer.GetSwapChainAspectRatio());
_camera.Update();
}
void Cutscene::OnNewActiveDuration(float newActiveDuration)
{
VERUS_RT_ASSERT(newActiveDuration != 0 && newActiveDuration != FLT_MAX);
for (int i = _beginIndex; i < _endIndex; ++i)
{
if (i + 1 < _vCommands.size())
_vCommands[i + 1]->UpdatePrevDelay(newActiveDuration);
_vCommands[i]->UpdateEndDelay(newActiveDuration);
}
}

View File

@ -5,50 +5,173 @@ namespace verus
{
namespace Game
{
class Cutscene : public Mechanics
class Cutscene : public Object, public Mechanics
{
public:
enum class Fade : int
class Command
{
none,
black,
count
};
protected:
Cutscene* _pCutscene = nullptr;
String _url;
float _delay = 0;
float _prevDelay = FLT_MAX;
float _endDelay = FLT_MAX;
float _duration = 0;
bool _active = false;
class Event
public:
virtual ~Command() {}
virtual void Parse(pugi::xml_node node);
virtual bool Update();
virtual bool DrawOverlay() { return false; }
virtual void OnBegin() { _active = true; }
virtual void OnEnd() { _active = false; }
virtual bool IsBarrier() const { return false; }
virtual bool SkipHere() const { return false; }
void SetCutscene(Cutscene* p) { _pCutscene = p; }
bool IsActive() const { return _active; }
bool IsDelayed() const { return _pCutscene->_timeSinceBarrier < _delay; }
float GetDelay() const { return _delay; }
void AdjustDelayBy(float amount) { _delay += amount; }
void UpdatePrevDelay(float newActiveDuration);
void UpdateEndDelay(float newActiveDuration);
float GetDuration() const { return _duration; }
void LimitActiveDuration(float limit);
float GetTime() const { return _pCutscene->_timeSinceBarrier - _delay; }
};
VERUS_TYPEDEFS(Command);
class BarrierCommand : public Command
{
public:
enum class Type : int
{
camera,
input,
motion,
sound,
sync,
tr,
count
};
private:
Type _type = Type::camera;
static PCommand Make();
virtual bool IsBarrier() const override { return true; }
};
VERUS_TYPEDEFS(Event);
VERUS_TYPEDEFS(BarrierCommand);
class CameraCommand : public Command, public IO::AsyncCallback, public Anim::MotionDelegate
{
Anim::Motion _motion;
String _cameraName;
public:
~CameraCommand();
static PCommand Make();
virtual void Parse(pugi::xml_node node) override;
virtual bool Update() override;
virtual void OnBegin() override;
virtual void OnEnd() override;
virtual void Async_Run(CSZ url, RcBlob blob) override;
virtual void Motion_OnTrigger(CSZ name, int state) override;
};
VERUS_TYPEDEFS(CameraCommand);
class ConfigCommand : public Command
{
int _tag = 1;
bool _callOnEnd = false;
bool _skipHere = false;
public:
static PCommand Make();
virtual void Parse(pugi::xml_node node) override;
virtual void OnBegin() override;
virtual bool SkipHere() const override { return _skipHere; }
};
VERUS_TYPEDEFS(ConfigCommand);
class FadeCommand : public Command
{
Vector4 _from = Vector4(0);
Vector4 _to = Vector4(0);
Easing _easing = Easing::none;
bool _skipHere = false;
public:
static PCommand Make();
virtual void Parse(pugi::xml_node node) override;
virtual bool DrawOverlay() override;
virtual bool SkipHere() const override { return _skipHere; }
};
VERUS_TYPEDEFS(FadeCommand);
class MotionCommand : public Command, public Anim::MotionDelegate
{
Anim::Motion _motion;
public:
static PCommand Make();
virtual bool Update() override;
virtual void Motion_OnTrigger(CSZ name, int state) override;
};
VERUS_TYPEDEFS(MotionCommand);
class SoundCommand : public Command
{
Audio::SoundPwn _sound;
Audio::SourcePtr _source;
float _gain = 1;
float _pitch = 1;
public:
static PCommand Make();
virtual void Parse(pugi::xml_node node) override;
virtual bool Update();
virtual void OnEnd() override;
};
VERUS_TYPEDEFS(SoundCommand);
private:
Vector<Event> _vEvents;
Scene::MainCamera _camera;
Fade _fadeIn = Fade::none;
Fade _fadeOut = Fade::none;
bool _interactive = false;
typedef PCommand(*PFNCREATOR)();
typedef Map<String, PFNCREATOR> TMapCreators;
String _url;
TMapCreators _mapCreators;
Vector<PCommand> _vCommands;
std::function<void(int)> _fnOnEnd;
PCameraCommand _pCurrentCameraCommand = nullptr;
Scene::MainCamera _camera;
int _beginIndex = 0;
int _endIndex = 0;
float _timeSinceBarrier = 0;
bool _interactive = false;
public:
Cutscene();
~Cutscene();
virtual Continue OnMouseMove(float x, float y) override;
virtual Continue HandleInput() override;
void Init();
void Done();
void Next(CSZ url, bool preload = false);
void Load(CSZ url);
virtual void OnBegin() override;
virtual void OnEnd() override;
virtual bool CanAutoPop() override;
// Commands:
void RegisterCommand(CSZ type, PFNCREATOR pCreator);
PCommand CreateCommand(CSZ type);
void DeleteCommands();
// Run commands:
void Skip();
bool SkipBarrier();
bool RunParallelCommands();
virtual Continue Update() override;
virtual Continue DrawOverlay() override;
virtual bool IsInputEnabled() override;
virtual Continue OnMouseMove(float x, float y) override;
virtual Scene::PMainCamera GetMainCamera() override;
void OnWindowSizeChanged();
void SetOnEndCallback(std::function<void(int)> fn) { _fnOnEnd = fn; }
void OnNewActiveDuration(float newActiveDuration);
};
VERUS_TYPEDEFS(Cutscene);
}

View File

@ -13,14 +13,20 @@ namespace verus
virtual void OnBegin() {}
virtual void OnEnd() {}
virtual bool CanAutoPop() { return false; }
virtual Continue HandleInput() { return Continue::yes; }
virtual Continue Update() { return Continue::yes; }
virtual Continue Draw() { return Continue::yes; }
virtual Continue DrawOverlay() { return Continue::yes; }
virtual void ApplyReport(const void* pReport) {}
virtual Continue GetBotDomainCenter(int id, RPoint3 center) { return Continue::yes; }
virtual Continue GetSpawnPosition(int id, RPoint3 pos) { return Continue::yes; };
virtual bool IsInputEnabled() { return true; }
virtual Continue OnDie(int id) { return Continue::yes; }
virtual Continue OnMouseMove(float x, float y) { return Continue::yes; }
virtual Continue OnTakeDamage(int id, float amount) { return Continue::yes; }
virtual Continue UpdateMultiplayer() { return Continue::yes; }
virtual Scene::PMainCamera GetMainCamera() { return nullptr; }
};

View File

@ -207,7 +207,7 @@ void Spirit::Rotate(RcVector3 front, float speed)
_yaw = Math::WrapAngle(_yaw + Math::Min(speed * dt, abs(dYaw)) * glm::sign(dYaw));
}
void Spirit::LookAt(RcPoint3 point)
void Spirit::LookAt(RcPoint3 point, bool forceTarget)
{
const Vector3 dir = point - GetPosition();
_yaw = Math::WrapAngle(atan2(
@ -216,6 +216,12 @@ void Spirit::LookAt(RcPoint3 point)
Vector3 dir2D = dir;
dir2D.setY(0);
_pitch = -atan(dir.getY() / VMath::length(dir2D));
if (forceTarget)
{
_yaw.ForceTarget();
_pitch.ForceTarget();
}
}
RcMatrix3 Spirit::GetPitchMatrix() const

View File

@ -87,7 +87,7 @@ namespace verus
//! Rotates smoothly across multiple frames.
void Rotate(RcVector3 front, float speed);
void LookAt(RcPoint3 point);
void LookAt(RcPoint3 point, bool forceTarget = false);
// Matrices:
RcMatrix3 GetPitchMatrix() const;

View File

@ -31,6 +31,7 @@ void EngineInit::Make()
void EngineInit::Free()
{
// Some object must outlive others, so the order is important:
if (_makeGUI)
Free_GUI();
if (_makeScene)
@ -74,9 +75,7 @@ void EngineInit::Init(Input::PKeyMapperDelegate pKeyMapperDelegate, CGI::Rendere
}
if (_makeCGI)
{
CGI::Renderer::I().Init(pRendererDelegate);
}
CGI::Renderer::I().Init(pRendererDelegate, _allowInitShaders);
// Static init:
if (_makeEffects)
@ -92,7 +91,7 @@ void EngineInit::Init(Input::PKeyMapperDelegate pKeyMapperDelegate, CGI::Rendere
}
// Helpers:
if (_makeCGI)
if (_makeCGI && _allowInitShaders)
CGI::DebugDraw::I().Init();
if (_makeScene)
Scene::Helpers::I().Init();
@ -118,7 +117,17 @@ void EngineInit::Init(Input::PKeyMapperDelegate pKeyMapperDelegate, CGI::Rendere
void EngineInit::InitCmd()
{
if (_makeEffects)
{
Effects::Ssao::I().InitCmd();
}
}
void EngineInit::ReducedFeatureSet()
{
_makeAudio = false;
_makeEffects = false;
_makeExtra = false;
_makeGUI = false;
_makeNet = false;
_makePhysics = false;
_makeScene = false;
_allowInitShaders = false;
}

View File

@ -18,23 +18,26 @@ namespace verus
class EngineInit
{
public:
bool _makeGlobal = true;
bool _makeNet = true;
bool _makeIO = true;
bool _makeInput = true;
bool _makeAudio = true;
bool _makeCGI = true;
bool _makePhysics = true;
bool _makeEffects = true;
bool _makeExtra = false;
bool _makeScene = true;
bool _makeGlobal = true;
bool _makeGUI = true;
bool _makeInput = true;
bool _makeIO = true;
bool _makeNet = true;
bool _makePhysics = true;
bool _makeScene = true;
bool _allowInitShaders = true;
void Make();
void Free();
void Init(Input::KeyMapperDelegate* pKeyMapperDelegate, CGI::RendererDelegate* pRendererDelegate);
void InitCmd();
void ReducedFeatureSet();
};
VERUS_TYPEDEFS(EngineInit);
}

View File

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

View File

@ -56,7 +56,7 @@ namespace verus
{
for (const auto& x : TStoreValues::_map)
{
if (Continue::yes != fn(_C(x.first), _C(x.second._s)))
if (Continue::no == fn(_C(x.first), _C(x.second._s)))
return;
}
}

View File

@ -193,6 +193,23 @@ CSZ Math::EasingToString(Easing easing)
return g_easings[+easing];
}
Quat Math::NLerp(float t, RcQuat qA, RcQuat qB)
{
Quat ret;
const auto dp = VMath::dot(qA, qB);
if (dp < 0)
{
const Quat qNeg = -qB;
ret = VMath::lerp(t, qA, qNeg);
}
else
{
ret = VMath::lerp(t, qA, qB);
}
ret = VMath::normalize(ret);
return ret;
}
Vector3 Math::TriangleNormal(RcPoint3 a, RcPoint3 b, RcPoint3 c)
{
return VMath::normalize(VMath::cross(a - b, a - c));

View File

@ -116,6 +116,7 @@ namespace verus
float ApplyEasing(Easing easing, float x);
Easing EasingFromString(CSZ s);
CSZ EasingToString(Easing easing);
Quat NLerp(float t, RcQuat qA, RcQuat qB);
// Shapes:
Vector3 TriangleNormal(RcPoint3 a, RcPoint3 b, RcPoint3 c);

View File

@ -71,7 +71,7 @@ namespace verus
{
VERUS_FOR(i, _wheelCount)
{
if (Continue::yes != fn(_pRaycastVehicle->getWheelInfo(i)))
if (Continue::no == fn(_pRaycastVehicle->getWheelInfo(i)))
return;
}
}

View File

@ -117,6 +117,50 @@ void Camera::ExcludeWaterLine(float h)
}
}
void Camera::ApplyMotion(CSZ boneName, Anim::RMotion motion, float time)
{
auto pBone = motion.FindBone(boneName);
if (!pBone)
return;
Quat q;
Vector3 euler, pos;
int state = 0;
pBone->ComputeRotationAt(time, euler, q);
pBone->ComputePositionAt(time, pos);
pBone->ComputeTriggerAt(time, state);
Point3 at;
Vector3 up;
if (state & 0x2)
{
Vector3 scale;
pBone->ComputeScaleAt(time, scale);
at = scale;
up = Vector3(0, 1, 0);
}
else
{
const Matrix3 matR(q);
at = pos + matR.getCol2();
up = matR.getCol1();
}
MoveEyeTo(pos);
MoveAtTo(at);
SetUpDirection(up);
Update();
}
void Camera::BakeMotion(CSZ boneName, Anim::RMotion motion, int frame)
{
auto pBone = motion.FindBone(boneName);
if (!pBone)
return;
const glm::quat q = glm::quatLookAt(-_frontDir.GLM(), _upDir.GLM());
pBone->InsertKeyframeRotation(frame, q);
pBone->InsertKeyframePosition(frame, _eyePos);
}
void Camera::SaveState(int slot)
{
StringStream ss;
@ -170,12 +214,20 @@ void MainCamera::Update()
void MainCamera::UpdateVP()
{
VERUS_QREF_RENDERER;
if (_currentFrame != renderer.GetFrameCount())
{
_matPrevVP = GetMatrixVP();
_currentFrame = renderer.GetFrameCount();
}
Camera::UpdateVP();
if (_cutMotionBlur)
{
_cutMotionBlur = false;
_matPrevVP = GetMatrixVP();
}
}
void MainCamera::GetPickingRay(RPoint3 pos, RVector3 dir) const

View File

@ -91,6 +91,9 @@ namespace verus
void ExcludeWaterLine(float h = 0.25f);
void ApplyMotion(CSZ boneName, Anim::RMotion motion, float time);
void BakeMotion(CSZ boneName, Anim::RMotion motion, int frame);
// State:
virtual void SaveState(int slot);
virtual void LoadState(int slot);
@ -110,6 +113,7 @@ namespace verus
Matrix4 _matPrevVP = Matrix4::identity(); // For motion blur.
PCursorPosProvider _pCpp = nullptr;
UINT64 _currentFrame = UINT64_MAX;
bool _cutMotionBlur = false;
public:
void operator=(const MainCamera& that);
@ -124,6 +128,7 @@ namespace verus
void SetCursorPosProvider(PCursorPosProvider p) { _pCpp = p; }
float ComputeMotionBlur(RcPoint3 pos, RcPoint3 posPrev) const;
void CutMotionBlur() { _cutMotionBlur = true; }
};
VERUS_TYPEDEFS(MainCamera);
}

View File

@ -22,6 +22,25 @@ void CameraOrbit::Update()
MainCamera::Update();
}
void CameraOrbit::UpdateUsingEyeAt()
{
const Vector3 toEye = _eyePos - _atPos;
const float distToEye = VMath::length(toEye);
const Vector3 dirToEye = toEye / distToEye;
const float yaw = Math::WrapAngle(atan2(dirToEye.getX(), dirToEye.getZ()));
const float pitch = asin(Math::Clamp<float>(-dirToEye.getY(), -1, 1));
_pitch = pitch;
_pitch.ForceTarget();
_yaw = yaw;
_yaw.ForceTarget();
_radius = distToEye;
_radius.ForceTarget();
Update();
}
void CameraOrbit::UpdateElastic()
{
if (!_elastic)

View File

@ -17,6 +17,7 @@ namespace verus
virtual ~CameraOrbit();
virtual void Update() override;
void UpdateUsingEyeAt();
void UpdateElastic();
virtual void DragController_GetParams(float& x, float& y) override;

View File

@ -181,8 +181,8 @@ void Grass::Update()
_phase = glm::fract(_phase + dt * 2.3f);
const float windSpeed = atmo.GetWindSpeed();
const float warpStrength = Math::Clamp<float>(windSpeed * (1 / 10.f), 0, 1.f);
const float turbulence = Math::Clamp<float>(windSpeed * (1 / 14.f), 0, 0.4f);
const float warpStrength = Math::Clamp<float>(windSpeed * (1 / 13.f), 0, 1.f);
const float turbulence = Math::Clamp<float>(windSpeed * (1 / 17.f), 0, 0.4f);
_warpSpring.Update(atmo.GetWindDirection() * (warpStrength * 30.f));
_turbulence = turbulence * turbulence;

View File

@ -568,15 +568,10 @@ void MaterialManager::Init()
void MaterialManager::InitCmd()
{
CGI::SamplerDesc strassSamplerDesc;
strassSamplerDesc.SetFilter("a");
strassSamplerDesc.SetAddressMode("rr");
strassSamplerDesc._mipLodBias = -2;
_texDefaultAlbedo.Init("[Textures]:Default.dds", false, true);
_texDefaultNormal.Init("[Textures]:Default.NM.dds", false, true);
_texDetail.Init("[Textures]:Detail.FX.dds", false, true);
_texStrass.Init("[Textures]:Strass.dds", false, true, &strassSamplerDesc);
_texStrass.Init("[Textures]:Strass.dds", false, true);
_cshDefault = Mesh::GetShader()->BindDescriptorSetTextures(1,
{

View File

@ -40,7 +40,7 @@ void Mesh::InitStatic()
CGI::Sampler::aniso,
CGI::Sampler::aniso,
CGI::Sampler::aniso,
CGI::Sampler::custom
CGI::Sampler::lodBias
}, CGI::ShaderStageFlags::fs);
s_shader[SHADER_MAIN]->CreateDescriptorSet(2, &s_ubPerMeshVS, sizeof(s_ubPerMeshVS), settings.GetLimits()._mesh_ubPerMeshVSCapacity, {}, CGI::ShaderStageFlags::vs);
s_shader[SHADER_MAIN]->CreateDescriptorSet(3, &s_ubSkeletonVS, sizeof(s_ubSkeletonVS), settings.GetLimits()._mesh_ubSkinningVSCapacity, {}, CGI::ShaderStageFlags::vs);

View File

@ -87,7 +87,7 @@ namespace verus
VERUS_FOREACH_X(TStoreModels::TMap, TStoreModels::_map, it)
{
auto& model = *it++;
if (Continue::yes != fn(model.second))
if (Continue::no == fn(model.second))
return;
}
}
@ -103,7 +103,7 @@ namespace verus
VERUS_FOREACH_X(TStoreSceneParticles::TMap, TStoreSceneParticles::_map, it)
{
auto& particles = *it++;
if (Continue::yes != fn(particles.second))
if (Continue::no == fn(particles.second))
return;
}
}
@ -119,7 +119,7 @@ namespace verus
VERUS_FOREACH_X(TStoreSites::TMap, TStoreSites::_map, it)
{
auto& site = *it++;
if (Continue::yes != fn(site.second))
if (Continue::no == fn(site.second))
return;
}
}
@ -171,7 +171,7 @@ namespace verus
MatchSelected(block) &&
(!query._blockMesh || block.GetUrl() == query._blockMesh) &&
(!query._blockMaterial || block.GetMaterial()->_name == query._blockMaterial))
if (Continue::yes != fn(block))
if (Continue::no == fn(block))
return;
}
}
@ -188,7 +188,7 @@ namespace verus
MatchName(emitter) &&
MatchSelected(emitter) &&
(!query._particlesUrl || emitter.GetUrl() == query._particlesUrl))
if (Continue::yes != fn(emitter))
if (Continue::no == fn(emitter))
return;
}
}
@ -200,7 +200,7 @@ namespace verus
if (
MatchName(light) &&
MatchSelected(light))
if (Continue::yes != fn(light))
if (Continue::no == fn(light))
return;
}
}
@ -212,7 +212,7 @@ namespace verus
if (
MatchName(prefab) &&
MatchSelected(prefab))
if (Continue::yes != fn(prefab))
if (Continue::no == fn(prefab))
return;
}
}
@ -224,7 +224,7 @@ namespace verus
if (
MatchName(trigger) &&
MatchSelected(trigger))
if (Continue::yes != fn(trigger))
if (Continue::no == fn(trigger))
return;
}
}

View File

@ -5,9 +5,9 @@
#include "LibDepth.hlsl"
#include "Bloom.inc.hlsl"
ConstantBuffer<UB_BloomVS> g_ubBloomVS : register(b0, space0);
ConstantBuffer<UB_BloomFS> g_ubBloomFS : register(b0, space1);
ConstantBuffer<UB_BloomGodRaysFS> g_ubBloomGodRaysFS : register(b0, space2);
ConstantBuffer<UB_BloomVS> g_ubBloomVS : register(b0, space0);
ConstantBuffer<UB_BloomFS> g_ubBloomFS : register(b0, space1);
ConstantBuffer<UB_BloomLightShaftsFS> g_ubBloomLightShaftsFS : register(b0, space2);
Texture2D g_texColor : register(t1, space1);
SamplerState g_samColor : register(s1, space1);
@ -50,24 +50,24 @@ FSO mainFS(VSO si)
{
FSO so;
#ifdef DEF_GOD_RAYS
const float maxDist = g_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.x;
const float sunGloss = g_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.y;
const float wideStrength = g_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.z;
const float sunStrength = g_ubBloomGodRaysFS._maxDist_sunGloss_wideStrength_sunStrength.w;
#ifdef DEF_LIGHT_SHAFTS
const float maxDist = g_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.x;
const float sunGloss = g_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.y;
const float wideStrength = g_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.z;
const float sunStrength = g_ubBloomLightShaftsFS._maxDist_sunGloss_wideStrength_sunStrength.w;
const float2 ndcPos = ToNdcPos(si.tc0);
const float rawDepth = g_texDepth.SampleLevel(g_samDepth, si.tc0, 0.f).r;
const float3 posW = DS_GetPosition(rawDepth, g_ubBloomGodRaysFS._matInvVP, ndcPos);
const float3 posW = DS_GetPosition(rawDepth, g_ubBloomLightShaftsFS._matInvVP, ndcPos);
const float3 eyePos = g_ubBloomGodRaysFS._eyePos.xyz;
const float3 eyePos = g_ubBloomLightShaftsFS._eyePos.xyz;
const float3 toPosW = posW - eyePos;
const float depth = length(toPosW);
const float3 pickingRayDir = toPosW / depth;
const float2 spec = pow(saturate(dot(g_ubBloomGodRaysFS._dirToSun.xyz, pickingRayDir)), float2(7, sunGloss));
const float2 spec = pow(saturate(dot(g_ubBloomLightShaftsFS._dirToSun.xyz, pickingRayDir)), float2(7, sunGloss));
const float strength = dot(spec, float2(wideStrength, sunStrength));
float3 godRays = 0.f;
float3 lightShafts = 0.f;
if (strength >= 0.0001f)
{
const float3 rand = Rand(si.pos.xy);
@ -93,21 +93,21 @@ FSO mainFS(VSO si)
g_samShadow,
pos,
pos,
g_ubBloomGodRaysFS._matShadow,
g_ubBloomGodRaysFS._matShadowCSM1,
g_ubBloomGodRaysFS._matShadowCSM2,
g_ubBloomGodRaysFS._matShadowCSM3,
g_ubBloomGodRaysFS._matScreenCSM,
g_ubBloomGodRaysFS._csmSplitRanges,
g_ubBloomGodRaysFS._shadowConfig,
g_ubBloomLightShaftsFS._matShadow,
g_ubBloomLightShaftsFS._matShadowCSM1,
g_ubBloomLightShaftsFS._matShadowCSM2,
g_ubBloomLightShaftsFS._matShadowCSM3,
g_ubBloomLightShaftsFS._matScreenCSM,
g_ubBloomLightShaftsFS._csmSplitRanges,
g_ubBloomLightShaftsFS._shadowConfig,
false);
acc += shadowMask * step(pickingRayLen, depth);
pickingRayLen += stride;
}
godRays = acc * (1.f / sampleCount) * g_ubBloomGodRaysFS._sunColor.rgb * g_ubBloomFS._exposure.x * strength;
lightShafts = acc * (1.f / sampleCount) * g_ubBloomLightShaftsFS._sunColor.rgb * g_ubBloomFS._exposure.x * strength;
}
so.color.rgb = godRays;
so.color.rgb = lightShafts;
#else
const float colorScale = g_ubBloomFS._colorScale_colorBias.x;
const float colorBias = g_ubBloomFS._colorScale_colorBias.y;
@ -126,4 +126,4 @@ FSO mainFS(VSO si)
#endif
//@main:#
//@main:#GodRays GOD_RAYS
//@main:#LightShafts LIGHT_SHAFTS

View File

@ -12,7 +12,7 @@ VERUS_UBUFFER UB_BloomFS
float4 _colorScale_colorBias;
};
VERUS_UBUFFER UB_BloomGodRaysFS
VERUS_UBUFFER UB_BloomLightShaftsFS
{
matrix _matInvVP;
float4 _dirToSun;