From 3d2902f6de9040f20006c0d58336cc19b3292169 Mon Sep 17 00:00:00 2001 From: Turanszki Janos Date: Wed, 6 May 2020 02:23:15 +0100 Subject: [PATCH] emitter particle system: sprite sheet animation, frame blending --- Editor/EmitterWindow.cpp | 117 +++++++++++++++++-- Editor/EmitterWindow.h | 7 ++ WickedEngine/ArchiveVersionHistory.txt | 1 + WickedEngine/ShaderInterop_EmittedParticle.h | 15 ++- WickedEngine/emittedparticleHF.hlsli | 3 +- WickedEngine/emittedparticlePS_soft.hlsl | 10 +- WickedEngine/emittedparticleVS.hlsl | 22 +++- WickedEngine/emittedparticle_simulateCS.hlsl | 3 +- WickedEngine/wiArchive.cpp | 2 +- WickedEngine/wiEmittedParticle.cpp | 38 +++++- WickedEngine/wiEmittedParticle.h | 10 ++ WickedEngine/wiVersion.cpp | 2 +- 12 files changed, 208 insertions(+), 22 deletions(-) diff --git a/Editor/EmitterWindow.cpp b/Editor/EmitterWindow.cpp index 046c36a28..0526b0366 100644 --- a/Editor/EmitterWindow.cpp +++ b/Editor/EmitterWindow.cpp @@ -13,12 +13,12 @@ EmitterWindow::EmitterWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) assert(GUI && "Invalid GUI!"); emitterWindow = new wiWindow(GUI, "Emitter Window"); - emitterWindow->SetSize(XMFLOAT2(700, 820)); + emitterWindow->SetSize(XMFLOAT2(680, 700)); GUI->AddWidget(emitterWindow); float x = 200; float y = 5; - float step = 25; + float step = 22; float itemheight = 20; @@ -191,6 +191,21 @@ EmitterWindow::EmitterWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) emitterWindow->AddWidget(volumeCheckBox); + frameBlendingCheckBox = new wiCheckBox("Frame Blending: "); + frameBlendingCheckBox->SetPos(XMFLOAT2(x + 400, y)); + frameBlendingCheckBox->SetSize(XMFLOAT2(itemheight, itemheight)); + frameBlendingCheckBox->OnClick([&](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->SetFrameBlendingEnabled(args.bValue); + } + }); + frameBlendingCheckBox->SetCheck(false); + frameBlendingCheckBox->SetTooltip("If sprite sheet animation is in effect, frames will be smoothly blended."); + emitterWindow->AddWidget(frameBlendingCheckBox); + + infoLabel = new wiLabel("EmitterInfo"); infoLabel->SetSize(XMFLOAT2(380, 120)); @@ -198,24 +213,97 @@ EmitterWindow::EmitterWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) emitterWindow->AddWidget(infoLabel); + y += 125; + + frameRateInput = new wiTextInputField(""); + frameRateInput->SetPos(XMFLOAT2(x, y)); + frameRateInput->SetSize(XMFLOAT2(40, 18)); + frameRateInput->SetText(""); + frameRateInput->SetTooltip("Enter a value to enable looping sprite sheet animation (frames per second). Set 0 for animation along paritcle lifetime."); + frameRateInput->SetDescription("Frame Rate: "); + frameRateInput->OnInputAccepted([this](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->framerate = args.fValue; + } + }); + emitterWindow->AddWidget(frameRateInput); + + frameWidthInput = new wiTextInputField(""); + frameWidthInput->SetPos(XMFLOAT2(x + 150, y)); + frameWidthInput->SetSize(XMFLOAT2(40, 18)); + frameWidthInput->SetText(""); + frameWidthInput->SetTooltip("Enter a value to enable sprite sheet frame width in pixels."); + frameWidthInput->SetDescription("Frame Width: "); + frameWidthInput->OnInputAccepted([this](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->frameWidth = (uint32_t)args.iValue; + } + }); + emitterWindow->AddWidget(frameWidthInput); + + frameHeightInput = new wiTextInputField(""); + frameHeightInput->SetPos(XMFLOAT2(x + 300, y)); + frameHeightInput->SetSize(XMFLOAT2(40, 18)); + frameHeightInput->SetText(""); + frameHeightInput->SetTooltip("Enter a value to enable sprite sheet frame height in pixels."); + frameHeightInput->SetDescription("Frame Height: "); + frameHeightInput->OnInputAccepted([this](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->frameHeight = (uint32_t)args.iValue; + } + }); + emitterWindow->AddWidget(frameHeightInput); + + frameCountInput = new wiTextInputField(""); + frameCountInput->SetPos(XMFLOAT2(x, y += step)); + frameCountInput->SetSize(XMFLOAT2(40, 18)); + frameCountInput->SetText(""); + frameCountInput->SetTooltip("Enter a value to enable the random sprite sheet frame selection's max frame number."); + frameCountInput->SetDescription("Frame Count: "); + frameCountInput->OnInputAccepted([this](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->frameCount = (uint32_t)args.iValue; + } + }); + emitterWindow->AddWidget(frameCountInput); + + horizontalFrameCountInput = new wiTextInputField(""); + horizontalFrameCountInput->SetPos(XMFLOAT2(x + 300, y)); + horizontalFrameCountInput->SetSize(XMFLOAT2(40, 18)); + horizontalFrameCountInput->SetText(""); + horizontalFrameCountInput->SetTooltip("Specifies how many sprite sheet frames are in the horizontal direction. 0 = all frames are placed horizontally."); + horizontalFrameCountInput->SetDescription("Horizontal Frame Count: "); + horizontalFrameCountInput->OnInputAccepted([this](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->horizontalFrameCount = (uint32_t)args.iValue; + } + }); + emitterWindow->AddWidget(horizontalFrameCountInput); + maxParticlesSlider = new wiSlider(100.0f, 1000000.0f, 10000, 100000, "Max particle count: "); maxParticlesSlider->SetSize(XMFLOAT2(360, itemheight)); - maxParticlesSlider->SetPos(XMFLOAT2(x, y += step + 120)); + maxParticlesSlider->SetPos(XMFLOAT2(x, y += step)); maxParticlesSlider->OnSlide([&](wiEventArgs args) { auto emitter = GetEmitter(); if (emitter != nullptr) { emitter->SetMaxParticleCount((uint32_t)args.iValue); } - }); + }); maxParticlesSlider->SetEnabled(false); maxParticlesSlider->SetTooltip("Set the maximum amount of particles this system can handle. This has an effect on the memory budget."); emitterWindow->AddWidget(maxParticlesSlider); - y += 30; - - ////////////////////////////////////////////////////////////////////////////////////////////////// - emitCountSlider = new wiSlider(0.0f, 10000.0f, 1.0f, 100000, "Emit count per sec: "); emitCountSlider->SetSize(XMFLOAT2(360, itemheight)); emitCountSlider->SetPos(XMFLOAT2(x, y += step)); @@ -360,7 +448,7 @@ EmitterWindow::EmitterWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) timestepSlider = new wiSlider(-1, 0.016f, -1, 100000, "Timestep: "); timestepSlider->SetSize(XMFLOAT2(360, itemheight)); - timestepSlider->SetPos(XMFLOAT2(x, y += step*2)); + timestepSlider->SetPos(XMFLOAT2(x, y += step)); timestepSlider->OnSlide([&](wiEventArgs args) { auto emitter = GetEmitter(); if (emitter != nullptr) @@ -378,8 +466,6 @@ EmitterWindow::EmitterWindow(EditorComponent* editor) : GUI(&editor->GetGUI()) //////////////// SPH //////////////////////////// - y += step; - sph_h_Slider = new wiSlider(0.1f, 100.0f, 1.0f, 100000, "SPH Smoothing Radius (h): "); sph_h_Slider->SetSize(XMFLOAT2(360, itemheight)); sph_h_Slider->SetPos(XMFLOAT2(x, y += step)); @@ -474,6 +560,7 @@ void EmitterWindow::SetEntity(Entity entity) meshComboBox->SetEnabled(true); debugCheckBox->SetEnabled(true); volumeCheckBox->SetEnabled(true); + frameBlendingCheckBox->SetEnabled(true); sortCheckBox->SetEnabled(true); depthCollisionsCheckBox->SetEnabled(true); sphCheckBox->SetEnabled(true); @@ -502,8 +589,15 @@ void EmitterWindow::SetEntity(Entity entity) sphCheckBox->SetCheck(emitter->IsSPHEnabled()); pauseCheckBox->SetCheck(emitter->IsPaused()); volumeCheckBox->SetCheck(emitter->IsVolumeEnabled()); + frameBlendingCheckBox->SetCheck(emitter->IsFrameBlendingEnabled()); maxParticlesSlider->SetValue((float)emitter->GetMaxParticleCount()); + frameRateInput->SetValue(emitter->framerate); + frameWidthInput->SetValue((int)emitter->frameWidth); + frameHeightInput->SetValue((int)emitter->frameHeight); + frameCountInput->SetValue((int)emitter->frameCount); + horizontalFrameCountInput->SetValue((int)emitter->horizontalFrameCount); + emitCountSlider->SetValue(emitter->count); emitSizeSlider->SetValue(emitter->size); emitRotationSlider->SetValue(emitter->rotation); @@ -531,6 +625,7 @@ void EmitterWindow::SetEntity(Entity entity) meshComboBox->SetEnabled(false); debugCheckBox->SetEnabled(false); volumeCheckBox->SetEnabled(false); + frameBlendingCheckBox->SetEnabled(false); sortCheckBox->SetEnabled(false); depthCollisionsCheckBox->SetEnabled(false); sphCheckBox->SetEnabled(false); diff --git a/Editor/EmitterWindow.h b/Editor/EmitterWindow.h index b5228ca9e..3e41739cc 100644 --- a/Editor/EmitterWindow.h +++ b/Editor/EmitterWindow.h @@ -43,6 +43,7 @@ public: wiCheckBox* pauseCheckBox; wiCheckBox* debugCheckBox; wiCheckBox* volumeCheckBox; + wiCheckBox* frameBlendingCheckBox; wiSlider* emitCountSlider; wiSlider* emitSizeSlider; wiSlider* emitRotationSlider; @@ -59,5 +60,11 @@ public: wiSlider* sph_p0_Slider; wiSlider* sph_e_Slider; + wiTextInputField* frameRateInput; + wiTextInputField* frameWidthInput; + wiTextInputField* frameHeightInput; + wiTextInputField* frameCountInput; + wiTextInputField* horizontalFrameCountInput; + }; diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index cfef6588c..3a49d73f2 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wiArchive versions +45: Serialized emitter spritesheet properties 44: Serialized AnimationComponent::amount 43: Serialized MeshComponent::vertex_windweights 42: Serialized hair particle sprite sheet properties diff --git a/WickedEngine/ShaderInterop_EmittedParticle.h b/WickedEngine/ShaderInterop_EmittedParticle.h index e45c2d6b3..3b2257748 100644 --- a/WickedEngine/ShaderInterop_EmittedParticle.h +++ b/WickedEngine/ShaderInterop_EmittedParticle.h @@ -28,6 +28,9 @@ static const uint PARTICLECOUNTER_OFFSET_DEADCOUNT = PARTICLECOUNTER_OFFSET_ALIV static const uint PARTICLECOUNTER_OFFSET_REALEMITCOUNT = PARTICLECOUNTER_OFFSET_DEADCOUNT + 4; static const uint PARTICLECOUNTER_OFFSET_ALIVECOUNT_AFTERSIMULATION = PARTICLECOUNTER_OFFSET_REALEMITCOUNT + 4; +static const uint EMITTER_OPTION_BIT_FRAME_BLENDING_ENABLED = 1 << 0; +static const uint EMITTER_OPTION_BIT_SPH_ENABLED = 1 << 1; + CBUFFER(EmittedParticleCB, CBSLOT_OTHER_EMITTEDPARTICLE) { float4x4 xEmitterWorld; @@ -52,6 +55,16 @@ CBUFFER(EmittedParticleCB, CBSLOT_OTHER_EMITTEDPARTICLE) float xEmitterOpacity; uint xEmitterMaxParticleCount; + uint2 xEmitterFrameSize; + uint xEmitterFrameCount; + uint xEmitterHorizontalFrameCount; + + float2 xEmitterTexMul; + float2 xEmitterResolution_rcp; + + float3 padding_xEmitter; + float xEmitterFrameRate; + float xSPH_h; // smoothing radius float xSPH_h_rcp; // 1.0f / smoothing radius float xSPH_h2; // smoothing radius ^ 2 @@ -63,7 +76,7 @@ CBUFFER(EmittedParticleCB, CBSLOT_OTHER_EMITTEDPARTICLE) float xSPH_p0; // reference density float xSPH_e; // viscosity constant - uint xSPH_ENABLED; // is SPH enabled? + uint xEmitterOptions; float xEmitterFixedTimestep; // we can force a fixed timestep (>0) onto the simulation to avoid blowing up float xParticleEmissive; diff --git a/WickedEngine/emittedparticleHF.hlsli b/WickedEngine/emittedparticleHF.hlsli index 34a0a4aee..8a14da6c0 100644 --- a/WickedEngine/emittedparticleHF.hlsli +++ b/WickedEngine/emittedparticleHF.hlsli @@ -5,10 +5,11 @@ struct VertextoPixel { float4 pos : SV_POSITION; float4 pos2D : TEXCOORD0; - float2 tex : TEXCOORD1; + float4 tex : TEXCOORD1; nointerpolation float size : TEXCOORD2; nointerpolation uint color : TEXCOORD3; float3 P : WORLDPOSITION; + nointerpolation float frameBlend : FRAMEBLEND; float2 unrotated_uv : UNROTATED_UV; }; diff --git a/WickedEngine/emittedparticlePS_soft.hlsl b/WickedEngine/emittedparticlePS_soft.hlsl index 0949e2ada..440b9c26a 100644 --- a/WickedEngine/emittedparticlePS_soft.hlsl +++ b/WickedEngine/emittedparticlePS_soft.hlsl @@ -5,7 +5,15 @@ float4 main(VertextoPixel input) : SV_TARGET { - float4 color = texture_0.Sample(sampler_linear_clamp,input.tex); + float4 color = texture_0.Sample(sampler_linear_clamp, input.tex.xy); + + [branch] + if (xEmitterOptions & EMITTER_OPTION_BIT_FRAME_BLENDING_ENABLED) + { + float4 color2 = texture_0.Sample(sampler_linear_clamp, input.tex.zw); + color = lerp(color, color2, input.frameBlend); + } + clip(color.a - 1.0f / 255.0f); float2 pTex = input.pos2D.xy / input.pos2D.w * float2(0.5f, -0.5f) + 0.5f; diff --git a/WickedEngine/emittedparticleVS.hlsl b/WickedEngine/emittedparticleVS.hlsl index c69a6983b..53aa91f4b 100644 --- a/WickedEngine/emittedparticleVS.hlsl +++ b/WickedEngine/emittedparticleVS.hlsl @@ -33,10 +33,23 @@ VertextoPixel main(uint fakeIndex : SV_VERTEXID) // expand the point into a billboard in view space: float3 quadPos = BILLBOARD[vertexID]; + quadPos.x = particle.color_mirror & 0x10000000 ? -quadPos.x : quadPos.x; + quadPos.y = particle.color_mirror & 0x20000000 ? -quadPos.y : quadPos.y; float2 uv = quadPos.xy * float2(0.5f, -0.5f) + 0.5f; - uv.x = particle.color_mirror & 0x10000000 ? 1.0f - uv.x : uv.x; - uv.y = particle.color_mirror & 0x20000000 ? 1.0f - uv.y : uv.y; - + float2 uv2 = uv; + + // Sprite sheet UV transform: + const float spriteframe = xEmitterFrameRate == 0 ? (lifeLerp * xEmitterFrameCount) : ((particle.life * xEmitterFrameRate) % xEmitterFrameCount); + const uint currentFrame = floor(spriteframe); + const uint nextFrame = ceil(spriteframe); + const float frameBlend = frac(spriteframe); + uint2 offset = uint2(currentFrame % xEmitterHorizontalFrameCount, currentFrame / xEmitterHorizontalFrameCount) * xEmitterFrameSize; + uv.xy *= xEmitterTexMul; + uv.xy += offset * xEmitterResolution_rcp; + uint2 offset2 = uint2(nextFrame % xEmitterHorizontalFrameCount, nextFrame / xEmitterHorizontalFrameCount) * xEmitterFrameSize; + uv2.xy *= xEmitterTexMul; + uv2.xy += offset2 * xEmitterResolution_rcp; + // rotate the billboard: float2x2 rot = float2x2( cos(rotation), -sin(rotation), @@ -59,11 +72,12 @@ VertextoPixel main(uint fakeIndex : SV_VERTEXID) Out.P = mul(g_xCamera_InvV, float4(Out.pos.xyz, 1)).xyz; Out.pos = mul(g_xCamera_Proj, Out.pos); - Out.tex = uv; + Out.tex = float4(uv, uv2); Out.size = size; Out.color = (particle.color_mirror & 0x00FFFFFF) | (uint(opacity * 255.0f) << 24); Out.pos2D = Out.pos; Out.unrotated_uv = quadPos.xy * float2(1, -1) / size * 0.5f + 0.5f; + Out.frameBlend = frameBlend; return Out; } \ No newline at end of file diff --git a/WickedEngine/emittedparticle_simulateCS.hlsl b/WickedEngine/emittedparticle_simulateCS.hlsl index 81369b275..10bb42eaf 100644 --- a/WickedEngine/emittedparticle_simulateCS.hlsl +++ b/WickedEngine/emittedparticle_simulateCS.hlsl @@ -126,7 +126,8 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) // reset force for next frame: particle.force = 0; - if (xSPH_ENABLED) + [branch] + if (xEmitterOptions & EMITTER_OPTION_BIT_SPH_ENABLED) { // drag: particle.velocity *= 0.98f; diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index d7d6e9071..636e653a3 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -7,7 +7,7 @@ using namespace std; // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! -uint64_t __archiveVersion = 44; +uint64_t __archiveVersion = 45; // this is the version number of which below the archive is not compatible with the current version uint64_t __archiveVersionBarrier = 22; diff --git a/WickedEngine/wiEmittedParticle.cpp b/WickedEngine/wiEmittedParticle.cpp index 2853c07e0..b237970a2 100644 --- a/WickedEngine/wiEmittedParticle.cpp +++ b/WickedEngine/wiEmittedParticle.cpp @@ -247,6 +247,8 @@ void wiEmittedParticle::UpdateGPU(const TransformComponent& transform, const Mat { device->EventBegin("UpdateEmittedParticles", cmd); + const TextureDesc& desc = material.GetBaseColorMap()->GetDesc(); + EmittedParticleCB cb; cb.xEmitterWorld = transform.world; cb.xEmitCount = (uint32_t)emit; @@ -267,6 +269,23 @@ void wiEmittedParticle::UpdateGPU(const TransformComponent& transform, const Mat cb.xParticleMass = mass; cb.xEmitterMaxParticleCount = MAX_PARTICLES; cb.xEmitterFixedTimestep = FIXED_TIMESTEP; + cb.xEmitterFrameSize = uint2(frameWidth == 0 ? desc.Width : frameWidth, frameHeight == 0 ? desc.Height : frameHeight); + cb.xEmitterFrameCount = std::max(1u, frameCount); + cb.xEmitterHorizontalFrameCount = std::max(1u, horizontalFrameCount == 0 ? frameCount : horizontalFrameCount); + cb.xEmitterTexMul = float2((float)cb.xEmitterFrameSize.x / (float)desc.Width, (float)cb.xEmitterFrameSize.y / (float)desc.Height); + cb.xEmitterResolution_rcp.x = 1.0f / (float)desc.Width; + cb.xEmitterResolution_rcp.y = 1.0f / (float)desc.Height; + cb.xEmitterFrameRate = framerate; + + cb.xEmitterOptions = 0; + if (IsSPHEnabled()) + { + cb.xEmitterOptions |= EMITTER_OPTION_BIT_SPH_ENABLED; + } + if (IsFrameBlendingEnabled()) + { + cb.xEmitterOptions |= EMITTER_OPTION_BIT_FRAME_BLENDING_ENABLED; + } // SPH: cb.xSPH_h = SPH_h; @@ -280,7 +299,6 @@ void wiEmittedParticle::UpdateGPU(const TransformComponent& transform, const Mat cb.xSPH_K = SPH_K; cb.xSPH_p0 = SPH_p0; cb.xSPH_e = SPH_e; - cb.xSPH_ENABLED = IsSPHEnabled() ? 1 : 0; device->UpdateBuffer(&constantBuffer, &cb, cmd); device->BindConstantBuffer(CS, &constantBuffer, CB_GETBINDSLOT(EmittedParticleCB), cmd); @@ -755,6 +773,15 @@ void wiEmittedParticle::Serialize(wiArchive& archive, wiECS::Entity seed) archive >> SPH_K; archive >> SPH_p0; archive >> SPH_e; + + if (archive.GetVersion() >= 45) + { + archive >> frameWidth; + archive >> frameHeight; + archive >> frameCount; + archive >> horizontalFrameCount; + archive >> framerate; + } } else { @@ -778,6 +805,15 @@ void wiEmittedParticle::Serialize(wiArchive& archive, wiECS::Entity seed) archive << SPH_K; archive << SPH_p0; archive << SPH_e; + + if (archive.GetVersion() >= 45) + { + archive << frameWidth; + archive << frameHeight; + archive << frameCount; + archive << horizontalFrameCount; + archive << framerate; + } } } diff --git a/WickedEngine/wiEmittedParticle.h b/WickedEngine/wiEmittedParticle.h index 126dae034..32d7b2f6d 100644 --- a/WickedEngine/wiEmittedParticle.h +++ b/WickedEngine/wiEmittedParticle.h @@ -73,6 +73,7 @@ public: DEPTHCOLLISION = 1 << 3, SPH_FLUIDSIMULATION = 1 << 4, HAS_VOLUME = 1 << 5, + FRAME_BLENDING = 1 << 6, }; uint32_t _flags = EMPTY; @@ -99,6 +100,13 @@ public: float SPH_p0 = 1.0f; // reference density float SPH_e = 0.018f; // viscosity constant + // Sprite sheet properties: + uint32_t frameWidth = 0; + uint32_t frameHeight = 0; + uint32_t frameCount = 0; + uint32_t horizontalFrameCount = 0; + float framerate = 0; // frames per second + void SetMaxParticleCount(uint32_t value); uint32_t GetMaxParticleCount() const { return MAX_PARTICLES; } uint32_t GetMemorySizeInBytes() const; @@ -112,6 +120,7 @@ public: inline bool IsDepthCollisionEnabled() const { return _flags & DEPTHCOLLISION; } inline bool IsSPHEnabled() const { return _flags & SPH_FLUIDSIMULATION; } inline bool IsVolumeEnabled() const { return _flags & HAS_VOLUME; } + inline bool IsFrameBlendingEnabled() const { return _flags & FRAME_BLENDING; } inline void SetDebug(bool value) { if (value) { _flags |= DEBUG; } else { _flags &= ~DEBUG; } } inline void SetPaused(bool value) { if (value) { _flags |= PAUSED; } else { _flags &= ~PAUSED; } } @@ -119,6 +128,7 @@ public: inline void SetDepthCollisionEnabled(bool value) { if (value) { _flags |= DEPTHCOLLISION; } else { _flags &= ~DEPTHCOLLISION; } } inline void SetSPHEnabled(bool value) { if (value) { _flags |= SPH_FLUIDSIMULATION; } else { _flags &= ~SPH_FLUIDSIMULATION; } } inline void SetVolumeEnabled(bool value) { if (value) { _flags |= HAS_VOLUME; } else { _flags &= ~HAS_VOLUME; } } + inline void SetFrameBlendingEnabled(bool value) { if (value) { _flags |= FRAME_BLENDING; } else { _flags &= ~FRAME_BLENDING; } } void Serialize(wiArchive& archive, wiECS::Entity seed = wiECS::INVALID_ENTITY); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 45d7db378..42ab4671c 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 41; // minor bug fixes, alterations, refactors, updates - const int revision = 1; + const int revision = 2; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);