mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-29 00:59:39 +00:00
commit
a1097fbd13
16 changed files with 807 additions and 134 deletions
|
|
@ -405,8 +405,10 @@ inline void Buffer::Read(Value& obj, Asset& r)
|
|||
inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseOffset)
|
||||
{
|
||||
byteLength = length ? length : stream.FileSize();
|
||||
//T3D_CHANGE_BEGIN
|
||||
if ((byteLength + baseOffset) > stream.FileSize())
|
||||
byteLength = stream.FileSize() - baseOffset;
|
||||
//T3D_CHANGE_END
|
||||
|
||||
if (baseOffset) {
|
||||
stream.Seek(baseOffset, aiOrigin_SET);
|
||||
|
|
|
|||
|
|
@ -822,8 +822,6 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
|
|||
if (node.skin) {
|
||||
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
|
||||
aiMesh* mesh = pScene->mMeshes[meshOffsets[mesh_idx]+primitiveNo];
|
||||
mesh->mNumBones = static_cast<unsigned int>(node.skin->jointNames.size());
|
||||
mesh->mBones = new aiBone*[mesh->mNumBones];
|
||||
|
||||
// GLTF and Assimp choose to store bone weights differently.
|
||||
// GLTF has each vertex specify which bones influence the vertex.
|
||||
|
|
@ -834,39 +832,98 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
|
|||
// both because it's somewhat slow and because, for many applications,
|
||||
// we then need to reconvert the data back into the vertex-to-bone
|
||||
// mapping which makes things doubly-slow.
|
||||
std::vector<std::vector<aiVertexWeight>> weighting(mesh->mNumBones);
|
||||
|
||||
//T3D_CHANGE_BEGIN
|
||||
// The following commented block has been completely replaced.
|
||||
// Portions of the replacement code block have been taken from:
|
||||
// https://github.com/ConfettiFX/The-Forge/blob/master/Common_3/ThirdParty/OpenSource/assimp/4.1.0/code/glTF2Importer.cpp#L823-L860
|
||||
//std::vector<std::vector<aiVertexWeight>> weighting(mesh->mNumBones);
|
||||
//BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
|
||||
|
||||
//for (uint32_t i = 0; i < mesh->mNumBones; ++i) {
|
||||
// aiBone* bone = new aiBone();
|
||||
|
||||
// Ref<Node> joint = node.skin->jointNames[i];
|
||||
// if (!joint->name.empty()) {
|
||||
// bone->mName = joint->name;
|
||||
// } else {
|
||||
// // Assimp expects each bone to have a unique name.
|
||||
// static const std::string kDefaultName = "bone_";
|
||||
// char postfix[10] = {0};
|
||||
// ASSIMP_itoa10(postfix, i);
|
||||
// bone->mName = (kDefaultName + postfix);
|
||||
// }
|
||||
// GetNodeTransform(bone->mOffsetMatrix, *joint);
|
||||
|
||||
// std::vector<aiVertexWeight>& weights = weighting[i];
|
||||
|
||||
// bone->mNumWeights = static_cast<uint32_t>(weights.size());
|
||||
// if (bone->mNumWeights > 0) {
|
||||
// bone->mWeights = new aiVertexWeight[bone->mNumWeights];
|
||||
// memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight));
|
||||
// } else {
|
||||
// // Assimp expects all bones to have at least 1 weight.
|
||||
// bone->mWeights = new aiVertexWeight[1];
|
||||
// bone->mNumWeights = 1;
|
||||
// bone->mWeights->mVertexId = 0;
|
||||
// bone->mWeights->mWeight = 0.f;
|
||||
// }
|
||||
// mesh->mBones[i] = bone;
|
||||
|
||||
std::vector<std::vector<aiVertexWeight>> weighting(node.skin->jointNames.size());
|
||||
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
|
||||
|
||||
for (uint32_t i = 0; i < mesh->mNumBones; ++i) {
|
||||
aiBone* bone = new aiBone();
|
||||
|
||||
Ref<Node> joint = node.skin->jointNames[i];
|
||||
if (!joint->name.empty()) {
|
||||
bone->mName = joint->name;
|
||||
} else {
|
||||
// Assimp expects each bone to have a unique name.
|
||||
static const std::string kDefaultName = "bone_";
|
||||
char postfix[10] = {0};
|
||||
ASSIMP_itoa10(postfix, i);
|
||||
bone->mName = (kDefaultName + postfix);
|
||||
}
|
||||
GetNodeTransform(bone->mOffsetMatrix, *joint);
|
||||
|
||||
std::vector<aiVertexWeight>& weights = weighting[i];
|
||||
|
||||
bone->mNumWeights = static_cast<uint32_t>(weights.size());
|
||||
if (bone->mNumWeights > 0) {
|
||||
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
|
||||
memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight));
|
||||
} else {
|
||||
// Assimp expects all bones to have at least 1 weight.
|
||||
bone->mWeights = new aiVertexWeight[1];
|
||||
bone->mNumWeights = 1;
|
||||
bone->mWeights->mVertexId = 0;
|
||||
bone->mWeights->mWeight = 0.f;
|
||||
}
|
||||
mesh->mBones[i] = bone;
|
||||
// CONFFX_BEGIN
|
||||
// Assimp doesn't support bones with no weight. We have to count the
|
||||
// number of bones that affect the mesh and limit it to just those bones.
|
||||
int numBones = 0;
|
||||
for (size_t i = 0; i < node.skin->jointNames.size(); ++i) {
|
||||
if (!weighting[i].empty())
|
||||
++numBones;
|
||||
}
|
||||
|
||||
mesh->mNumBones = numBones;
|
||||
if (numBones > 0)
|
||||
{
|
||||
mesh->mBones = new aiBone*[mesh->mNumBones];
|
||||
|
||||
int j = 0;
|
||||
for (size_t i = 0; i < node.skin->jointNames.size(); ++i) {
|
||||
if (!weighting[i].empty())
|
||||
{
|
||||
aiBone* bone = new aiBone();
|
||||
|
||||
Ref<Node> joint = node.skin->jointNames[i];
|
||||
bone->mName = joint->name.empty() ? joint->id : joint->name;
|
||||
|
||||
// Get the inverseBindMatrix for the joint, grab the position out of row 4,
|
||||
// invert the matrix and put the position back as column 4.
|
||||
aiMatrix4x4 *tmpMat;
|
||||
uint8_t *matPtr = node.skin->inverseBindMatrices->GetPointer();
|
||||
tmpMat = (aiMatrix4x4*)matPtr;
|
||||
bone->mOffsetMatrix = tmpMat[i];
|
||||
aiVector3D tmpPos(bone->mOffsetMatrix.d1, bone->mOffsetMatrix.d2, bone->mOffsetMatrix.d3);
|
||||
bone->mOffsetMatrix.d1 = bone->mOffsetMatrix.d2 = bone->mOffsetMatrix.d3 = 0.0;
|
||||
bone->mOffsetMatrix.Inverse();
|
||||
bone->mOffsetMatrix.a4 = tmpPos.x;
|
||||
bone->mOffsetMatrix.b4 = tmpPos.y;
|
||||
bone->mOffsetMatrix.c4 = tmpPos.z;
|
||||
|
||||
std::vector<aiVertexWeight>& weights = weighting[i];
|
||||
|
||||
bone->mNumWeights = static_cast<uint32_t>(weights.size());
|
||||
if (bone->mNumWeights > 0) {
|
||||
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
|
||||
memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight));
|
||||
}
|
||||
mesh->mBones[j++] = bone;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
mesh->mBones = nullptr;
|
||||
// CONFFX_END
|
||||
//T3D_CHANGE_END
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -921,7 +978,10 @@ struct AnimationSamplers {
|
|||
aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers)
|
||||
{
|
||||
aiNodeAnim* anim = new aiNodeAnim();
|
||||
anim->mNodeName = node.name;
|
||||
//T3D_CHANGE_BEGIN
|
||||
//anim->mNodeName = node.name;
|
||||
anim->mNodeName = node.name.empty() ? node.id : node.name;
|
||||
//T3D_CHANGE_END
|
||||
|
||||
static const float kMillisecondsFromSeconds = 1000.f;
|
||||
|
||||
|
|
@ -1042,15 +1102,27 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
|
|||
|
||||
std::unordered_map<unsigned int, AnimationSamplers> samplers = GatherSamplers(anim);
|
||||
|
||||
ai_anim->mNumChannels = static_cast<uint32_t>(samplers.size());
|
||||
//T3D_CHANGE_BEGIN
|
||||
//ai_anim->mNumChannels = static_cast<uint32_t>(samplers.size());
|
||||
//if (ai_anim->mNumChannels > 0) {
|
||||
// ai_anim->mChannels = new aiNodeAnim*[ai_anim->mNumChannels];
|
||||
// int j = 0;
|
||||
// for (auto& iter : samplers) {
|
||||
// ai_anim->mChannels[j] = CreateNodeAnim(r, r.nodes[iter.first], iter.second);
|
||||
// ++j;
|
||||
// }
|
||||
//}
|
||||
|
||||
ai_anim->mNumChannels = r.skins.Size() > 0 ? r.skins[0].jointNames.size() : 0;
|
||||
if (ai_anim->mNumChannels > 0) {
|
||||
ai_anim->mChannels = new aiNodeAnim*[ai_anim->mNumChannels];
|
||||
int j = 0;
|
||||
for (auto& iter : samplers) {
|
||||
ai_anim->mChannels[j] = CreateNodeAnim(r, r.nodes[iter.first], iter.second);
|
||||
for (auto& iter : r.skins[0].jointNames) {
|
||||
ai_anim->mChannels[j] = CreateNodeAnim(r, *iter, samplers[iter.GetIndex()]);
|
||||
++j;
|
||||
}
|
||||
}
|
||||
//T3D_CHANGE_END
|
||||
|
||||
// Use the latest keyframe for the duration of the animation
|
||||
double maxDuration = 0;
|
||||
|
|
|
|||
|
|
@ -345,8 +345,10 @@ inline void Buffer::Read(Value& obj, Asset& r)
|
|||
inline bool Buffer::LoadFromStream(IOStream& stream, size_t length, size_t baseOffset)
|
||||
{
|
||||
byteLength = length ? length : stream.FileSize();
|
||||
//T3D_CHANGE_BEGIN
|
||||
if ((byteLength + baseOffset) > stream.FileSize())
|
||||
byteLength = stream.FileSize() - baseOffset;
|
||||
//T3D_CHANGE_END
|
||||
|
||||
if (baseOffset) {
|
||||
stream.Seek(baseOffset, aiOrigin_SET);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//-----------------------------------------------------------------------------
|
||||
//#define TORQUE_PBR_MATERIALS
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "ts/loader/appSequence.h"
|
||||
|
|
@ -38,7 +39,7 @@ String AppMaterial::cleanString(const String& str)
|
|||
String cleanStr(str);
|
||||
|
||||
// Replace invalid characters with underscores
|
||||
const String badChars(" -,.+=*/");
|
||||
const String badChars(" -,.+=*/[]%$~;:");
|
||||
for (String::SizeType i = 0; i < badChars.length(); i++)
|
||||
cleanStr.replace(badChars[i], '_');
|
||||
|
||||
|
|
@ -52,50 +53,27 @@ String AppMaterial::cleanString(const String& str)
|
|||
AssimpAppMaterial::AssimpAppMaterial(const char* matName)
|
||||
{
|
||||
name = matName;
|
||||
diffuseColor = LinearColorF::ONE;
|
||||
specularColor = LinearColorF::ONE;
|
||||
specularPower = 0.8f;
|
||||
doubleSided = false;
|
||||
|
||||
// Set some defaults
|
||||
flags |= TSMaterialList::S_Wrap;
|
||||
flags |= TSMaterialList::T_Wrap;
|
||||
}
|
||||
|
||||
AssimpAppMaterial::AssimpAppMaterial(const struct aiMaterial* mtl)
|
||||
AssimpAppMaterial::AssimpAppMaterial(aiMaterial* mtl) :
|
||||
mAIMat(mtl)
|
||||
{
|
||||
aiString matName;
|
||||
mtl->Get(AI_MATKEY_NAME, matName);
|
||||
name = matName.C_Str();
|
||||
if ( name.isEmpty() )
|
||||
name = "defaultMaterial";
|
||||
Con::printf("[ASSIMP] Loaded Material: %s", matName.C_Str());
|
||||
|
||||
// Opacity
|
||||
F32 opacity = 0.0f;
|
||||
mtl->Get(AI_MATKEY_OPACITY, opacity);
|
||||
|
||||
// Diffuse color
|
||||
aiColor3D diff_color (0.f, 0.f, 0.f);
|
||||
mtl->Get(AI_MATKEY_COLOR_DIFFUSE, diff_color);
|
||||
diffuseColor = LinearColorF(diff_color.r, diff_color.g, diff_color.b, opacity);
|
||||
|
||||
// Spec Color color
|
||||
aiColor3D spec_color (0.f, 0.f, 0.f);
|
||||
mtl->Get(AI_MATKEY_COLOR_DIFFUSE, spec_color );
|
||||
specularColor = LinearColorF(spec_color.r, spec_color.g, spec_color.b, 1.0f);
|
||||
|
||||
// Specular Power
|
||||
mtl->Get(AI_MATKEY_SHININESS_STRENGTH, specularPower);
|
||||
|
||||
// Double-Sided
|
||||
S32 dbl_sided = 0;
|
||||
mtl->Get(AI_MATKEY_TWOSIDED, dbl_sided);
|
||||
doubleSided = (dbl_sided != 0);
|
||||
|
||||
// Set some defaults
|
||||
flags |= TSMaterialList::S_Wrap;
|
||||
flags |= TSMaterialList::T_Wrap;
|
||||
if (name.isEmpty())
|
||||
{
|
||||
name = cleanString(TSShapeLoader::getShapePath().getFileName());;
|
||||
name += "_defMat";
|
||||
}
|
||||
Con::printf("[ASSIMP] Loading Material: %s", name.c_str());
|
||||
#ifdef TORQUE_DEBUG
|
||||
enumerateMaterialProperties(mtl);
|
||||
#endif
|
||||
}
|
||||
|
||||
Material* AssimpAppMaterial::createMaterial(const Torque::Path& path) const
|
||||
|
|
@ -105,34 +83,264 @@ Material* AssimpAppMaterial::createMaterial(const Torque::Path& path) const
|
|||
String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
|
||||
String cleanName = cleanString(getName());
|
||||
|
||||
// Prefix the material name with the filename (if not done already by TSShapeConstructor prefix)
|
||||
//if (!cleanName.startsWith(cleanFile))
|
||||
// cleanName = cleanFile + "_" + cleanName;
|
||||
|
||||
// Determine the blend operation for this material
|
||||
Material::BlendOp blendOp = (flags & TSMaterialList::Translucent) ? Material::LerpAlpha : Material::None;
|
||||
if (flags & TSMaterialList::Additive)
|
||||
blendOp = Material::Add;
|
||||
else if (flags & TSMaterialList::Subtractive)
|
||||
blendOp = Material::Sub;
|
||||
|
||||
// Create the Material definition
|
||||
const String oldScriptFile = Con::getVariable("$Con::File");
|
||||
Con::setVariable("$Con::File", path.getFullPath()); // modify current script path so texture lookups are correct
|
||||
Material *newMat = MATMGR->allocateAndRegister( cleanName, getName() );
|
||||
Material *newMat = MATMGR->allocateAndRegister(cleanName, getName());
|
||||
Con::setVariable("$Con::File", oldScriptFile); // restore script path
|
||||
|
||||
newMat->mDiffuseMapFilename[0] = "";
|
||||
newMat->mNormalMapFilename[0] = "";
|
||||
newMat->mSpecularMapFilename[0] = "";
|
||||
|
||||
newMat->mDiffuse[0] = diffuseColor;
|
||||
//newMat->mSpecular[0] = specularColor;
|
||||
//newMat->mSpecularPower[0] = specularPower;
|
||||
|
||||
newMat->mDoubleSided = doubleSided;
|
||||
newMat->mTranslucent = (bool)(flags & TSMaterialList::Translucent);
|
||||
newMat->mTranslucentBlendOp = blendOp;
|
||||
initMaterial(path, newMat);
|
||||
|
||||
return newMat;
|
||||
}
|
||||
}
|
||||
|
||||
void AssimpAppMaterial::initMaterial(const Torque::Path& path, Material* mat) const
|
||||
{
|
||||
String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName());
|
||||
String cleanName = cleanString(getName());
|
||||
|
||||
// Determine the blend mode and transparency for this material
|
||||
Material::BlendOp blendOp = Material::None;
|
||||
bool translucent = false;
|
||||
float opacity = 1.0f;
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_OPACITY, opacity))
|
||||
{
|
||||
if (opacity != 1.0f)
|
||||
{
|
||||
translucent = true;
|
||||
int blendInt;
|
||||
blendOp = Material::LerpAlpha;
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_BLEND_FUNC, blendInt))
|
||||
{
|
||||
if (blendInt == aiBlendMode_Additive)
|
||||
blendOp = Material::Add;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // No opacity key, see if it's defined as a gltf property
|
||||
aiString opacityMode;
|
||||
if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaMode", 0, 0, opacityMode))
|
||||
{
|
||||
if (dStrcmp("MASK", opacityMode.C_Str()) == 0)
|
||||
{
|
||||
translucent = true;
|
||||
blendOp = Material::LerpAlpha;
|
||||
|
||||
float cutoff;
|
||||
if (AI_SUCCESS == mAIMat->Get("$mat.gltf.alphaCutoff", 0, 0, cutoff))
|
||||
{
|
||||
mat->mAlphaRef = (U32)(cutoff * 255); // alpha ref 0-255
|
||||
mat->mAlphaTest = true;
|
||||
}
|
||||
}
|
||||
else if (dStrcmp("OPAQUE", opacityMode.C_Str()) != 0)
|
||||
{
|
||||
translucent = true;
|
||||
blendOp = Material::LerpAlpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
mat->mTranslucent = translucent;
|
||||
mat->mTranslucentBlendOp = blendOp;
|
||||
|
||||
// Assign color values.
|
||||
LinearColorF diffuseColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
aiColor3D read_color(1.f, 1.f, 1.f);
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_DIFFUSE, read_color))
|
||||
diffuseColor.set(read_color.r, read_color.g, read_color.b, opacity);
|
||||
mat->mDiffuse[0] = diffuseColor;
|
||||
|
||||
aiString texName;
|
||||
String torquePath;
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), texName))
|
||||
{
|
||||
torquePath = texName.C_Str();
|
||||
if (!torquePath.isEmpty())
|
||||
mat->mDiffuseMapFilename[0] = cleanTextureName(torquePath, cleanFile);
|
||||
}
|
||||
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texName))
|
||||
{
|
||||
torquePath = texName.C_Str();
|
||||
if (!torquePath.isEmpty())
|
||||
mat->mNormalMapFilename[0] = cleanTextureName(torquePath, cleanFile);
|
||||
}
|
||||
|
||||
#ifdef TORQUE_PBR_MATERIALS
|
||||
float floatVal;
|
||||
if (AI_SUCCESS == mAIMat->Get("$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0, floatVal))
|
||||
{ // The shape has pbr material definitions
|
||||
String aoName, rmName; // occlusion and roughness/metalness maps
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0), texName))
|
||||
aoName = texName.C_Str();
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_UNKNOWN, 0), texName))
|
||||
rmName = texName.C_Str();
|
||||
|
||||
//if (aoName.isNotEmpty() && (aoName == rmName))
|
||||
// mat->mOrmMapFilename[0] = cleanTextureName(aoName, cleanFile); // It's an ORM map
|
||||
//else if (aoName.isNotEmpty() || rmName.isNotEmpty())
|
||||
if (aoName.isNotEmpty() || rmName.isNotEmpty())
|
||||
{ // If we have either map, fill all three slots
|
||||
if (rmName.isNotEmpty())
|
||||
{
|
||||
mat->mRoughMapFilename[0] = cleanTextureName(rmName, cleanFile); // Roughness
|
||||
mat->mSmoothnessChan[0] = 1.0f;
|
||||
mat->mInvertSmoothness = (floatVal == 1.0f);
|
||||
mat->mMetalMapFilename[0] = cleanTextureName(rmName, cleanFile); // Metallic
|
||||
mat->mMetalChan[0] = 2.0f;
|
||||
}
|
||||
if (aoName.isNotEmpty())
|
||||
{
|
||||
mat->mAOMapFilename[0] = cleanTextureName(aoName, cleanFile); // occlusion
|
||||
mat->mAOChan[0] = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
mat->mAOMapFilename[0] = cleanTextureName(rmName, cleanFile); // occlusion
|
||||
mat->mAOChan[0] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, 0), texName))
|
||||
{
|
||||
torquePath = texName.C_Str();
|
||||
if (!torquePath.isEmpty())
|
||||
mat->mSpecularMapFilename[0] = cleanTextureName(torquePath, cleanFile);
|
||||
}
|
||||
|
||||
LinearColorF specularColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_COLOR_SPECULAR, read_color))
|
||||
specularColor.set(read_color.r, read_color.g, read_color.b, opacity);
|
||||
mat->mSpecular[0] = specularColor;
|
||||
|
||||
// Specular Power
|
||||
F32 specularPower = 1.0f;
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS_STRENGTH, specularPower))
|
||||
mat->mSpecularPower[0] = specularPower;
|
||||
|
||||
// Specular
|
||||
F32 specularStrength = 0.0f;
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_SHININESS, specularStrength))
|
||||
mat->mSpecularStrength[0] = specularStrength;
|
||||
#endif
|
||||
|
||||
// Double-Sided
|
||||
bool doubleSided = false;
|
||||
S32 dbl_sided = 0;
|
||||
if (AI_SUCCESS == mAIMat->Get(AI_MATKEY_TWOSIDED, dbl_sided))
|
||||
doubleSided = (dbl_sided != 0);
|
||||
mat->mDoubleSided = doubleSided;
|
||||
}
|
||||
|
||||
String AssimpAppMaterial::cleanTextureName(String& texName, String& shapeName)
|
||||
{
|
||||
String cleanStr;
|
||||
|
||||
if (texName[0] == '*')
|
||||
{
|
||||
cleanStr = shapeName;
|
||||
cleanStr += "_cachedTex";
|
||||
cleanStr += texName.substr(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cleanStr = texName;
|
||||
cleanStr.replace('\\', '/');
|
||||
}
|
||||
|
||||
return cleanStr;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
void AssimpAppMaterial::enumerateMaterialProperties(aiMaterial* mtl)
|
||||
{
|
||||
for (U32 i = 0; i < mtl->mNumProperties; ++i)
|
||||
{
|
||||
aiMaterialProperty* matProp = mtl->mProperties[i];
|
||||
String outText;
|
||||
if (matProp)
|
||||
{
|
||||
outText = String::ToString(" Key: %s, Index: %d, Semantic: ", matProp->mKey.C_Str(), matProp->mIndex);
|
||||
switch (matProp->mSemantic)
|
||||
{
|
||||
case aiTextureType_NONE:
|
||||
outText += "aiTextureType_NONE";
|
||||
break;
|
||||
case aiTextureType_DIFFUSE:
|
||||
outText += "aiTextureType_DIFFUSE";
|
||||
break;
|
||||
case aiTextureType_SPECULAR:
|
||||
outText += "aiTextureType_SPECULAR";
|
||||
break;
|
||||
case aiTextureType_AMBIENT:
|
||||
outText += "aiTextureType_AMBIENT";
|
||||
break;
|
||||
case aiTextureType_EMISSIVE:
|
||||
outText += "aiTextureType_EMISSIVE";
|
||||
break;
|
||||
case aiTextureType_HEIGHT:
|
||||
outText += "aiTextureType_HEIGHT";
|
||||
break;
|
||||
case aiTextureType_NORMALS:
|
||||
outText += "aiTextureType_NORMALS";
|
||||
break;
|
||||
case aiTextureType_SHININESS:
|
||||
outText += "aiTextureType_SHININESS";
|
||||
break;
|
||||
case aiTextureType_OPACITY:
|
||||
outText += "aiTextureType_OPACITY";
|
||||
break;
|
||||
case aiTextureType_DISPLACEMENT:
|
||||
outText += "aiTextureType_DISPLACEMENT";
|
||||
break;
|
||||
case aiTextureType_LIGHTMAP:
|
||||
outText += "aiTextureType_LIGHTMAP";
|
||||
break;
|
||||
case aiTextureType_REFLECTION:
|
||||
outText += "aiTextureType_REFLECTION";
|
||||
break;
|
||||
default:
|
||||
outText += "aiTextureType_UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
aiString stringProp;
|
||||
F32* floatProp;
|
||||
double* doubleProp;
|
||||
S32* intProp;
|
||||
|
||||
switch (matProp->mType)
|
||||
{
|
||||
case aiPTI_Float:
|
||||
floatProp = (F32*)matProp->mData;
|
||||
for (U32 j = 0; j < matProp->mDataLength / sizeof(F32); ++j)
|
||||
outText += String::ToString(", %0.4f", floatProp[j]);
|
||||
break;
|
||||
case aiPTI_Double:
|
||||
doubleProp = (double*)matProp->mData;
|
||||
for (U32 j = 0; j < matProp->mDataLength / sizeof(double); ++j)
|
||||
outText += String::ToString(", %0.4lf", doubleProp[j]);
|
||||
break;
|
||||
case aiPTI_String:
|
||||
aiGetMaterialString(mtl, matProp->mKey.C_Str(), matProp->mSemantic, matProp->mIndex, &stringProp);
|
||||
outText += String::ToString(", %s", stringProp.C_Str());
|
||||
break;
|
||||
case aiPTI_Integer:
|
||||
intProp = (S32*)matProp->mData;
|
||||
for (U32 j = 0; j < matProp->mDataLength / sizeof(S32); ++j)
|
||||
outText += String::ToString(", %d", intProp[j]);
|
||||
break;
|
||||
case aiPTI_Buffer:
|
||||
outText += ", aiPTI_Buffer format data";
|
||||
break;
|
||||
default:
|
||||
outText += ", Unknown data type";
|
||||
}
|
||||
|
||||
Con::printf("%s", outText.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -26,6 +26,7 @@
|
|||
#ifndef _APPMATERIAL_H_
|
||||
#include "ts/loader/appMaterial.h"
|
||||
#endif
|
||||
#include <assimp/scene.h>
|
||||
|
||||
class Material;
|
||||
|
||||
|
|
@ -34,18 +35,22 @@ class AssimpAppMaterial : public AppMaterial
|
|||
typedef AppMaterial Parent;
|
||||
|
||||
String name;
|
||||
LinearColorF diffuseColor;
|
||||
LinearColorF specularColor;
|
||||
F32 specularPower;
|
||||
bool doubleSided;
|
||||
aiMaterial* mAIMat;
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
void enumerateMaterialProperties(aiMaterial* mtl);
|
||||
#endif
|
||||
static String cleanTextureName(String& texName, String& shapeName);
|
||||
|
||||
public:
|
||||
|
||||
AssimpAppMaterial(const char* matName);
|
||||
AssimpAppMaterial(const struct aiMaterial* mtl);
|
||||
AssimpAppMaterial(aiMaterial* mtl);
|
||||
~AssimpAppMaterial() { }
|
||||
|
||||
String getName() const { return name; }
|
||||
Material* createMaterial(const Torque::Path& path) const;
|
||||
void initMaterial(const Torque::Path& path, Material* mat) const;
|
||||
};
|
||||
|
||||
#endif // _ASSIMP_APPMATERIAL_H_
|
||||
|
|
|
|||
|
|
@ -202,6 +202,15 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
|
|||
|
||||
MatrixF boneTransform;
|
||||
AssimpAppNode::assimpToTorqueMat(mMeshData->mBones[b]->mOffsetMatrix, boneTransform);
|
||||
Point3F boneScale = boneTransform.getScale();
|
||||
if (boneScale != Point3F::One)
|
||||
{
|
||||
Point3F scaleMult = Point3F::One / boneScale;
|
||||
Point3F scalePos = boneTransform.getPosition();
|
||||
boneTransform.scale(scaleMult);
|
||||
scalePos /= scaleMult;
|
||||
boneTransform.setPosition(scalePos);
|
||||
}
|
||||
initialTransforms.push_back(boneTransform);
|
||||
|
||||
//Weights
|
||||
|
|
@ -225,20 +234,32 @@ void AssimpAppMesh::lockMesh(F32 t, const MatrixF& objOffset)
|
|||
vertexIndex.setSize(nonZeroWeights);
|
||||
boneIndex.setSize(nonZeroWeights);
|
||||
|
||||
// Copy the weights to our vectors in vertex order
|
||||
// Copy the weights to our vectors in vertex order and
|
||||
// normalize vertex weights (force weights for each vert to sum to 1)
|
||||
U32 nextWeight = 0;
|
||||
for (U32 i = 0; i < mMeshData->mNumVertices; i++)
|
||||
for (U32 i = 0; i < mMeshData->mNumVertices; ++i)
|
||||
{
|
||||
for (U32 ind = 0; ind < nonZeroWeights; ind++)
|
||||
U32 vertStart = nextWeight;
|
||||
F32 invTotalWeight = 0;
|
||||
for (U32 ind = 0; ind < nonZeroWeights; ++ind)
|
||||
{
|
||||
if (tmpVertexIndex[ind] == i)
|
||||
{
|
||||
weight[nextWeight] = tmpWeight[ind];
|
||||
invTotalWeight += tmpWeight[ind];
|
||||
vertexIndex[nextWeight] = tmpVertexIndex[ind];
|
||||
boneIndex[nextWeight] = tmpBoneIndex[ind];
|
||||
nextWeight++;
|
||||
}
|
||||
}
|
||||
|
||||
// Now normalize the vertex weights
|
||||
if (invTotalWeight > 0.0)
|
||||
{
|
||||
invTotalWeight = 1.0f / invTotalWeight;
|
||||
for (U32 ind = vertStart; ind < nextWeight; ++ind)
|
||||
weight[ind] *= invTotalWeight;
|
||||
}
|
||||
}
|
||||
|
||||
if ( noUVFound )
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <assimp/types.h>
|
||||
|
||||
aiAnimation* AssimpAppNode::sActiveSequence = NULL;
|
||||
F32 AssimpAppNode::sTimeMultiplier = 1.0f;
|
||||
|
||||
AssimpAppNode::AssimpAppNode(const struct aiScene* scene, const struct aiNode* node, AssimpAppNode* parent)
|
||||
: mInvertMeshes(false),
|
||||
|
|
@ -132,15 +133,15 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
|
|||
F32 lastT = 0.0;
|
||||
for (U32 key = 0; key < nodeAnim->mNumPositionKeys; ++key)
|
||||
{
|
||||
F32 curT = (F32)nodeAnim->mPositionKeys[key].mTime;
|
||||
F32 curT = sTimeMultiplier * (F32)nodeAnim->mPositionKeys[key].mTime;
|
||||
curPos.set(nodeAnim->mPositionKeys[key].mValue.x, nodeAnim->mPositionKeys[key].mValue.y, nodeAnim->mPositionKeys[key].mValue.z);
|
||||
if (curT > t)
|
||||
if ((curT > t) && (key > 0))
|
||||
{
|
||||
F32 factor = (t - lastT) / (curT - lastT);
|
||||
trans.interpolate(lastPos, curPos, factor);
|
||||
break;
|
||||
}
|
||||
else if ((curT == t) || (key == nodeAnim->mNumPositionKeys - 1))
|
||||
else if ((curT >= t) || (key == nodeAnim->mNumPositionKeys - 1))
|
||||
{
|
||||
trans = curPos;
|
||||
break;
|
||||
|
|
@ -161,16 +162,16 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
|
|||
F32 lastT = 0.0;
|
||||
for (U32 key = 0; key < nodeAnim->mNumRotationKeys; ++key)
|
||||
{
|
||||
F32 curT = (F32)nodeAnim->mRotationKeys[key].mTime;
|
||||
F32 curT = sTimeMultiplier * (F32)nodeAnim->mRotationKeys[key].mTime;
|
||||
curRot.set(nodeAnim->mRotationKeys[key].mValue.x, nodeAnim->mRotationKeys[key].mValue.y,
|
||||
nodeAnim->mRotationKeys[key].mValue.z, nodeAnim->mRotationKeys[key].mValue.w);
|
||||
if (curT > t)
|
||||
if ((curT > t) && (key > 0))
|
||||
{
|
||||
F32 factor = (t - lastT) / (curT - lastT);
|
||||
rot.interpolate(lastRot, curRot, factor);
|
||||
break;
|
||||
}
|
||||
else if ((curT == t) || (key == nodeAnim->mNumRotationKeys - 1))
|
||||
else if ((curT >= t) || (key == nodeAnim->mNumRotationKeys - 1))
|
||||
{
|
||||
rot = curRot;
|
||||
break;
|
||||
|
|
@ -190,15 +191,15 @@ void AssimpAppNode::getAnimatedTransform(MatrixF& mat, F32 t, aiAnimation* animS
|
|||
F32 lastT = 0.0;
|
||||
for (U32 key = 0; key < nodeAnim->mNumScalingKeys; ++key)
|
||||
{
|
||||
F32 curT = (F32)nodeAnim->mScalingKeys[key].mTime;
|
||||
F32 curT = sTimeMultiplier * (F32)nodeAnim->mScalingKeys[key].mTime;
|
||||
curScale.set(nodeAnim->mScalingKeys[key].mValue.x, nodeAnim->mScalingKeys[key].mValue.y, nodeAnim->mScalingKeys[key].mValue.z);
|
||||
if (curT > t)
|
||||
if ((curT > t) && (key > 0))
|
||||
{
|
||||
F32 factor = (t - lastT) / (curT - lastT);
|
||||
scale.interpolate(lastScale, curScale, factor);
|
||||
break;
|
||||
}
|
||||
else if ((curT == t) || (key == nodeAnim->mNumScalingKeys - 1))
|
||||
else if ((curT >= t) || (key == nodeAnim->mNumScalingKeys - 1))
|
||||
{
|
||||
scale = curScale;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public:
|
|||
}
|
||||
|
||||
static aiAnimation* sActiveSequence;
|
||||
static F32 sTimeMultiplier;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
const char *getName() { return mName; }
|
||||
|
|
|
|||
|
|
@ -16,19 +16,24 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
|
|||
seqStart(0.0f),
|
||||
mAnim(a)
|
||||
{
|
||||
// From: http://sir-kimmi.de/assimp/lib_html/data.html#anims
|
||||
// An aiAnimation has a duration. The duration as well as all time stamps are given in ticks.
|
||||
// To get the correct timing, all time stamp thus have to be divided by aiAnimation::mTicksPerSecond.
|
||||
// Beware, though, that certain combinations of file format and exporter don't always store this
|
||||
// information in the exported file. In this case, mTicksPerSecond is set to 0 to indicate the lack of knowledge.
|
||||
mSequenceName = mAnim->mName.C_Str();
|
||||
if (mSequenceName.isEmpty())
|
||||
mSequenceName = "ambient";
|
||||
Con::printf("\n[Assimp] Adding %s animation", mSequenceName.c_str());
|
||||
|
||||
fps = (mAnim->mTicksPerSecond > 0) ? mAnim->mTicksPerSecond : 30.0f;
|
||||
|
||||
U32 maxKeys = 0;
|
||||
F32 maxEndTime = 0;
|
||||
F32 minFrameTime = 1000.0f;
|
||||
F32 minFrameTime = 100000.0f;
|
||||
// Detect the frame rate (minimum time between keyframes) and max sequence time
|
||||
for (U32 i = 0; i < mAnim->mNumChannels; ++i)
|
||||
{
|
||||
aiNodeAnim *nodeAnim = mAnim->mChannels[i];
|
||||
maxKeys = getMax(maxKeys, nodeAnim->mNumPositionKeys);
|
||||
maxKeys = getMax(maxKeys, nodeAnim->mNumRotationKeys);
|
||||
maxKeys = getMax(maxKeys, nodeAnim->mNumScalingKeys);
|
||||
|
||||
if (nodeAnim->mNumPositionKeys)
|
||||
maxEndTime = getMax(maxEndTime, (F32) nodeAnim->mPositionKeys[nodeAnim->mNumPositionKeys-1].mTime);
|
||||
if (nodeAnim->mNumRotationKeys)
|
||||
|
|
@ -53,9 +58,31 @@ AssimpAppSequence::AssimpAppSequence(aiAnimation *a) :
|
|||
}
|
||||
}
|
||||
|
||||
fps = (minFrameTime > 0.0f) ? 1.0f / minFrameTime : fps;
|
||||
fps = mClamp(fps, TSShapeLoader::MinFrameRate, TSShapeLoader::MaxFrameRate);
|
||||
seqEnd = maxEndTime;
|
||||
S32 timeFactor = Con::getIntVariable("$Assimp::AnimTiming", 1);
|
||||
S32 fpsRequest = Con::getIntVariable("$Assimp::AnimFPS", 30);
|
||||
if (timeFactor == 0)
|
||||
{ // Timing specified in frames
|
||||
fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
|
||||
maxKeys = getMax(maxKeys, (U32)maxEndTime); // Keys won't be assigned for every frame.
|
||||
seqEnd = maxKeys / fps;
|
||||
mTimeMultiplier = 1.0f / fps;
|
||||
}
|
||||
else
|
||||
{ // Timing specified in seconds or ms depending on format
|
||||
if (maxEndTime > 1000.0f || mAnim->mDuration > 1000.0f)
|
||||
{
|
||||
timeFactor = 1000.0f; // If it's more than 1000 seconds, assume it's ms.
|
||||
Con::setIntVariable("$Assimp::AnimTiming", 1000);
|
||||
}
|
||||
|
||||
timeFactor = mClamp(timeFactor, 1, 1000);
|
||||
minFrameTime /= (F32)timeFactor;
|
||||
maxEndTime /= (F32)timeFactor;
|
||||
fps = (minFrameTime > 0.0f) ? 1.0f / minFrameTime : fps;
|
||||
fps = mClamp(fpsRequest, 5 /*TSShapeLoader::MinFrameRate*/, TSShapeLoader::MaxFrameRate);
|
||||
seqEnd = maxEndTime;
|
||||
mTimeMultiplier = 1.0f / timeFactor;
|
||||
}
|
||||
}
|
||||
|
||||
AssimpAppSequence::~AssimpAppSequence()
|
||||
|
|
@ -65,7 +92,10 @@ AssimpAppSequence::~AssimpAppSequence()
|
|||
void AssimpAppSequence::setActive(bool active)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
AssimpAppNode::sActiveSequence = mAnim;
|
||||
AssimpAppNode::sTimeMultiplier = mTimeMultiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (AssimpAppNode::sActiveSequence == mAnim)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@
|
|||
|
||||
class AssimpAppSequence : public AppSequence
|
||||
{
|
||||
String mSequenceName;
|
||||
F32 seqStart;
|
||||
F32 seqEnd;
|
||||
F32 mTimeMultiplier; // The factor needed to convert the sequence data timestamp to seconds
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -37,7 +39,7 @@ public:
|
|||
virtual S32 getNumTriggers() const { return 0; }
|
||||
virtual void getTrigger(S32 index, TSShape::Trigger& trigger) const { trigger.state = 0; }
|
||||
|
||||
virtual const char* getName() const { return mAnim->mName.C_Str(); }
|
||||
virtual const char* getName() const { return mSequenceName.c_str(); }
|
||||
|
||||
F32 getStart() const { return seqStart; }
|
||||
F32 getEnd() const { return seqEnd; }
|
||||
|
|
|
|||
|
|
@ -200,6 +200,10 @@ void AssimpShapeLoader::enumerateScene()
|
|||
Con::printf("[ASSIMP] Mesh Count: %d", mScene->mNumMeshes);
|
||||
Con::printf("[ASSIMP] Material Count: %d", mScene->mNumMaterials);
|
||||
|
||||
// Extract embedded textures
|
||||
for (U32 i = 0; i < mScene->mNumTextures; ++i)
|
||||
extractTexture(i, mScene->mTextures[i]);
|
||||
|
||||
// Load all the materials.
|
||||
for ( U32 i = 0; i < mScene->mNumMaterials; i++ )
|
||||
AppMesh::appMaterials.push_back(new AssimpAppMaterial(mScene->mMaterials[i]));
|
||||
|
|
@ -302,8 +306,11 @@ void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path)
|
|||
if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) )
|
||||
{
|
||||
// Only update existing materials if forced to
|
||||
if ( ColladaUtils::getOptions().forceUpdateMaterials )
|
||||
persistMgr.setDirty( mappedMat );
|
||||
if (Con::getBoolVariable("$Assimp::ForceUpdateMats", false))
|
||||
{
|
||||
mat->initMaterial(scriptPath, mappedMat);
|
||||
persistMgr.setDirty(mappedMat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -322,8 +329,6 @@ void AssimpShapeLoader::updateMaterialsScript(const Torque::Path &path)
|
|||
/// Check if an up-to-date cached DTS is available for this DAE file
|
||||
bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path)
|
||||
{
|
||||
return false;
|
||||
|
||||
// Generate the cached filename
|
||||
Torque::Path cachedPath(path);
|
||||
cachedPath.setExtension("cached.dts");
|
||||
|
|
@ -332,13 +337,13 @@ bool AssimpShapeLoader::canLoadCachedDTS(const Torque::Path& path)
|
|||
FileTime cachedModifyTime;
|
||||
if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime))
|
||||
{
|
||||
bool forceLoadDAE = Con::getBoolVariable("$assimp::forceLoad", false);
|
||||
bool forceLoad = Con::getBoolVariable("$assimp::forceLoad", false);
|
||||
|
||||
FileTime daeModifyTime;
|
||||
if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) ||
|
||||
(!forceLoadDAE && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0) ))
|
||||
(!forceLoad && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0) ))
|
||||
{
|
||||
// DAE not found, or cached DTS is newer
|
||||
// Original file not found, or cached DTS is newer
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -393,6 +398,56 @@ void AssimpShapeLoader::detectDetails()
|
|||
AssimpAppMesh::fixDetailSize(singleDetail, Con::getIntVariable("$Assimp::singleDetailSize", 2));
|
||||
}
|
||||
|
||||
void AssimpShapeLoader::extractTexture(U32 index, aiTexture* pTex)
|
||||
{ // Cache an embedded texture to disk
|
||||
updateProgress(Load_EnumerateScene, "Extracting Textures...", mScene->mNumTextures, index);
|
||||
Con::printf("[Assimp] Extracting Texture %s, W: %d, H: %d, %d of %d, format hint: (%s)", pTex->mFilename.C_Str(),
|
||||
pTex->mWidth, pTex->mHeight, index, mScene->mNumTextures, pTex->achFormatHint);
|
||||
|
||||
// Create the texture filename
|
||||
String cleanFile = AppMaterial::cleanString(TSShapeLoader::getShapePath().getFileName());
|
||||
String texName = String::ToString("%s_cachedTex%d", cleanFile.c_str(), index);
|
||||
Torque::Path texPath = shapePath;
|
||||
texPath.setFileName(texName);
|
||||
|
||||
if (pTex->mHeight == 0)
|
||||
{ // Compressed format, write the data directly to disc
|
||||
texPath.setExtension(pTex->achFormatHint);
|
||||
FileStream *outputStream;
|
||||
if ((outputStream = FileStream::createAndOpen(texPath.getFullPath(), Torque::FS::File::Write)) != NULL)
|
||||
{
|
||||
outputStream->setPosition(0);
|
||||
outputStream->write(pTex->mWidth, pTex->pcData);
|
||||
outputStream->close();
|
||||
delete outputStream;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Embedded pixel data, fill a bitmap and save it.
|
||||
GFXTexHandle shapeTex;
|
||||
shapeTex.set(pTex->mWidth, pTex->mHeight, GFXFormatR8G8B8A8_SRGB, &GFXDynamicTextureSRGBProfile,
|
||||
String::ToString("AssimpShapeLoader (%s:%i)", __FILE__, __LINE__), 1, 0);
|
||||
GFXLockedRect *rect = shapeTex.lock();
|
||||
|
||||
for (U32 y = 0; y < pTex->mHeight; ++y)
|
||||
{
|
||||
for (U32 x = 0; x < pTex->mWidth; ++x)
|
||||
{
|
||||
U32 targetIndex = (y * rect->pitch) + (x * 4);
|
||||
U32 sourceIndex = ((y * pTex->mWidth) + x) * 4;
|
||||
rect->bits[targetIndex] = pTex->pcData[sourceIndex].r;
|
||||
rect->bits[targetIndex + 1] = pTex->pcData[sourceIndex].g;
|
||||
rect->bits[targetIndex + 2] = pTex->pcData[sourceIndex].b;
|
||||
rect->bits[targetIndex + 3] = pTex->pcData[sourceIndex].a;
|
||||
}
|
||||
}
|
||||
shapeTex.unlock();
|
||||
|
||||
texPath.setExtension("png");
|
||||
shapeTex->dumpToDisk("PNG", texPath.getFullPath());
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/// This function is invoked by the resource manager based on file extension.
|
||||
TSShape* assimpLoadShape(const Torque::Path &path)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#ifndef _TSSHAPELOADER_H_
|
||||
#include "ts/loader/tsShapeLoader.h"
|
||||
#endif
|
||||
#include <assimp/texture.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class AssimpShapeLoader : public TSShapeLoader
|
||||
|
|
@ -37,6 +38,7 @@ protected:
|
|||
|
||||
virtual bool ignoreNode(const String& name);
|
||||
void detectDetails();
|
||||
void extractTexture(U32 index, aiTexture* pTex);
|
||||
|
||||
public:
|
||||
AssimpShapeLoader();
|
||||
|
|
|
|||
|
|
@ -393,6 +393,107 @@
|
|||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiTextCtrl() {
|
||||
text = "Animation Timing:";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiTextRightProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "10 311";
|
||||
Extent = "85 16";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
hovertime = "1000";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
new GuiPopUpMenuCtrl() {
|
||||
maxPopupHeight = "200";
|
||||
sbUsesNAColor = "0";
|
||||
reverseTextList = "0";
|
||||
bitmapBounds = "16 16";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiPopUpMenuProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "100 310";
|
||||
Extent = "86 18";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Select the timing units used in the animation data.";
|
||||
hovertime = "1000";
|
||||
internalName = "animTiming";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
new GuiTextCtrl() {
|
||||
text = "FPS:";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiTextRightProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "200 311";
|
||||
Extent = "20 16";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
hovertime = "1000";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
new GuiTextEditCtrl() {
|
||||
historySize = "0";
|
||||
password = "0";
|
||||
tabComplete = "0";
|
||||
sinkAllKeyEvents = "0";
|
||||
passwordMask = "*";
|
||||
text = "2";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiTextEditProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "225 310";
|
||||
Extent = "26 18";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Frames per second for all animations when Animation Timing type is Frames (5 - 60)";
|
||||
hovertime = "1000";
|
||||
internalName = "animFPS";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiTextCtrl() {
|
||||
text = "LOD";
|
||||
maxLength = "1024";
|
||||
|
|
@ -582,6 +683,28 @@
|
|||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiCheckBoxCtrl() {
|
||||
useInactiveState = "0";
|
||||
text = " Force update materials.cs";
|
||||
groupNum = "-1";
|
||||
buttonType = "ToggleButton";
|
||||
useMouseEvents = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiCheckBoxProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "210 150";
|
||||
Extent = "200 13";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
variable = "$Assimp::ForceUpdateMats";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Forces update of materials.cs (even if Materials already exist)";
|
||||
hovertime = "1000";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiButtonCtrl() {
|
||||
text = "OK";
|
||||
groupNum = "-1";
|
||||
|
|
@ -643,6 +766,10 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
|
|||
$Assimp::FindDegenerates = true;
|
||||
$Assimp::FindInvalidData = true;
|
||||
$Assimp::JoinIdenticalVertices = true;
|
||||
$Assimp::FlipNormals = false;
|
||||
|
||||
$Assimp::AnimTiming = 1; // Seconds
|
||||
$Assimp::AnimFPS = 30; // Framerate when timing is frames.
|
||||
}
|
||||
|
||||
%this-->upAxis.clear();
|
||||
|
|
@ -656,9 +783,15 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
|
|||
%this-->lodType.add("SingleSize", 1);
|
||||
%this-->lodType.add("TrailingNumber", 2);
|
||||
%this-->lodType.setSelected($Assimp::lodType);
|
||||
|
||||
%this-->singleDetailSize.text = $Assimp::singleDetailSize;
|
||||
|
||||
%this-->animTiming.clear();
|
||||
%this-->animTiming.add("Frames", 0);
|
||||
%this-->animTiming.add("Seconds", 1);
|
||||
%this-->animTiming.add("Milliseconds", 1000);
|
||||
%this-->animTiming.setSelected($Assimp::AnimTiming);
|
||||
%this-->animFPS.text = $Assimp::AnimFPS;
|
||||
|
||||
//Triangulate is a default(currently mandatory) behavior
|
||||
$Assimp::Triangulate = true;
|
||||
|
||||
|
|
@ -681,6 +814,9 @@ function AssimpImportDlg::onOK(%this)
|
|||
$Assimp::lodType = %this-->lodType.getSelected();
|
||||
$Assimp::singleDetailSize = %this-->singleDetailSize.getText();
|
||||
|
||||
$Assimp::AnimTiming = %this-->animTiming.getSelected();
|
||||
$Assimp::AnimFPS = %this-->animFPS.getText();
|
||||
|
||||
// Load the shape (always from the DAE)
|
||||
$assimp::forceLoad = true;
|
||||
eval(%this.cmd);
|
||||
|
|
|
|||
|
|
@ -261,13 +261,13 @@ function ShapeEdSelectWindow::onSelect( %this, %path )
|
|||
// Prompt user to save the old shape if it is dirty
|
||||
if ( ShapeEditor.isDirty() )
|
||||
{
|
||||
%cmd = "ColladaImportDlg.showDialog( \"" @ %path @ "\", \"ShapeEditor.selectShape( \\\"" @ %path @ "\\\", ";
|
||||
%cmd = "showImportDialog( \"" @ %path @ "\", \"ShapeEditor.selectShape( \\\"" @ %path @ "\\\", ";
|
||||
MessageBoxYesNoCancel( "Shape Modified", "Would you like to save your changes?", %cmd @ "true );\" );", %cmd @ "false );\" );" );
|
||||
}
|
||||
else
|
||||
{
|
||||
%cmd = "ShapeEditor.selectShape( \"" @ %path @ "\", false );";
|
||||
ColladaImportDlg.showDialog( %path, %cmd );
|
||||
showImportDialog( %path, %cmd );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -393,6 +393,107 @@
|
|||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiTextCtrl() {
|
||||
text = "Animation Timing:";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiTextRightProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "10 311";
|
||||
Extent = "85 16";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
hovertime = "1000";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
new GuiPopUpMenuCtrl() {
|
||||
maxPopupHeight = "200";
|
||||
sbUsesNAColor = "0";
|
||||
reverseTextList = "0";
|
||||
bitmapBounds = "16 16";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiPopUpMenuProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "100 310";
|
||||
Extent = "86 18";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Select the timing units used in the animation data.";
|
||||
hovertime = "1000";
|
||||
internalName = "animTiming";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
new GuiTextCtrl() {
|
||||
text = "FPS:";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiTextRightProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "200 311";
|
||||
Extent = "20 16";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
hovertime = "1000";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
new GuiTextEditCtrl() {
|
||||
historySize = "0";
|
||||
password = "0";
|
||||
tabComplete = "0";
|
||||
sinkAllKeyEvents = "0";
|
||||
passwordMask = "*";
|
||||
text = "2";
|
||||
maxLength = "1024";
|
||||
Margin = "0 0 0 0";
|
||||
Padding = "0 0 0 0";
|
||||
AnchorTop = "1";
|
||||
AnchorBottom = "0";
|
||||
AnchorLeft = "1";
|
||||
AnchorRight = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiTextEditProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "225 310";
|
||||
Extent = "26 18";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Frames per second for all animations when Animation Timing type is Frames (5 - 60)";
|
||||
hovertime = "1000";
|
||||
internalName = "animFPS";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiTextCtrl() {
|
||||
text = "LOD";
|
||||
maxLength = "1024";
|
||||
|
|
@ -582,6 +683,28 @@
|
|||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiCheckBoxCtrl() {
|
||||
useInactiveState = "0";
|
||||
text = " Force update materials.cs";
|
||||
groupNum = "-1";
|
||||
buttonType = "ToggleButton";
|
||||
useMouseEvents = "0";
|
||||
isContainer = "0";
|
||||
Profile = "ToolsGuiCheckBoxProfile";
|
||||
HorizSizing = "right";
|
||||
VertSizing = "bottom";
|
||||
position = "210 150";
|
||||
Extent = "200 13";
|
||||
MinExtent = "8 2";
|
||||
canSave = "1";
|
||||
Visible = "1";
|
||||
variable = "$Assimp::ForceUpdateMats";
|
||||
tooltipprofile = "ToolsGuiToolTipProfile";
|
||||
ToolTip = "Forces update of materials.cs (even if Materials already exist)";
|
||||
hovertime = "1000";
|
||||
canSaveDynamicFields = "0";
|
||||
};
|
||||
|
||||
new GuiButtonCtrl() {
|
||||
text = "OK";
|
||||
groupNum = "-1";
|
||||
|
|
@ -643,6 +766,10 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
|
|||
$Assimp::FindDegenerates = true;
|
||||
$Assimp::FindInvalidData = true;
|
||||
$Assimp::JoinIdenticalVertices = true;
|
||||
$Assimp::FlipNormals = false;
|
||||
|
||||
$Assimp::AnimTiming = 1; // Seconds
|
||||
$Assimp::AnimFPS = 30; // Framerate when timing is frames.
|
||||
}
|
||||
|
||||
%this-->upAxis.clear();
|
||||
|
|
@ -656,9 +783,15 @@ function AssimpImportDlg::showDialog(%this, %shapePath, %cmd)
|
|||
%this-->lodType.add("SingleSize", 1);
|
||||
%this-->lodType.add("TrailingNumber", 2);
|
||||
%this-->lodType.setSelected($Assimp::lodType);
|
||||
|
||||
%this-->singleDetailSize.text = $Assimp::singleDetailSize;
|
||||
|
||||
%this-->animTiming.clear();
|
||||
%this-->animTiming.add("Frames", 0);
|
||||
%this-->animTiming.add("Seconds", 1);
|
||||
%this-->animTiming.add("Milliseconds", 1000);
|
||||
%this-->animTiming.setSelected($Assimp::AnimTiming);
|
||||
%this-->animFPS.text = $Assimp::AnimFPS;
|
||||
|
||||
//Triangulate is a default(currently mandatory) behavior
|
||||
$Assimp::Triangulate = true;
|
||||
|
||||
|
|
@ -681,6 +814,9 @@ function AssimpImportDlg::onOK(%this)
|
|||
$Assimp::lodType = %this-->lodType.getSelected();
|
||||
$Assimp::singleDetailSize = %this-->singleDetailSize.getText();
|
||||
|
||||
$Assimp::AnimTiming = %this-->animTiming.getSelected();
|
||||
$Assimp::AnimFPS = %this-->animFPS.getText();
|
||||
|
||||
// Load the shape (always from the DAE)
|
||||
$assimp::forceLoad = true;
|
||||
eval(%this.cmd);
|
||||
|
|
|
|||
|
|
@ -261,7 +261,7 @@ function ShapeEdSelectWindow::onSelect( %this, %path )
|
|||
// Prompt user to save the old shape if it is dirty
|
||||
if ( ShapeEditor.isDirty() )
|
||||
{
|
||||
%cmd = "ColladaImportDlg.showDialog( \"" @ %path @ "\", \"ShapeEditor.selectShape( \\\"" @ %path @ "\\\", ";
|
||||
%cmd = "showImportDialog( \"" @ %path @ "\", \"ShapeEditor.selectShape( \\\"" @ %path @ "\\\", ";
|
||||
MessageBoxYesNoCancel( "Shape Modified", "Would you like to save your changes?", %cmd @ "true );\" );", %cmd @ "false );\" );" );
|
||||
}
|
||||
else
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue