Skip to content

Commit d9b3ec5

Browse files
committed
Initial copy of Chapter 12
1 parent 90a82d1 commit d9b3ec5

120 files changed

Lines changed: 175504 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Chapter12/Actor.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// ----------------------------------------------------------------
2+
// From Game Programming in C++ by Sanjay Madhav
3+
// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
4+
//
5+
// Released under the BSD License
6+
// See LICENSE.txt for full details.
7+
// ----------------------------------------------------------------
8+
9+
#include "Actor.h"
10+
#include "Game.h"
11+
#include "Component.h"
12+
#include <algorithm>
13+
14+
Actor::Actor(Game* game)
15+
:mState(EActive)
16+
,mPosition(Vector3::Zero)
17+
,mRotation(Quaternion::Identity)
18+
,mScale(1.0f)
19+
,mGame(game)
20+
,mRecomputeTransform(true)
21+
{
22+
mGame->AddActor(this);
23+
}
24+
25+
Actor::~Actor()
26+
{
27+
mGame->RemoveActor(this);
28+
// Need to delete components
29+
// Because ~Component calls RemoveComponent, need a different style loop
30+
while (!mComponents.empty())
31+
{
32+
delete mComponents.back();
33+
}
34+
}
35+
36+
void Actor::Update(float deltaTime)
37+
{
38+
if (mState == EActive)
39+
{
40+
if (mRecomputeTransform)
41+
{
42+
ComputeWorldTransform();
43+
}
44+
UpdateComponents(deltaTime);
45+
UpdateActor(deltaTime);
46+
}
47+
}
48+
49+
void Actor::UpdateComponents(float deltaTime)
50+
{
51+
for (auto comp : mComponents)
52+
{
53+
comp->Update(deltaTime);
54+
}
55+
}
56+
57+
void Actor::UpdateActor(float deltaTime)
58+
{
59+
}
60+
61+
void Actor::ComputeWorldTransform()
62+
{
63+
mRecomputeTransform = false;
64+
// Scale, then rotate, then translate
65+
mWorldTransform = Matrix4::CreateScale(mScale);
66+
mWorldTransform *= Matrix4::CreateFromQuaternion(mRotation);
67+
mWorldTransform *= Matrix4::CreateTranslation(mPosition);
68+
69+
// Inform components world transform updated
70+
for (auto comp : mComponents)
71+
{
72+
comp->OnUpdateWorldTransform();
73+
}
74+
}
75+
76+
void Actor::RotateToNewForward(const Vector3& forward)
77+
{
78+
// Figure out difference between original (unit x) and new
79+
float dot = Vector3::Dot(Vector3::UnitX, forward);
80+
float angle = Math::Acos(dot);
81+
// Facing down X
82+
if (dot > 0.9999f)
83+
{
84+
SetRotation(Quaternion::Identity);
85+
}
86+
// Facing down -X
87+
else if (dot < -0.9999f)
88+
{
89+
SetRotation(Quaternion(Vector3::UnitZ, Math::Pi));
90+
}
91+
else
92+
{
93+
// Rotate about axis from cross product
94+
Vector3 axis = Vector3::Cross(Vector3::UnitX, forward);
95+
axis.Normalize();
96+
SetRotation(Quaternion(axis, angle));
97+
}
98+
}
99+
100+
void Actor::AddComponent(Component* component)
101+
{
102+
mComponents.emplace_back(component);
103+
std::sort(mComponents.begin(), mComponents.end(), [](Component* a, Component* b) {
104+
return a->GetUpdateOrder() < b->GetUpdateOrder();
105+
});
106+
}
107+
108+
void Actor::RemoveComponent(Component* component)
109+
{
110+
auto iter = std::find(mComponents.begin(), mComponents.end(), component);
111+
if (iter != mComponents.end())
112+
{
113+
mComponents.erase(iter);
114+
}
115+
}

Chapter12/Actor.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// ----------------------------------------------------------------
2+
// From Game Programming in C++ by Sanjay Madhav
3+
// Copyright (C) 2017 Sanjay Madhav. All rights reserved.
4+
//
5+
// Released under the BSD License
6+
// See LICENSE.txt for full details.
7+
// ----------------------------------------------------------------
8+
9+
#pragma once
10+
#include <vector>
11+
#include "Math.h"
12+
class Actor
13+
{
14+
public:
15+
enum State
16+
{
17+
EActive,
18+
EPaused,
19+
EDead
20+
};
21+
22+
Actor(class Game* game);
23+
virtual ~Actor();
24+
25+
// Update function called from Game (not overridable)
26+
void Update(float deltaTime);
27+
// Updates all the components attached to the actor (not overridable)
28+
void UpdateComponents(float deltaTime);
29+
// Any actor-specific update code (overridable)
30+
virtual void UpdateActor(float deltaTime);
31+
// Any actor-specific input code (overridable)
32+
virtual void ProcessInput(const uint8_t* keys) { }
33+
34+
// Getters/setters
35+
const Vector3& GetPosition() const { return mPosition; }
36+
void SetPosition(const Vector3& pos) { mPosition = pos; mRecomputeTransform = true; }
37+
float GetScale() const { return mScale; }
38+
void SetScale(float scale) { mScale = scale; mRecomputeTransform = true; }
39+
const Quaternion& GetRotation() const { return mRotation; }
40+
void SetRotation(const Quaternion& rotation) { mRotation = rotation; mRecomputeTransform = true; }
41+
42+
void ComputeWorldTransform();
43+
const Matrix4& GetWorldTransform() const { return mWorldTransform; }
44+
45+
Vector3 GetForward() const { return Vector3::Transform(Vector3::UnitX, mRotation); }
46+
Vector3 GetRight() const { return Vector3::Transform(Vector3::UnitY, mRotation); }
47+
48+
void RotateToNewForward(const Vector3& forward);
49+
50+
State GetState() const { return mState; }
51+
void SetState(State state) { mState = state; }
52+
53+
class Game* GetGame() { return mGame; }
54+
55+
56+
// Add/remove components
57+
void AddComponent(class Component* component);
58+
void RemoveComponent(class Component* component);
59+
private:
60+
// Actor's state
61+
State mState;
62+
63+
// Transform
64+
Matrix4 mWorldTransform;
65+
Vector3 mPosition;
66+
Quaternion mRotation;
67+
float mScale;
68+
bool mRecomputeTransform;
69+
70+
std::vector<class Component*> mComponents;
71+
class Game* mGame;
72+
};

Chapter12/Animation.cpp

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#include "Animation.h"
2+
#include "Skeleton.h"
3+
#include <fstream>
4+
#include <sstream>
5+
#include <rapidjson/document.h>
6+
#include <SDL/SDL_log.h>
7+
8+
9+
bool Animation::Load(const std::string& fileName)
10+
{
11+
std::ifstream file(fileName);
12+
if (!file.is_open())
13+
{
14+
SDL_Log("File not found: Animation %s", fileName.c_str());
15+
return false;
16+
}
17+
18+
std::stringstream fileStream;
19+
fileStream << file.rdbuf();
20+
std::string contents = fileStream.str();
21+
rapidjson::StringStream jsonStr(contents.c_str());
22+
rapidjson::Document doc;
23+
doc.ParseStream(jsonStr);
24+
25+
if (!doc.IsObject())
26+
{
27+
SDL_Log("Animation %s is not valid json", fileName.c_str());
28+
return false;
29+
}
30+
31+
int ver = doc["version"].GetInt();
32+
33+
// Check the metadata
34+
if (ver != 1)
35+
{
36+
SDL_Log("Animation %s unknown format", fileName.c_str());
37+
return false;
38+
}
39+
40+
const rapidjson::Value& sequence = doc["sequence"];
41+
if (!sequence.IsObject())
42+
{
43+
SDL_Log("Animation %s doesn't have a sequence.", fileName.c_str());
44+
return false;
45+
}
46+
47+
const rapidjson::Value& frames = sequence["frames"];
48+
const rapidjson::Value& length = sequence["length"];
49+
const rapidjson::Value& bonecount = sequence["bonecount"];
50+
51+
if (!frames.IsUint() || !length.IsDouble() || !bonecount.IsUint())
52+
{
53+
SDL_Log("Sequence %s has invalid frames, length, or bone count.", fileName.c_str());
54+
return false;
55+
}
56+
57+
mNumFrames = frames.GetUint();
58+
mDuration = length.GetDouble();
59+
mNumBones = bonecount.GetUint();
60+
mFrameDuration = mDuration / (mNumFrames - 1);
61+
62+
mTracks.resize(mNumBones);
63+
64+
const rapidjson::Value& tracks = sequence["tracks"];
65+
66+
if (!tracks.IsArray())
67+
{
68+
SDL_Log("Sequence %s missing a tracks array.", fileName.c_str());
69+
return false;
70+
}
71+
72+
for (rapidjson::SizeType i = 0; i < tracks.Size(); i++)
73+
{
74+
if (!tracks[i].IsObject())
75+
{
76+
SDL_Log("Animation %s: Track element %d is invalid.", fileName.c_str(), i);
77+
return false;
78+
}
79+
80+
size_t boneIndex = tracks[i]["bone"].GetUint();
81+
82+
const rapidjson::Value& transforms = tracks[i]["transforms"];
83+
if (!transforms.IsArray())
84+
{
85+
SDL_Log("Animation %s: Track element %d is missing transforms.", fileName.c_str(), i);
86+
return false;
87+
}
88+
89+
BoneTransform temp;
90+
91+
if (transforms.Size() < mNumFrames)
92+
{
93+
SDL_Log("Animation %s: Track element %d has fewer frames than expected.", fileName.c_str(), i);
94+
return false;
95+
}
96+
97+
for (rapidjson::SizeType j = 0; j < transforms.Size(); j++)
98+
{
99+
const rapidjson::Value& rot = transforms[j]["rot"];
100+
const rapidjson::Value& trans = transforms[j]["trans"];
101+
102+
if (!rot.IsArray() || !trans.IsArray())
103+
{
104+
SDL_Log("Skeleton %s: Bone %d is invalid.", fileName.c_str(), i);
105+
return false;
106+
}
107+
108+
temp.mRotation.x = rot[0].GetDouble();
109+
temp.mRotation.y = rot[1].GetDouble();
110+
temp.mRotation.z = rot[2].GetDouble();
111+
temp.mRotation.w = rot[3].GetDouble();
112+
113+
temp.mTranslation.x = trans[0].GetDouble();
114+
temp.mTranslation.y = trans[1].GetDouble();
115+
temp.mTranslation.z = trans[2].GetDouble();
116+
117+
mTracks[boneIndex].emplace_back(temp);
118+
}
119+
}
120+
121+
return true;
122+
}
123+
124+
void Animation::GetGlobalPoseAtTime(std::vector<Matrix4>& outPoses, const Skeleton* inSkeleton, float inTime) const
125+
{
126+
if (outPoses.size() != mNumBones)
127+
{
128+
outPoses.resize(mNumBones);
129+
}
130+
131+
// Figure out the current frame index and next frame
132+
// (This assumes inTime is bounded by [0, AnimDuration]
133+
size_t frame = static_cast<size_t>(inTime / mFrameDuration);
134+
size_t nextFrame = frame + 1;
135+
// Calculate fractional value between frame and next frame
136+
float pct = inTime / mFrameDuration - frame;
137+
138+
// Setup the pose for the root
139+
if (mTracks[0].size() > 0)
140+
{
141+
// Interpolate between the current frame's pose and the next frame
142+
BoneTransform interp = BoneTransform::Interpolate(mTracks[0][frame],
143+
mTracks[0][nextFrame], pct);
144+
outPoses[0] = interp.ToMatrix();
145+
}
146+
else
147+
{
148+
outPoses[0] = Matrix4::Identity;
149+
}
150+
151+
const std::vector<Skeleton::Bone>& bones = inSkeleton->GetBones();
152+
// Now setup the poses for the rest
153+
for (size_t bone = 1; bone < mNumBones; bone++)
154+
{
155+
Matrix4 localMat; // (Defaults to identity)
156+
if (mTracks[bone].size() > 0)
157+
{
158+
BoneTransform interp = BoneTransform::Interpolate(mTracks[bone][frame],
159+
mTracks[bone][nextFrame], pct);
160+
localMat = interp.ToMatrix();
161+
}
162+
163+
outPoses[bone] = localMat * outPoses[bones[bone].mParent];
164+
}
165+
}

Chapter12/Animation.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
#include "BoneTransform.h"
3+
#include <vector>
4+
#include <string>
5+
6+
class Animation
7+
{
8+
public:
9+
bool Load(const std::string& fileName);
10+
11+
size_t GetNumBones() const { return mNumBones; }
12+
size_t GetNumFrames() const { return mNumFrames; }
13+
float GetDuration() const { return mDuration; }
14+
float GetFrameDuration() const { return mFrameDuration; }
15+
16+
// Fills the provided vector with the global (current) pose matrices for each
17+
// bone at the specified time in the animation. It is expected that the time
18+
// is >= 0.0f and <= mDuration
19+
void GetGlobalPoseAtTime(std::vector<Matrix4>& outPoses, const class Skeleton* inSkeleton, float inTime) const;
20+
private:
21+
// Number of bones for the animation
22+
size_t mNumBones;
23+
// Number of frames in the animation
24+
size_t mNumFrames;
25+
// Duration of the animation in seconds
26+
float mDuration;
27+
// Duration of each frame in the animation
28+
float mFrameDuration;
29+
// Transform information for each frame on the track
30+
// Each index in the outer vector is a bone, inner vector
31+
// is a frame
32+
std::vector<std::vector<BoneTransform>> mTracks;
33+
};

Chapter12/Assets/Blip.png

889 Bytes
Loading

Chapter12/Assets/ButtonBlue.png

530 Bytes
Loading

Chapter12/Assets/ButtonYellow.png

491 Bytes
Loading
621 KB
Binary file not shown.

0 commit comments

Comments
 (0)