Bomberman-OverlordEngine-x64 / OverlordEngine / Components / ParticleEmitterComponent.cpp
ParticleEmitterComponent.cpp
Raw
#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<ParticleMaterial>();
	
	CreateVertexBuffer(sceneContext);

	m_pParticleTexture = ContentManager::Load<TextureData>(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<VertexParticle*>(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);
	}
}