오늘은 애니메이션을 가진 모델을 인스턴싱할 수 있도록 코드를 수정해주자. 우선 메인코드에서 모델과 모델애니메이션을 부르는 코드를 추가해주자.
AnimInstancingDemo.cpp
void AnimInstancingDemo::Init()
{
RESOURCES->Init();
_shader = make_shared<Shader>(L"22. AnimInstancingDemo.fx");
// Camera
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform()->SetPosition(Vec3{ 0.f, 0.f, -5.f });
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
shared_ptr<class Model> m1 = make_shared<Model>();
m1->ReadModel(L"Kachujin/Kachujin");
m1->ReadMaterial(L"Kachujin/Kachujin");
m1->ReadAnimation(L"Kachujin/Idle");
m1->ReadAnimation(L"Kachujin/Run");
m1->ReadAnimation(L"Kachujin/Slash");
for (int32 i = 0; i < 500; i++)
{
auto obj = make_shared<GameObject>();
obj->GetOrAddTransform()->SetPosition(Vec3(rand() % 100, 0, rand() % 100));
obj->GetOrAddTransform()->SetScale(Vec3(0.01f));
obj->AddComponent(make_shared<ModelRenderer>(_shader));
{
obj->GetModelRenderer()->SetModel(m1);
}
_objs.push_back(obj);
}
RENDER->Init(_shader);
}
이렇게 해주면 애니메이션이 적용되지 않은 기본자세의 모델이 출력되고 있는 모습을 볼 수 있다.
이제 코드를 본격적으로 수정해보자. 쉐이더코드를 먼저 수정해주자. 우리가 최종적으로 애니메이션 작업을 했던
TweenDemo.fx 코드를 참고하여 글로벌에서 루트, 루트에서 다시 다른자세로 가는 행렬을 구해줬던 부분을 넣어주는게
핵심이다.
AnimInstancingDemo.fx
#include "00. Global.fx"
#include "00. Light.fx"
#define MAX_MODEL_TRANSFORMS 250
#define MAX_MODEL_KEYFRAMES 500
struct KeyframeDesc
{
int animIndex;
uint currFrame;
uint nextFrame;
float ratio; //프레임 블렌딩용
float sumTime;
float speed;
float2 padding;
};
struct TweenFrameDesc
{
float tweenDuration; //트랜지션 지속시간
float tweenRatio;
float tweenSumTime;
float padding;
KeyframeDesc curr;
KeyframeDesc next;
};
cbuffer TweenBuffer
{
TweenFrameDesc TweenFrames;
};
cbuffer BoneBuffer
{
matrix BoneTransform[MAX_MODEL_TRANSFORMS];
};
uint BoneIndex;
Texture2DArray TransformMap;
struct VS_IN
{
float4 position : POSITION;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
float3 tangent : TANGENT;
float4 blendIndices : BLEND_INDICES;
float4 blendWeights : BLEND_WEIGHTS;
// INSTANCING
uint instanceID : SV_INSTANCEID;
matrix world : INST;
};
struct VS_OUT
{
float4 position : SV_POSITION;
float3 worldPosition : POSITION1;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
};
matrix GetAnimationMatrix(VS_IN input)
{
float indices[4] = { input.blendIndices.x, input.blendIndices.y, input.blendIndices.z, input.blendIndices.w };
float weights[4] = { input.blendWeights.x, input.blendWeights.y, input.blendWeights.z, input.blendWeights.w };
int animIndex[2];
int currFrame[2];
int nextFrame[2];
float ratio[2];
animIndex[0] = TweenFrames.curr.animIndex;
currFrame[0] = TweenFrames.curr.currFrame;
nextFrame[0] = TweenFrames.curr.nextFrame;
ratio[0] = TweenFrames.curr.ratio;
animIndex[1] = TweenFrames.next.animIndex;
currFrame[1] = TweenFrames.next.currFrame;
nextFrame[1] = TweenFrames.next.nextFrame;
ratio[1] = TweenFrames.next.ratio;
float4 c0, c1, c2, c3;
float4 n0, n1, n2, n3;
matrix curr = 0;
matrix next = 0;
matrix transform = 0;
//각 뼈의 가중치에 따른 행렬 누적
for (int i = 0; i < 4; i++)
{
//처음 애니메이션
c0 = TransformMap.Load(int4(indices[i] * 4 + 0, currFrame[0], animIndex[0], 0));
c1 = TransformMap.Load(int4(indices[i] * 4 + 1, currFrame[0], animIndex[0], 0));
c2 = TransformMap.Load(int4(indices[i] * 4 + 2, currFrame[0], animIndex[0], 0));
c3 = TransformMap.Load(int4(indices[i] * 4 + 3, currFrame[0], animIndex[0], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformMap.Load(int4(indices[i] * 4 + 0, nextFrame[0], animIndex[0], 0));
n1 = TransformMap.Load(int4(indices[i] * 4 + 1, nextFrame[0], animIndex[0], 0));
n2 = TransformMap.Load(int4(indices[i] * 4 + 2, nextFrame[0], animIndex[0], 0));
n3 = TransformMap.Load(int4(indices[i] * 4 + 3, nextFrame[0], animIndex[0], 0));
next = matrix(n0, n1, n2, n3);
matrix result = lerp(curr, next, ratio[0]);
// 다음 애니메이션
if (animIndex[1] >= 0)
{
c0 = TransformMap.Load(int4(indices[i] * 4 + 0, currFrame[1], animIndex[1], 0));
c1 = TransformMap.Load(int4(indices[i] * 4 + 1, currFrame[1], animIndex[1], 0));
c2 = TransformMap.Load(int4(indices[i] * 4 + 2, currFrame[1], animIndex[1], 0));
c3 = TransformMap.Load(int4(indices[i] * 4 + 3, currFrame[1], animIndex[1], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformMap.Load(int4(indices[i] * 4 + 0, nextFrame[1], animIndex[1], 0));
n1 = TransformMap.Load(int4(indices[i] * 4 + 1, nextFrame[1], animIndex[1], 0));
n2 = TransformMap.Load(int4(indices[i] * 4 + 2, nextFrame[1], animIndex[1], 0));
n3 = TransformMap.Load(int4(indices[i] * 4 + 3, nextFrame[1], animIndex[1], 0));
next = matrix(n0, n1, n2, n3);
matrix nextResult = lerp(curr, next, ratio[1]);
result = lerp(result, nextResult, TweenFrames.tweenRatio);
}
transform += mul(weights[i], result);
}
return transform;
}
VS_OUT VS(VS_IN input)
{
VS_OUT output;
//output.position = mul(input.position, BoneTransform[BoneIndex]); //루트기준좌표
matrix m = GetAnimationMatrix(input);
output.position = mul(input.position, m);
//물체마다 World 가져오기
output.position = mul(output.position, input.world); // W
output.worldPosition = output.position;
output.position = mul(output.position, VP);
output.uv = input.uv;
output.normal = input.normal;
return output;
}
float4 PS(VS_OUT input) : SV_TARGET
{
//float4 color = ComputeLight(input.normal, input.uv, input.worldPosition);
float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
return color;
}
technique11 T0
{
PASS_VP(P0, VS, PS)
};
이제 본격적으로 정보를 전달해주는 부분인 ModelAnimator를 수정해주자. 우선 생각해봐야하는 것은 인스턴싱을 한다고 했을 때 어느부분이 동일하고 어느부분이 다를까를 생각해봐야한다. 이때 우리가 애니메이션 별로 매프레임마다 뼈의
위치가 어디있는지 모아서 SRV를 통해 텍스처맵을 전달해주고 있는데 이 부분을 어떻게 해줄지 생각해보자.
일단 텍스처맵은 애니메이션이 급격하게 바뀌는 것이 아니라서 공용으로 사용해주어도 좋을 것 같다.
TweenDesc은 현재 애니메이션 정보와 다음 애니메이션 정보를 가지고 있는데 이것을 공용으로 사용해주면 같은 오브젝트 정보를 가지고 있다면 무조건 같은 애니메이션이 실행되기 때문에 같은 오브젝트라도 각기 다르게 움직일 수 있어야 하기 때문에 수정해줘야한다.
일단 이러한 정보를 자기혼자 렌더링하는 함수에서 인스턴싱을 할 수 있도록 Animator 클래스 코드를 수정해주자
ModelAnimator.h
#pragma once
#include "Component.h"
class Model;
struct AnimTransform
{
using TransformArrayType = array<Matrix, MAX_MODEL_TRANSFORMS>;
//2차배열
array<TransformArrayType, MAX_MODEL_KEYFRAMES> transforms;
};
class ModelAnimator : public Component
{
using Super = Component;
public:
ModelAnimator(shared_ptr<Shader> shader);
~ModelAnimator();
void SetModel(shared_ptr<Model> model);
void SetPass(uint8 pass) { _pass = pass; }
virtual void Update() override;
void UpdateTweenData();
void RenderInstancing(shared_ptr<class InstancingBuffer>& buffer);
InstanceID GetInstanceID();
private:
void CreateTexture();
void CreateAnimationTransform(uint32 index);
private:
vector<AnimTransform> _animTransforms;
ComPtr<ID3D11Texture2D> _texture;
//Texture
ComPtr<ID3D11ShaderResourceView> _srv;
private:
TweenDesc _tweenDesc;
private:
shared_ptr<Shader> _shader;
uint8 _pass = 0; //통로
shared_ptr<Model> _model;
};
ModelAnimator.cpp
#include "pch.h"
#include "ModelAnimator.h"
#include "Material.h"
#include "ModelMesh.h"
#include "Model.h"
#include "ModelAnimation.h"
ModelAnimator::ModelAnimator(shared_ptr<Shader> shader)
: Super(ComponentType::Animator), _shader(shader)
{
// TEST
_tweenDesc.next.animIndex = rand() % 3;
_tweenDesc.tweenSumTime += rand() % 100;
}
ModelAnimator::~ModelAnimator()
{
}
void ModelAnimator::Update()
{
}
void ModelAnimator::UpdateTweenData()
{
TweenDesc& desc = _tweenDesc;
desc.curr.sumTime += DT;
//현재 애니메이션
{
shared_ptr<ModelAnimation> currentAnim = _model->GetAnimationByIndex(desc.curr.animIndex);
if (currentAnim)
{
float timePerFrame = 1 / (currentAnim->frameRate * desc.curr.speed);
if (desc.curr.sumTime >= timePerFrame)
{
desc.curr.sumTime = 0;
desc.curr.currFrame = (desc.curr.currFrame + 1) % currentAnim->frameCount;
desc.curr.nextFrame = (desc.curr.currFrame + 1) % currentAnim->frameCount;
}
desc.curr.ratio = (desc.curr.sumTime / timePerFrame);
}
}
// 다음 애니메이션이 예약 되어 있다면
if (desc.next.animIndex >= 0)
{
desc.tweenSumTime += DT;
desc.tweenRatio = desc.tweenSumTime / desc.tweenDuration;
if (desc.tweenRatio >= 1.f)
{
// 애니메이션 교체 성공
desc.curr = desc.next;
desc.ClearNextAnim();
}
else
{
// 교체중
shared_ptr<ModelAnimation> nextAnim = _model->GetAnimationByIndex(desc.next.animIndex);
desc.next.sumTime += DT;
float timePerFrame = 1.f / (nextAnim->frameRate * desc.next.speed);
if (desc.next.ratio >= 1.f)
{
desc.next.sumTime = 0;
desc.next.currFrame = (desc.next.currFrame + 1) % nextAnim->frameCount;
desc.next.nextFrame = (desc.next.currFrame + 1) % nextAnim->frameCount;
}
desc.next.ratio = desc.next.sumTime / timePerFrame;
}
}
}
void ModelAnimator::SetModel(shared_ptr<Model> model)
{
_model = model;
const auto& materials = _model->GetMaterials();
for (auto& material : materials)
{
material->SetShader(_shader);
}
}
void ModelAnimator::RenderInstancing(shared_ptr<class InstancingBuffer>& buffer)
{
if (_model == nullptr)
return;
//TODO
if (_texture == nullptr)
CreateTexture();
//SRV를 전달
_shader->GetSRV("TransformMap")->SetResource(_srv.Get());
//Bones -> shader
BoneDesc boneDesc;
const uint32 boneCount = _model->GetBoneCount();
for (uint32 i = 0; i < boneCount; i++)
{
shared_ptr<ModelBone> bone = _model->GetBoneByIndex(i);
boneDesc.transforms[i] = bone->transform;
}
RENDER->PushBoneData(boneDesc);
const auto& meshes = _model->GetMeshes();
for (auto& mesh : meshes)
{
if (mesh->material)
mesh->material->Update();
//BoneIndex
_shader->GetScalar("BoneIndex")->SetInt(mesh->boneIndex);
mesh->vertexBuffer->PushData();
mesh->indexBuffer->PushData();
buffer->PushData();
_shader->DrawIndexedInstanced(0, _pass, mesh->indexBuffer->GetCount(), buffer->GetCount());
}
}
InstanceID ModelAnimator::GetInstanceID()
{
return make_pair((uint64)_model.get(), (uint64)_shader.get());
}
void ModelAnimator::CreateTexture()
{
if (_model->GetAnimationCount() == 0)
return;
_animTransforms.resize(_model->GetAnimationCount());
for (uint32 i = 0; i < _model->GetAnimationCount(); i++)
CreateAnimationTransform(i);
//CreateTexture
{
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC));
desc.Width = MAX_MODEL_TRANSFORMS * 4; //4칸으로 쪼개기
desc.Height = MAX_MODEL_KEYFRAMES;
desc.ArraySize = _model->GetAnimationCount(); //텍스처배열
desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; // 16바이트
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
//한층
const uint32 dataSize = MAX_MODEL_TRANSFORMS * sizeof(Matrix);
//층을 프레임만큼
const uint32 pageSize = dataSize * MAX_MODEL_KEYFRAMES;
//애니메이션 개수만큼
void* mallocPtr = ::malloc(pageSize * _model->GetAnimationCount());
// 파편화된 데이터를 조립한다.
for (uint32 c = 0; c < _model->GetAnimationCount(); c++)
{
uint32 startOffset = c * pageSize;
//포인터 연산 - ~칸씩 앞으로 가기 때문에
BYTE* pageStartPtr = reinterpret_cast<BYTE*>(mallocPtr) + startOffset;
for (uint32 f = 0; f < MAX_MODEL_KEYFRAMES; f++)
{
void* ptr = pageStartPtr + dataSize * f;
::memcpy(ptr, _animTransforms[c].transforms[f].data(), dataSize);
}
}
// 리소스 만들기
vector<D3D11_SUBRESOURCE_DATA> subResources(_model->GetAnimationCount());
for (uint32 c = 0; c < _model->GetAnimationCount(); c++)
{
void* ptr = (BYTE*)mallocPtr + c * pageSize;
subResources[c].pSysMem = ptr;
subResources[c].SysMemPitch = dataSize;
subResources[c].SysMemSlicePitch = pageSize;
}
//텍스처 배열이 전달된다.
HRESULT hr = DEVICE->CreateTexture2D(&desc, subResources.data(), _texture.GetAddressOf());
CHECK(hr);
::free(mallocPtr);
}
// Create SRV
{
D3D11_SHADER_RESOURCE_VIEW_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
desc.Texture2DArray.MipLevels = 1;
desc.Texture2DArray.ArraySize = _model->GetAnimationCount();
HRESULT hr = DEVICE->CreateShaderResourceView(_texture.Get(), &desc, _srv.GetAddressOf());
CHECK(hr);
}
}
void ModelAnimator::CreateAnimationTransform(uint32 index)
{
//캐싱용
vector<Matrix> tempAnimBoneTransforms(MAX_MODEL_TRANSFORMS, Matrix::Identity);
shared_ptr<ModelAnimation> animation = _model->GetAnimationByIndex(index);
//프레임순회
for (uint32 f = 0; f < animation->frameCount; f++)
{
//본 순회
for (uint32 b = 0; b < _model->GetBoneCount(); b++)
{
shared_ptr<ModelBone> bone = _model->GetBoneByIndex(b);
Matrix matAnimation;
shared_ptr<ModelKeyframe> frame = animation->GetKeyframe(bone->name);
if (frame != nullptr)
{
ModelKeyframeData& data = frame->transforms[f];
Matrix S, R, T;
S = Matrix::CreateScale(data.scale.x, data.scale.y, data.scale.z);
R = Matrix::CreateFromQuaternion(data.rotation);
T = Matrix::CreateTranslation(data.translation.x, data.translation.y, data.translation.z);
matAnimation = S * R * T;
}
else
{
matAnimation = Matrix::Identity;
}
// 처음 T 포즈기준
Matrix toRootMatrix = bone->transform;
Matrix invGlobal = toRootMatrix.Invert();
int32 parentIndex = bone->parentIndex;
Matrix matParent = Matrix::Identity;
if(parentIndex>=0)
matParent = tempAnimBoneTransforms[parentIndex];
tempAnimBoneTransforms[b] = matAnimation * matParent;
//기본-> 다른자세
_animTransforms[index].transforms[f][b] = invGlobal * tempAnimBoneTransforms[b];
}
}
}
이렇게 해준다음 TweenData를 각각의 오브젝트에 따라 설정해주기 위해 배열로 만들어준다. 이때 쉐이더 코드에서 INSTANCE ID를 통해 몇번째 인스턴싱인지 알 수 있기 때문에 이를 통해 Tweendata에서 몇번째인지도 알 수 있다.
AnimInstancingDemo.fx
#include "00. Global.fx"
#include "00. Light.fx"
#define MAX_MODEL_TRANSFORMS 250
#define MAX_MODEL_KEYFRAMES 500
#define MAX_MODEL_INSTANCE 500
struct KeyframeDesc
{
int animIndex;
uint currFrame;
uint nextFrame;
float ratio; //프레임 블렌딩용
float sumTime;
float speed;
float2 padding;
};
struct TweenFrameDesc
{
float tweenDuration; //트랜지션 지속시간
float tweenRatio;
float tweenSumTime;
float padding;
KeyframeDesc curr;
KeyframeDesc next;
};
cbuffer TweenBuffer
{
TweenFrameDesc TweenFrames[MAX_MODEL_INSTANCE];
};
cbuffer BoneBuffer
{
matrix BoneTransform[MAX_MODEL_TRANSFORMS];
};
uint BoneIndex;
Texture2DArray TransformMap;
struct VS_IN
{
float4 position : POSITION;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
float3 tangent : TANGENT;
float4 blendIndices : BLEND_INDICES;
float4 blendWeights : BLEND_WEIGHTS;
// INSTANCING
uint instanceID : SV_INSTANCEID;
matrix world : INST;
};
struct VS_OUT
{
float4 position : SV_POSITION;
float3 worldPosition : POSITION1;
float2 uv : TEXCOORD;
float3 normal : NORMAL;
};
matrix GetAnimationMatrix(VS_IN input)
{
float indices[4] = { input.blendIndices.x, input.blendIndices.y, input.blendIndices.z, input.blendIndices.w };
float weights[4] = { input.blendWeights.x, input.blendWeights.y, input.blendWeights.z, input.blendWeights.w };
int animIndex[2];
int currFrame[2];
int nextFrame[2];
float ratio[2];
animIndex[0] = TweenFrames[input.instanceID].curr.animIndex;
currFrame[0] = TweenFrames[input.instanceID].curr.currFrame;
nextFrame[0] = TweenFrames[input.instanceID].curr.nextFrame;
ratio[0] = TweenFrames[input.instanceID].curr.ratio;
animIndex[1] = TweenFrames[input.instanceID].next.animIndex;
currFrame[1] = TweenFrames[input.instanceID].next.currFrame;
nextFrame[1] = TweenFrames[input.instanceID].next.nextFrame;
ratio[1] = TweenFrames[input.instanceID].next.ratio;
float4 c0, c1, c2, c3;
float4 n0, n1, n2, n3;
matrix curr = 0;
matrix next = 0;
matrix transform = 0;
//각 뼈의 가중치에 따른 행렬 누적
for (int i = 0; i < 4; i++)
{
//처음 애니메이션
c0 = TransformMap.Load(int4(indices[i] * 4 + 0, currFrame[0], animIndex[0], 0));
c1 = TransformMap.Load(int4(indices[i] * 4 + 1, currFrame[0], animIndex[0], 0));
c2 = TransformMap.Load(int4(indices[i] * 4 + 2, currFrame[0], animIndex[0], 0));
c3 = TransformMap.Load(int4(indices[i] * 4 + 3, currFrame[0], animIndex[0], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformMap.Load(int4(indices[i] * 4 + 0, nextFrame[0], animIndex[0], 0));
n1 = TransformMap.Load(int4(indices[i] * 4 + 1, nextFrame[0], animIndex[0], 0));
n2 = TransformMap.Load(int4(indices[i] * 4 + 2, nextFrame[0], animIndex[0], 0));
n3 = TransformMap.Load(int4(indices[i] * 4 + 3, nextFrame[0], animIndex[0], 0));
next = matrix(n0, n1, n2, n3);
matrix result = lerp(curr, next, ratio[0]);
// 다음 애니메이션
if (animIndex[1] >= 0)
{
c0 = TransformMap.Load(int4(indices[i] * 4 + 0, currFrame[1], animIndex[1], 0));
c1 = TransformMap.Load(int4(indices[i] * 4 + 1, currFrame[1], animIndex[1], 0));
c2 = TransformMap.Load(int4(indices[i] * 4 + 2, currFrame[1], animIndex[1], 0));
c3 = TransformMap.Load(int4(indices[i] * 4 + 3, currFrame[1], animIndex[1], 0));
curr = matrix(c0, c1, c2, c3);
n0 = TransformMap.Load(int4(indices[i] * 4 + 0, nextFrame[1], animIndex[1], 0));
n1 = TransformMap.Load(int4(indices[i] * 4 + 1, nextFrame[1], animIndex[1], 0));
n2 = TransformMap.Load(int4(indices[i] * 4 + 2, nextFrame[1], animIndex[1], 0));
n3 = TransformMap.Load(int4(indices[i] * 4 + 3, nextFrame[1], animIndex[1], 0));
next = matrix(n0, n1, n2, n3);
matrix nextResult = lerp(curr, next, ratio[1]);
result = lerp(result, nextResult, TweenFrames[input.instanceID].tweenRatio);
}
transform += mul(weights[i], result);
}
return transform;
}
VS_OUT VS(VS_IN input)
{
VS_OUT output;
//output.position = mul(input.position, BoneTransform[BoneIndex]); //루트기준좌표
matrix m = GetAnimationMatrix(input);
output.position = mul(input.position, m);
//물체마다 World 가져오기
output.position = mul(output.position, input.world); // W
output.worldPosition = output.position;
output.position = mul(output.position, VP);
output.uv = input.uv;
output.normal = input.normal;
return output;
}
float4 PS(VS_OUT input) : SV_TARGET
{
//float4 color = ComputeLight(input.normal, input.uv, input.worldPosition);
float4 color = DiffuseMap.Sample(LinearSampler, input.uv);
return color;
}
technique11 T0
{
PASS_VP(P0, VS, PS)
};
이 쉐이더에 맞게 정보를 넘겨주는 버퍼를 만들어주자. 이것 또한 배열로 만들어주면된다.
RenderManager.h
struct InstancedTweenDesc
{
TweenDesc tweens[MAX_MODEL_INSTANCE];
};
class RenderManager
{
DECLARE_SINGLE(RenderManager);
public:
void PushTweenData(const InstancedTweenDesc& desc);
private:
InstancedTweenDesc _tweenDesc;
shared_ptr<ConstantBuffer<InstancedTweenDesc>> _tweenBuffer;
ComPtr<ID3DX11EffectConstantBuffer> _tweenEffectBuffer;
};
RenderManager.cpp
#include "pch.h"
#include "RenderManager.h"
#include "Camera.h"
void RenderManager::Init(shared_ptr<Shader> shader)
{
_tweenBuffer = make_shared<ConstantBuffer<InstancedTweenDesc>>();
_tweenBuffer->Create();
_tweenEffectBuffer = _shader->GetConstantBuffer("TweenBuffer");
}
void RenderManager::PushTweenData(const InstancedTweenDesc& desc)
{
_tweenDesc = desc;
_tweenBuffer->CopyData(_tweenDesc);
_tweenEffectBuffer->SetConstantBuffer(_tweenBuffer->GetComPtr().Get());
}
이렇게 버퍼를 넘겨주는 부분을 만들었지만 인스턴싱버퍼에는 각 오브젝트의 월드좌표만 가지고 있기 때문에 이 ModelAnimator의 TweenDesc 정보를 꺼내서 만들어준 버퍼를 통해 넘겨줘야한다.
일단 Animator에서 TweenDesc를 가져오는 함수를 만들어주자.
ModelAnimator.h
#pragma once
#include "Component.h"
class ModelAnimator : public Component
{
public:
TweenDesc& GetTweenDesc() { return _tweenDesc; }
};
그렇게 해준다음 매니저에서 정보를 업데이트 해준 다음 각 오브젝트의 TweenDesc를 가져온다음에 버퍼를 통해 밀어넣어주면된다.
InstancingManager.cpp
void InstancingManager::RenderAnimRenderer(vector<shared_ptr<GameObject>>& gameObjects)
{
map<InstanceID, vector<shared_ptr<GameObject>>> cache;
for (shared_ptr<GameObject>& gameObject : gameObjects)
{
if (gameObject->GetModelAnimator() == nullptr)
continue;
const InstanceID instanceId = gameObject->GetModelAnimator()->GetInstanceID();
cache[instanceId].push_back(gameObject);
}
for (auto& pair : cache)
{
shared_ptr<InstancedTweenDesc> tweenDesc = make_shared<InstancedTweenDesc>();
const vector<shared_ptr<GameObject>>& vec = pair.second;
//if (vec.size() == 1)
//{
// vec[0]->GetModelAnimator()->RenderSingle();
//}
//else
{
const InstanceID instanceId = pair.first;
for (int32 i = 0; i < vec.size(); i++)
{
const shared_ptr<GameObject>& gameObject = vec[i];
InstancingData data;
data.world = gameObject->GetTransform()->GetWorldMatrix();
AddData(instanceId, data);
// INSTANCING
gameObject->GetModelAnimator()->UpdateTweenData();
tweenDesc->tweens[i] = gameObject->GetModelAnimator()->GetTweenDesc();
}
//트윈데이터 넣어주기
RENDER->PushTweenData(*tweenDesc.get());
shared_ptr<InstancingBuffer>& buffer = _buffers[instanceId];
vec[0]->GetModelAnimator()->RenderInstancing(buffer);
}
}
}
이렇게 해주면 안정적인 프레임으로 각각의 오브젝트들에서 애니메이션이 동작하는것을 볼 수 있다.
'게임공부 > Directx11' 카테고리의 다른 글
[Directx11][C++][3D]21. Quaternion (0) | 2024.09.29 |
---|---|
[Directx11][C++][3D]20. 통합 & 정리 (1) | 2024.09.28 |
[Directx11][C++][3D]18. 인스턴싱(MeshRenderer,ModelRenderer) (0) | 2024.09.24 |
[Directx11][C++][3D]17. 인스턴싱과 드로우콜 (1) | 2024.09.23 |
[Directx11][C++][3D]16. 애니메이션(활용4,스카이박스) (0) | 2024.09.21 |