#pragma once
#include <fstream>
#include <map>
#include <vector>
#include <string>
#include "GasVertex.h"
#include "GasMaterial.h"
#include "GasBatch.h"
#include "GasNode.h"
#include "GasBone.h"
#include "AnimationHeader.h"
using namespace std;
class Exporter : public SceneExport
{
public:
Exporter();
~Exporter();
/** Exports the scene. */
int DoExport( const TCHAR* name, ExpInterface *ei, Interface* i,
BOOL suppressprompts=FALSE, DWORD options=0 );
/** Show DLL's "About..." box */
void ShowAbout( HWND hwnd );
/** Number of extensions supported */
int ExtCount();
/** Extension #n (i.e. "3DS") */
const TCHAR* Ext( int n );
/** Long ASCII description (i.e. "Autodesk 3D Studio File") */
const TCHAR* LongDesc();
/** Short ASCII description (i.e. "3D Studio") */
const TCHAR* ShortDesc();
/** ASCII Author name */
const TCHAR* AuthorName();
/** ASCII Copyright message */
const TCHAR* CopyrightMessage();
/** Other message #1 */
const TCHAR* OtherMessage1();
/** Other message #2 */
const TCHAR* OtherMessage2();
/** Version number * 100 (i.e. v3.01 = 301) */
unsigned int Version();
/** Returns TRUE if option supported. */
BOOL SupportsOptions( int ext, DWORD options );
/** Processes the given node to collect mesh, material, and animation data
Recursively collects data for all the node's children as well
*/
int ProcessNode(IGameNode* node, int index);
/**
Processes a single mesh to collect material, vertex, and normal data
*/
void ProcessMesh(IGameNode* node, GasNode* gasNode);
/**
Record animation data at the given time
Stores data in gasNodes[ID]
*/
void GetTreeAnimAt(int ID, int currTime);
/**
Creates list of all children without processing them
(Can collect each node's data again in a second pass
to avoid recomputing tranform data needlessly)
*/
int BuildNodeList(IGameNode* node);
/**
Get the length of the animation
*/
void GetAnimationTime(IGameScene* scene);
private:
IGameScene* m_igame;
ofstream file;
void deinit();
Exporter( const Exporter& );
Exporter& operator=( const Exporter& );
int currTime;
AnimationHeader animHeader;
std::map<std::string, GasMaterial> materialMap; //map of all unique materials in scene
std::vector<GasBatchStr> batches;
std::vector<GasNode*> gasnodes;
std::vector<IGameNode*> gamenodes;
/**
Get the ibo index for the given vert in the batch vertex list
Returns the index if found, -1 if not in the list
*/
int GetListIndex(GasVertex& vert, GasBatchStr& batch);
int GetListIndex(GasSkinVertex& vert, GasBatchStr& batch);
/**
Gets the name of the texture without the filepath
Texture name limited to 64 characters to save space
*/
void GetTextureName(std::string filepath, char* arr);
/**
Get the index of the specified material in the material list
Returns either the index or -1 if not found
*/
int GetMaterialID(std::string materialName);
/**
Gets the node's transformation matrix (without any parent transformations)
*/
GMatrix GetMatrix(IGameNode* node, TimeValue time);
void Transpose(GMatrix& mat);
/**
Get the node index in the node list
Returns the index if found, -1 if not in the list
*/
int GetGlobalNodeIndex(IGameNode* node);
int GetLocalNodeListIndex(IGameNode* node, std::vector<GasBone>& bones);
/**
Finds the 4 bones with the greatest weights and stores them in weights and bones
(last weight not recorded, but can be recomputed as 1 - (otherWeightTotal)
*/
void GetBonesAndWeights(std::vector< std::pair<float, int> >& weightList,
Point3& weights, Point4& bones);
/**
Print out all collected information as a binary file
Data is printed as
VersionNumber
Header:
# of materials, offset to get to first material
# of batches, offset to get to first batch
# of nodes, offset to get to first node
# of animations, offset to get to first animation
All materials printed sequentially
All batched printed sequentially
All nodes printed sequentially
*/
void PrintHeader(); //describes layout of entire file
void PrintMaterials(); //lists all materials
void PrintVertices(); //lists all vertices
void PrintBatches(); //lists all batches
void PrintNodes(); //lists all nodes
void PrintEOF(); //end the file
/**
Helper function to compute how large batches will be when written
as binary data
*/
int ComputeBatchesSize();
/** To reduce the final file size, only record trans/rot/scale info
if there is an animation playing. No need to record matrices for
times when the animation isn't playing
*/
bool ShouldExportTranslations(int nodeIndex);
bool ShouldExportRotations(int nodeIndex);
bool ShouldExportScales(int nodeIndex);
};
#include "stdafx.h"
#include "Exporter.h"
#include "Utils.h"
#include "TextureMapIndexConstants.h"
#include "GasBone.h"
using namespace std;
Exporter::Exporter() :
m_igame( 0 )
{
}
Exporter::~Exporter()
{
while(gasnodes.size() > 0)
{
delete gasnodes[gasnodes.size() - 1];
gasnodes.pop_back();
}
deinit();
}
void Exporter::deinit()
{
if ( m_igame != 0 )
{
m_igame->ReleaseIGame();
m_igame = 0;
}
}
int Exporter::DoExport( const TCHAR* name, ExpInterface* ei, Interface* i, BOOL suppressprompts, DWORD options )
{
try
{
const bool exportselected = 0 != (options & SCENE_EXPORT_SELECTED); // export only selected objects
file.open(name, ios::binary);
if(!file.is_open())
{
throw std::exception( "Failed to open file for writing" );
}
// initialize IGame
m_igame = GetIGameInterface();
if ( !m_igame )
throw std::exception( "Failed to initialize IGame interface" );
// Switch the coordinate system so that Y is up and Z is forward
// to match the target engine's coordinate system
IGameConversionManager* cm = GetConversionManager();
cm->SetCoordSystem(IGameConversionManager::IGAME_OGL);
m_igame->InitialiseIGame( exportselected );
GetAnimationTime(m_igame);
currTime = animHeader.StartTime;
int num = m_igame->GetTopLevelNodeCount();
//get all nodes in scene (in order!)
GasNode* root = new GasNode();
root->ID = 0;
GetCharArrayFromString("", root->name);
gasnodes.push_back(root);
gamenodes.push_back(0);
int ID = 0;
for(int i = 0; i < num; ++i)
{
IGameNode* node = m_igame->GetTopLevelNode(i);
ID = BuildNodeList(node);
root->childIDs.push_back(ID);
}
//get all DATA in the scene (in order!)
int newID = 0;
for(int i = 0; i < num; ++i)
{
IGameNode* node = m_igame->GetTopLevelNode(i);
newID = ProcessNode(node, newID + 1);
}
//get data for time 0 (starting values)
GetTreeAnimAt(0, 0);
//get real animation data
for(int i = animHeader.StartTime; i <= animHeader.EndTime; i += animHeader.FrameRate)
{
GetTreeAnimAt(0, i);
}
// write all info into a binary file for the engine
PrintHeader();
PrintMaterials();
PrintBatches();
PrintNodes();
file.close();
// release initialized stuff
deinit();
}
catch ( std::exception& e )
{
TSTR tstr( e.what() );
MessageBox( i->GetMAXHWnd(), tstr, _T("Export Error"), MB_OK|MB_ICONERROR );
deinit();
}
return TRUE;
}
void Exporter::GetAnimationTime(IGameScene* scene)
{
TimeValue start = scene->GetSceneStartTime();
TimeValue end = scene->GetSceneEndTime();
animHeader.StartTime = int(start);
animHeader.EndTime = int(end);
animHeader.FrameRate = 4800 / 30;//30 fps
}
void Exporter::Transpose(GMatrix& mat)
{
float temp;
for(int i = 1; i < 4; ++i)
{
for(int j = 0; j < i; ++j)
{
temp = mat[i][j];
mat[i][j] = mat[j][i];
mat[j][i] = temp;
}
}
}
GMatrix Exporter::GetMatrix(IGameNode* node, TimeValue time)
{
GMatrix m;
if(node->GetNodeParent() != 0)
m = node->GetWorldTM(time) * node->GetNodeParent()->GetWorldTM(time).Inverse();
else
m = node->GetWorldTM(time);
return m;
}
int Exporter::BuildNodeList(IGameNode* node)
{
GasNode* gasNode = new GasNode();
std::string name = node->GetName();
GetCharArrayFromString(name, gasNode->name);
gasNode->ID = gasnodes.size();
gasnodes.push_back(gasNode);
gamenodes.push_back(node);
int id = gasnodes.size() - 1;
//Process children recursively...
int num = node->GetChildCount();
int ID;
for(int i = 0; i < num; ++i)
{
ID = BuildNodeList(node->GetNodeChild(i));
gasNode->childIDs.push_back(ID);
}
return id;
}
int Exporter::ProcessNode(IGameNode* node, int index)
{
IGameObject* obj = node->GetIGameObject();
GasNode* gasNode = gasnodes[index];
// For this exporter, only interested in meshes
// ignore all other node types
switch(obj->GetIGameType())
{
case IGameObject::IGAME_MESH:
{
ProcessMesh(node, gasNode);
}
break;
}
//Process children recursively...
int num = node->GetChildCount();
for(int i = 0; i < num; ++i)
{
index = ProcessNode(node->GetNodeChild(i), index + 1);
}
return index;
}
void Exporter::GetTreeAnimAt(int ID, int currTime)
{
if(ID != 0)
{
GMatrix mat = GetMatrix(gamenodes[ID], currTime);
gasnodes[ID]->localRotations.push_back(mat.Rotation());
gasnodes[ID]->localScales.push_back(mat.Scaling());
gasnodes[ID]->localTranslations.push_back(mat.Translation());
}
//recurse for all children
for(int i = 0; i < gasnodes[ID]->childIDs.size(); ++i)
{
GetTreeAnimAt( gasnodes[ID]->childIDs[i], currTime );
}
}
void Exporter::ProcessMesh(IGameNode* node, GasNode* gasnode)
{
map<string, IGameMaterial*> mats;
map<string, IGameMaterial*>::iterator it;
IGameObject* obj = node->GetIGameObject();
IGameMesh* mesh = reinterpret_cast<IGameMesh*>(obj);
mesh->InitializeData();
IGameModifier* modifier = 0;
IGameSkin* skin = 0;
bool foundSkinning = false;
int numberOfModifiers = mesh->GetNumModifiers();
for(int i = 0; i < numberOfModifiers; ++i)
{
modifier = mesh->GetIGameModifier(i);
if(modifier != 0 && modifier->IsSkin())
{
foundSkinning = true;
skin = (IGameSkin*) modifier;
break;
}
}
gasnode->isSkinned = foundSkinning;
// Get all materials used in this mesh
// For each material, store all the faces that use that material together
for(int i = 0; i < mesh->GetNumberOfFaces(); ++i)
{
IGameMaterial* material = mesh->GetMaterialFromFace(i);
// Get the texture associated with this material
if(material != 0)
{
std::string name = material->GetMaterialName();
mats.insert(pair<string, IGameMaterial*>(name, material));
char texname[64];
memset(texname, 0, sizeof(char) * 64);
int numtextures = material->GetNumberOfTextureMaps();
IGameTextureMap* texmaptest = 0;
IGameTextureMap* texmap = 0;
// Only interested in the diffuse texture
for(int i = 0; i < numtextures; ++i)
{
texmaptest = material->GetIGameTextureMap(i);
if(texmaptest != 0 && texmaptest->GetStdMapSlot() == ID_DI)
{
texmap = material->GetIGameTextureMap(i);
break;
}
else if(i == numtextures - 1)
texmap = 0;
}
if(texmap != 0)
GetTextureName(texmap->GetBitmapFileName(), texname);
//Store the name of the texture in the material
char matname[64];
memset(matname, 0, (sizeof(char) * 64));
GetCharArrayFromString(name, matname);
materialMap.insert( pair<string, GasMaterial>(name, GasMaterial(matname, texname)) );
}
else
{ //use a default material instead
mats.insert(pair<string, IGameMaterial*>("", nullptr));
materialMap.insert( pair<string, GasMaterial>("", GasMaterial("", "")) );
}
}
Point3 pos, normal, weights;
Point2 tex;
Point4 bones;
// For each material, get the pos/texcoord/normal info
// Also collect any animation data (bones and weights)
for(it = mats.begin(); it != mats.end(); it++)
{
GasBatchStr batch = GasBatchStr((*it).first);
batch.isSkinned = foundSkinning;
for(int i = 0; i < mesh->GetNumberOfFaces(); ++i)
{
FaceEx* faceex = mesh->GetFace(i);
string materialName = "";
IGameMaterial* tempmat = mesh->GetMaterialFromFace(i);
if(tempmat != 0)
materialName = tempmat->GetMaterialName();
//worldTM = model to world
//world-1 = world to model(local)
GMatrix minv = node->GetWorldTM().Inverse();
//same for normal, but clear out the translation component
GMatrix normalinv = node->GetWorldTM().Inverse();
Point4 row = Point4(0,0,0,1);
normalinv.SetRow(3, row);
if(materialName == (*it).first)
{
for(int j = 0; j < 3; ++j)
{
//convert from world space to local space!
pos = mesh->GetVertex(faceex->vert[j]) * minv;
tex = mesh->GetTexVertex(faceex->texCoord[j]);
normal = (mesh->GetNormal(faceex->norm[j]) * normalinv);
if(foundSkinning)
{
int numBones = skin->GetNumberOfBones(faceex->vert[j]);
vector< pair<float, int> > weightList;
//add bones to list and get weights for each bone
for(int i = 0; i < numBones; ++i)
{
IGameNode* bone = skin->GetIGameBone(faceex->vert[j],i);
int localindex = GetLocalNodeListIndex(bone, gasnode->bones);
int globalindex = 0;
if(localindex == -1)
{
localindex = gasnode->bones.size();
//compute transform and add it to the list
globalindex = GetGlobalNodeIndex(bone);
GasBone newBone = GasBone( bone->GetName(), globalindex );
newBone.TM = node->GetWorldTM(0) * bone->GetWorldTM(0).Inverse();
gasnode->bones.push_back( newBone );
}
else
{
globalindex = GetGlobalNodeIndex(bone);
}
weightList.push_back( pair<float, int>(skin->GetWeight(faceex->vert[j], i), localindex) );
}
//determine which weights to use (and therefore which bones)
GetBonesAndWeights(weightList, weights, bones);
}
int index = 0;
GasVertex vert = GasVertex(pos, tex, normal);
GasSkinVertex vertex = GasSkinVertex(pos, tex, normal, bones, weights);
// Since skinned and unskinned meshes are handled seperately in the engine,
// Store the animation data in the correct list
if( !foundSkinning )
index = GetListIndex(vert, batch);
else
index = GetListIndex(vertex, batch);
if(index == -1)
{
if(!foundSkinning)
batch.indices.push_back(unsigned int(batch.vertices.size()));
else
batch.indices.push_back(unsigned int(batch.skinnedVertices.size()));
if(!foundSkinning)
batch.vertices.push_back(vert);
else
batch.skinnedVertices.push_back(vertex);
}
else
{
batch.indices.push_back(index);
}
}
}
}
gasnode->batchIDs.push_back(batches.size());
batches.push_back(batch);
}
}
void Exporter::ShowAbout( HWND hwnd )
{
}
int Exporter::ExtCount()
{
return 1;
}
const TCHAR* Exporter::Ext( int /*n*/ )
{
return _T("gas");
}
const TCHAR* Exporter::LongDesc()
{
return _T("3dsmax Exporter");
}
const TCHAR* Exporter::ShortDesc()
{
return _T("Exporter");
}
const TCHAR* Exporter::AuthorName()
{
return _T("Elizabeth Labelle");
}
const TCHAR* Exporter::CopyrightMessage()
{
return _T("Copyright (C) 2012");
}
const TCHAR* Exporter::OtherMessage1()
{
return _T("");
}
const TCHAR* Exporter::OtherMessage2()
{
return _T("");
}
unsigned int Exporter::Version()
{
return 1;
}
BOOL Exporter::SupportsOptions( int ext, DWORD options )
{
return TRUE;
}
int Exporter::GetMaterialID(std::string materialName)
{
map<std::string, GasMaterial>::iterator it;
int index = 0;
for(it = materialMap.begin(); it != materialMap.end(); it++)
{
if((*it).first == materialName)
return index;
index++;
}
return -1;
}
int Exporter::GetListIndex(GasVertex& vert, GasBatchStr& batch)
{
for(int i = 0; i < batch.vertices.size(); ++i)
{
if(batch.vertices[i] == vert)
return i;
}
return -1;
}
int Exporter::GetListIndex(GasSkinVertex& vert, GasBatchStr& batch)
{
for(int i = 0; i < batch.skinnedVertices.size(); ++i)
{
if(batch.skinnedVertices[i] == vert)
return i;
}
return -1;
}
void Exporter::GetTextureName(std::string filepath, char* arr)
{
int index = int(filepath.find_last_of('\\'));
if(index >= 0)
filepath = filepath.substr(index + 1);
GetCharArrayFromString(filepath, arr);
}
int Exporter::ComputeBatchesSize()
{
int size = 0;
for(int i = 0; i < batches.size(); ++i)
{
size += batches[i].GetSize();
}
return size;
}
void Exporter::PrintHeader()
{
float versionNum = 2.0f;
file.write(reinterpret_cast<char*>(&versionNum), sizeof(float));
int matOffset = int(sizeof(float) + sizeof(MatHeader) + sizeof(BatchHeader)
+ sizeof(NodeHeader) + sizeof(AnimationHeader));
int batchOffset = matOffset + int(sizeof(GasMaterial) * materialMap.size());
int nodeOffset = batchOffset + ComputeBatchesSize();
MatHeader mat = MatHeader(int(materialMap.size()), matOffset);
BatchHeader batch = BatchHeader(int(batches.size()), batchOffset);
NodeHeader node = NodeHeader(int(gasnodes.size()), nodeOffset);
file.write(reinterpret_cast<char*>(&mat), sizeof(MatHeader));
file.write(reinterpret_cast<char*>(&batch), sizeof(BatchHeader));
file.write(reinterpret_cast<char*>(&node), sizeof(NodeHeader));
file.write(reinterpret_cast<char*>(&animHeader), sizeof(AnimationHeader));
}
void Exporter::PrintMaterials()
{
std::map<std::string, GasMaterial>::iterator it;
for(it = materialMap.begin(); it != materialMap.end(); ++it)
{
GasMaterial gasmat = (*it).second;
file.write(reinterpret_cast<char*>(&gasmat), sizeof(gasmat));
}
}
void Exporter::PrintBatches()
{
for(int i = 0; i < batches.size(); ++i)
{
int numVertices = 0;
if(batches[i].isSkinned)
numVertices = int(batches[i].skinnedVertices.size());
else
numVertices = int(batches[i].vertices.size());
int numIndices = int(batches[i].indices.size());
unsigned int matID = unsigned int(GetMaterialID(batches[i].matname));
file.write(reinterpret_cast<char*>(&numVertices), sizeof(int));
file.write(reinterpret_cast<char*>(&numIndices), sizeof(int));
file.write(reinterpret_cast<char*>(&matID), sizeof(unsigned int));
file.write(reinterpret_cast<char*>(&(batches[i].isSkinned)), sizeof(bool));
if(batches[i].isSkinned)
{
for(int j = 0; j < batches[i].skinnedVertices.size(); ++j)
{
GasSkinVertex gasskinvert = batches[i].skinnedVertices[j];
file.write(reinterpret_cast<char*>(&gasskinvert), sizeof(GasSkinVertex));
int size = sizeof(GasSkinVertex);
}
}
else
{
for(int j = 0; j < batches[i].vertices.size(); ++j)
{
GasVertex gasvert = batches[i].vertices[j];
file.write(reinterpret_cast<char*>(&gasvert), sizeof(GasVertex));
}
}
for(int j = 0; j < batches[i].indices.size(); ++j)
{
unsigned int index = batches[i].indices[j];
file.write(reinterpret_cast<char*>(&index), sizeof(unsigned int));
}
}
}
void Exporter::PrintNodes()
{
for(int i = 0; i < gasnodes.size(); ++i)
{
int numBatches = int(gasnodes[i]->batchIDs.size());
int numChildren = int(gasnodes[i]->childIDs.size());
int numBones = int(gasnodes[i]->bones.size());
unsigned int ID = gasnodes[i]->ID;
bool isSkinned = gasnodes[i]->isSkinned;
file.write(reinterpret_cast<char*>(&(gasnodes[i]->name[0])), sizeof(char) * 64);
file.write(reinterpret_cast<char*>(&numBatches), sizeof(unsigned int));
file.write(reinterpret_cast<char*>(&numChildren), sizeof(unsigned int));
file.write(reinterpret_cast<char*>(&numBones), sizeof(int));
file.write(reinterpret_cast<char*>(&ID), sizeof(unsigned int));
file.write(reinterpret_cast<char*>(&isSkinned), sizeof(bool));
for(int j = 0; j < gasnodes[i]->batchIDs.size(); ++j)
file.write(reinterpret_cast<char*>(&(gasnodes[i]->batchIDs[j])), sizeof(unsigned int));
for(int j = 0; j < gasnodes[i]->childIDs.size(); ++j)
file.write(reinterpret_cast<char*>(&(gasnodes[i]->childIDs[j])), sizeof(unsigned int));
//bones
for(int j = 0; j < gasnodes[i]->bones.size(); ++j)
{
file.write(reinterpret_cast<char*>(&(gasnodes[i]->bones[j].ID)), sizeof(unsigned int));
file.write(reinterpret_cast<char*>(&(gasnodes[i]->bones[j].TM)), sizeof(GMatrix));
}
if(i == 0)
{
for(int j = 0; j < gasnodes[i]->localTranslations.size(); ++j)
{
Quat r = gasnodes[i]->localRotations[j];
Point3 s = gasnodes[i]->localScales[j];
Point3 t = gasnodes[i]->localTranslations[j];
file.write(reinterpret_cast<char*>(&r), sizeof(Quat));
file.write(reinterpret_cast<char*>(&t), sizeof(Point3));
file.write(reinterpret_cast<char*>(&s), sizeof(Point3));
}
}
else
{
bool hasRotations = ShouldExportRotations(i);
file.write(reinterpret_cast<char*>(&hasRotations), sizeof(bool));
for(int j = 0; j < gasnodes[i]->localRotations.size(); ++j)
{
Quat r = gasnodes[i]->localRotations[j];
file.write(reinterpret_cast<char*>(&r), sizeof(Quat));
}
bool hasTranslations = ShouldExportTranslations(i);
file.write(reinterpret_cast<char*>(&hasTranslations), sizeof(bool));
for(int j = 0; j < gasnodes[i]->localTranslations.size(); ++j)
{
Point3 t = gasnodes[i]->localTranslations[j];
file.write(reinterpret_cast<char*>(&t), sizeof(Point3));
}
bool hasScales = ShouldExportScales(i);
file.write(reinterpret_cast<char*>(&hasScales), sizeof(bool));
for(int j = 0; j < gasnodes[i]->localScales.size(); ++j)
{
Point3 s = gasnodes[i]->localScales[j];
file.write(reinterpret_cast<char*>(&s), sizeof(Point3));
}
}
}
}
void Exporter::PrintEOF()
{
file << EOF;
}
int Exporter::GetGlobalNodeIndex(IGameNode* node)
{
string name = node->GetName();
for(int i = 0; i < gasnodes.size(); ++i)
{
string name2 = gasnodes[i]->name;
if(name == name2)
return i;
}
return -1;
}
int Exporter::GetLocalNodeListIndex(IGameNode* node, std::vector<GasBone>& bones)
{
string name = node->GetName();
for(int i = 0; i < bones.size(); ++i)
{
string name2 = bones[i].name;
if(name == name2)
return i;
}
return -1;
}
bool mySort(pair<float, int> a, pair<float, int> b)
{
return a.first > b.first;
}
void Exporter::GetBonesAndWeights(std::vector< std::pair<float, int> >& weightList, Point3& weights, Point4& bones)
{
sort(weightList.begin(), weightList.end(), mySort);
weights = Point3(0,0,0);
bones = Point4(0,0,0,0);
//first 4 in list are highest
for(int i = 0; i < 4; ++i)
{
if(weightList.size() > i)
{
if(i != 3)
weights[i] = weightList[i].first;
bones[i] = weightList[i].second;
}
else
break;
}
}
bool Exporter::ShouldExportTranslations(int nodeIndex)
{
GasNode* n = gasnodes[nodeIndex];
for(int i = 0; i < n->localTranslations.size() - 1; ++i)
{
if(n->localTranslations[i] != n->localTranslations[i + 1])
return true;
}
n->localTranslations.resize(1);
return false;
}
bool Exporter::ShouldExportRotations(int nodeIndex)
{
GasNode* n = gasnodes[nodeIndex];
for(int i = 0; i < n->localRotations.size() - 1; ++i)
{
for(int j = 0; j < 4; ++j)
{
if((n->localRotations[i])[j] != (n->localRotations[i + 1])[j])
return true;
}
}
n->localRotations.resize(1);
return false;
}
bool Exporter::ShouldExportScales(int nodeIndex)
{
GasNode* n = gasnodes[nodeIndex];
for(int i = 0; i < n->localScales.size() - 1; ++i)
{
if(n->localScales[i] != n->localScales[i + 1])
return true;
}
n->localScales.resize(1);
return false;
}
float4x4 worldMatrixArray[100]:WORLDMATARRAY;//boneTM array for each mesh
float4x4 vp:VIEWPROJECTION;
float3 freeLight:FREELIGHT;
Texture2D tex;
SamplerState defss
{
AddressU = Wrap;
AddressV = Wrap;
};
void MyVS(float3 pos:apos, float2 tc:atexcoord,
float3 normal:N,
unsigned int4 bones:abones, float3 weight:aweights,
out float4 opos:SV_POSITION, out float2 otc:TEX,
out float3 onormal:NORMAL, out float3 olight:LIGHT)
{
opos = float4(0,0,0,0);
onormal = float4(0,0,0,0);
int numBones = 4;
int iBone = 0;
float sumWeights = 1.0f;
for(;iBone < numBones - 1; iBone++)
{
sumWeights -= weight[iBone];
opos += mul( float4(pos, 1), worldMatrixArray[ bones[iBone] ]) * weight[iBone];
onormal += mul(normal, (float3x3)worldMatrixArray[ bones[iBone] ]) * weight[iBone];
}
opos = mul(opos, vp);
otc = tc;
olight = normalize(freeLight - pos);
}
float4 MyPS(float4 pos:SV_POSITION, float2 tc:TEX,
float3 normal:NORMAL, float3 light:LIGHT):SV_Target
{
float3 normal2 = normalize(normal);
float3 light2 = normalize(light);
float Idiff = max( dot(normal2, light2), 0.1);
return tex.Sample(defss, tc) * Idiff;
}
technique11 MyTechnique
{
pass P0
{
SetVertexShader(CompileShader(vs_4_0, MyVS()));
SetPixelShader(CompileShader(ps_4_0, MyPS()));
SetGeometryShader(NULL);
}
}