diff --git a/data/levels/cemetery/cemetery.xml b/data/levels/cemetery/cemetery.xml
index fd02c6d..2dd0d07 100644
--- a/data/levels/cemetery/cemetery.xml
+++ b/data/levels/cemetery/cemetery.xml
@@ -12,10 +12,18 @@
-
+
-
+
+
+
\ No newline at end of file
diff --git a/data/models/weapons/v_m3.iqm b/data/models/weapons/v_m3.iqm
new file mode 100644
index 0000000..f6b5315
Binary files /dev/null and b/data/models/weapons/v_m3.iqm differ
diff --git a/data/models/weapons/v_ump.iqm b/data/models/weapons/v_ump.iqm
new file mode 100644
index 0000000..30fa0a7
Binary files /dev/null and b/data/models/weapons/v_ump.iqm differ
diff --git a/data/scripts/actors/actor_player.lua b/data/scripts/actors/actor_player.lua
index 94bfb9b..c6dd04b 100644
--- a/data/scripts/actors/actor_player.lua
+++ b/data/scripts/actors/actor_player.lua
@@ -7,6 +7,16 @@ function actor_player:on_init()
self:create_body()
self:activate_camera()
+
+ local ent = engine.create_entity("test_object")
+ engine.add_entity_to_world(ent)
+
+ self.m_weapon_entity_id = ent:get_id()
+
+ self.m_in_reload = false
+
+ --local ent2 = engine.get_entity_from_id(self.m_weapon_entity_id)
+ --console.print(ent2:get_classname())
end
function actor_player:on_shutdown()
@@ -19,8 +29,39 @@ function actor_player:on_update(dt)
self:update_camera_look()
--self:update_camera_movement(dt)
self:update_body_movement(dt)
+
+ local ent = engine.get_entity_from_id(self.m_weapon_entity_id)
+ ent:set_relative_position_to_camera(self)
+
+ if self:get_action() == ACTION_FIRE and self.m_in_reload == false then
+ ent:play_animation(ent:find_animation("shoot1"), ANIM_PLAYBACK_NONE)
+ end
+
+ if self:get_action() == ACTION_RELOAD and self.m_in_reload == false then
+ ent:play_animation(ent:find_animation("reload"), ANIM_PLAYBACK_NONE)
+ self.m_in_reload = true
+ end
+
+
+ if ent:get_current_animation() == ent:find_animation("shoot1") and
+ ent:get_current_animation_time() >= ent:get_animation_time(ent:find_animation("shoot1")) then
+ ent:play_animation(ent:find_animation("idle1"), ANIM_PLAYBACK_REPEAT)
+ end
+
+ if self.m_in_reload == true and ent:get_current_animation_time() >= ent:get_animation_time(ent:find_animation("reload")) then
+ ent:play_animation(ent:find_animation("idle1"), ANIM_PLAYBACK_REPEAT)
+ self.m_in_reload = false
+ end
+ -- if ent:get_current_animation() == ent:find_animation("reload") and
+ -- ent:get_current_animation_time() >= ent:get_animation_time(ent:find_animation("reload")) then
+ -- ent:play_animation(ent:find_animation("idle1"), ANIM_PLAYBACK_NONE)
+ --end
end
function actor_player:on_collide(other)
console.print(string.format("actor_player:on_collide: %s", other:get_classname()))
-end
\ No newline at end of file
+end
+
+function play_sound( filename, is_3d, posx, posy, posz )
+ -- body
+end
diff --git a/data/scripts/test_object.lua b/data/scripts/test_object.lua
index 464cbb6..4282f6b 100644
--- a/data/scripts/test_object.lua
+++ b/data/scripts/test_object.lua
@@ -3,14 +3,44 @@ test_object = inherit_table(game_object)
function test_object:on_init()
game_object.on_init(self)
+
+ self:load_model("data/models/weapons/v_ump.iqm")
+
+ self.m_test_id = self:find_animation("idle1")
+ console.print(string.format("test_object:on_init: self.m_test_id = %d", self.m_test_id))
+
- self:load_model("data/models/scene_walls.obj")
-
- self.m_test = 0.0
+ self:play_animation(self.m_test_id, ANIM_PLAYBACK_REPEAT)
+
+-- self.m_test = 0.0
end
function test_object:on_update(dt)
game_object.on_update(self, dt)
- self:set_position(self.m_test, 0.0, 0.0)
- self.m_test = self.m_test + ( 0.2 * dt )
-end
\ No newline at end of file
+-- self:set_position(self.m_test, 2.0, 0.0)
+-- self.m_test = self.m_test + ( 0.2 * dt )
+end
+
+function test_object:set_relative_position_to_camera( ent )
+ local camX, camY, camZ = camera.get_position()
+ local frontX, frontY, frontZ = camera.get_front()
+ local rightX, rightY, rightZ = camera.get_right()
+ local upX, upY, upZ = camera.get_up()
+
+ local yaw = camera.get_yaw()
+ local pitch = camera.get_pitch()
+
+ self:set_rotation(pitch, -yaw, 0.0)
+
+ local offsetx = 0.0
+ local offsety = 0.0
+ local offsetz = 0.0
+
+ local x = camX + (rightX * offsetx) + (upX * offsety) + (frontX * offsetz)
+ local y = camY + (rightY * offsetx) + (upY * offsety) + (frontY * offsetz)
+ local z = camZ + (rightZ * offsetx) + (upZ * offsety) + (frontZ * offsetz)
+ self:set_position(x, y, z)
+
+ -- force update transform
+ self:update_transform()
+end
diff --git a/data/shaders/unlit_generic.vs b/data/shaders/unlit_generic.vs
index 6b1ca53..aecf713 100644
--- a/data/shaders/unlit_generic.vs
+++ b/data/shaders/unlit_generic.vs
@@ -14,33 +14,9 @@ uniform mat4 u_viewMatrix;
uniform mat4 u_projectionMatrix;
uniform mat4 u_modelViewProjection;
-vec3 CalcOmniLight()
-{
- vec3 lightPos = vec3(0.1, 2.1, 0.1);
- float d = distance(lightPos, v_position);
- vec3 L = normalize(lightPos-v_position);
- vec3 N = normalize(v_normal);
- vec3 col = vec3( max(0, dot(N, L) / d) );
- col = col * 0.8 + 0.2;
- return col;
-}
-
-vec3 CalcDirLight()
-{
- vec3 lightPos = vec3(5.0, 10.0, 1.0);
- //lightPos = -lightPos;
-
- vec3 L = normalize(lightPos);
- vec3 N = normalize(v_normal);
- vec3 col = vec3( max(0, dot(N, L)) );
- col = col * 0.8 + 0.2;
- return col;
-}
-
void main() {
v_position = vec3( u_modelMatrix * vec4(a_position, 1.0) );
v_normal = vec3( mat3(u_modelMatrix) * a_normal );
v_texcoord = a_texcoord;
- v_finalColor = CalcDirLight();
gl_Position = u_modelViewProjection * vec4(a_position, 1);
}
\ No newline at end of file
diff --git a/data/shaders/unlit_generic_skin.vs b/data/shaders/unlit_generic_skin.vs
new file mode 100644
index 0000000..e5f6b1b
--- /dev/null
+++ b/data/shaders/unlit_generic_skin.vs
@@ -0,0 +1,34 @@
+#version 120
+
+attribute vec3 a_position;
+attribute vec3 a_normal;
+attribute vec2 a_texcoord;
+attribute vec4 a_boneIds;
+attribute vec4 a_weights;
+
+varying vec3 v_position;
+varying vec3 v_normal;
+varying vec2 v_texcoord;
+varying vec3 v_finalColor;
+
+uniform mat4 u_modelMatrix;
+uniform mat4 u_viewMatrix;
+uniform mat4 u_projectionMatrix;
+uniform mat4 u_modelViewProjection;
+uniform mat4 u_boneMatrices[128];
+
+void main() {
+ // calculate bone transform
+ mat4 skinMatrix = a_weights.x * u_boneMatrices[ int( a_boneIds.x ) ]
+ + a_weights.y * u_boneMatrices[ int( a_boneIds.y ) ]
+ + a_weights.z * u_boneMatrices[ int( a_boneIds.z ) ]
+ + a_weights.w * u_boneMatrices[ int( a_boneIds.w ) ];
+
+ // Position
+ v_position = vec3( skinMatrix * vec4( a_position, 1.0 ) );
+ v_position = vec3( u_modelMatrix * vec4( v_position, 1.0 ) );
+
+ v_normal = vec3( mat3(u_modelMatrix) * a_normal );
+ v_texcoord = a_texcoord;
+ gl_Position = u_projectionMatrix * u_viewMatrix * vec4( v_position, 1.0 );
+}
\ No newline at end of file
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
index 76ccf49..a2d38b6 100644
--- a/src/engine/engine.cpp
+++ b/src/engine/engine.cpp
@@ -306,7 +306,7 @@ void Engine::NewGame(const char* mapname)
Disconnect();
g_PhysicsWorld = new PhysicsWorld();
- //g_PhysicsWorld->ToggleDebugDraw();
+ g_PhysicsWorld->ToggleDebugDraw();
g_world = new World();
diff --git a/src/engine/inputmanager.h b/src/engine/inputmanager.h
index 5772206..eecf4d7 100644
--- a/src/engine/inputmanager.h
+++ b/src/engine/inputmanager.h
@@ -40,6 +40,7 @@ public:
void OnMouseMoveAction(float x, float y);
InputLayout& GetKeyboard() { return m_keyboard; }
+ InputLayout& GetMouse() { return m_mouse; }
glm::vec2& GetMousePos() { return m_mousePos; }
glm::vec2& GetMouseDeltaPos() { return m_deltaMousePos; }
diff --git a/src/game/game.cpp b/src/game/game.cpp
index 6d0cb52..513952f 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -32,10 +32,19 @@ LuaPlus::LuaObject engineCreateEntity(const char* classname)
{
Entity* entity = static_cast(g_game->Lua_CreateEntity(classname));
SDL_assert_always(entity);
+
+ entity->Init();
return entity->GetLuaObject();
}
+LuaPlus::LuaObject engineGetEntityFromID(lua_Integer id)
+{
+ LuaPlus::LuaObject lookup = GetLuaState().GetGlobal("g_entities");
+ LuaPlus::LuaObject entityTable = lookup.GetByIndex(id);
+ return entityTable;
+}
+
void engineAddEntityToWorld(LuaPlus::LuaObject& object)
{
LuaPlus::LuaObject cppclass = object["__object"];
@@ -45,6 +54,92 @@ void engineAddEntityToWorld(LuaPlus::LuaObject& object)
g_world->AddEntity(entity);
}
+int cameraGetPos(LuaPlus::LuaState* state)
+{
+ glm::vec3 v = glm::vec3(0.0f);
+
+ Camera* camera = g_cameraManager.GetActiveCamera();
+ if (camera)
+ v = camera->GetPosition();
+
+ state->PushNumber(v.x);
+ state->PushNumber(v.y);
+ state->PushNumber(v.z);
+
+ return 3;
+}
+
+int cameraGetFront(LuaPlus::LuaState* state)
+{
+ glm::vec3 v = glm::vec3(0.0f);
+
+ Camera* camera = g_cameraManager.GetActiveCamera();
+ if (camera)
+ v = camera->GetFront();
+
+ state->PushNumber(v.x);
+ state->PushNumber(v.y);
+ state->PushNumber(v.z);
+
+ return 3;
+}
+
+int cameraGetRight(LuaPlus::LuaState* state)
+{
+ glm::vec3 v = glm::vec3(0.0f);
+
+ Camera* camera = g_cameraManager.GetActiveCamera();
+ if (camera)
+ v = camera->GetRight();
+
+ state->PushNumber(v.x);
+ state->PushNumber(v.y);
+ state->PushNumber(v.z);
+
+ return 3;
+}
+
+int cameraGetUp(LuaPlus::LuaState* state)
+{
+ glm::vec3 v = glm::vec3(0.0f);
+
+ Camera* camera = g_cameraManager.GetActiveCamera();
+ if (camera)
+ v = camera->GetUp();
+
+ state->PushNumber(v.x);
+ state->PushNumber(v.y);
+ state->PushNumber(v.z);
+
+ return 3;
+}
+
+int cameraGetYaw(LuaPlus::LuaState* state)
+{
+ float v = 0.0f;
+
+ Camera* camera = g_cameraManager.GetActiveCamera();
+ if (camera)
+ v = camera->GetYaw();
+
+ state->PushNumber(v);
+
+ return 1;
+}
+
+int cameraGetPitch(LuaPlus::LuaState* state)
+{
+ float v = 0.0f;
+
+ Camera* camera = g_cameraManager.GetActiveCamera();
+ if (camera)
+ v = camera->GetPitch();
+
+ state->PushNumber(v);
+
+ return 1;
+}
+
void registerEngine()
{
using namespace LuaPlus;
@@ -55,10 +150,24 @@ void registerEngine()
engineTable.RegisterDirect("warning", &engineWarning);
engineTable.RegisterDirect("create_entity", &engineCreateEntity);
engineTable.RegisterDirect("add_entity_to_world", &engineAddEntityToWorld);
+ engineTable.RegisterDirect("get_entity_from_id", &engineGetEntityFromID);
LuaObject consoleTable = GetLuaState().GetGlobals().CreateTable("console");
consoleTable.RegisterDirect("print", &consoleMsg);
+ LuaObject cameraTable = GetLuaState().GetGlobals().CreateTable("camera");
+ cameraTable.Register("get_position", &cameraGetPos);
+ cameraTable.Register("get_front", &cameraGetFront);
+ cameraTable.Register("get_right", &cameraGetRight);
+ cameraTable.Register("get_up", &cameraGetUp);
+ cameraTable.Register("get_yaw", &cameraGetYaw);
+ cameraTable.Register("get_pitch", &cameraGetPitch);
+
+ // action globals
+ GetLuaState().DoString("ACTION_FIRE = 0");
+ GetLuaState().DoString("ACTION_ALT_FIRE = 1");
+ GetLuaState().DoString("ACTION_RELOAD = 2");
+
// animations globals
GetLuaState().DoString("ANIM_PLAYBACK_NONE = 0");
GetLuaState().DoString("ANIM_PLAYBACK_REPEAT = 1");
@@ -231,6 +340,8 @@ void Game::LoadLevelXML(const char* mapname)
entity->CreateTestBody();
}
+ entity->Init();
+
//IEntityBase* entity = g_entityManager->CreateEntity(classname.as_string());
g_world->AddEntity(entity);
}
diff --git a/src/game/game_object.cpp b/src/game/game_object.cpp
index 7e4b013..fe59268 100644
--- a/src/game/game_object.cpp
+++ b/src/game/game_object.cpp
@@ -67,6 +67,7 @@ REGISTER_ENTITY(Entity);
Entity::Entity() :
m_model(nullptr),
+ m_skeleton(nullptr),
m_shape(nullptr),
m_rigidBody(nullptr),
m_bodyDirty(false)
@@ -99,7 +100,11 @@ Entity::~Entity()
void Entity::Update(float dt)
{
- UpdateBody();
+ if (m_skeleton)
+ UpdateSkeleton(dt);
+
+ if (m_rigidBody)
+ UpdateBody();
if ( m_onUpdateFunction.IsFunction())
{
@@ -112,12 +117,12 @@ void Entity::Render()
{
if (m_model)
{
- m_model->Draw(GetWorldTransform());
+ m_model->Draw(GetWorldTransform(), m_skeleton);
- BoundingBox bbox = m_model->GetBoundingBox();
+ /*BoundingBox bbox = m_boundingBox;
bbox.TransformAABB(GetWorldTransform());
- g_debugRender->DrawBoundingBox(bbox, glm::vec3(1.0f, 0.0f, 0.0f));
+ g_debugRender->DrawBoundingBox(bbox, glm::vec3(1.0f, 0.0f, 0.0f));*/
}
}
@@ -148,7 +153,17 @@ void Entity::SetName(const std::string& name)
void Entity::LoadModel(const char* filename)
{
+ if (m_skeleton) {
+ delete m_skeleton;
+ m_skeleton = nullptr;
+ }
+
m_model = g_modelSystem->LoadModel(filename);
+ if (m_model)
+ m_boundingBox = m_model->GetBoundingBox();
+
+ if (m_model && m_model->GetNumAnimations())
+ m_skeleton = m_model->CreateSkeletonInstance();
}
void Entity::SetVisible(bool visible)
@@ -160,6 +175,33 @@ bool Entity::GetVisible()
return true;
}
+void Entity::UpdateSkeleton(float dt)
+{
+ if (m_model && m_skeleton)
+ {
+ m_model->UpdateSkeletonInstance(m_skeleton, dt);
+
+ m_boundingBox = m_model->GetBoundingBox();
+
+ for (int i = 0; i < m_skeleton->m_jointMatrices.size(); i++)
+ {
+ m_boundingBox.m_min = glm::min(m_boundingBox.m_min, glm::vec3(m_skeleton->m_jointMatrices[i][3]));
+ m_boundingBox.m_max = glm::max(m_boundingBox.m_max, glm::vec3(m_skeleton->m_jointMatrices[i][3]));
+
+ }
+ }
+}
+
+void Entity::Init()
+{
+ // call init
+ if (m_onInitFunction.IsFunction())
+ {
+ LuaPlus::LuaFunctionVoid onInitFunction = m_onInitFunction;
+ onInitFunction(m_luaObject);
+ }
+}
+
void Entity::InitFromTable(LuaPlus::LuaObject& _object)
{
SDL_assert_always(!_object.IsNil() && "Invalid object passed");
@@ -184,21 +226,31 @@ void Entity::InitFromTable(LuaPlus::LuaObject& _object)
// assign C++ object
m_luaObject.SetLightUserdata("__object", this);
-
- // call init
- if (m_onInitFunction.IsFunction())
- {
- LuaPlus::LuaFunctionVoid onInitFunction = m_onInitFunction;
- onInitFunction(m_luaObject);
- }
}
void Entity::RegisterBaseFunctions()
{
m_luaObject.Register("load_model", *this, &Entity::Lua_LoadModel);
+ m_luaObject.Register("update_transform", *this, &Entity::Lua_UpdateTransform);
m_luaObject.Register("translate", *this, &Entity::Lua_Translate);
m_luaObject.Register("set_position", *this, &Entity::Lua_SetPosition);
+ m_luaObject.Register("get_position", *this, &Entity::Lua_GetPosition);
+ m_luaObject.Register("set_rotation", *this, &Entity::Lua_SetRotation);
m_luaObject.Register("get_classname", *this, &Entity::Lua_GetClassname);
+ m_luaObject.Register("get_id", *this, &Entity::Lua_GetID);
+
+ // animation
+ m_luaObject.Register("find_animation", *this, &Entity::Lua_FindAnimation);
+ m_luaObject.Register("play_animation", *this, &Entity::Lua_PlayAnimation);
+ m_luaObject.Register("get_current_animation", *this, &Entity::Lua_GetCurrentAnimation);
+ m_luaObject.Register("get_current_animation_time", *this, &Entity::Lua_GetCurrentAnimationTime);
+ m_luaObject.Register("get_animation_time", *this, &Entity::Lua_GetAnimationTime);
+
+ //int Lua_FindAnimation(LuaPlus::LuaState * state);
+ //int Lua_PlayAnimation(LuaPlus::LuaState * state);
+ //int Lua_GetCurrentAnimation(LuaPlus::LuaState * state);
+ //int Lua_GetCurrentAnimationTime(LuaPlus::LuaState * state);
+ //int Lua_GetAnimationTime(LuaPlus::LuaState * state);
// m_luaObject.RegisterDirect("load_model", &Entity_LoadModel);
//m_luaObject.RegisterDirect("set_visible", &Entity_SetVisible);
@@ -223,6 +275,13 @@ void Entity::Help_SetPosition(float x, float y, float z)
m_position.z = z;
}
+void Entity::Help_SetRotation(float x, float y, float z)
+{
+ m_rotation.x = x;
+ m_rotation.y = y;
+ m_rotation.z = z;
+}
+
int Entity::Lua_LoadModel(LuaPlus::LuaState* state)
{
LuaPlus::LuaStack stack(state);
@@ -265,12 +324,104 @@ int Entity::Lua_SetPosition(LuaPlus::LuaState* state)
return 0;
}
+int Entity::Lua_GetPosition(LuaPlus::LuaState* state)
+{
+ state->PushNumber(m_position.x);
+ state->PushNumber(m_position.y);
+ state->PushNumber(m_position.z);
+ return 3;
+}
+
+int Entity::Lua_SetRotation(LuaPlus::LuaState* state)
+{
+ LuaPlus::LuaStack stack(state);
+
+ float x = stack[2].GetNumber();
+ float y = stack[3].GetNumber();
+ float z = stack[4].GetNumber();
+
+ Help_SetRotation(x, y, z);
+
+ return 0;
+}
+
int Entity::Lua_GetClassname(LuaPlus::LuaState* state)
{
state->PushString(GetClassname());
return 1;
}
+int Entity::Lua_GetID(LuaPlus::LuaState* state)
+{
+ state->PushInteger(GetID());
+ return 1;
+}
+
+int Entity::Lua_UpdateTransform(LuaPlus::LuaState* state)
+{
+ UpdateTransform();
+
+ return 0;
+}
+
+int Entity::Lua_FindAnimation(LuaPlus::LuaState* state)
+{
+ LuaPlus::LuaStack stack(state);
+ const char* name = stack[2].GetString();
+
+ if (m_model)
+ state->PushInteger(m_model->FindAnimation(name));
+ else
+ state->PushInteger(INVALID_ANIMATION_HANDLE);
+
+ return 1;
+}
+
+int Entity::Lua_PlayAnimation(LuaPlus::LuaState* state)
+{
+ LuaPlus::LuaStack stack(state);
+ AnimationId_t id = stack[2].GetInteger();
+ int mode = stack[3].GetInteger();
+
+ if (m_skeleton)
+ m_skeleton->PlayAnimation(id, mode);
+
+ return 0;
+}
+
+int Entity::Lua_GetCurrentAnimation(LuaPlus::LuaState* state)
+{
+ if (m_skeleton)
+ state->PushInteger(m_skeleton->GetCurrentAnimation());
+ else
+ state->PushInteger(INVALID_ANIMATION_HANDLE);
+
+ return 1;
+}
+
+int Entity::Lua_GetCurrentAnimationTime(LuaPlus::LuaState* state)
+{
+ if (m_skeleton)
+ state->PushNumber(m_skeleton->GetCurrentTime());
+ else
+ state->PushNumber(0.0f);
+
+ return 1;
+}
+
+int Entity::Lua_GetAnimationTime(LuaPlus::LuaState* state)
+{
+ LuaPlus::LuaStack stack(state);
+ AnimationId_t id = stack[2].GetInteger();
+
+ if (m_skeleton)
+ state->PushNumber(m_skeleton->GetAnimationTime(id));
+ else
+ state->PushNumber(0.0f);
+
+ return 1;
+}
+
const LuaPlus::LuaObject& Entity::GetLuaObject()
{
return m_luaObject;
@@ -503,7 +654,7 @@ void ActorBase::CreatePlayerBody()
m_rigidBody->setFriction(0.0f);
m_rigidBody->setAnisotropicFriction(btVector3(0.0f, 0.0f, 0.0f));
- m_rigidBody->setDamping(0.1f, 0.0f);
+ m_rigidBody->setDamping(0.0f, 0.0f);
m_rigidBody->setActivationState(DISABLE_DEACTIVATION);
@@ -541,6 +692,7 @@ void ActorBase::RegisterFunctions()
m_luaObject.Register("update_camera_movement", *this, &ActorBase::Lua_UpdateCameraMovement);
m_luaObject.Register("create_body", *this, &ActorBase::Lua_CreateBody);
m_luaObject.Register("update_body_movement", *this, &ActorBase::Lua_UpdateBodyMovement);
+ m_luaObject.Register("get_action", *this, &ActorBase::Lua_GetAction);
//m_luaObject.RegisterDirect("activate_camera", &ActorBase_ActivateCamera);
//m_luaObject.RegisterDirect("update_camera_look", &ActorBase_UpdateCameraLook);
@@ -583,6 +735,40 @@ int ActorBase::Lua_CreateBody(LuaPlus::LuaState* state)
return 0;
}
+int ActorBase::Lua_GetAction(LuaPlus::LuaState* state)
+{
+// uint32_t action = 0;
+
+ //float x, y;
+ //SDL_MouseButtonFlags buttons = SDL_GetMouseState(&x, &y);
+
+ //if (buttons & SDL_BUTTON_MASK(SDL_BUTTON_LEFT))
+ // state->PushInteger(0);
+ //if (buttons & SDL_BUTTON_MASK(SDL_BUTTON_RIGHT))
+ // state->PushInteger(1);
+ //else
+ // state->PushInteger(-1);
+
+ int action = GenAction();
+ state->PushInteger(action);
+
+ return 1;
+}
+
+int ActorBase::GenAction()
+{
+ int action = -1;
+
+ if (g_inputManager.GetMouse().IsKeyDown(EMouseButton_Left))
+ action = 0;
+ else if (g_inputManager.GetMouse().IsKeyDown(EMouseButton_Right))
+ action = 1;
+ else if (g_inputManager.GetKeyboard().IsKeyDown(SDLK_R))
+ action = 2;
+
+ return action;
+}
+
uint32_t ActorBase::GenMovementDir()
{
uint32_t movementDir = EMovementDir_None;
diff --git a/src/game/game_object.h b/src/game/game_object.h
index 4da9f58..cf9f4a5 100644
--- a/src/game/game_object.h
+++ b/src/game/game_object.h
@@ -77,6 +77,10 @@ public:
void SetVisible(bool visible);
bool GetVisible();
+ void UpdateSkeleton(float dt);
+
+ void Init();
+
// Game entity physics
void CreateTestBody();
void UpdateBody();
@@ -91,11 +95,22 @@ public:
// Game entity lua wrappers
void Help_Translate(float x, float y, float z);
void Help_SetPosition(float x, float y, float z);
+ void Help_SetRotation(float x, float y, float z);
int Lua_LoadModel(LuaPlus::LuaState* state);
int Lua_Translate(LuaPlus::LuaState* state);
int Lua_SetPosition(LuaPlus::LuaState* state);
+ int Lua_GetPosition(LuaPlus::LuaState* state);
+ int Lua_SetRotation(LuaPlus::LuaState* state);
int Lua_GetClassname(LuaPlus::LuaState* state);
+ int Lua_GetID(LuaPlus::LuaState* state);
+ int Lua_UpdateTransform(LuaPlus::LuaState* state);
+
+ int Lua_FindAnimation(LuaPlus::LuaState* state);
+ int Lua_PlayAnimation(LuaPlus::LuaState* state);
+ int Lua_GetCurrentAnimation(LuaPlus::LuaState* state);
+ int Lua_GetCurrentAnimationTime(LuaPlus::LuaState* state);
+ int Lua_GetAnimationTime(LuaPlus::LuaState* state);
const LuaPlus::LuaObject& GetLuaObject();
@@ -103,6 +118,7 @@ protected:
std::string m_name;
Model* m_model;
+ SkeletonInstance* m_skeleton;
LuaPlus::LuaObject m_luaObject;
LuaPlus::LuaObject m_onInitFunction;
@@ -149,8 +165,10 @@ public:
int Lua_UpdateCameraLook(LuaPlus::LuaState* state);
int Lua_ActivateCamera(LuaPlus::LuaState* state);
int Lua_CreateBody(LuaPlus::LuaState* state);
+ int Lua_GetAction(LuaPlus::LuaState* state);
private:
+ int GenAction();
uint32_t GenMovementDir();
private:
diff --git a/src/render/animation.cpp b/src/render/animation.cpp
new file mode 100644
index 0000000..c75e7ed
--- /dev/null
+++ b/src/render/animation.cpp
@@ -0,0 +1,80 @@
+#include "animation.h"
+#include "filesystem.h"
+#include "model.h"
+
+SkeletonInstance::SkeletonInstance() :
+ m_model(nullptr),
+ m_animation(INVALID_ANIMATION_HANDLE),
+ m_time(0.0f),
+ m_looped(false)
+{
+}
+
+SkeletonInstance::~SkeletonInstance()
+{
+}
+
+bool SkeletonInstance::IsValid()
+{
+ return m_model != nullptr;
+}
+
+void SkeletonInstance::LoadAnimation(const char* filename)
+{
+ if (!m_model)
+ return;
+
+ //m_model->LoadAnimation(filename);
+}
+
+AnimationId_t SkeletonInstance::FindAnimation(const char* name)
+{
+ if (!m_model)
+ return INVALID_ANIMATION_HANDLE;
+
+ return m_model->FindAnimation(name);
+}
+
+uint32_t SkeletonInstance::GetNumAnimations()
+{
+ if (!m_model)
+ return 0;
+
+ return m_model->GetNumAnimations();
+}
+
+float SkeletonInstance::GetAnimationTime(AnimationId_t id)
+{
+ if (!m_model)
+ return 0.0f;
+
+ const Animation* animation = m_model->GetAnimation(id);
+
+ float frameTime = 1.0f / animation->framerate;
+ float totalDuration = animation->numFrames * frameTime;
+ return totalDuration;
+}
+
+void SkeletonInstance::PlayAnimation(AnimationId_t id, bool looped)
+{
+ m_time = 0.0f;
+ m_animation = id;
+ m_looped = looped;
+}
+
+void SkeletonInstance::StopAnimation()
+{
+ m_time = 0.0f;
+ m_animation = INVALID_ANIMATION_HANDLE;
+ m_looped = -1;
+}
+
+AnimationId_t SkeletonInstance::GetCurrentAnimation()
+{
+ return m_animation;
+}
+
+float SkeletonInstance::GetCurrentTime()
+{
+ return m_time;
+}
diff --git a/src/render/animation.h b/src/render/animation.h
new file mode 100644
index 0000000..2b6f92d
--- /dev/null
+++ b/src/render/animation.h
@@ -0,0 +1,68 @@
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include
+
+#include "render_shared.h"
+
+#define INVALID_ANIMATION_HANDLE -1
+
+// Animation types
+typedef /*int16_t*/ int AnimationId_t;
+
+class Model;
+
+/* Joint */
+struct Joint
+{
+ char name[64];
+ int parentId;
+
+ glm::vec3 origin;
+ glm::quat orient;
+};
+
+/* Animation */
+struct Animation
+{
+ char name[64];
+ uint32_t numPoses;
+ uint32_t numFrames;
+ float framerate;
+ std::vector< glm::vec3 > origin;
+ std::vector< glm::quat > orient;
+};
+
+/* Skeleton */
+class SkeletonInstance
+{
+public:
+ Model* m_model;
+ std::vector m_joints;
+ std::vector m_jointMatrices;
+ std::vector m_finalMatrices;
+ AnimationId_t m_animation;
+ float m_time;
+ bool m_looped;
+
+public:
+ SkeletonInstance();
+ ~SkeletonInstance();
+
+ bool IsValid();
+
+ void LoadAnimation(const char* filename);
+
+ AnimationId_t FindAnimation(const char* name);
+ uint32_t GetNumAnimations();
+ float GetAnimationTime(AnimationId_t id);
+
+ void PlayAnimation(AnimationId_t id, bool looped);
+ void StopAnimation();
+
+ AnimationId_t GetCurrentAnimation();
+ float GetCurrentTime();
+
+};
+
+#endif // !ANIMATION_H
diff --git a/src/render/iqm.h b/src/render/iqm.h
new file mode 100644
index 0000000..a450504
--- /dev/null
+++ b/src/render/iqm.h
@@ -0,0 +1,134 @@
+#ifndef __IQM_H__
+#define __IQM_H__
+
+#define IQM_MAGIC "INTERQUAKEMODEL"
+#define IQM_VERSION 2
+
+struct iqmheader
+{
+ char magic[16];
+ unsigned int version;
+ unsigned int filesize;
+ unsigned int flags;
+ unsigned int num_text, ofs_text;
+ unsigned int num_meshes, ofs_meshes;
+ unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
+ unsigned int num_triangles, ofs_triangles, ofs_adjacency;
+ unsigned int num_joints, ofs_joints;
+ unsigned int num_poses, ofs_poses;
+ unsigned int num_anims, ofs_anims;
+ unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
+ unsigned int num_comment, ofs_comment;
+ unsigned int num_extensions, ofs_extensions;
+};
+
+struct iqmmesh
+{
+ unsigned int name;
+ unsigned int material;
+ unsigned int first_vertex, num_vertexes;
+ unsigned int first_triangle, num_triangles;
+};
+
+enum
+{
+ IQM_POSITION = 0,
+ IQM_TEXCOORD = 1,
+ IQM_NORMAL = 2,
+ IQM_TANGENT = 3,
+ IQM_BLENDINDEXES = 4,
+ IQM_BLENDWEIGHTS = 5,
+ IQM_COLOR = 6,
+ IQM_CUSTOM = 0x10
+};
+
+enum
+{
+ IQM_BYTE = 0,
+ IQM_UBYTE = 1,
+ IQM_SHORT = 2,
+ IQM_USHORT = 3,
+ IQM_INT = 4,
+ IQM_UINT = 5,
+ IQM_HALF = 6,
+ IQM_FLOAT = 7,
+ IQM_DOUBLE = 8
+};
+
+struct iqmtriangle
+{
+ unsigned int vertex[3];
+};
+
+struct iqmadjacency
+{
+ unsigned int triangle[3];
+};
+
+struct iqmjointv1
+{
+ unsigned int name;
+ int parent;
+ float translate[3], rotate[3], scale[3];
+};
+
+struct iqmjoint
+{
+ unsigned int name;
+ int parent;
+ float translate[3], rotate[4], scale[3];
+};
+
+struct iqmposev1
+{
+ int parent;
+ unsigned int mask;
+ float channeloffset[9];
+ float channelscale[9];
+};
+
+struct iqmpose
+{
+ int parent;
+ unsigned int mask;
+ float channeloffset[10];
+ float channelscale[10];
+};
+
+struct iqmanim
+{
+ unsigned int name;
+ unsigned int first_frame, num_frames;
+ float framerate;
+ unsigned int flags;
+};
+
+enum
+{
+ IQM_LOOP = 1<<0
+};
+
+struct iqmvertexarray
+{
+ unsigned int type;
+ unsigned int flags;
+ unsigned int format;
+ unsigned int size;
+ unsigned int offset;
+};
+
+struct iqmbounds
+{
+ float bbmin[3], bbmax[3];
+ float xyradius, radius;
+};
+
+struct iqmextension
+{
+ unsigned int name;
+ unsigned int num_data, ofs_data;
+ unsigned int ofs_extensions; // pointer to next extension
+};
+
+#endif
+
diff --git a/src/render/model.cpp b/src/render/model.cpp
index 3c4e102..d133dbc 100644
--- a/src/render/model.cpp
+++ b/src/render/model.cpp
@@ -2,6 +2,7 @@
#include
#include
#include "ifilesystem.h"
+#include "core.h"
#include "log.h"
#include "render.h"
#include "gpu_buffer.h"
@@ -11,8 +12,11 @@
#include "model.h"
#include "modelsystem.h"
#include "texturesmanager.h"
+#include "iqm.h"
+#include "debugrender.h"
extern Shader* g_unlitShader;
+extern Shader* g_unlitSkinnedShader;
static std::string getFileNameWithoutExtension(const std::string& filename)
{
@@ -26,9 +30,8 @@ static std::string getFileNameWithoutExtension(const std::string& filename)
Model::Model()
{
- m_data.vb = nullptr;
- m_data.ib = nullptr;
m_AlbedoTexture = nullptr;
+ m_numPoses = 0;
//m_boundingBox.min = glm::vec3(0.0f);
//m_boundingBox.max = glm::vec3(0.0f);
@@ -38,11 +41,382 @@ Model::~Model()
{
m_AlbedoTexture = nullptr;
- if (m_data.vb)
+ for (int i = 0; i < m_meshes.size(); i++)
+ ReleaseModelData(m_meshes[i]);
+}
+
+// IQM
+
+typedef unsigned char byte;
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef signed long long int llong;
+typedef unsigned long long int ullong;
+
+inline iqmheader* LoadIQMHeader(const char* filename)
+{
+ FileHandle_t handle = GetFileSystem()->OpenFile(filename, "rb");
+ SDL_assert(handle != kInvalidFileHandleValue);
+
+ size_t size = GetFileSystem()->GetFileLength(handle);
+
+ char* buffer = (char*)malloc(size);
+ GetFileSystem()->ReadFile(handle, buffer, size);
+
+ GetFileSystem()->CloseFile(handle);
+
+ return (iqmheader*)buffer;
+}
+
+inline void* IQMHeaderPtr(iqmheader* pHeader, uint offset)
+{
+ return ((byte*)pHeader) + offset;
+}
+
+static bool g_bIsLittleEndian = false;
+
+static void EndianSwap_Init()
+{
+ union
{
- delete m_data.vb;
- m_data.vb = nullptr;
+ int i;
+ uchar b[sizeof(int)];
+ } conv;
+ conv.i = 1;
+
+ g_bIsLittleEndian = conv.b[0] != 0;
+}
+
+static inline bool islittleendian() { union { int i; uchar b[sizeof(int)]; } conv; conv.i = 1; return conv.b[0] != 0; }
+inline ushort endianswap16(ushort n) { return (n << 8) | (n >> 8); }
+inline uint endianswap32(uint n) { return (n << 24) | (n >> 24) | ((n >> 8) & 0xFF00) | ((n << 8) & 0xFF0000); }
+template inline T endianswap(T n) { union { T t; uint i; } conv; conv.t = n; conv.i = endianswap32(conv.i); return conv.t; }
+template<> inline ushort endianswap(ushort n) { return endianswap16(n); }
+template<> inline short endianswap(short n) { return endianswap16(n); }
+template<> inline uint endianswap(uint n) { return endianswap32(n); }
+template<> inline int endianswap(int n) { return endianswap32(n); }
+template inline void endianswap(T* buf, int len) { for (T* end = &buf[len]; buf < end; buf++) *buf = endianswap(*buf); }
+template inline T lilswap(T n) { return islittleendian() ? n : endianswap(n); }
+template inline void lilswap(T* buf, int len) { if (!islittleendian()) endianswap(buf, len); }
+template inline T bigswap(T n) { return islittleendian() ? endianswap(n) : n; }
+template inline void bigswap(T* buf, int len) { if (islittleendian()) endianswap(buf, len); }
+
+template T getval(FILE* f) { T n; return fread(&n, 1, sizeof(n), f) == sizeof(n) ? n : 0; }
+template T getlil(FILE* f) { return lilswap(getval(f)); }
+template T getbig(FILE* f) { return bigswap(getval(f)); }
+
+void Model::LoadIqm(const char* filename)
+{
+ iqmheader* pHdr = LoadIQMHeader(filename);
+ pHdr->version = lilswap(pHdr->version);
+ pHdr->filesize = lilswap(pHdr->filesize);
+ pHdr->num_vertexarrays = lilswap(pHdr->num_vertexarrays);
+ pHdr->num_vertexes = lilswap(pHdr->num_vertexes);
+ pHdr->num_triangles = lilswap(pHdr->num_triangles);
+ pHdr->num_meshes = lilswap(pHdr->num_meshes);
+ pHdr->num_joints = lilswap(pHdr->num_joints);
+ pHdr->num_poses = lilswap(pHdr->num_poses);
+ pHdr->num_anims = lilswap(pHdr->num_anims);
+ pHdr->num_frames = lilswap(pHdr->num_frames);
+ pHdr->num_framechannels = lilswap(pHdr->num_framechannels);
+
+ pHdr->ofs_vertexarrays = lilswap(pHdr->ofs_vertexarrays);
+ pHdr->ofs_triangles = lilswap(pHdr->ofs_triangles);
+ pHdr->ofs_meshes = lilswap(pHdr->ofs_meshes);
+ pHdr->ofs_joints = lilswap(pHdr->ofs_joints);
+ pHdr->ofs_poses = lilswap(pHdr->ofs_poses);
+ pHdr->ofs_anims = lilswap(pHdr->ofs_anims);
+ pHdr->ofs_frames = lilswap(pHdr->ofs_frames);
+
+ m_numPoses = pHdr->num_poses;
+
+ if (pHdr->version != IQM_VERSION)
+ {
+ Msg("Model::LoadIQM: %s is outdated. current is %i, model %i", filename, IQM_VERSION, pHdr->version);
+ free(pHdr);
+ return;
}
+
+ if (pHdr->filesize > (16 << 20))
+ {
+ Msg("Model::LoadIQM: %s is bigger(%i) than 16 MB!", filename, pHdr->filesize);
+ free(pHdr);
+ return;
+ }
+
+ iqmvertexarray* pVertexArrays = (iqmvertexarray*)IQMHeaderPtr(pHdr, pHdr->ofs_vertexarrays);
+
+ float* position = nullptr;
+ float* normal = nullptr;
+ float* texcoord = nullptr;
+ byte* weight = nullptr;
+ byte* boneid = nullptr;
+
+ for (uint32_t i = 0; i < pHdr->num_vertexarrays; i++)
+ {
+ const iqmvertexarray& vertarray = pVertexArrays[i];
+
+ if (vertarray.type == IQM_POSITION)
+ {
+ if (vertarray.format != IQM_FLOAT || vertarray.size != 3)
+ Core::Error("Model::LoadIQM: %s wrong model! position attribute are not vec3 and float.", filename);
+
+ position = (float*)IQMHeaderPtr(pHdr, vertarray.offset);
+ }
+ else if (vertarray.type == IQM_NORMAL)
+ {
+ if (vertarray.format != IQM_FLOAT || vertarray.size != 3)
+ Core::Error("Model::LoadIQM: %s wrong model! normal attribute are not vec3 and float.", filename);
+
+ normal = (float*)IQMHeaderPtr(pHdr, vertarray.offset);
+ }
+ else if (vertarray.type == IQM_TEXCOORD)
+ {
+ if (vertarray.format != IQM_FLOAT || vertarray.size != 2)
+ Core::Error("Model::LoadIQM: %s wrong model! texcoord attribute are not vec2 and float.", filename);
+
+ texcoord = (float*)IQMHeaderPtr(pHdr, vertarray.offset);
+ }
+ else if (vertarray.type == IQM_BLENDWEIGHTS)
+ {
+ if (vertarray.format != IQM_UBYTE || vertarray.size != 4)
+ Core::Error("Model::LoadIQM: %s wrong model! bone weights attribute are not vec4 and ubyte.", filename);
+
+ weight = (byte*)IQMHeaderPtr(pHdr, vertarray.offset);
+ }
+ else if (vertarray.type == IQM_BLENDINDEXES)
+ {
+ if (vertarray.format != IQM_UBYTE || vertarray.size != 4)
+ Core::Error("Model::LoadIQM: %s wrong model! bone indices attribute are not vec4 and ubyte.", filename);
+
+ boneid = (byte*)IQMHeaderPtr(pHdr, vertarray.offset);
+ }
+ }
+
+ std::vector< SkinnedMeshVertex > vertices;
+ vertices.resize(pHdr->num_vertexes);
+
+ std::vector< uint > indices;
+ indices.resize(pHdr->num_triangles * 3);
+
+ iqmmesh* pMeshes = (iqmmesh*)IQMHeaderPtr(pHdr, pHdr->ofs_meshes);
+ iqmtriangle* pTriangles = (iqmtriangle*)IQMHeaderPtr(pHdr, pHdr->ofs_triangles);
+// Material* pMaterial = nullptr;
+ bool firstMesh = true;
+
+ for (int i = 0; i < pHdr->num_meshes; ++i)
+ {
+ iqmmesh* pMesh = &pMeshes[i];
+
+ for (uint32_t j = 0; j < pMesh->num_vertexes; ++j)
+ {
+ memcpy(&vertices[j].position, &position[(pMesh->first_vertex + j) * 3], sizeof(vertices[j].position));
+ memcpy(&vertices[j].normal, &normal[(pMesh->first_vertex + j) * 3], sizeof(vertices[j].normal));
+ memcpy(&vertices[j].texcoord, &texcoord[(pMesh->first_vertex + j) * 2], sizeof(vertices[j].texcoord));
+
+ if (boneid)
+ {
+ vertices[j].boneIds.x = (float)boneid[(pMesh->first_vertex + j) * 4];
+ vertices[j].boneIds.y = (float)boneid[(pMesh->first_vertex + j) * 4 + 1];
+ vertices[j].boneIds.z = (float)boneid[(pMesh->first_vertex + j) * 4 + 2];
+ vertices[j].boneIds.w = (float)boneid[(pMesh->first_vertex + j) * 4 + 3];
+ }
+ else
+ {
+ vertices[j].boneIds.x = 0.0f;
+ vertices[j].boneIds.y = 0.0f;
+ vertices[j].boneIds.z = 0.0f;
+ vertices[j].boneIds.w = 0.0f;
+ }
+
+ if (weight)
+ {
+ vertices[j].weights.x = weight[(pMesh->first_vertex + j) * 4] / 255.0f;
+ vertices[j].weights.y = weight[(pMesh->first_vertex + j) * 4 + 1] / 255.0f;
+ vertices[j].weights.z = weight[(pMesh->first_vertex + j) * 4 + 2] / 255.0f;
+ vertices[j].weights.w = weight[(pMesh->first_vertex + j) * 4 + 3] / 255.0f;
+ }
+ else
+ {
+ vertices[j].weights.x = 0.0f;
+ vertices[j].weights.y = 0.0f;
+ vertices[j].weights.z = 0.0f;
+ vertices[j].weights.w = 0.0f;
+ }
+
+ if (i == 0 && (firstMesh && j == 0))
+ {
+ m_boundingBox.m_min = vertices[j].position;
+ m_boundingBox.m_max= vertices[j].position;
+ }
+ else
+ {
+ m_boundingBox.m_min = glm::min(m_boundingBox.m_min, vertices[j].position);
+ m_boundingBox.m_max = glm::max(m_boundingBox.m_max, vertices[j].position);
+ }
+ }
+
+ // Re-order triangle face counting to counter-clockwise
+ for (uint j = 0; j < pMesh->num_triangles; j++)
+ {
+ uint a = pTriangles[pMesh->first_triangle + j].vertex[0];
+ uint b = pTriangles[pMesh->first_triangle + j].vertex[1];
+ uint c = pTriangles[pMesh->first_triangle + j].vertex[2];
+ indices[j * 3 + 0] = c - pMesh->first_vertex;
+ indices[j * 3 + 1] = b - pMesh->first_vertex;
+ indices[j * 3 + 2] = a - pMesh->first_vertex;
+ }
+
+ if (pHdr->ofs_text)
+ {
+ // #TODO: weird weird getting material name string is so wrong
+
+ const char* str = (char*)pHdr + pHdr->ofs_text;
+ const char* materialname = &str[pMesh->material];
+
+ //if (materialname)
+ // pMaterial = g_materialSystem.LoadMaterial(materialname);
+ }
+
+ ModelData_t mesh = {};
+ //mesh.m_vertices = vertices;
+ //mesh.m_indices = indices;
+ mesh.vb = g_renderDevice->CreateVertexBuffer(vertices.data(), pMesh->num_vertexes * sizeof(SkinnedMeshVertex), true);
+ mesh.vbcount = pMesh->num_vertexes;
+ mesh.ib = g_renderDevice->CreateIndexBuffer(indices.data(), pMesh->num_triangles * sizeof(iqmtriangle), false);
+ mesh.ibcount = pMesh->num_triangles * 3;
+ //mesh.m_material = pMaterial;
+ //
+ //if (!mesh.m_material)
+ // mesh.m_material = g_materialSystem.CreateMaterial("default_model", "textures/system/notex.tga", NULL, true);
+
+ m_meshes.push_back(mesh);
+
+ firstMesh = false;
+ }
+
+ // get string data
+ char* pStrings = (char*)IQMHeaderPtr(pHdr, pHdr->ofs_text);
+
+ // load joints
+ iqmjoint* pJoints = (iqmjoint*)IQMHeaderPtr(pHdr, pHdr->ofs_joints);
+ m_joints.resize(pHdr->num_joints);
+
+ for (uint32_t i = 0; i < pHdr->num_joints; i++)
+ {
+ const char* jointName = &pStrings[pJoints[i].name];
+ strncpy(m_joints[i].name, jointName, sizeof(m_joints[i].name));
+
+ m_joints[i].parentId = pJoints[i].parent;
+
+ memcpy(&m_joints[i].origin, pJoints[i].translate, sizeof(glm::vec3));
+ memcpy(&m_joints[i].orient, pJoints[i].rotate, sizeof(glm::vec4));
+
+ m_joints[i].orient = normalize(m_joints[i].orient);
+
+ //Msg("%i origin %.2f %.2f %.2f, orient %.2f %.2f %.2f %.2f(w)",
+ // i,
+ // m_joints[i].origin.x, m_joints[i].origin.y, m_joints[i].origin.z,
+ // m_joints[i].orient.x, m_joints[i].orient.y, m_joints[i].orient.z, m_joints[i].orient.w);
+ }
+
+ m_basePose = m_joints;
+ m_inverseBasePose.resize(m_basePose.size());
+
+ for (int i = 0; i < m_basePose.size(); i++)
+ {
+ // calculate inverse transform matrix
+ glm::mat4 mat = glm::translate(glm::mat4(1.0f), m_basePose[i].origin);
+ mat *= glm::toMat4(m_basePose[i].orient);
+
+ m_inverseBasePose[i] = glm::inverse(mat);
+ if (m_basePose[i].parentId >= 0)
+ {
+ m_inverseBasePose[i] *= m_inverseBasePose[m_basePose[i].parentId];
+ }
+ }
+
+ // load animations
+ iqmanim* pAnims = (iqmanim*)IQMHeaderPtr(pHdr, pHdr->ofs_anims);
+ iqmpose* pPoses = (iqmpose*)IQMHeaderPtr(pHdr, pHdr->ofs_poses);
+ ushort* pFramedata = (ushort*)IQMHeaderPtr(pHdr, pHdr->ofs_frames);
+
+ // #TODO: tracks are Array class, with resize no ctor are called
+ // m_animations.resize(pHdr->num_anims);
+
+ //m_jointTranslation.resize(pHdr->num_frames* pHdr->num_poses);
+
+ glm::vec3 origin;
+ glm::vec3 scale;
+ glm::quat orient;
+
+ for (uint32_t i = 0; i < pHdr->num_anims; i++)
+ {
+ Animation animation;
+
+ const char* animName = &pStrings[pAnims[i].name];
+ strncpy(animation.name, animName, sizeof(animation.name));
+
+ animation.numPoses = m_numPoses;
+ animation.numFrames = pAnims[i].num_frames;
+ animation.framerate = pAnims[i].framerate;
+ if (animation.framerate <= 0.0f)
+ animation.framerate = 30.0f;
+
+ Msg("Animation: %s %i %i %i", animName, pAnims[i].first_frame, pAnims[i].num_frames, pAnims[i].framerate);
+
+ animation.origin.resize(pHdr->num_poses * pAnims[i].num_frames);
+ animation.orient.resize(pHdr->num_poses * pAnims[i].num_frames);
+
+ int dcounter = pAnims[i].first_frame * pHdr->num_framechannels;
+
+ for (uint32_t j = 0; j < pAnims[i].num_frames; j++)
+ {
+ for (uint32_t k = 0; k < pHdr->num_poses; k++)
+ {
+ const iqmpose& p = pPoses[k];
+ origin.x = p.channeloffset[0]; if (p.mask & 0x01) origin.x += *pFramedata++ * p.channelscale[0];
+ origin.y = p.channeloffset[1]; if (p.mask & 0x02) origin.y += *pFramedata++ * p.channelscale[1];
+ origin.z = p.channeloffset[2]; if (p.mask & 0x04) origin.z += *pFramedata++ * p.channelscale[2];
+ orient.x = p.channeloffset[3]; if (p.mask & 0x08) orient.x += *pFramedata++ * p.channelscale[3];
+ orient.y = p.channeloffset[4]; if (p.mask & 0x10) orient.y += *pFramedata++ * p.channelscale[4];
+ orient.z = p.channeloffset[5]; if (p.mask & 0x20) orient.z += *pFramedata++ * p.channelscale[5];
+ orient.w = p.channeloffset[6]; if (p.mask & 0x40) orient.w += *pFramedata++ * p.channelscale[6];
+ scale.x = p.channeloffset[7]; if (p.mask & 0x80) scale.x += *pFramedata++ * p.channelscale[7];
+ scale.y = p.channeloffset[8]; if (p.mask & 0x100) scale.y += *pFramedata++ * p.channelscale[8];
+ scale.z = p.channeloffset[9]; if (p.mask & 0x200) scale.z += *pFramedata++ * p.channelscale[9];
+
+ // normalize orientation
+ orient = glm::normalize(orient);
+
+ // assign to the animation track
+ animation.origin[j * pHdr->num_poses + k] = origin;
+ animation.orient[j * pHdr->num_poses + k] = orient;
+ }
+ }
+
+ m_animations.push_back(animation);
+ }
+
+ m_jointMatrices.resize(m_joints.size());
+ m_finalMatrices.resize(m_joints.size());
+
+ // matrices
+ for (int i = 0; i < m_joints.size(); ++i)
+ {
+ glm::mat4 mat = glm::translate(glm::mat4(1.0f), m_joints[i].origin);
+ mat *= glm::toMat4(m_joints[i].orient);
+
+ if (m_joints[i].parentId >= 0)
+ m_jointMatrices[i] = m_jointMatrices[m_joints[i].parentId] * mat;
+ else
+ m_jointMatrices[i] = mat;
+
+ m_finalMatrices[i] = m_jointMatrices[i] * m_inverseBasePose[i];
+ }
+
+ free(pHdr);
}
void Model::LoadObj(const char* filename)
@@ -168,8 +542,10 @@ void Model::LoadObj(const char* filename)
m_Vertices = vertices;
+ ModelData_t m_data = {};
m_data.vb = g_renderDevice->CreateVertexBuffer(vertices.data(), (int)sizeof(StaticMeshVertex) * (int)vertices.size());
m_data.vbcount = vertices.size();
+ m_meshes.push_back(m_data);
std::string mtlfilename = getFileNameWithoutExtension(filename);
mtlfilename += ".mtl";
@@ -218,86 +594,157 @@ void Model::LoadMtl(const char* filename)
fclose(file);
}
-void Model::Draw(const glm::mat4& model, bool isTransparent /*= false*/)
+void Model::Draw(const glm::mat4& model, SkeletonInstance* instance /*= nullptr*/)
{
SDL_assert(g_unlitShader);
- glFrontFace(GL_CCW);
- glDepthFunc(GL_LESS);
+ Shader* shader = instance ? g_unlitSkinnedShader : g_unlitShader;
- g_renderDevice->SetCullFace(true);
- g_renderDevice->SetDepthTest(true);
- g_renderDevice->SetDepthWrite(true);
-
- if (isTransparent)
+ for (int i = 0; i < m_meshes.size(); i++)
{
- // Enable blending
- g_renderDevice->SetBlending(true);
- g_renderDevice->SetBlendingFunction(BF_SRC_ALPHA, BF_ONE_MINUS_SRC_ALPHA);
+ ModelData_t& m_data = m_meshes[i];
- glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, .5f);
- g_shaderSystem->SetUniformFloat4(g_unlitShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr(color));
- }
- else
- {
- g_renderDevice->SetBlending(false);
+ glFrontFace(GL_CCW);
+ glDepthFunc(GL_LESS);
- //glm::vec4 color = glm::vec4(1.f, 1.f, 1.f, 1.f);
- //g_shaderSystem->SetUniformFloat4(g_unlitShader, UNIFORM_CUSTOM_COLOR, glm::value_ptr(color));
+ g_renderDevice->SetCullFace(true);
+ g_renderDevice->SetDepthTest(true);
+ {
+
+
+ if (m_data.ib)
+
+
+ g_renderDevice->DrawElements(PT_TRIANGLES, m_data.ibcount, 0, NULL);
+
+ glm::mat4 worldMat;
+ glm::mat4 worldMatParent;
+ for (int i = 0; i < instance->m_jointMatrices.size(); i++)
+ {
+ worldMat = model * instance->m_jointMatrices[i];
+ glm::vec3 worldPos = worldMat[3];
+ // g_debugRender->DrawAxis(worldPos);
+
+ const Joint& joint = instance->m_joints[i];
+ if (joint.parentId != -1)
+ {
+ worldMatParent = model * instance->m_jointMatrices[joint.parentId];
+ g_debugRender->DrawLine(worldMat[3], worldMatParent[3], glm::vec3(1.0f, 1.0f, 1.0f));
+ }
+ }
}
- g_renderDevice->SetVerticesBuffer(m_data.vb);
+}
- g_shaderSystem->SetShader(g_unlitShader);
- g_shaderSystem->SetUniformMatrix(g_unlitShader, UNIFORM_MODEL_MATRIX, &model[0]);
+AnimationId_t Model::FindAnimation(const char* name)
+{
+ for (int i = 0, numAnims = GetNumAnimations(); i < numAnims; i++)
+ {
+ if (strcmp(m_animations[i].name, name) == 0)
+ return i;
+ }
- //static float test = 0.0f;
- //test += g_systemTimer->GetDelta() * 6.0f;
+ return -1;
+}
- //glm::mat4 model = glm::mat4(1.0f);
+uint32_t Model::GetNumAnimations()
+{
+ return (uint32_t)m_animations.size();
+}
- //int32_t x = 0, y = 0;
- //g_inputSystem->GetMousePosition(&x, &y);
- //
- //glm::mat4 model = glm::mat4(1.0f);
- //model = glm::rotate(model, test, glm::vec3(0.0f, 1.0f, 0.0f));
- //
- //float realY = (float)g_renderView.height - (float)y - 1;
- //
- //glm::vec4 viewport = glm::vec4(0.0f, 0.0f, (float)g_renderView.width, (float)g_renderView.height);
- //glm::vec3 pos = glm::unProject(
- // glm::vec3((float)x, realY, 0.0f),
- // model,
- // g_renderView.proj,
- // viewport);
- //
- //pos *= 50.0f;
- //
- //model = glm::translate(model, pos);
+const Animation* Model::GetAnimation(AnimationId_t index)
+{
+ if (-1 >= index || index >= GetNumAnimations())
+ Core::Error("Model::GetAnimation: index %d is out of bounds.", index);
- glm::mat4 mvp = glm::identity();
- mvp = g_render->GetProjectionMatrix() * g_render->GetViewMatrix() * model;
- g_shaderSystem->SetUniformMatrix(g_unlitShader, UNIFORM_MVP_MATRIX, &mvp[0]);
+ return &m_animations[index];
+}
- g_texturesManager->SetTexture(0, m_AlbedoTexture);
- g_shaderSystem->SetUniformSampler(g_unlitShader, SAMPLER_ALBEDO, 0);
+int Model::GetNumPoses()
+{
+ return m_numPoses;
+}
- g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
+SkeletonInstance* Model::CreateSkeletonInstance()
+{
+ SkeletonInstance* instance = new SkeletonInstance();
+ instance->m_model = this;
+ instance->m_joints = m_joints;
+ instance->m_jointMatrices.resize(m_joints.size());
+ instance->m_finalMatrices.resize(m_joints.size());
+ instance->m_animation = INVALID_ANIMATION_HANDLE;
+ instance->m_time = 0.0;
+ instance->m_looped = false;
-#if 0
- glm::mat4 mvp = glm::identity();
- mvp = g_renderView.proj * g_renderView.view * model;
- g_shaderSystem->SetUniformMatrix(g_litShader, UNIFORM_MVP_MATRIX, &mvp[0]);
+ for (int i = 0; i < m_joints.size(); ++i)
+ {
+ glm::mat4 mat = glm::translate(glm::mat4(1.0f), m_joints[i].origin);
+ mat *= glm::toMat4(m_joints[i].orient);
- g_texturesManager->SetTexture(0, m_AlbedoTexture);
- g_shaderSystem->SetUniformSampler(g_litShader, SAMPLER_ALBEDO, 0);
+ if (m_joints[i].parentId >= 0)
+ instance->m_jointMatrices[i] = instance->m_jointMatrices[m_joints[i].parentId] * mat;
+ else
+ instance->m_jointMatrices[i] = mat;
- g_renderDevice->DrawArrays(PT_TRIANGLES, 0, m_data.vbcount);
+ instance->m_finalMatrices[i] = instance->m_jointMatrices[i] * m_inverseBasePose[i];
+ }
+
+ return instance;
+}
+
+void Model::UpdateSkeletonInstance(SkeletonInstance* instance, float dt)
+{
+ if (!instance)
+ return;
+
+ if (instance->m_animation == -1)
+ return;
+
+ const Animation& animation = m_animations[instance->m_animation];
+
+ instance->m_time += dt;
+
+ float frameTime = 1.0f / animation.framerate;
+ float totalDuration = animation.numFrames * frameTime;
+
+ if (instance->m_time >= totalDuration && instance->m_looped)
+ instance->m_time = 0.0f;
+
+ float animationFrame = instance->m_time / frameTime;
+ int frameA = (int)(floor(animationFrame));
+ int frameB = (frameA + 1) % animation.numFrames;
+
+ if (!instance->m_looped && frameA >= animation.numFrames - 1)
+ frameA = animation.numFrames - 1;
+
+ float t = animationFrame - frameA;
+
+ for (int i = 0; i < instance->m_joints.size(); ++i)
+ {
+ const glm::vec3& a = animation.origin[frameA * m_numPoses + i];
+ const glm::vec3& b = animation.origin[frameB * m_numPoses + i];
+
+ const glm::quat& qa = animation.orient[frameA * m_numPoses + i];
+ const glm::quat& qb = animation.orient[frameB * m_numPoses + i];
+
+ instance->m_joints[i].origin = glm::lerp(a, b, t);
+ instance->m_joints[i].orient = glm::normalize(glm::slerp(qa, qb, t));
+ }
+
+ // matrices
+ for (int i = 0; i < instance->m_joints.size(); ++i)
+ {
+ glm::mat4 mat = glm::translate(glm::mat4(1.0f), instance->m_joints[i].origin);
+ mat *= glm::toMat4(instance->m_joints[i].orient);
+
+ if (m_joints[i].parentId >= 0)
+ instance->m_jointMatrices[i] = instance->m_jointMatrices[m_joints[i].parentId] * mat;
+ else
+ instance->m_jointMatrices[i] = mat;
+
+ instance->m_finalMatrices[i] = instance->m_jointMatrices[i] * m_inverseBasePose[i];
+ }
- BoundingBox bbox = m_boundingBox;
- TransformBoundingBox(bbox, model);
- g_debugRender->DrawBoundingBox(bbox, glm::vec3(1.0f));
-#endif
}
void ReleaseModelData(ModelData_t& data)
diff --git a/src/render/model.h b/src/render/model.h
index 8767d80..32bfaee 100644
--- a/src/render/model.h
+++ b/src/render/model.h
@@ -5,6 +5,7 @@
#include "boundingbox.h"
#include "render_shared.h"
+#include "animation.h"
#include
#include
@@ -31,18 +32,46 @@ public:
Model();
~Model();
+ void LoadIqm(const char* filename);
void LoadObj(const char* filename);
void LoadMtl(const char* filename);
- void Draw(const glm::mat4& model, bool isTransparent = false);
+ void Draw(const glm::mat4& model, SkeletonInstance* instance = nullptr);
const BoundingBox& GetBoundingBox() { return m_boundingBox; }
+ // Animation
+ AnimationId_t FindAnimation(const char* name);
+ uint32_t GetNumAnimations();
+
+ const Animation* GetAnimation(AnimationId_t index);
+
+ int GetNumPoses();
+
+ SkeletonInstance* CreateSkeletonInstance();
+ void UpdateSkeletonInstance(SkeletonInstance* instance, float dt);
+
+// void PlayAnimation(AnimationId_t id, bool looped);
+// void StopAnimation();
+
private:
+ std::vector m_joints;
+ std::vector m_basePose;
+ std::vector m_inverseBasePose;
+ std::vector m_jointMatrices;
+ std::vector m_finalMatrices;
+
+ std::vector m_animations;
+
std::vector m_Vertices;
- ModelData_t m_data;
+
+
+ std::vector m_meshes;
+
BoundingBox m_boundingBox;
Texture2D* m_AlbedoTexture;
+
+ int m_numPoses;
};
#endif // !MODEL_H
diff --git a/src/render/modelsystem.cpp b/src/render/modelsystem.cpp
index 5cca027..74aaf9c 100644
--- a/src/render/modelsystem.cpp
+++ b/src/render/modelsystem.cpp
@@ -23,11 +23,15 @@ static InputLayoutDesc_t g_staticVertexLayout[] = {
static InputLayoutDesc_t g_skinnedVertexLayout[] = {
{ VERTEXATTR_VEC3, SHADERSEMANTIC_POSITION },
{ VERTEXATTR_VEC3, SHADERSEMANTIC_NORMAL },
- { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD }
+ { VERTEXATTR_VEC2, SHADERSEMANTIC_TEXCOORD },
+ { VERTEXATTR_VEC4, SHADERSEMANTIC_BONEIDS },
+ { VERTEXATTR_VEC4, SHADERSEMANTIC_WEIGHTS },
};
Shader* g_unlitShader = nullptr;
Shader* g_litShader = nullptr;
+Shader* g_unlitSkinnedShader = nullptr;
+Shader* g_litSkinnedShader = nullptr;
static std::string getFileNameWithoutExtension(const std::string& filename)
{
@@ -56,6 +60,10 @@ void ModelSystem::Init()
// Load lighted model generic shader
g_litShader = g_shaderSystem->CreateShader("lit_generic", "data/shaders/lit_generic.vs", "data/shaders/lit_generic.ps",
g_staticVertexLayout, sizeof(g_staticVertexLayout) / sizeof(g_staticVertexLayout[0]));
+
+ // Load unlighted skinned model generic shader
+ g_unlitSkinnedShader = g_shaderSystem->CreateShader("unlit_generic_skin", "data/shaders/unlit_generic_skin.vs", "data/shaders/unlit_generic.ps",
+ g_skinnedVertexLayout, sizeof(g_skinnedVertexLayout) / sizeof(g_skinnedVertexLayout[0]));
}
void ModelSystem::Shutdown()
@@ -92,6 +100,10 @@ Model* ModelSystem::LoadModel(const char* filename)
{
model->LoadObj(filename);
}
+ else if (strstr(filename, ".iqm"))
+ {
+ model->LoadIqm(filename);
+ }
else
{
// Find file extension
diff --git a/src/render/render_shared.h b/src/render/render_shared.h
index cf97195..abc6fa6 100644
--- a/src/render/render_shared.h
+++ b/src/render/render_shared.h
@@ -5,6 +5,8 @@
#include
#include
+#include
+#include
typedef uint32_t index_t;
@@ -207,6 +209,7 @@ enum ShaderUniform_t
UNIFORM_SUN_COLOR,
UNIFORM_SUN_AMBIENT,
UNIFORM_CAMERA_POS,
+ UNIFORM_BONE_MATRICES,
UNIFORM_MAX,
};
@@ -238,4 +241,13 @@ struct StaticMeshVertex
glm::vec2 texcoord;
};
+struct SkinnedMeshVertex
+{
+ glm::vec3 position;
+ glm::vec3 normal;
+ glm::vec2 texcoord;
+ glm::vec4 boneIds;
+ glm::vec4 weights;
+};
+
#endif
\ No newline at end of file
diff --git a/src/render/shadersystem.cpp b/src/render/shadersystem.cpp
index cc0caaf..982a69f 100644
--- a/src/render/shadersystem.cpp
+++ b/src/render/shadersystem.cpp
@@ -13,6 +13,7 @@ static const char* g_uniformNameTable[UNIFORM_MAX] =
"u_sunColor",
"u_sunAmbientColor",
"u_cameraPos",
+ "u_boneMatrices[0]"
};
static const char* g_samplersNameTable[SAMPLER_MAX] =
@@ -152,9 +153,9 @@ void ShaderSystem::SetUniformFloat4(const Shader* shader, ShaderUniform_t unifor
glUniform4fv( shader->m_glUniformLocation[ uniform ], 1, ( const GLfloat* )data );
}
-void ShaderSystem::SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data )
+void ShaderSystem::SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data, int numMatrices /*= 0*/ )
{
- glUniformMatrix4fv( shader->m_glUniformLocation[ uniform ], 1, GL_FALSE, ( const GLfloat* )data );
+ glUniformMatrix4fv( shader->m_glUniformLocation[ uniform ], numMatrices, GL_FALSE, ( const GLfloat* )data );
}
void Shader::AllocateAttributes()
diff --git a/src/render/shadersystem.h b/src/render/shadersystem.h
index 38e6a9f..ddcdacd 100644
--- a/src/render/shadersystem.h
+++ b/src/render/shadersystem.h
@@ -21,7 +21,7 @@ public:
void SetUniformSampler( const Shader* shader, ShaderSamplers_t sampler, int index );
void SetUniformFloat4( const Shader* shader, ShaderUniform_t uniform, const void* data );
- void SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data );
+ void SetUniformMatrix( const Shader* shader, ShaderUniform_t uniform, const void* data, int numMatrices = 1 );
private:
std::vector m_shaders;