forked from 0ad/0ad
Ykkrosh
173c56140c
* Skinning is done in a way that works when there's more than one bone influencing a vertex. * PMDs now store vertexes in world-space instead of bind-space. (The loader converts the old-version PMDs so they still work.) * Moved SkinPoint, SkinNormal into CModelDef so it could use them when loading the old PMDs. * Made the FastNormals approach non-optional, so the inverse-transpose bone matrices could be removed. Changed the explanation of why it's a valid approach. * Quaternion: Made GetInverse assume that the quaternions have unit length (which they do when they're representing 3D rotations). * lib: Added support for DDS files that aren't a multiple of 4x4 (most useful for 1x1, 2x2, etc that are still powers of two). * Actor Viewer: Added white terrain texture to the minimal test mod, so shadows are visible. Changed default so walk/run animations don't move the unit along the ground. * Removed some redundant repetition in doc comments. * Removed some unnecessary #includes. This was SVN commit r4696.
276 lines
6.4 KiB
C++
276 lines
6.4 KiB
C++
#include "precompiled.h"
|
|
|
|
#include "ActorViewer.h"
|
|
|
|
#include "View.h"
|
|
|
|
#include "graphics/Model.h"
|
|
#include "graphics/ObjectManager.h"
|
|
#include "graphics/Patch.h"
|
|
#include "graphics/SkeletonAnim.h"
|
|
#include "graphics/SkeletonAnimDef.h"
|
|
#include "graphics/Terrain.h"
|
|
#include "graphics/TextureEntry.h"
|
|
#include "graphics/TextureManager.h"
|
|
#include "graphics/Unit.h"
|
|
#include "graphics/UnitManager.h"
|
|
#include "maths/MathUtil.h"
|
|
#include "ps/Font.h"
|
|
#include "ps/GameSetup/Config.h"
|
|
#include "ps/ProfileViewer.h"
|
|
#include "renderer/Renderer.h"
|
|
#include "renderer/Scene.h"
|
|
#include "renderer/SkyManager.h"
|
|
|
|
struct ActorViewerImpl : public Scene
|
|
{
|
|
CUnit* Unit;
|
|
CStrW CurrentUnitID;
|
|
CStrW CurrentUnitAnim;
|
|
float CurrentSpeed;
|
|
bool WalkEnabled;
|
|
bool GroundEnabled;
|
|
bool ShadowsEnabled;
|
|
|
|
SColor4ub Background;
|
|
|
|
CTerrain Terrain;
|
|
|
|
// Simplistic implementation of the Scene interface
|
|
void EnumerateObjects(const CFrustum& UNUSED(frustum), SceneCollector* c)
|
|
{
|
|
if (GroundEnabled)
|
|
c->Submit(Terrain.GetPatch(0, 0));
|
|
|
|
if (Unit)
|
|
c->SubmitRecursive(Unit->GetModel());
|
|
}
|
|
};
|
|
|
|
ActorViewer::ActorViewer()
|
|
: m(*new ActorViewerImpl())
|
|
{
|
|
m.Unit = NULL;
|
|
m.WalkEnabled = false;
|
|
m.Background = SColor4ub(255, 255, 255, 255);
|
|
|
|
// Set up the renderer
|
|
g_TexMan.LoadTerrainTextures();
|
|
g_Renderer.LoadAlphaMaps();
|
|
g_Renderer.GetSkyManager()->m_RenderSky = false;
|
|
// (TODO: should these be unloaded properly some time? and what should
|
|
// happen if we want the actor viewer and scenario editor loaded at
|
|
// the same time?)
|
|
|
|
// Create a tiny empty piece of terrain, just so we can put shadows
|
|
// on it without having to think too hard
|
|
m.Terrain.Initialize(1, NULL);
|
|
CTextureEntry* tex = g_TexMan.FindTexture("whiteness");
|
|
if (tex)
|
|
{
|
|
CPatch* patch = m.Terrain.GetPatch(0, 0);
|
|
for (int i = 0; i < PATCH_SIZE; ++i)
|
|
{
|
|
for (int j = 0; j < PATCH_SIZE; ++j)
|
|
{
|
|
CMiniPatch& mp = patch->m_MiniPatches[i][j];
|
|
mp.Tex1 = tex->GetHandle();
|
|
mp.Tex1Priority = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ActorViewer::~ActorViewer()
|
|
{
|
|
delete m.Unit;
|
|
delete &m;
|
|
}
|
|
|
|
CUnit* ActorViewer::GetUnit()
|
|
{
|
|
return m.Unit;
|
|
}
|
|
|
|
void ActorViewer::SetActor(const CStrW& id, const CStrW& animation)
|
|
{
|
|
bool needsAnimReload = false;
|
|
|
|
if (! m.Unit || id != m.CurrentUnitID)
|
|
{
|
|
delete m.Unit;
|
|
m.Unit = NULL;
|
|
|
|
// If there's no actor to display, return with nothing loaded
|
|
if (id.empty())
|
|
return;
|
|
|
|
m.Unit = CUnit::Create((CStr)id, NULL, std::set<CStr>());
|
|
|
|
if (! m.Unit)
|
|
return;
|
|
|
|
float angle = PI;
|
|
float s = sin(angle);
|
|
float c = cos(angle);
|
|
CMatrix3D mat;
|
|
mat._11 = -c; mat._12 = 0.0f; mat._13 = -s; mat._14 = CELL_SIZE * PATCH_SIZE/2;
|
|
mat._21 = 0.0f; mat._22 = 1.0f; mat._23 = 0.0f; mat._24 = 0.0f;
|
|
mat._31 = s; mat._32 = 0.0f; mat._33 = -c; mat._34 = CELL_SIZE * PATCH_SIZE/2;
|
|
mat._41 = 0.0f; mat._42 = 0.0f; mat._43 = 0.0f; mat._44 = 1.0f;
|
|
m.Unit->GetModel()->SetTransform(mat);
|
|
m.Unit->GetModel()->ValidatePosition();
|
|
|
|
needsAnimReload = true;
|
|
}
|
|
|
|
if (animation != m.CurrentUnitAnim)
|
|
needsAnimReload = true;
|
|
|
|
if (needsAnimReload)
|
|
{
|
|
CStr anim = ((CStr)animation).LowerCase();
|
|
|
|
float speed;
|
|
// TODO: this is just copied from template_unit.xml and isn't the
|
|
// same for all units. But we don't know anything about entities here,
|
|
// so what to do?
|
|
if (anim == "walk")
|
|
speed = 7.f;
|
|
else if (anim == "run")
|
|
speed = 12.f;
|
|
else
|
|
speed = 0.f;
|
|
m.CurrentSpeed = speed;
|
|
|
|
m.Unit->SetEntitySelection(anim);
|
|
m.Unit->SetRandomAnimation(anim, false, speed);
|
|
}
|
|
|
|
m.CurrentUnitID = id;
|
|
m.CurrentUnitAnim = animation;
|
|
}
|
|
|
|
void ActorViewer::SetBackgroundColour(const SColor4ub& colour)
|
|
{
|
|
m.Background = colour;
|
|
m.Terrain.SetBaseColour(colour);
|
|
}
|
|
|
|
void ActorViewer::SetWalkEnabled(bool enabled) { m.WalkEnabled = enabled; }
|
|
void ActorViewer::SetGroundEnabled(bool enabled) { m.GroundEnabled = enabled; }
|
|
void ActorViewer::SetShadowsEnabled(bool enabled) { m.ShadowsEnabled = enabled; }
|
|
|
|
void ActorViewer::SetStatsEnabled(bool enabled)
|
|
{
|
|
if (enabled)
|
|
g_ProfileViewer.ShowTable("renderer");
|
|
else
|
|
g_ProfileViewer.ShowTable("");
|
|
}
|
|
|
|
void ActorViewer::Render()
|
|
{
|
|
m.Terrain.MakeDirty(RENDERDATA_UPDATE_COLOR);
|
|
|
|
g_Renderer.SetClearColor(*(u32*)&m.Background);
|
|
|
|
// Disable shadows locally (avoid clobbering global state)
|
|
bool oldShadows = g_Renderer.GetOptionBool(CRenderer::OPT_SHADOWS);
|
|
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, m.ShadowsEnabled);
|
|
|
|
g_Renderer.BeginFrame();
|
|
|
|
// Find the centre of the interesting region, in the middle of the patch
|
|
// and half way up the model (assuming there is one)
|
|
CVector3D centre;
|
|
if (m.Unit)
|
|
m.Unit->GetModel()->GetBounds().GetCentre(centre);
|
|
else
|
|
centre.Y = 0.f;
|
|
centre.X = centre.Z = CELL_SIZE * PATCH_SIZE/2;
|
|
|
|
CCamera camera = View::GetView_Actor()->GetCamera();
|
|
camera.m_Orientation.Translate(centre.X, centre.Y, centre.Z);
|
|
camera.UpdateFrustum();
|
|
|
|
g_Renderer.SetSceneCamera(camera, camera);
|
|
|
|
g_Renderer.RenderScene(&m);
|
|
|
|
// ....
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glPushAttrib(GL_ENABLE_BIT);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glDisable(GL_CULL_FACE);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_BLEND);
|
|
glDisable(GL_ALPHA_TEST);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
CFont font("console");
|
|
font.Bind();
|
|
|
|
g_ProfileViewer.RenderProfile();
|
|
|
|
glPopAttrib();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
|
|
g_Renderer.EndFrame();
|
|
|
|
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS, oldShadows);
|
|
|
|
oglCheck();
|
|
}
|
|
|
|
void ActorViewer::Update(float dt)
|
|
{
|
|
if (m.Unit)
|
|
{
|
|
m.Unit->GetModel()->Update(dt);
|
|
|
|
CMatrix3D mat = m.Unit->GetModel()->GetTransform();
|
|
|
|
if (m.WalkEnabled && m.CurrentSpeed)
|
|
{
|
|
// Move the model by speed*dt forwards
|
|
float z = mat.GetTranslation().Z;
|
|
z -= m.CurrentSpeed*dt;
|
|
// Wrap at the edges, so it doesn't run off into the horizon
|
|
if (z < CELL_SIZE*PATCH_SIZE * 0.4f)
|
|
z = CELL_SIZE*PATCH_SIZE * 0.6f;
|
|
mat.Translate(0.f, 0.f, z - mat.GetTranslation().Z);
|
|
}
|
|
|
|
m.Unit->GetModel()->SetTransform(mat);
|
|
m.Unit->GetModel()->ValidatePosition();
|
|
}
|
|
}
|
|
|
|
bool ActorViewer::HasAnimation() const
|
|
{
|
|
if (m.Unit &&
|
|
m.Unit->GetModel()->GetAnimation() &&
|
|
m.Unit->GetModel()->GetAnimation()->m_AnimDef &&
|
|
m.Unit->GetModel()->GetAnimation()->m_AnimDef->GetNumFrames() > 1)
|
|
return true;
|
|
|
|
if (m.Unit && m.WalkEnabled && m.CurrentSpeed)
|
|
return true;
|
|
|
|
return false;
|
|
}
|