#include "stdafx.h" #include "ParticleEmitterComponent.h" #include "Misc/ParticleMaterial.h" ParticleMaterial* ParticleEmitterComponent::m_pParticleMaterial{}; ParticleEmitterComponent::ParticleEmitterComponent(const std::wstring& assetFile, const ParticleEmitterSettings& emitterSettings, UINT particleCount): m_ParticlesArray(new Particle[particleCount]), m_ParticleCount(particleCount), //How big is our particle buffer? m_MaxParticles(particleCount), //How many particles to draw (max == particleCount) m_AssetFile(assetFile), m_EmitterSettings(emitterSettings) { m_enablePostDraw = true; //This enables the PostDraw function for the component } ParticleEmitterComponent::~ParticleEmitterComponent() { //TODO_W9(L"Implement Destructor") delete m_ParticlesArray; m_pVertexBuffer->Release(); m_pVertexBuffer = nullptr; } void ParticleEmitterComponent::Initialize(const SceneContext& sceneContext) { //TODO_W9(L"Implement Initialize") if (m_pParticleMaterial == nullptr) m_pParticleMaterial = MaterialManager::Get()->CreateMaterial(); CreateVertexBuffer(sceneContext); m_pParticleTexture = ContentManager::Load(m_AssetFile); } void ParticleEmitterComponent::CreateVertexBuffer(const SceneContext& sceneContext) { //TODO_W9(L"Implement CreateVertexBuffer") if (m_pVertexBuffer != nullptr) m_pVertexBuffer->Release(); D3D11_BUFFER_DESC bufferDesc; bufferDesc.Usage = D3D11_USAGE_DYNAMIC; bufferDesc.ByteWidth = sizeof(VertexParticle) * m_ParticleCount; bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; bufferDesc.MiscFlags = 0; HRESULT hr = sceneContext.d3dContext.pDevice->CreateBuffer(&bufferDesc, nullptr, &m_pVertexBuffer); if (FAILED(hr)) { HANDLE_ERROR(hr); } } void ParticleEmitterComponent::Update(const SceneContext& sceneContext) { //TODO_W9(L"Implement Update") float averageEnergy{ (m_EmitterSettings.minEnergy + m_EmitterSettings.maxEnergy) / 2.f }; float particleInterval{ 1.0f / (m_ParticleCount / averageEnergy) }; m_LastParticleSpawn += sceneContext.pGameTime->GetElapsed(); m_ActiveParticles = 0; D3D11_MAPPED_SUBRESOURCE mappedResource; sceneContext.d3dContext.pDeviceContext->Map(m_pVertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); VertexParticle* pBuffer = reinterpret_cast(mappedResource.pData); float elapsed = sceneContext.pGameTime->GetElapsed(); for (UINT i{}; i < m_ParticleCount; i++) { Particle& pParticle = m_ParticlesArray[i]; if (pParticle.isActive) { UpdateParticle(pParticle, elapsed); } if (!pParticle.isActive && (m_LastParticleSpawn >= particleInterval) || m_IsFirstParticle) { SpawnParticle(pParticle); m_IsFirstParticle = false; m_LastParticleSpawn = 0; } if (pParticle.isActive) { pBuffer[m_ActiveParticles] = pParticle.vertexInfo; m_ActiveParticles++; } } sceneContext.d3dContext.pDeviceContext->Unmap(m_pVertexBuffer, 0); } void ParticleEmitterComponent::UpdateParticle(Particle& p, float elapsedTime) const { //TODO_W9(L"Implement UpdateParticle") if (!p.isActive) return; p.currentEnergy -= elapsedTime; if (p.currentEnergy <= 0) { p.isActive = false; return; } float lifePercent{ p.currentEnergy / p.totalEnergy }; //position XMVECTOR vel = XMLoadFloat3(&m_EmitterSettings.velocity); XMVECTOR posOffset = XMVectorScale(vel, elapsedTime); XMVECTOR pos = XMLoadFloat3(&p.vertexInfo.Position); XMVECTOR newPos = XMVectorAdd(pos, posOffset); XMStoreFloat3(&p.vertexInfo.Position, newPos); //color p.vertexInfo.Color = m_EmitterSettings.color; p.vertexInfo.Color.w *= lifePercent; // * 2.f; //std::cout << "p.sizeChange = " << p.sizeChange << ", " << "p.initialSize = " << p.initialSize << std::endl; //size if (p.sizeChange != 1) { //lerp p.vertexInfo.Size = (1 - lifePercent) * (p.sizeChange * p.initialSize) + lifePercent * p.initialSize; } } void ParticleEmitterComponent::SpawnParticle(Particle& p) { //TODO_W9(L"Implement SpawnParticle") p.isActive = true; //energy float randomEnergy{ MathHelper::randF(m_EmitterSettings.minEnergy, m_EmitterSettings.maxEnergy) }; p.totalEnergy = randomEnergy; p.currentEnergy = randomEnergy; //position XMFLOAT3 randomDirection{ 1,0,0 }; float pitch = MathHelper::randF(-XM_PI, XM_PI); float yaw = MathHelper::randF(-XM_PI, XM_PI); float roll = MathHelper::randF(-XM_PI, XM_PI); XMMATRIX rotationMat = XMMatrixRotationRollPitchYaw(pitch, yaw, roll); XMVECTOR dir = XMLoadFloat3(&randomDirection); dir = XMVector3TransformNormal(dir, rotationMat); float magnitude{ MathHelper::randF(m_EmitterSettings.minEmitterRadius, m_EmitterSettings.maxEmitterRadius) }; dir = XMVectorScale(dir, magnitude); XMVECTOR startPos = XMLoadFloat3(&GetTransform()->GetWorldPosition()); startPos = XMVectorAdd(startPos, dir); XMStoreFloat3(&randomDirection, startPos); p.vertexInfo.Position = randomDirection; //size p.initialSize = MathHelper::randF(m_EmitterSettings.minSize, m_EmitterSettings.maxSize); p.vertexInfo.Size = p.initialSize; p.sizeChange = MathHelper::randF(m_EmitterSettings.minScale, m_EmitterSettings.maxScale); //rotation p.vertexInfo.Rotation = MathHelper::randF(-XM_PI, XM_PI); //color p.vertexInfo.Color = m_EmitterSettings.color; } void ParticleEmitterComponent::PostDraw(const SceneContext& sceneContext) { //TODO_W9(L"Implement PostDraw") m_pParticleMaterial->SetVariable_Matrix(L"gWorldViewProj", sceneContext.pCamera->GetViewProjection()); m_pParticleMaterial->SetVariable_Matrix(L"gViewInverse", sceneContext.pCamera->GetViewInverse()); m_pParticleMaterial->SetVariable_Texture(L"gParticleTexture", m_pParticleTexture); MaterialTechniqueContext matTechContext = m_pParticleMaterial->GetTechniqueContext(); //1. Set Input Layout sceneContext.d3dContext.pDeviceContext->IASetInputLayout(matTechContext.pInputLayout); //2. Set Primitive Topology sceneContext.d3dContext.pDeviceContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST); //3. Set VertexBuffer constexpr UINT stride = sizeof(VertexParticle); constexpr UINT offset = 0; sceneContext.d3dContext.pDeviceContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset); //4. Draw D3DX11_TECHNIQUE_DESC techDesc{}; matTechContext.pTechnique->GetDesc(&techDesc); for (UINT p = 0; p < techDesc.Passes; ++p) { matTechContext.pTechnique->GetPassByIndex(p)->Apply(0, sceneContext.d3dContext.pDeviceContext); sceneContext.d3dContext.pDeviceContext->DrawIndexed(m_ActiveParticles, 0, 0); } } void ParticleEmitterComponent::DrawImGui() { if(ImGui::CollapsingHeader("Particle System")) { ImGui::SliderUInt("Count", &m_ParticleCount, 0, m_MaxParticles); ImGui::InputFloatRange("Energy Bounds", &m_EmitterSettings.minEnergy, &m_EmitterSettings.maxEnergy); ImGui::InputFloatRange("Size Bounds", &m_EmitterSettings.minSize, &m_EmitterSettings.maxSize); ImGui::InputFloatRange("Scale Bounds", &m_EmitterSettings.minScale, &m_EmitterSettings.maxScale); ImGui::InputFloatRange("Radius Bounds", &m_EmitterSettings.minEmitterRadius, &m_EmitterSettings.maxEmitterRadius); ImGui::InputFloat3("Velocity", &m_EmitterSettings.velocity.x); ImGui::ColorEdit4("Color", &m_EmitterSettings.color.x, ImGuiColorEditFlags_NoInputs); } }