#include <fbxsdk.h>
#include <math.h>
#ifdef WIN32
#include <windows.h>
#endif
#define GLUT_DISABLE_ATEXIT_HACK
#if defined(__MACH__)
#include <GLUT/glut.h>
#else
#include "GL/glut.h"
#endif
#include <fbxfilesdk/fbxfilesdk_nsuse.h>
#include "InitScene.h"
#include "SetCamera.h"
#include "DrawScene.h"
#include "Texture.h"
#include "../Common/Common.h"
void ExitFunction();
void CreateMenus();
void DrawText(KString& pText);
void CameraSelectionCallback(int pItem);
void AnimStackSelectionCallback(int pItem);
void MenuSelectionCallback(int pItem);
void PoseSelectionCallback(int pItem);
void ShadingModeSelectionCallback(int pItem);
void TimerCallback(int);
void DisplayCallback();
void ReshapeCallback(int pWidth, int pHeight);
void KeyboardCallback(unsigned char pKey, int, int);
void MouseCallback(int button, int state, int x, int y);
void MotionCallback(int x, int y);
KString ExtractDirectory(KString pFilePath);
KFbxSdkManager* gSdkManager;
KFbxImporter* gImporter;
KFbxScene* gScene;
KFbxAnimStack* gCurrentAnimationStack;
KFbxAnimLayer* gCurrentAnimationLayer;
int gSceneStatus;
ShadingManager * gShadingManager;
template<class KString>
class DeletionPolicy
{
public:
static inline void DeleteIt(KString** ptr)
{
if ( *ptr != NULL )
{
delete *ptr;
*ptr = NULL;
}
}
};
KScopedPtr<KString, DeletionPolicy<KString> > gWindowMessage;
KScopedPtr<KString, DeletionPolicy<KString> > gFileName;
KArrayTemplate<KFbxNode*> gCameraArray;
KArrayTemplate<KString*> gAnimStackNameArray;
KArrayTemplate<KFbxPose*> gPoseArray;
KTime gPeriod, gStart, gStop, gCurrentTime;
int gPoseIndex;
int gX, gY;
KFbxVector4 gCamPosition, gCamCenter;
double gRoll;
int gCameraStatus;
enum { UNLOADED, MUST_BE_LOADED, MUST_BE_REFRESHED, REFRESHED };
enum { CAMERA_NOTHING, CAMERA_ORBIT, CAMERA_ZOOM, CAMERA_PAN};
#define PRODUCER_PERSPECTIVE_ITEM 100
#define PRODUCER_TOP_ITEM 101
#define PRODUCER_BOTTOM_ITEM 102
#define PRODUCER_FRONT_ITEM 103
#define PRODUCER_BACK_ITEM 104
#define PRODUCER_RIGHT_ITEM 105
#define PRODUCER_LEFT_ITEM 106
#define CAMERA_SWITCHER_ITEM 107
#define PLAY_ANIMATION 200
const int MENU_SHADING_MODE_WIREFRAME = 300;
const int MENU_SHADING_MODE_SHADED = 301;
const char * MENU_STRING_SHADING_MODE_WIREFRAME = "Wireframe";
const char * MENU_STRING_SHADING_MODE_SHADED = "Shaded";
const int MENU_EXIT = 400;
#define SAMPLE_FILENAME "humanoid.fbx"
class MyMemoryAllocator : public KFbxMemoryAllocator
{
public:
MyMemoryAllocator()
: KFbxMemoryAllocator(MyMalloc, MyCalloc, MyRealloc, MyFree, MyMsize)
{
}
~MyMemoryAllocator()
{
}
static void* MyMalloc(size_t pSize)
{
char *p = (char*)malloc(pSize+1);
*p = '#';
return p+1;
}
static void* MyCalloc(size_t pCount,size_t pSize)
{
char *p = (char*)calloc(pCount, pSize+1);
*p = '#';
return p+1;
}
static void* MyRealloc(void* pData, size_t pSize)
{
if (pData)
{
if (*((char*)pData-1)=='#')
{
char *p = (char*)realloc((char*)pData-1, pSize+1);
*p = '#';
return p+1;
}
else
{
char *p = (char*)realloc((char*)pData, pSize+1);
*p = '#';
return p+1;
}
}
else
{
char *p = (char*)realloc(NULL, pSize+1);
*p = '#';
return p+1;
}
}
static void MyFree(void* pData)
{
if (*((char*)pData-1)=='#')
{
free((char*)pData-1);
}
else
{
free(pData);
}
}
static size_t MyMsize(void* pData)
{
if (*((char*)pData-1)=='#')
{
return _msize((char*)pData-1);
}
else
{
return _msize((char*)pData);
}
}
};
int main(int argc, char** argv)
{
atexit(ExitFunction);
#ifdef _DEBUG
MyMemoryAllocator lMyMemoryAllocator;
KFbxSdkManager::SetMemoryAllocator(&lMyMemoryAllocator);
#endif
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(720, 486);
glutInitWindowPosition(100, 100);
glutCreateWindow("ViewScene");
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glClearColor(0.0, 0.0, 0.0, 0.0);
glutDisplayFunc(DisplayCallback);
glutReshapeFunc(ReshapeCallback);
glutKeyboardFunc(KeyboardCallback);
glutMouseFunc(MouseCallback);
glutMotionFunc(MotionCallback);
gSceneStatus = UNLOADED;
InitializeSdkObjects(gSdkManager, gScene);
gWindowMessage.Reset(new KString());
gFileName.Reset(new KString());
if(argc <= 1)
{
*gFileName = SAMPLE_FILENAME;
}
else
{
*gFileName = argv[1];
}
if (gSdkManager)
{
int lFileFormat = -1;
gImporter = KFbxImporter::Create(gSdkManager,"");
if (!gSdkManager->GetIOPluginRegistry()->DetectReaderFileFormat(*gFileName, lFileFormat) )
{
lFileFormat = gSdkManager->GetIOPluginRegistry()->FindReaderIDByDescription( "FBX binary (*.fbx)" );;
}
if(gImporter->Initialize(gFileName->Buffer(), lFileFormat) == true)
{
*gWindowMessage = "Importing file ";
*gWindowMessage += *gFileName;
*gWindowMessage += "\nPlease wait!";
gSceneStatus = MUST_BE_LOADED;
}
else
{
*gWindowMessage = "Unable to open file ";
*gWindowMessage += *gFileName;
*gWindowMessage += "\nError reported: ";
*gWindowMessage += gImporter->GetLastErrorString();
*gWindowMessage += "\nEsc to exit";
}
}
else
{
*gWindowMessage = "Unable to create the FBX SDK manager";
*gWindowMessage += "\nEsc to exit";
}
gPoseIndex = -1;
glutMainLoop();
return 0;
}
void ExitFunction()
{
if (gSdkManager) gSdkManager->Destroy();
gSdkManager = NULL;
FbxSdkDeleteAndClear(gAnimStackNameArray);
delete gShadingManager;
}
void ImportFunction()
{
if (gSceneStatus == MUST_BE_LOADED)
{
if(gImporter->Import(gScene) == true)
{
gSceneStatus = MUST_BE_REFRESHED;
KFbxAxisSystem SceneAxisSystem = gScene->GetGlobalSettings().GetAxisSystem();
KFbxAxisSystem OurAxisSystem(KFbxAxisSystem::YAxis, KFbxAxisSystem::ParityOdd, KFbxAxisSystem::RightHanded);
if( SceneAxisSystem != OurAxisSystem )
{
OurAxisSystem.ConvertScene(gScene);
}
KFbxSystemUnit SceneSystemUnit = gScene->GetGlobalSettings().GetSystemUnit();
if( SceneSystemUnit.GetScaleFactor() != 1.0 )
{
KFbxSystemUnit OurSystemUnit(1.0);
OurSystemUnit.ConvertScene(gScene);
}
ConvertNurbsAndPatch(gSdkManager, gScene);
PreparePointCacheData(gScene);
FillCameraArray(gScene, gCameraArray);
gScene->FillAnimStackNameArray(gAnimStackNameArray);
FillPoseArray(gScene, gPoseArray);
gShadingManager = new ShadingManager(KFbxExtractDirectory(gImporter->GetFileName()));
gShadingManager->Initialize(gScene);
CreateMenus();
*gWindowMessage = "File ";
*gWindowMessage += *gFileName;
*gWindowMessage += "\nClick on the right mouse button to enter menu.";
*gWindowMessage += "\nEsc to exit.";
gPeriod.SetTime(0, 0, 0, 1, 0, gScene->GetGlobalSettings().GetTimeMode());
glutTimerFunc((unsigned int) gPeriod.GetMilliSeconds(), TimerCallback, 0);
}
else
{
gSceneStatus = UNLOADED;
*gWindowMessage = "Unable to import file ";
*gWindowMessage += *gFileName;
*gWindowMessage += "\nError reported: ";
*gWindowMessage += gImporter->GetLastErrorString();
}
gImporter->Destroy();
}
}
KString ExtractDirectory(KString pFilePath)
{
KString lFilePath = pFilePath;
while (lFilePath.FindAndReplace("\\","/")) {}
int lIndex = lFilePath.ReverseFind('/');
if (lIndex >= 0)
{
lFilePath = lFilePath.Left(lIndex+1);
}
return lFilePath;
}
void CreateMenus()
{
int i;
int lCameraMenu = glutCreateMenu(CameraSelectionCallback);
glutAddMenuEntry(PRODUCER_PERSPECTIVE, PRODUCER_PERSPECTIVE_ITEM);
glutAddMenuEntry(PRODUCER_TOP, PRODUCER_TOP_ITEM);
glutAddMenuEntry(PRODUCER_BOTTOM, PRODUCER_BOTTOM_ITEM);
glutAddMenuEntry(PRODUCER_FRONT, PRODUCER_FRONT_ITEM);
glutAddMenuEntry(PRODUCER_BACK, PRODUCER_BACK_ITEM);
glutAddMenuEntry(PRODUCER_RIGHT, PRODUCER_RIGHT_ITEM);
glutAddMenuEntry(PRODUCER_LEFT, PRODUCER_LEFT_ITEM);
if (gCameraArray.GetCount() > 0)
{
glutAddMenuEntry(CAMERA_SWITCHER, CAMERA_SWITCHER_ITEM);
}
for (i = 0; i < gCameraArray.GetCount(); i++)
{
glutAddMenuEntry(gCameraArray[i]->GetName(), i);
}
int lAnimStackMenu = glutCreateMenu(AnimStackSelectionCallback);
int lCurrentAnimStackIndex = 0;
for (i = 0; i < gAnimStackNameArray.GetCount(); i++)
{
glutAddMenuEntry(gAnimStackNameArray[i]->Buffer(), i);
if (gAnimStackNameArray[i]->Compare(KFbxGet<KString>(gScene->ActiveAnimStackName)) == 0)
{
lCurrentAnimStackIndex = i;
}
}
AnimStackSelectionCallback(lCurrentAnimStackIndex);
const int lShadingModeMenu = glutCreateMenu(ShadingModeSelectionCallback);
glutAddMenuEntry(MENU_STRING_SHADING_MODE_WIREFRAME, MENU_SHADING_MODE_WIREFRAME);
glutAddMenuEntry(MENU_STRING_SHADING_MODE_SHADED, MENU_SHADING_MODE_SHADED);
int lBindPoseCount = 0;
int lRestPoseCount = 0;
int lBindPoseMenu = glutCreateMenu(PoseSelectionCallback);
for (i = 0; i < gPoseArray.GetCount(); i++)
{
if (gPoseArray[i]->IsBindPose())
{
glutAddMenuEntry(gPoseArray[i]->GetName(), i);
lBindPoseCount++;
}
}
int lRestPoseMenu = glutCreateMenu(PoseSelectionCallback);
for (i = 0; i < gPoseArray.GetCount(); i++)
{
if (gPoseArray[i]->IsRestPose())
{
glutAddMenuEntry(gPoseArray[i]->GetName(), i);
lRestPoseCount++;
}
}
int lPoseMenu = 0;
if (lBindPoseCount>0 || lRestPoseCount>0)
{
lPoseMenu = glutCreateMenu(PoseSelectionCallback);
if (lBindPoseCount>0)
glutAddSubMenu("Bind Pose", lBindPoseMenu);
if (lRestPoseCount>0)
glutAddSubMenu("Rest Pose", lRestPoseMenu);
}
glutCreateMenu(MenuSelectionCallback);
glutAddSubMenu("Select Camera", lCameraMenu);
glutAddSubMenu("Select Animation Stack", lAnimStackMenu);
glutAddSubMenu("Select Shading Mode", lShadingModeMenu);
if (lBindPoseCount>0 || lRestPoseCount>0)
glutAddSubMenu("Go to Pose", lPoseMenu);
glutAddMenuEntry("Play", PLAY_ANIMATION);
glutAddMenuEntry("Exit", MENU_EXIT);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
void DrawText(KString& pText)
{
glColor3f(1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, glutGet(GLUT_WINDOW_WIDTH), 0, glutGet(GLUT_WINDOW_HEIGHT));
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
const float lX = 5;
float lY = glutGet(GLUT_WINDOW_HEIGHT) - 20;
glRasterPos2f(lX, lY);
const int lCount = static_cast<int>(pText.GetLen());
for (int i = 0; i < lCount; i++)
{
const char lC = pText.Buffer()[i];
if (lC == '\n')
{
lY -= 20;
glRasterPos2f(lX, lY);
}
else
{
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, lC);
}
}
if (gSceneStatus != UNLOADED && gSceneStatus != MUST_BE_LOADED)
{
const int lTotalFrame = static_cast<int>((gStop - gStart).GetSecondDouble() / gPeriod.GetSecondDouble());
const int lCurrentFrame = static_cast<int>((gCurrentTime - gStart).GetSecondDouble() / gPeriod.GetSecondDouble());
const KString lFrameMessage = KString(lCurrentFrame) + "[0-" + KString(lTotalFrame) + "]";
const int lFrameMessageCount = static_cast<int>(lFrameMessage.GetLen());
glRasterPos2f(lX, 20);
for (int lIndex = 0; lIndex < lFrameMessageCount; ++lIndex)
{
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, lFrameMessage[lIndex]);
}
}
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
}
void CameraSelectionCallback(int pItem)
{
KFbxGlobalSettings& lGlobalCameraSettings = gScene->GetGlobalSettings();
if (pItem == PRODUCER_PERSPECTIVE_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_PERSPECTIVE);
}
else if (pItem == PRODUCER_TOP_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_TOP);
}
else if (pItem == PRODUCER_BOTTOM_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_BOTTOM);
}
else if (pItem == PRODUCER_FRONT_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_FRONT);
}
else if (pItem == PRODUCER_BACK_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_BACK);
}
else if (pItem == PRODUCER_RIGHT_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_RIGHT);
}
else if (pItem == PRODUCER_LEFT_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(PRODUCER_LEFT);
}
else if (pItem == CAMERA_SWITCHER_ITEM)
{
lGlobalCameraSettings.SetDefaultCamera(CAMERA_SWITCHER);
}
else if (pItem >= 0 && pItem < gCameraArray.GetCount())
{
lGlobalCameraSettings.SetDefaultCamera((char *)gCameraArray[pItem]->GetName());
}
gSceneStatus = MUST_BE_REFRESHED;
}
void AnimStackSelectionCallback( int pItem )
{
int lNbAnimStacks = gAnimStackNameArray.GetCount();
if (!lNbAnimStacks || pItem >= lNbAnimStacks)
{
return;
}
gCurrentAnimationStack = gScene->FindMember(FBX_TYPE(KFbxAnimStack), gAnimStackNameArray[pItem]->Buffer());
if (gCurrentAnimationStack == NULL)
{
return;
}
gCurrentAnimationLayer = gCurrentAnimationStack->GetMember(FBX_TYPE(KFbxAnimLayer), 0);
gScene->GetEvaluator()->SetContext(gCurrentAnimationStack);
KFbxTakeInfo* lCurrentTakeInfo = gScene->GetTakeInfo(*(gAnimStackNameArray[pItem]));
if (lCurrentTakeInfo)
{
gStart = lCurrentTakeInfo->mLocalTimeSpan.GetStart();
gStop = lCurrentTakeInfo->mLocalTimeSpan.GetStop();
}
else
{
KTimeSpan lTimeLineTimeSpan;
gScene->GetGlobalSettings().GetTimelineDefaultTimeSpan(lTimeLineTimeSpan);
gStart = lTimeLineTimeSpan.GetStart();
gStop = lTimeLineTimeSpan.GetStop();
}
gCurrentTime = gStart;
gSceneStatus = MUST_BE_REFRESHED;
}
void PoseSelectionCallback(int pItem)
{
gPoseIndex = pItem;
gSceneStatus = MUST_BE_REFRESHED;
}
void ShadingModeSelectionCallback(int pItem)
{
if (pItem == MENU_SHADING_MODE_WIREFRAME)
{
gShadingManager->SetShadingMode(ShadingManager::SHADING_MODE_WIREFRAME);
}
else if (pItem == MENU_SHADING_MODE_SHADED)
{
gShadingManager->SetShadingMode(ShadingManager::SHADING_MODE_SHADED);
}
}
void MenuSelectionCallback(int pItem)
{
if (pItem == PLAY_ANIMATION)
{
gPoseIndex = -1;
}
else if (pItem == MENU_EXIT)
{
exit(0);
}
gSceneStatus = MUST_BE_REFRESHED;
}
void TimerCallback(int)
{
if (gSceneStatus == MUST_BE_REFRESHED)
{
glutPostRedisplay();
}
if (gStop > gStart)
{
gSceneStatus = MUST_BE_REFRESHED;
gCurrentTime += gPeriod;
if (gCurrentTime > gStop)
{
gCurrentTime = gStart;
}
}
else
{
gSceneStatus = REFRESHED;
}
glutTimerFunc((unsigned int) gPeriod.GetMilliSeconds(), TimerCallback, 0);
}
void DisplayCallback()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (gSceneStatus != UNLOADED && gSceneStatus != MUST_BE_LOADED)
{
gShadingManager->InitializeForOneFrame();
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
SetCamera(gScene, gCurrentTime, gCurrentAnimationLayer, gCameraArray);
if (gPoseIndex == -1)
{
DrawScene(gScene, gCurrentTime);
}
else
{
DrawSceneAtPose(gScene, gCurrentTime, gPoseIndex);
}
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
}
DrawText(*gWindowMessage);
glutSwapBuffers();
if (gSceneStatus == MUST_BE_LOADED)
{
ImportFunction();
}
}
void ReshapeCallback(int pWidth, int pHeight)
{
glViewport(0, 0, (GLsizei) pWidth, (GLsizei) pHeight);
}
void KeyboardCallback(unsigned char pKey, int, int)
{
if (pKey == 27)
{
exit(0);
}
if (pKey == 43 || pKey == 61)
{
KFbxCamera* lCamera = GetCurrentCamera(gScene);
if(lCamera)
{
CameraZoomIn(gScene, 10);
gSceneStatus = MUST_BE_REFRESHED;
}
}
if (pKey == 45 || pKey == 95)
{
KFbxCamera* lCamera = GetCurrentCamera(gScene);
if(lCamera)
{
CameraZoomOut(gScene, 10);
gSceneStatus = MUST_BE_REFRESHED;
}
}
if (pKey == 'N' || pKey == 'n')
{
gShadingManager->SetDrawNormal(!gShadingManager->IsDrawNormal());
}
}
void MouseCallback(int button, int state, int x, int y) {
KFbxCamera* lCamera = GetCurrentCamera(gScene);
if (lCamera)
{
gCamPosition = lCamera->Position.Get();
gCamCenter = lCamera->InterestPosition.Get();
gRoll = lCamera->Roll.Get();
}
gX = x;
gY = y;
switch (button)
{
case GLUT_LEFT_BUTTON:
switch (state) {
case GLUT_DOWN:
if (gCameraStatus == CAMERA_ZOOM) {
gCameraStatus = CAMERA_PAN;
} else {
gCameraStatus = CAMERA_ORBIT;
}
break;
default:
if (gCameraStatus == CAMERA_PAN) {
gCameraStatus = CAMERA_ZOOM;
} else {
gCameraStatus = CAMERA_NOTHING;
}
break;
}
break;
case GLUT_MIDDLE_BUTTON:
switch (state) {
case GLUT_DOWN:
if (gCameraStatus == CAMERA_ORBIT) {
gCameraStatus = CAMERA_PAN;
} else {
gCameraStatus = CAMERA_ZOOM;
}
break;
default:
if (gCameraStatus == CAMERA_PAN) {
gCameraStatus = CAMERA_ORBIT;
} else {
gCameraStatus = CAMERA_NOTHING;
}
break;
}
break;
}
}
void MotionCallback(int x, int y) {
int motion;
switch (gCameraStatus) {
case CAMERA_ORBIT:
CameraOrbit(gScene, gCamPosition, gRoll, x-gX, gY-y);
gSceneStatus = MUST_BE_REFRESHED;
break;
case CAMERA_ZOOM:
motion = gY-y;
if (motion > 0) {
CameraZoomIn(gScene, motion);
} else {
CameraZoomOut(gScene, -motion);
}
gY = y;
gSceneStatus = MUST_BE_REFRESHED;
break;
case CAMERA_PAN:
CameraPan(gScene, gCamPosition, gCamCenter, gRoll, x-gX, gY-y);
gSceneStatus = MUST_BE_REFRESHED;
break;
}
}
#ifdef WIN32
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
return main(__argc,__argv);
}
#endif