상세 컨텐츠

본문 제목

HumanGL: Human Model에 애니메이션을 적용하기 위한 준비 - (6)

Computer Graphics/HumanGL

by Banjosh 2024. 10. 19. 18:32

본문

 이번에는 계층적 모델링을 적용한 Human Model애니메이션 기능을 추가하기 위한 기본 틀을 구현해 보았다. 키보드 입력을 받아서 STOP, WALK, ROTATE, JUMP 이렇게 네 가지 애니메이션을 실행할 수 있도록 설계하였다.

 

애니메이션 시스템 설계

애니메이션 관리는 Animation 클래스를 통해 이루어진다. Model 클래스와는 별개로 Animation 클래스는 모델의 현재 애니메이션 상태를 관리하며, 키보드 입력을 통해 상태를 갱신하고, 갱신된 상태에 맞는 Action을 실행한다.

Animation 클래스

Animation 클래스는 현재 모델의 애니메이션 상태를 관리하는 역할을 한다. 키보드 입력을 통해 상태를 갱신하고, 해당 상태에 맞는 액션을 실행한다. 실행된 액션에 따라 애니메이션 변환 행렬이 업데이트되며, draw() 함수에서 각 파트에 변환이 적용된다.

#ifndef ANIMATION_H
# define ANIMATION_H

# include "HumanGL.h"
# include "action.h"

class Animation {
public:
	static std::unique_ptr<Animation> createAnimationManager();
	void changeState(const int inputState, const glmath::vec3& dir);
	std::map<ePart,Transform> getTransform();

private:
	Animation();
	
	int m_state;
	std::map<ePart, ObjectInfo> m_objectInfoList;
	std::map<ePart, Transform> m_transformList;
	std::map<eAct, std::unique_ptr<Action>> m_actionList;
};

#endif

 

주요 함수:

  • createAnimationManager(): 애니메이션 관리 객체 생성.
  • changeState(): 키 입력을 받아 모델의 애니메이션 상태를 갱신.
  • getTransform(): 파트별 애니메이션 변환 행렬 리스트를 반환.

주요 변수:

  • m_state: 모델의 애니메이션 상태.
  • m_objectInfoList: 애니메이션을 구현할 때 사용되는 파트별 정보.
  • m_transformList: 각 파트의 변환 정보를 저장.
  • m_actionList: 실행 가능한 액션 리스트.

 

Action 클래스

애니메이션의 각 동작은 Action 클래스를 통해 구현되며, Stop, Walk, Rotate, Jump의 네 가지 액션이 각각 독립적인 클래스로 존재한다. Action 클래스는 추상 클래스이며, 각 액션 클래스가 이를 상속받아 doAction() 함수에서 해당 애니메이션에 맞는 변환을 수행한다.

#ifndef ACTION_H
# define ACTION_H

# include "humanGL.h"
# include <map>
# include <cmath>

class Action {
public:
	virtual int doAction(std::map<ePart, Transform>& transformList, std::map<ePart, ObjectInfo>& objectInfoList) = 0;
	virtual ~Action() = default;
protected:
	static float walkTheta;
	static float breathTheta;
	static float landTheta;
	static float jumpTime;
	static float frameTime;
};

class Stop : public Action {
public:
	virtual int doAction(std::map<ePart, Transform>& transformList, std::map<ePart, ObjectInfo>& objectInfoList) override;
	virtual ~Stop() override = default;
};

class Rotate : public Action {
public:
	virtual int doAction(std::map<ePart, Transform>& transformList, std::map<ePart, ObjectInfo>& objectInfoList) override;
	virtual ~Rotate() override = default;
};

class Jump : public Action {
public:
	virtual int doAction(std::map<ePart, Transform>& transformList, std::map<ePart, ObjectInfo>& objectInfoList) override;
	virtual ~Jump() override = default;
};

class Walk : public Action {
public:
	virtual int doAction(std::map<ePart, Transform>& transformList, std::map<ePart, ObjectInfo>& objectInfoList) override;
	virtual ~Walk() override = default;
};

#endif

 

eAct

애니메이션 상태를 정의하는 열거형으로, 현재 모델이 어떤 액션을 하고 있는지를 나타낸다. STOP, ROTATE, JUMP, WALK와 같은 상태를 표현한다. 

enum class eAct
{
	STOP = (1 << 0),
	ROTATE = (1 << 1),
	JUMP = (1 << 2),
	WALK = (1 << 3),
	FULLBIT = (1 << 4) - 1,
};

 

ePart

 

모델의 각 부위를 나타내는 열거형이다. 머리, 팔, 다리 등 모델의 각 부분을 구분한다.

enum class ePart
{
	PELVIS,
	BODY,
	HAIR,
	LEFT_EYE,
	LEFT_PUPIL,
	RIGHT_EYE,
	RIGHT_PUPIL,
	NOSE,
	MOUSE,
	HEAD,
	LEFT_UPPER_ARM,
	LEFT_SLEEVE,
	LEFT_LOWER_ARM,
	RIGHT_UPPER_ARM,
	RIGHT_SLEEVE,
	RIGHT_LOWER_ARM,
	LEFT_UPPER_LEG,
	LEFT_LOWER_LEG,
	RIGHT_UPPER_LEG,
	RIGHT_LOWER_LEG,
	LEFT_FOOT,
	RIGHT_FOOT,
	GROUND,
	NONE,
};

 

 

Transform

각 애니메이션 동작에서 변환된 위치, 회전, 크기를 저장하는 구조체로, 애니메이션을 통해 변환된 결과를 저장한다.

struct Transform {
	glmath::vec3 translation;
	glmath::quat rotation;
	glmath::vec3 scale;

	Transform() : translation(glmath::vec3(0.0f)), rotation(glmath::quat(0.0f)), scale(glmath::vec3(1.0f)) {};
};

 

ObjectInfo

각 파트가 가진 속성 정보로, 애니메이션 변환을 할 때 기준이 되는 데이터를 관리한다. 현재 위치, 각도, 속도, 목표 방향 등 변환에 필요한 다양한 정보를 저장한다.

struct ObjectInfo {
	glmath::vec3 velocity;
	glmath::vec3 scale;
	glmath::vec3 translation;
	glmath::vec3 currentAngle;
	glmath::vec3 actionAngle;
	glmath::vec3 currentDirection;
	glmath::vec3 targetDirection;

	ObjectInfo()
		: velocity(glmath::vec3(0.0f)), scale(glmath::vec3(1.0f)), translation(glmath::vec3(0.0f)),
		  currentAngle(glmath::vec3(0.0f)), actionAngle(glmath::vec3(0.0f)),
		  currentDirection(glmath::vec3(0.0f, 0.0f, -1.0f)), targetDirection(glmath::vec3(0.0f, 0.0f, -1.0f)) {};
};

 

render loop

 

 메인 루프에서 애니메이션 처리가 추가되었다. processAnimation() 함수를 통해 현재 키 입력을 검사하고, 애니메이션 상태를 업데이트한다.

while (!glfwWindowShouldClose(window)) {
    glfwPollEvents();
    context->processCameraControl(window);
    context->processAnimation(window);
    context->render();
    glfwSwapBuffers(window);
}

 

 

render() 함수

void Context::render() {

	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


	glmath::mat4 view = m_camera.getViewMatrix();
	glmath::mat4 projection = glmath::perspective(glmath::radians(45.0f), (float)m_width / (float)m_height, 0.01f, 60.0f);
	std::map<ePart, Transform> transformList = m_animationManager->getTransform();

	m_program->useProgram();
	Model::s_stack.push(projection * view);
	m_ground->draw(m_program.get(), transformList);
	m_human->draw(m_program.get(), transformList);
	Model::s_stack.pop();
}

 

 애니메이션이 적용된 변환 행렬을 transformList로 받아 draw() 함수에서 이를 반영해 렌더링한다.


결론

 이와 같이 Human Model에 애니메이션을 적용하기 위한 기초적인 틀을 마련하였다. 키보드 입력에 따라 모델이 정지, 걷기, 회전, 점프하는 다양한 애니메이션을 처리할 수 있으며, Action 클래스를 통해 이를 간단하게 확장할 수 있다.

다음 단계에서는 각 애니메이션 동작을 구현하고, 부드럽게 실행되는 애니메이션을 목표로 개선해나갈 것이다.

관련글 더보기