Improved particles and trajectory rendering #430
|
@ -157,15 +157,17 @@
|
|||
Scale: {x: 1, y: 1, z: 1}
|
||||
IsActive: true
|
||||
classSHADE::SHParticleEmitterComponent:
|
||||
Emission Count: 1
|
||||
Emission Count: 7
|
||||
Is Passive: true
|
||||
Emission Interval: 0.200000003
|
||||
Emission Interval: 0.0939999968
|
||||
Min Life: 2
|
||||
Max Life: 3
|
||||
Minimum Speed: 3
|
||||
Maximum Speed: 6
|
||||
Minimum Size: 0
|
||||
Maximum Size: 0.5
|
||||
Angular Ranges And Offset: {x: 0, y: 0, z: 0, w: 0}
|
||||
Angular Ranges And Offset: {x: 0.699999988, y: 0.469999999, z: 0, w: 0}
|
||||
Rotation Speed: 0
|
||||
Texture Asset ID: 63456868
|
||||
IsActive: true
|
||||
Scripts: ~
|
|
@ -1,5 +1,7 @@
|
|||
#version 450
|
||||
|
||||
#define PI 3.14159265f
|
||||
|
||||
layout(local_size_x = 128) in;
|
||||
|
||||
struct EmitterParameters
|
||||
|
@ -7,14 +9,15 @@ struct EmitterParameters
|
|||
vec4 angularRangesAndOffsets;
|
||||
float minSpeed;
|
||||
float maxSpeed;
|
||||
float padding[2];
|
||||
float rotationSpeed;
|
||||
uint textureIndex;
|
||||
vec4 lifeAndSizeRange; // min life, max life, min size, max size
|
||||
};
|
||||
|
||||
struct ParticleData
|
||||
{
|
||||
vec4 position;
|
||||
vec4 rotation;
|
||||
vec4 orientationSpeedDecay;
|
||||
vec4 velocity;
|
||||
vec4 acceleration;
|
||||
vec4 scaleAndDecay;
|
||||
|
@ -149,8 +152,18 @@ void main()
|
|||
particle.scaleAndDecay.x = particleSize;
|
||||
particle.scaleAndDecay.z = particleSize;
|
||||
|
||||
particle.rotation = vec4 (5.0f);
|
||||
// Set the texture for the particle
|
||||
particle.textureIndex = emitterParams.data.textureIndex;
|
||||
|
||||
// Set orientation and rotation speed
|
||||
if (emitterParams.data.rotationSpeed != 0.0f)
|
||||
particle.orientationSpeedDecay = vec4 (rand(seed) * PI, emitterParams.data.rotationSpeed, 0.0f, 0.0f);
|
||||
else
|
||||
particle.orientationSpeedDecay = vec4 (0.0f);
|
||||
|
||||
|
||||
particle.acceleration = vec4 (0.01f);
|
||||
|
||||
|
||||
inputParticles.data[index] = particle;
|
||||
}
|
Binary file not shown.
|
@ -13,7 +13,7 @@ struct DrawArraysIndirectArgs
|
|||
struct ParticleData
|
||||
{
|
||||
vec4 position;
|
||||
vec4 rotation;
|
||||
vec4 orientationSpeedDecay;
|
||||
vec4 velocity;
|
||||
vec4 acceleration;
|
||||
vec4 scaleAndDecay;
|
||||
|
|
Binary file not shown.
|
@ -1,15 +1,26 @@
|
|||
#version 460 core
|
||||
#extension GL_EXT_nonuniform_qualifier : require
|
||||
|
||||
layout (location = 0) out vec4 fragColor;
|
||||
|
||||
layout (set = 0, binding = 1) uniform sampler2D textures[]; // for textures (global)
|
||||
|
||||
// between shader stages
|
||||
layout(location = 0) in struct
|
||||
{
|
||||
vec2 uv; // location = 0
|
||||
|
||||
} In;
|
||||
|
||||
// material stuff
|
||||
layout(location = 1) flat in struct
|
||||
{
|
||||
uint textureIndex;
|
||||
} InFlat;
|
||||
|
||||
|
||||
void main ()
|
||||
{
|
||||
fragColor = vec4 (1.0f);
|
||||
fragColor = vec4 (texture(textures [nonuniformEXT(InFlat.textureIndex)], In.uv));
|
||||
if (fragColor.a < 0.01f)
|
||||
discard;
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -19,7 +19,7 @@ struct GenericData
|
|||
struct ParticleData
|
||||
{
|
||||
vec4 position;
|
||||
vec4 rotation;
|
||||
vec4 orientationSpeedDecay;
|
||||
vec4 velocity;
|
||||
vec4 acceleration;
|
||||
vec4 scaleAndDecay;
|
||||
|
@ -56,9 +56,14 @@ layout (std430, set = 2, binding = 4) coherent restrict buffer IndicesData
|
|||
layout(location = 0) out struct
|
||||
{
|
||||
vec2 uv; // location = 0
|
||||
|
||||
} Out;
|
||||
|
||||
// material stuff
|
||||
layout(location = 1) out struct
|
||||
{
|
||||
uint textureIndex; // location = 1
|
||||
} OutFlat;
|
||||
|
||||
vec2 CreateQuad (in uint vertexID)
|
||||
{
|
||||
uint b = 1 << vertexID;
|
||||
|
@ -90,6 +95,7 @@ void main()
|
|||
vec3 viewUp = normalize(vec3 (cameraData.viewMat[0][1], cameraData.viewMat[1][1], cameraData.viewMat[2][1]));
|
||||
|
||||
particlePos = particle.position.xyz + (viewRight * particlePos.x * particleScaleData.x) + (viewUp * particlePos.y * particleScaleData.y);
|
||||
OutFlat.textureIndex = particle.textureIndex;
|
||||
|
||||
gl_Position = cameraData.vpMat * vec4(particlePos, 1.0f);
|
||||
}
|
Binary file not shown.
|
@ -811,6 +811,8 @@ namespace SHADE
|
|||
if (!component)
|
||||
return;
|
||||
|
||||
auto gfxSystem = SHSystemManager::GetSystem<SHGraphicsSystem>();
|
||||
|
||||
ImGui::PushID(SHFamilyID<SHComponent>::GetID<SHParticleEmitterComponent>());
|
||||
|
||||
const auto componentType = rttr::type::get(*component);
|
||||
|
@ -860,6 +862,47 @@ namespace SHADE
|
|||
comp->SetMaxSpeed(val);
|
||||
});
|
||||
|
||||
SHEditorWidgets::DragFloat("Rotation Speed",
|
||||
[comp = component]()
|
||||
{
|
||||
return comp->GetMaxSpeed();
|
||||
},
|
||||
[comp = component](float val)
|
||||
{
|
||||
comp->SetMaxSpeed(val);
|
||||
});
|
||||
|
||||
SHEditorWidgets::DragInt("Texture Index",
|
||||
[comp = component]()
|
||||
{
|
||||
return comp->GetTextureAssetID();
|
||||
},
|
||||
[comp = component](AssetID val)
|
||||
{
|
||||
comp->SetTextureAssetID(val);
|
||||
});
|
||||
if (SHDragDrop::BeginTarget())
|
||||
{
|
||||
if (AssetID* payload = SHDragDrop::AcceptPayload<AssetID>(SHDragDrop::DRAG_RESOURCE))
|
||||
{
|
||||
Handle<SHTexture> texture = SHResourceManager::LoadOrGet<SHTexture>(*payload);
|
||||
gfxSystem->BuildTextures();
|
||||
|
||||
if (texture)
|
||||
{
|
||||
component->SetTextureIndex(texture->TextureArrayIndex);
|
||||
component->SetTextureAssetID(*payload);
|
||||
}
|
||||
else
|
||||
{
|
||||
SHLOG_WARNING("[] Attempted to load invalid texture! Texture for particle not set. ");
|
||||
}
|
||||
|
||||
SHDragDrop::EndTarget();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SHEditorWidgets::CheckBox("Is Passive", [comp = component]() {return comp->GetPassive(); }, [comp = component](bool flag) {comp->SetPassive(flag); });
|
||||
|
||||
}
|
||||
|
|
|
@ -62,6 +62,21 @@ namespace SHADE
|
|||
cpuEmitterData.maxSpeed = speed;
|
||||
}
|
||||
|
||||
void SHParticleEmitterComponent::SetRotationSpeed(float speed) noexcept
|
||||
{
|
||||
cpuEmitterData.rotationSpeed = speed;
|
||||
}
|
||||
|
||||
void SHParticleEmitterComponent::SetTextureIndex(uint32_t index) noexcept
|
||||
{
|
||||
cpuEmitterData.textureIndex = index;
|
||||
}
|
||||
|
||||
void SHParticleEmitterComponent::SetTextureAssetID(AssetID id)
|
||||
{
|
||||
textureAssetID = id;
|
||||
}
|
||||
|
||||
void SHParticleEmitterComponent::SetMinSize(float size) noexcept
|
||||
{
|
||||
cpuEmitterData.lifeAndSizeRange.z = size;
|
||||
|
@ -113,6 +128,21 @@ namespace SHADE
|
|||
return cpuEmitterData.maxSpeed;
|
||||
}
|
||||
|
||||
float SHParticleEmitterComponent::GetRotationSpeed(void) const noexcept
|
||||
{
|
||||
return cpuEmitterData.rotationSpeed;
|
||||
}
|
||||
|
||||
uint32_t SHParticleEmitterComponent::GetTextureIndex(void) const noexcept
|
||||
{
|
||||
return cpuEmitterData.textureIndex;
|
||||
}
|
||||
|
||||
AssetID SHParticleEmitterComponent::GetTextureAssetID(void) const noexcept
|
||||
{
|
||||
return textureAssetID;
|
||||
}
|
||||
|
||||
float SHParticleEmitterComponent::GetMinSize(void) const noexcept
|
||||
{
|
||||
return cpuEmitterData.lifeAndSizeRange.z;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Math/Vector/SHVec4.h"
|
||||
#include "ECS_Base/Components/SHComponent.h"
|
||||
#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h"
|
||||
#include <Assets/SHAssetMacros.h>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
@ -25,7 +26,11 @@ namespace SHADE
|
|||
//! Maximum starting velocity
|
||||
float maxSpeed;
|
||||
|
||||
float padding[2];
|
||||
//! Rotational speed of the quad
|
||||
float rotationSpeed;
|
||||
|
||||
//! Texture used by the particle
|
||||
uint32_t textureIndex;
|
||||
|
||||
//! Spawn lifetime and size range (min and max)
|
||||
SHVec4 lifeAndSizeRange;
|
||||
|
@ -104,6 +109,9 @@ namespace SHADE
|
|||
//! For all the dynamic SSBOs in the descriptor set
|
||||
std::array<std::array<uint32_t, 5>, SHGraphicsConstants::NUM_FRAME_BUFFERS> dynamicOffsets{};
|
||||
|
||||
//! For the emitter to use to give particles their texture
|
||||
AssetID textureAssetID;
|
||||
|
||||
public:
|
||||
void OnCreate(void) override final;
|
||||
void OnDestroy(void) override final;
|
||||
|
@ -118,6 +126,9 @@ namespace SHADE
|
|||
void SetAngularRangesAndOffsets (SHVec4 const& ranges) noexcept;
|
||||
void SetMinSpeed (float speed) noexcept;
|
||||
void SetMaxSpeed (float speed) noexcept;
|
||||
void SetRotationSpeed (float speed) noexcept;
|
||||
void SetTextureIndex (uint32_t index) noexcept;
|
||||
void SetTextureAssetID (AssetID id);
|
||||
void SetMinSize (float size) noexcept;
|
||||
void SetMaxSize (float size) noexcept;
|
||||
|
||||
|
@ -130,6 +141,9 @@ namespace SHADE
|
|||
SHVec4 const& GetAngularRangesAndOffsets (void) const noexcept;
|
||||
float GetMinSpeed (void) const noexcept;
|
||||
float GetMaxSpeed (void) const noexcept;
|
||||
float GetRotationSpeed (void) const noexcept;
|
||||
uint32_t GetTextureIndex (void) const noexcept;
|
||||
AssetID GetTextureAssetID (void) const noexcept;
|
||||
float GetMinSize (void) const noexcept;
|
||||
float GetMaxSize (void) const noexcept;
|
||||
|
||||
|
|
|
@ -200,9 +200,9 @@ namespace SHADE
|
|||
};
|
||||
|
||||
// ...copy assign barriers on heap
|
||||
preUpdateBarriers[EMITTER_INDEX * 3] = inputParticleDataBarrierPreUpdate;
|
||||
preUpdateBarriers[(EMITTER_INDEX * 3) + 1] = freelistDataBarrierPreUpdate;
|
||||
preUpdateBarriers[(EMITTER_INDEX * 3) + 2] = indicesDataBarrierPreUpdate;
|
||||
preUpdateBarriers.push_back(inputParticleDataBarrierPreUpdate);
|
||||
preUpdateBarriers.push_back(freelistDataBarrierPreUpdate);
|
||||
preUpdateBarriers.push_back(indicesDataBarrierPreUpdate);
|
||||
|
||||
// make new barrier on stack...
|
||||
vk::BufferMemoryBarrier outputParticleDataBarrierPostUpdate
|
||||
|
@ -235,9 +235,9 @@ namespace SHADE
|
|||
};
|
||||
|
||||
// ...copy assign barriers on heap
|
||||
postUpdateBarriers[EMITTER_INDEX * 3] = outputParticleDataBarrierPostUpdate;
|
||||
postUpdateBarriers[(EMITTER_INDEX * 3) + 1] = indicesDataBarrierPostUpdate;
|
||||
postUpdateBarriers[(EMITTER_INDEX * 3) + 2] = drawDataBarrierPostUpdate;
|
||||
postUpdateBarriers.push_back(outputParticleDataBarrierPostUpdate);
|
||||
postUpdateBarriers.push_back(indicesDataBarrierPostUpdate);
|
||||
postUpdateBarriers.push_back(drawDataBarrierPostUpdate);
|
||||
}
|
||||
|
||||
void SHParticleSubSystem::Init(Handle<SHVkLogicalDevice> device, Handle<SHVkDescriptorPool> inDescPool, Handle<SHVkRenderpass> compatibleRenderpass, Handle<SHSubpass> subpass, Handle<SHVkShaderModule> VS, Handle<SHVkShaderModule> FS, Handle<SHVkShaderModule> emitCS, Handle<SHVkShaderModule> defaultUpdateCS) noexcept
|
||||
|
@ -334,15 +334,15 @@ namespace SHADE
|
|||
// Every emitter will have its own barrier for its particle data and freelist data. Indices data is not needed since
|
||||
// it's mainly used in update and rendering so a barrier for it is NOT needed here.
|
||||
std::vector<vk::BufferMemoryBarrier> preUpdateBarriers{};
|
||||
preUpdateBarriers.resize(emitters.size() * 3);
|
||||
preUpdateBarriers.reserve(emitters.size() * 3);
|
||||
|
||||
// After we invoke the updates for the emitters, we need to make sure all particles and indices data are done updating
|
||||
// before we issue them for rendering.
|
||||
std::vector<vk::BufferMemoryBarrier> postUpdateBarriers{};
|
||||
postUpdateBarriers.resize(emitters.size() * 3);
|
||||
postUpdateBarriers.reserve(emitters.size() * 3);
|
||||
|
||||
std::vector<vk::BufferMemoryBarrier> preDrawBarriers{};
|
||||
preDrawBarriers.resize(emitters.size());
|
||||
preDrawBarriers.reserve(emitters.size());
|
||||
|
||||
|
||||
// If we wanted to be VERY safe, a barrier would be good here to make sure output particles have finish reading input particles in
|
||||
|
@ -365,45 +365,51 @@ namespace SHADE
|
|||
|
||||
for (auto& emitter : emitters)
|
||||
{
|
||||
if (!emitter.initialized)
|
||||
InitializeComponent(emitter);
|
||||
|
||||
// Set emitter emit flag to true here if there are ready to be emitted
|
||||
if (emitter.isPassive)
|
||||
if (emitter.isActive)
|
||||
{
|
||||
// decrement emission timer
|
||||
emitter.timeBeforeEmission -= dt;
|
||||
if (!emitter.initialized)
|
||||
InitializeComponent(emitter);
|
||||
|
||||
// Check if time to emit
|
||||
if (emitter.timeBeforeEmission <= 0.0f)
|
||||
// Set emitter emit flag to true here if there are ready to be emitted
|
||||
if (emitter.isPassive)
|
||||
{
|
||||
// reset timer
|
||||
emitter.timeBeforeEmission = emitter.emissionInterval;
|
||||
// decrement emission timer
|
||||
emitter.timeBeforeEmission -= dt;
|
||||
|
||||
// Emit later
|
||||
emitter.toEmit = true;
|
||||
// Check if time to emit
|
||||
if (emitter.timeBeforeEmission <= 0.0f)
|
||||
{
|
||||
// reset timer
|
||||
emitter.timeBeforeEmission = emitter.emissionInterval;
|
||||
|
||||
// Emit later
|
||||
emitter.toEmit = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (emitter.toEmit) // take note that if emitter is not passive, this can also be set to true outside of this function
|
||||
{
|
||||
// Copy data to host visible buffer of emitter
|
||||
emitter.emitterData->WriteToMemory (&emitter.cpuEmitterData, sizeof (SHParticleEmitterComponent::GPUEmitterStruct), 0, emitterDataOffset);
|
||||
|
||||
// Call emit function here
|
||||
EmitComponent(cmdBuffer, emitter, frameIndex);
|
||||
}
|
||||
|
||||
// prepare barriers
|
||||
PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex);
|
||||
|
||||
// Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so).
|
||||
emitter.toEmit = false;
|
||||
}
|
||||
|
||||
if (emitter.toEmit) // take note that if emitter is not passive, this can also be set to true outside of this function
|
||||
{
|
||||
// Copy data to host visible buffer of emitter
|
||||
emitter.emitterData->WriteToMemory (&emitter.cpuEmitterData, sizeof (SHParticleEmitterComponent::GPUEmitterStruct), 0, emitterDataOffset);
|
||||
|
||||
// Call emit function here
|
||||
EmitComponent(cmdBuffer, emitter, frameIndex);
|
||||
}
|
||||
|
||||
// prepare barriers
|
||||
PreparePrePostUpdateBarriers(preUpdateBarriers, postUpdateBarriers, emitter, i, frameIndex);
|
||||
|
||||
// Emitter will emit once and stop emitting next frame (unless specified before reaching here to do so).
|
||||
emitter.toEmit = false;
|
||||
++i;
|
||||
}
|
||||
|
||||
|
||||
// issue the barrier to wait
|
||||
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {});
|
||||
if (!preUpdateBarriers.empty())
|
||||
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader | vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect, vk::PipelineStageFlagBits::eComputeShader, {}, {}, preUpdateBarriers, {});
|
||||
|
||||
|
||||
|
||||
|
@ -417,14 +423,17 @@ namespace SHADE
|
|||
|
||||
for (auto& emitter : emitters)
|
||||
{
|
||||
UpdateCompoennt(cmdBuffer, emitter, frameIndex);
|
||||
if (emitter.isActive)
|
||||
UpdateCompoennt(cmdBuffer, emitter, frameIndex);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* AFTER UPDATING, RENDER PARTICLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
// issue the barrier to wait for output particles to be done updating and indices data to finish being modified.
|
||||
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {});
|
||||
if (!postUpdateBarriers.empty())
|
||||
cmdBuffer->PipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eVertexShader | vk::PipelineStageFlagBits::eDrawIndirect | vk::PipelineStageFlagBits::eVertexShader, {}, {}, postUpdateBarriers, {});
|
||||
|
||||
}
|
||||
|
||||
|
@ -457,10 +466,13 @@ namespace SHADE
|
|||
// TODO: Issue barrier for output particle data. Semaphore should also be issued outside in SHGraphicsSystem
|
||||
for (auto& emitter : emitters)
|
||||
{
|
||||
// bind the descriptor sets required for emitting particles
|
||||
cmdBuffer->BindDescriptorSet(emitter.particleDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), emitter.dynamicOffsets[frameIndex]);
|
||||
if (emitter.isActive)
|
||||
{
|
||||
// bind the descriptor sets required for emitting particles
|
||||
cmdBuffer->BindDescriptorSet(emitter.particleDescriptorSet, SH_PIPELINE_TYPE::GRAPHICS, mappings.at(SHPredefinedDescriptorTypes::PARTICLES), emitter.dynamicOffsets[frameIndex]);
|
||||
|
||||
RenderComponent(cmdBuffer, emitter, frameIndex);
|
||||
RenderComponent(cmdBuffer, emitter, frameIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -514,6 +514,8 @@ namespace YAML
|
|||
static constexpr std::string_view MAX_SIZE_TAG = "Maximum Size";
|
||||
static constexpr std::string_view MIN_SPEED_TAG = "Minimum Speed";
|
||||
static constexpr std::string_view MAX_SPEED_TAG = "Maximum Speed";
|
||||
static constexpr std::string_view ROTATION_SPEED_TAG = "Rotation Speed";
|
||||
static constexpr std::string_view TEXTURE_ASSET_ID_TAG = "Texture Asset ID";
|
||||
|
||||
static YAML::Node encode(SHParticleEmitterComponent const& rhs)
|
||||
{
|
||||
|
@ -528,11 +530,15 @@ namespace YAML
|
|||
node[MIN_SIZE_TAG.data()] = rhs.GetMinSize();
|
||||
node[MAX_SIZE_TAG.data()] = rhs.GetMaxSize();
|
||||
node[ANGULAR_RANGES_OFFSET_TAG.data()] = rhs.GetAngularRangesAndOffsets();
|
||||
node[ROTATION_SPEED_TAG.data()] = rhs.GetRotationSpeed();
|
||||
node[TEXTURE_ASSET_ID_TAG.data()] = rhs.GetTextureAssetID();
|
||||
|
||||
return node;
|
||||
}
|
||||
static bool decode(YAML::Node const& node, SHParticleEmitterComponent& rhs)
|
||||
{
|
||||
auto gfxSystem = SHSystemManager::GetSystem<SHGraphicsSystem>();
|
||||
|
||||
if (node[EMISSION_COUNT_TAG.data()].IsDefined())
|
||||
rhs.SetEmissionCount(node[EMISSION_COUNT_TAG.data()].as<uint32_t>());
|
||||
|
||||
|
@ -560,6 +566,20 @@ namespace YAML
|
|||
if (node[MAX_SIZE_TAG.data()].IsDefined())
|
||||
rhs.SetMaxSize(node[MAX_SIZE_TAG.data()].as<float>());
|
||||
|
||||
if (node[ROTATION_SPEED_TAG.data()].IsDefined())
|
||||
rhs.SetRotationSpeed(node[ROTATION_SPEED_TAG.data()].as<float>());
|
||||
|
||||
if (node[TEXTURE_ASSET_ID_TAG.data()].IsDefined())
|
||||
{
|
||||
AssetID id = node[TEXTURE_ASSET_ID_TAG.data()].as<AssetID>();
|
||||
|
||||
Handle<SHTexture> texture = SHResourceManager::LoadOrGet<SHTexture>(id);
|
||||
gfxSystem->BuildTextures();
|
||||
|
||||
rhs.SetTextureIndex(texture->TextureArrayIndex);
|
||||
rhs.SetTextureAssetID(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue