Model loading and texturing

This commit is contained in:
Dane Johnson
2021-01-18 18:25:47 -06:00
parent 66bf7776c7
commit 155b572aca
1283 changed files with 533814 additions and 42 deletions

View File

@@ -0,0 +1,604 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file LWOAnimation.cpp
* @brief LWOAnimationResolver utility class
*
* It's a very generic implementation of LightWave's system of
* componentwise-animated stuff. The one and only fully free
* implementation of LightWave envelopes of which I know.
*/
#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) && (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
#include <functional>
// internal headers
#include "LWOFileData.h"
#include <assimp/anim.h>
using namespace Assimp;
using namespace Assimp::LWO;
// ------------------------------------------------------------------------------------------------
// Construct an animation resolver from a given list of envelopes
AnimResolver::AnimResolver(std::list<Envelope>& _envelopes,double tick)
: envelopes (_envelopes)
, sample_rate (0.)
, envl_x(), envl_y(), envl_z()
, end_x(), end_y(), end_z()
, flags()
, sample_delta()
{
trans_x = trans_y = trans_z = NULL;
rotat_x = rotat_y = rotat_z = NULL;
scale_x = scale_y = scale_z = NULL;
first = last = 150392.;
// find transformation envelopes
for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
(*it).old_first = 0;
(*it).old_last = (*it).keys.size()-1;
if ((*it).keys.empty()) continue;
switch ((*it).type) {
// translation
case LWO::EnvelopeType_Position_X:
trans_x = &*it;break;
case LWO::EnvelopeType_Position_Y:
trans_y = &*it;break;
case LWO::EnvelopeType_Position_Z:
trans_z = &*it;break;
// rotation
case LWO::EnvelopeType_Rotation_Heading:
rotat_x = &*it;break;
case LWO::EnvelopeType_Rotation_Pitch:
rotat_y = &*it;break;
case LWO::EnvelopeType_Rotation_Bank:
rotat_z = &*it;break;
// scaling
case LWO::EnvelopeType_Scaling_X:
scale_x = &*it;break;
case LWO::EnvelopeType_Scaling_Y:
scale_y = &*it;break;
case LWO::EnvelopeType_Scaling_Z:
scale_z = &*it;break;
default:
continue;
};
// convert from seconds to ticks
for (std::vector<LWO::Key>::iterator d = (*it).keys.begin(); d != (*it).keys.end(); ++d)
(*d).time *= tick;
// set default animation range (minimum and maximum time value for which we have a keyframe)
first = std::min(first, (*it).keys.front().time );
last = std::max(last, (*it).keys.back().time );
}
// deferred setup of animation range to increase performance.
// typically the application will want to specify its own.
need_to_setup = true;
}
// ------------------------------------------------------------------------------------------------
// Reset all envelopes to their original contents
void AnimResolver::ClearAnimRangeSetup()
{
for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
(*it).keys.erase((*it).keys.begin(),(*it).keys.begin()+(*it).old_first);
(*it).keys.erase((*it).keys.begin()+(*it).old_last+1,(*it).keys.end());
}
}
// ------------------------------------------------------------------------------------------------
// Insert additional keys to match LWO's pre& post behaviours.
void AnimResolver::UpdateAnimRangeSetup()
{
// XXX doesn't work yet (hangs if more than one envelope channels needs to be interpolated)
for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
if ((*it).keys.empty()) continue;
const double my_first = (*it).keys.front().time;
const double my_last = (*it).keys.back().time;
const double delta = my_last-my_first;
const size_t old_size = (*it).keys.size();
const float value_delta = (*it).keys.back().value - (*it).keys.front().value;
// NOTE: We won't handle reset, linear and constant here.
// See DoInterpolation() for their implementation.
// process pre behaviour
switch ((*it).pre) {
case LWO::PrePostBehaviour_OffsetRepeat:
case LWO::PrePostBehaviour_Repeat:
case LWO::PrePostBehaviour_Oscillate:
{
const double start_time = delta - std::fmod(my_first-first,delta);
std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(),(*it).keys.end(),
[start_time](double t) { return start_time > t; }),m;
size_t ofs = 0;
if (n != (*it).keys.end()) {
// copy from here - don't use iterators, insert() would invalidate them
ofs = (*it).keys.end()-n;
(*it).keys.insert((*it).keys.begin(),ofs,LWO::Key());
std::copy((*it).keys.end()-ofs,(*it).keys.end(),(*it).keys.begin());
}
// do full copies. again, no iterators
const unsigned int num = (unsigned int)((my_first-first) / delta);
(*it).keys.resize((*it).keys.size() + num*old_size);
n = (*it).keys.begin()+ofs;
bool reverse = false;
for (unsigned int i = 0; i < num; ++i) {
m = n+old_size*(i+1);
std::copy(n,n+old_size,m);
if ((*it).pre == LWO::PrePostBehaviour_Oscillate && (reverse = !reverse))
std::reverse(m,m+old_size-1);
}
// update time values
n = (*it).keys.end() - (old_size+1);
double cur_minus = delta;
unsigned int tt = 1;
for (const double tmp = delta*(num+1);cur_minus <= tmp;cur_minus += delta,++tt) {
m = (delta == tmp ? (*it).keys.begin() : n - (old_size+1));
for (;m != n; --n) {
(*n).time -= cur_minus;
// offset repeat? add delta offset to key value
if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
(*n).value += tt * value_delta;
}
}
}
break;
}
default:
// silence compiler warning
break;
}
// process post behaviour
switch ((*it).post) {
case LWO::PrePostBehaviour_OffsetRepeat:
case LWO::PrePostBehaviour_Repeat:
case LWO::PrePostBehaviour_Oscillate:
break;
default:
// silence compiler warning
break;
}
}
}
// ------------------------------------------------------------------------------------------------
// Extract bind pose matrix
void AnimResolver::ExtractBindPose(aiMatrix4x4& out)
{
// If we have no envelopes, return identity
if (envelopes.empty()) {
out = aiMatrix4x4();
return;
}
aiVector3D angles, scaling(1.f,1.f,1.f), translation;
if (trans_x) translation.x = trans_x->keys[0].value;
if (trans_y) translation.y = trans_y->keys[0].value;
if (trans_z) translation.z = trans_z->keys[0].value;
if (rotat_x) angles.x = rotat_x->keys[0].value;
if (rotat_y) angles.y = rotat_y->keys[0].value;
if (rotat_z) angles.z = rotat_z->keys[0].value;
if (scale_x) scaling.x = scale_x->keys[0].value;
if (scale_y) scaling.y = scale_y->keys[0].value;
if (scale_z) scaling.z = scale_z->keys[0].value;
// build the final matrix
aiMatrix4x4 s,rx,ry,rz,t;
aiMatrix4x4::RotationZ(angles.z, rz);
aiMatrix4x4::RotationX(angles.y, rx);
aiMatrix4x4::RotationY(angles.x, ry);
aiMatrix4x4::Translation(translation,t);
aiMatrix4x4::Scaling(scaling,s);
out = t*ry*rx*rz*s;
}
// ------------------------------------------------------------------------------------------------
// Do a single interpolation on a channel
void AnimResolver::DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
LWO::Envelope* envl,double time, float& fill)
{
if (envl->keys.size() == 1) {
fill = envl->keys[0].value;
return;
}
// check whether we're at the beginning of the animation track
if (cur == envl->keys.begin()) {
// ok ... this depends on pre behaviour now
// we don't need to handle repeat&offset repeat&oszillate here, see UpdateAnimRangeSetup()
switch (envl->pre)
{
case LWO::PrePostBehaviour_Linear:
DoInterpolation2(cur,cur+1,time,fill);
return;
case LWO::PrePostBehaviour_Reset:
fill = 0.f;
return;
default : //case LWO::PrePostBehaviour_Constant:
fill = (*cur).value;
return;
}
}
// check whether we're at the end of the animation track
else if (cur == envl->keys.end()-1 && time > envl->keys.rbegin()->time) {
// ok ... this depends on post behaviour now
switch (envl->post)
{
case LWO::PrePostBehaviour_Linear:
DoInterpolation2(cur,cur-1,time,fill);
return;
case LWO::PrePostBehaviour_Reset:
fill = 0.f;
return;
default : //case LWO::PrePostBehaviour_Constant:
fill = (*cur).value;
return;
}
}
// Otherwise do a simple interpolation
DoInterpolation2(cur-1,cur,time,fill);
}
// ------------------------------------------------------------------------------------------------
// Almost the same, except we won't handle pre/post conditions here
void AnimResolver::DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
std::vector<LWO::Key>::const_iterator end,double time, float& fill)
{
switch ((*end).inter) {
case LWO::IT_STEP:
// no interpolation at all - take the value of the last key
fill = (*beg).value;
return;
default:
// silence compiler warning
break;
}
// linear interpolation - default
double duration = (*end).time - (*beg).time;
if (duration > 0.0) {
fill = (*beg).value + ((*end).value - (*beg).value)*(float)(((time - (*beg).time) / duration));
} else {
fill = (*beg).value;
}
}
// ------------------------------------------------------------------------------------------------
// Subsample animation track by given key values
void AnimResolver::SubsampleAnimTrack(std::vector<aiVectorKey>& /*out*/,
double /*time*/ ,double /*sample_delta*/ )
{
//ai_assert(out.empty() && sample_delta);
//const double time_start = out.back().mTime;
// for ()
}
// ------------------------------------------------------------------------------------------------
// Track interpolation
void AnimResolver::InterpolateTrack(std::vector<aiVectorKey>& out,aiVectorKey& fill,double time)
{
// subsample animation track?
if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
SubsampleAnimTrack(out,time, sample_delta);
}
fill.mTime = time;
// get x
if ((*cur_x).time == time) {
fill.mValue.x = (*cur_x).value;
if (cur_x != envl_x->keys.end()-1) /* increment x */
++cur_x;
else end_x = true;
}
else DoInterpolation(cur_x,envl_x,time,(float&)fill.mValue.x);
// get y
if ((*cur_y).time == time) {
fill.mValue.y = (*cur_y).value;
if (cur_y != envl_y->keys.end()-1) /* increment y */
++cur_y;
else end_y = true;
}
else DoInterpolation(cur_y,envl_y,time,(float&)fill.mValue.y);
// get z
if ((*cur_z).time == time) {
fill.mValue.z = (*cur_z).value;
if (cur_z != envl_z->keys.end()-1) /* increment z */
++cur_z;
else end_x = true;
}
else DoInterpolation(cur_z,envl_z,time,(float&)fill.mValue.z);
}
// ------------------------------------------------------------------------------------------------
// Build linearly subsampled keys from three single envelopes, one for each component (x,y,z)
void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
LWO::Envelope* _envl_x,
LWO::Envelope* _envl_y,
LWO::Envelope* _envl_z,
unsigned int _flags)
{
envl_x = _envl_x;
envl_y = _envl_y;
envl_z = _envl_z;
flags = _flags;
// generate default channels if none are given
LWO::Envelope def_x, def_y, def_z;
LWO::Key key_dummy;
key_dummy.time = 0.f;
if ((envl_x && envl_x->type == LWO::EnvelopeType_Scaling_X) ||
(envl_y && envl_y->type == LWO::EnvelopeType_Scaling_Y) ||
(envl_z && envl_z->type == LWO::EnvelopeType_Scaling_Z)) {
key_dummy.value = 1.f;
}
else key_dummy.value = 0.f;
if (!envl_x) {
envl_x = &def_x;
envl_x->keys.push_back(key_dummy);
}
if (!envl_y) {
envl_y = &def_y;
envl_y->keys.push_back(key_dummy);
}
if (!envl_z) {
envl_z = &def_z;
envl_z->keys.push_back(key_dummy);
}
// guess how many keys we'll get
size_t reserve;
double sr = 1.;
if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
if (!sample_rate)
sr = 100.f;
else sr = sample_rate;
sample_delta = 1.f / sr;
reserve = (size_t)(
std::max( envl_x->keys.rbegin()->time,
std::max( envl_y->keys.rbegin()->time, envl_z->keys.rbegin()->time )) * sr);
}
else reserve = std::max(envl_x->keys.size(),std::max(envl_x->keys.size(),envl_z->keys.size()));
out.reserve(reserve+(reserve>>1));
// Iterate through all three arrays at once - it's tricky, but
// rather interesting to implement.
cur_x = envl_x->keys.begin();
cur_y = envl_y->keys.begin();
cur_z = envl_z->keys.begin();
end_x = end_y = end_z = false;
while (1) {
aiVectorKey fill;
if ((*cur_x).time == (*cur_y).time && (*cur_x).time == (*cur_z).time ) {
// we have a keyframe for all of them defined .. this means
// we don't need to interpolate here.
fill.mTime = (*cur_x).time;
fill.mValue.x = (*cur_x).value;
fill.mValue.y = (*cur_y).value;
fill.mValue.z = (*cur_z).value;
// subsample animation track
if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
//SubsampleAnimTrack(out,cur_x, cur_y, cur_z, d, sample_delta);
}
}
// Find key with lowest time value
else if ((*cur_x).time <= (*cur_y).time && !end_x) {
if ((*cur_z).time <= (*cur_x).time && !end_z) {
InterpolateTrack(out,fill,(*cur_z).time);
}
else {
InterpolateTrack(out,fill,(*cur_x).time);
}
}
else if ((*cur_z).time <= (*cur_y).time && !end_y) {
InterpolateTrack(out,fill,(*cur_y).time);
}
else if (!end_y) {
// welcome on the server, y
InterpolateTrack(out,fill,(*cur_y).time);
}
else {
// we have reached the end of at least 2 channels,
// only one is remaining. Extrapolate the 2.
if (end_y) {
InterpolateTrack(out,fill,(end_x ? (*cur_z) : (*cur_x)).time);
}
else if (end_x) {
InterpolateTrack(out,fill,(end_z ? (*cur_y) : (*cur_z)).time);
}
else { // if (end_z)
InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
}
}
double lasttime = fill.mTime;
out.push_back(fill);
if (lasttime >= (*cur_x).time) {
if (cur_x != envl_x->keys.end()-1)
++cur_x;
else end_x = true;
}
if (lasttime >= (*cur_y).time) {
if (cur_y != envl_y->keys.end()-1)
++cur_y;
else end_y = true;
}
if (lasttime >= (*cur_z).time) {
if (cur_z != envl_z->keys.end()-1)
++cur_z;
else end_z = true;
}
if( end_x && end_y && end_z ) /* finished? */
break;
}
if (flags & AI_LWO_ANIM_FLAG_START_AT_ZERO) {
for (std::vector<aiVectorKey>::iterator it = out.begin(); it != out.end(); ++it)
(*it).mTime -= first;
}
}
// ------------------------------------------------------------------------------------------------
// Extract animation channel
void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int flags /*= 0*/)
{
*out = NULL;
//FIXME: crashes if more than one component is animated at different timings, to be resolved.
// If we have no envelopes, return NULL
if (envelopes.empty()) {
return;
}
// We won't spawn an animation channel if we don't have at least one envelope with more than one keyframe defined.
const bool trans = ((trans_x && trans_x->keys.size() > 1) || (trans_y && trans_y->keys.size() > 1) || (trans_z && trans_z->keys.size() > 1));
const bool rotat = ((rotat_x && rotat_x->keys.size() > 1) || (rotat_y && rotat_y->keys.size() > 1) || (rotat_z && rotat_z->keys.size() > 1));
const bool scale = ((scale_x && scale_x->keys.size() > 1) || (scale_y && scale_y->keys.size() > 1) || (scale_z && scale_z->keys.size() > 1));
if (!trans && !rotat && !scale)
return;
// Allocate the output animation
aiNodeAnim* anim = *out = new aiNodeAnim();
// Setup default animation setup if necessary
if (need_to_setup) {
UpdateAnimRangeSetup();
need_to_setup = false;
}
// copy translation keys
if (trans) {
std::vector<aiVectorKey> keys;
GetKeys(keys,trans_x,trans_y,trans_z,flags);
anim->mPositionKeys = new aiVectorKey[ anim->mNumPositionKeys = static_cast<unsigned int>(keys.size()) ];
std::copy(keys.begin(),keys.end(),anim->mPositionKeys);
}
// copy rotation keys
if (rotat) {
std::vector<aiVectorKey> keys;
GetKeys(keys,rotat_x,rotat_y,rotat_z,flags);
anim->mRotationKeys = new aiQuatKey[ anim->mNumRotationKeys = static_cast<unsigned int>(keys.size()) ];
// convert heading, pitch, bank to quaternion
// mValue.x=Heading=Rot(Y), mValue.y=Pitch=Rot(X), mValue.z=Bank=Rot(Z)
// Lightwave's rotation order is ZXY
aiVector3D X(1.0,0.0,0.0);
aiVector3D Y(0.0,1.0,0.0);
aiVector3D Z(0.0,0.0,1.0);
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
aiQuatKey& qk = anim->mRotationKeys[i];
qk.mTime = keys[i].mTime;
qk.mValue = aiQuaternion(Y,keys[i].mValue.x)*aiQuaternion(X,keys[i].mValue.y)*aiQuaternion(Z,keys[i].mValue.z);
}
}
// copy scaling keys
if (scale) {
std::vector<aiVectorKey> keys;
GetKeys(keys,scale_x,scale_y,scale_z,flags);
anim->mScalingKeys = new aiVectorKey[ anim->mNumScalingKeys = static_cast<unsigned int>(keys.size()) ];
std::copy(keys.begin(),keys.end(),anim->mScalingKeys);
}
}
#endif // no lwo or no lws

View File

@@ -0,0 +1,346 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file LWOAnimation.h
* @brief LWOAnimationResolver utility class
*
* This is for all lightwave-related file format, not only LWO.
* LWS isthe main purpose.
*/
#ifndef AI_LWO_ANIMATION_INCLUDED
#define AI_LWO_ANIMATION_INCLUDED
//
#include <vector>
#include <list>
struct aiNodeAnim;
struct aiVectorKey;
namespace Assimp {
namespace LWO {
// ---------------------------------------------------------------------------
/** \brief List of recognized LWO envelopes
*/
enum EnvelopeType
{
EnvelopeType_Position_X = 0x1,
EnvelopeType_Position_Y = 0x2,
EnvelopeType_Position_Z = 0x3,
EnvelopeType_Rotation_Heading = 0x4,
EnvelopeType_Rotation_Pitch = 0x5,
EnvelopeType_Rotation_Bank = 0x6,
EnvelopeType_Scaling_X = 0x7,
EnvelopeType_Scaling_Y = 0x8,
EnvelopeType_Scaling_Z = 0x9,
// -- currently not yet handled
EnvelopeType_Color_R = 0xa,
EnvelopeType_Color_G = 0xb,
EnvelopeType_Color_B = 0xc,
EnvelopeType_Falloff_X = 0xd,
EnvelopeType_Falloff_Y = 0xe,
EnvelopeType_Falloff_Z = 0xf,
EnvelopeType_Unknown
};
// ---------------------------------------------------------------------------
/** \brief List of recognized LWO interpolation modes
*/
enum InterpolationType
{
IT_STEP, IT_LINE, IT_TCB, IT_HERM, IT_BEZI, IT_BEZ2
};
// ---------------------------------------------------------------------------
/** \brief List of recognized LWO pre or post range behaviours
*/
enum PrePostBehaviour
{
PrePostBehaviour_Reset = 0x0,
PrePostBehaviour_Constant = 0x1,
PrePostBehaviour_Repeat = 0x2,
PrePostBehaviour_Oscillate = 0x3,
PrePostBehaviour_OffsetRepeat = 0x4,
PrePostBehaviour_Linear = 0x5
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO animation keyframe
*/
struct Key {
Key() AI_NO_EXCEPT
: time()
, value()
, inter(IT_LINE)
, params() {
// empty
}
//! Current time
double time;
//! Current value
float value;
//! How to interpolate this key with previous key?
InterpolationType inter;
//! Interpolation parameters
float params[5];
// for std::find()
operator double () {
return time;
}
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO animation envelope
*/
struct Envelope {
Envelope() AI_NO_EXCEPT
: index()
, type(EnvelopeType_Unknown)
, pre(PrePostBehaviour_Constant)
, post(PrePostBehaviour_Constant)
, old_first(0)
, old_last(0) {
// empty
}
//! Index of this envelope
unsigned int index;
//! Type of envelope
EnvelopeType type;
//! Pre- and post-behavior
PrePostBehaviour pre,post;
//! Keyframes for this envelope
std::vector<Key> keys;
// temporary data for AnimResolver
size_t old_first,old_last;
};
// ---------------------------------------------------------------------------
//! @def AI_LWO_ANIM_FLAG_SAMPLE_ANIMS
//! Flag for AnimResolver, subsamples the input data with the rate specified
//! by AnimResolver::SetSampleRate().
#define AI_LWO_ANIM_FLAG_SAMPLE_ANIMS 0x1
// ---------------------------------------------------------------------------
//! @def AI_LWO_ANIM_FLAG_START_AT_ZERO
//! Flag for AnimResolver, ensures that the animations starts at zero.
#define AI_LWO_ANIM_FLAG_START_AT_ZERO 0x2
// ---------------------------------------------------------------------------
/** @brief Utility class to build Assimp animations from LWO envelopes.
*
* Used for both LWO and LWS (MOT also).
*/
class AnimResolver
{
public:
// ------------------------------------------------------------------
/** @brief Construct an AnimResolver from a given list of envelopes
* @param envelopes Input envelopes. May be empty.
* @param Output tick rate, per second
* @note The input envelopes are possibly modified.
*/
AnimResolver(std::list<Envelope>& envelopes, double tick);
public:
// ------------------------------------------------------------------
/** @brief Extract the bind-pose transformation matrix.
* @param out Receives bind-pose transformation matrix
*/
void ExtractBindPose(aiMatrix4x4& out);
// ------------------------------------------------------------------
/** @brief Extract a node animation channel
* @param out Receives a pointer to a newly allocated node anim.
* If there's just one keyframe defined, *out is set to NULL and
* no animation channel is computed.
* @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
*/
void ExtractAnimChannel(aiNodeAnim** out, unsigned int flags = 0);
// ------------------------------------------------------------------
/** @brief Set the sampling rate for ExtractAnimChannel().
*
* Non-linear interpolations are subsampled with this rate (keys
* per second). Closer sampling positions, if existent, are kept.
* The sampling rate defaults to 0, if this value is not changed and
* AI_LWO_ANIM_FLAG_SAMPLE_ANIMS is specified for ExtractAnimChannel(),
* the class finds a suitable sample rate by itself.
*/
void SetSampleRate(double sr) {
sample_rate = sr;
}
// ------------------------------------------------------------------
/** @brief Getter for SetSampleRate()
*/
double GetSampleRate() const {
return sample_rate;
}
// ------------------------------------------------------------------
/** @brief Set the animation time range
*
* @param first Time where the animation starts, in ticks
* @param last Time where the animation ends, in ticks
*/
void SetAnimationRange(double _first, double _last) {
first = _first;
last = _last;
ClearAnimRangeSetup();
UpdateAnimRangeSetup();
}
protected:
// ------------------------------------------------------------------
/** @brief Build linearly subsampled keys from 3 single envelopes
* @param out Receives output keys
* @param envl_x X-component envelope
* @param envl_y Y-component envelope
* @param envl_z Z-component envelope
* @param flags Any combination of the AI_LWO_ANIM_FLAG_XXX flags.
* @note Up to two input envelopes may be NULL
*/
void GetKeys(std::vector<aiVectorKey>& out,
LWO::Envelope* envl_x,
LWO::Envelope* envl_y,
LWO::Envelope* envl_z,
unsigned int flags);
// ------------------------------------------------------------------
/** @brief Resolve a single animation key by applying the right
* interpolation to it.
* @param cur Current key
* @param envl Envelope working on
* @param time time to be interpolated
* @param fill Receives the interpolated output value.
*/
void DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
LWO::Envelope* envl,double time, float& fill);
// ------------------------------------------------------------------
/** @brief Almost the same, except we won't handle pre/post
* conditions here.
* @see DoInterpolation
*/
void DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
std::vector<LWO::Key>::const_iterator end,double time, float& fill);
// ------------------------------------------------------------------
/** @brief Interpolate 2 tracks if one is given
*
* @param out Receives extra output keys
* @param key_out Primary output key
* @param time Time to interpolate for
*/
void InterpolateTrack(std::vector<aiVectorKey>& out,
aiVectorKey& key_out,double time);
// ------------------------------------------------------------------
/** @brief Subsample an animation track by a given sampling rate
*
* @param out Receives output keys. Last key at input defines the
* time where subsampling starts.
* @param time Time to end subsampling at
* @param sample_delta Time delta between two samples
*/
void SubsampleAnimTrack(std::vector<aiVectorKey>& out,
double time,double sample_delta);
// ------------------------------------------------------------------
/** @brief Delete all keys which we inserted to match anim setup
*/
void ClearAnimRangeSetup();
// ------------------------------------------------------------------
/** @brief Insert extra keys to match LWO's pre and post behaviours
* in a given time range [first...last]
*/
void UpdateAnimRangeSetup();
private:
std::list<Envelope>& envelopes;
double sample_rate;
LWO::Envelope* trans_x, *trans_y, *trans_z;
LWO::Envelope* rotat_x, *rotat_y, *rotat_z;
LWO::Envelope* scale_x, *scale_y, *scale_z;
double first, last;
bool need_to_setup;
// temporary storage
LWO::Envelope* envl_x, * envl_y, * envl_z;
std::vector<LWO::Key>::const_iterator cur_x,cur_y,cur_z;
bool end_x, end_y, end_z;
unsigned int flags;
double sample_delta;
};
} // end namespace LWO
} // end namespace Assimp
#endif // !! AI_LWO_ANIMATION_INCLUDED

View File

@@ -0,0 +1,428 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the LWO importer class for the older LWOB
file formats, including materials */
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
// Internal headers
#include "LWOLoader.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWOBFile()
{
LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
bool running = true;
while (running)
{
if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
if (mFileBuffer + head.length > end)
{
throw DeadlyImportError("LWOB: Invalid chunk length");
break;
}
uint8_t* const next = mFileBuffer+head.length;
switch (head.type)
{
// vertex list
case AI_LWO_PNTS:
{
if (!mCurLayer->mTempPoints.empty())
ASSIMP_LOG_WARN("LWO: PNTS chunk encountered twice");
else LoadLWOPoints(head.length);
break;
}
// face list
case AI_LWO_POLS:
{
if (!mCurLayer->mFaces.empty())
ASSIMP_LOG_WARN("LWO: POLS chunk encountered twice");
else LoadLWOBPolygons(head.length);
break;
}
// list of tags
case AI_LWO_SRFS:
{
if (!mTags->empty())
ASSIMP_LOG_WARN("LWO: SRFS chunk encountered twice");
else LoadLWOTags(head.length);
break;
}
// surface chunk
case AI_LWO_SURF:
{
LoadLWOBSurface(head.length);
break;
}
}
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWOBPolygons(unsigned int length)
{
// first find out how many faces and vertices we'll finally need
LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
LE_NCONST uint16_t* cursor = (LE_NCONST uint16_t*)mFileBuffer;
// perform endianness conversions
#ifndef AI_BUILD_BIG_ENDIAN
while (cursor < end)ByteSwap::Swap2(cursor++);
cursor = (LE_NCONST uint16_t*)mFileBuffer;
#endif
unsigned int iNumFaces = 0,iNumVertices = 0;
CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end);
// allocate the output array and copy face indices
if (iNumFaces)
{
cursor = (LE_NCONST uint16_t*)mFileBuffer;
mCurLayer->mFaces.resize(iNumFaces);
FaceList::iterator it = mCurLayer->mFaces.begin();
CopyFaceIndicesLWOB(it,cursor,end);
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces,
LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max)
{
while (cursor < end && max--)
{
uint16_t numIndices;
// must have 2 shorts left for numIndices and surface
if (end - cursor < 2) {
throw DeadlyImportError("LWOB: Unexpected end of file");
}
::memcpy(&numIndices, cursor++, 2);
// must have enough left for indices and surface
if (end - cursor < (1 + numIndices)) {
throw DeadlyImportError("LWOB: Unexpected end of file");
}
verts += numIndices;
faces++;
cursor += numIndices;
int16_t surface;
::memcpy(&surface, cursor++, 2);
if (surface < 0)
{
// there are detail polygons
::memcpy(&numIndices, cursor++, 2);
CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices);
}
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator& it,
LE_NCONST uint16_t*& cursor,
const uint16_t* const end,
unsigned int max)
{
while (cursor < end && max--)
{
LWO::Face& face = *it;++it;
uint16_t numIndices;
::memcpy(&numIndices, cursor++, 2);
face.mNumIndices = numIndices;
if(face.mNumIndices)
{
if (cursor + face.mNumIndices >= end)
{
break;
}
face.mIndices = new unsigned int[face.mNumIndices];
for (unsigned int i = 0; i < face.mNumIndices;++i) {
unsigned int & mi = face.mIndices[i];
uint16_t index;
::memcpy(&index, cursor++, 2);
mi = index;
if (mi > mCurLayer->mTempPoints.size())
{
ASSIMP_LOG_WARN("LWOB: face index is out of range");
mi = (unsigned int)mCurLayer->mTempPoints.size()-1;
}
}
} else {
ASSIMP_LOG_WARN("LWOB: Face has 0 indices");
}
int16_t surface;
::memcpy(&surface, cursor++, 2);
if (surface < 0)
{
surface = -surface;
// there are detail polygons.
uint16_t numPolygons;
::memcpy(&numPolygons, cursor++, 2);
if (cursor < end)
{
CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
}
}
face.surfaceIndex = surface-1;
}
}
// ------------------------------------------------------------------------------------------------
LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned int size)
{
list.push_back(LWO::Texture());
LWO::Texture* tex = &list.back();
std::string type;
GetS0(type,size);
const char* s = type.c_str();
if(strstr(s, "Image Map"))
{
// Determine mapping type
if(strstr(s, "Planar"))
tex->mapMode = LWO::Texture::Planar;
else if(strstr(s, "Cylindrical"))
tex->mapMode = LWO::Texture::Cylindrical;
else if(strstr(s, "Spherical"))
tex->mapMode = LWO::Texture::Spherical;
else if(strstr(s, "Cubic"))
tex->mapMode = LWO::Texture::Cubic;
else if(strstr(s, "Front"))
tex->mapMode = LWO::Texture::FrontProjection;
}
else
{
// procedural or gradient, not supported
ASSIMP_LOG_ERROR_F("LWOB: Unsupported legacy texture: ", type);
}
return tex;
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWOBSurface(unsigned int size)
{
LE_NCONST uint8_t* const end = mFileBuffer + size;
mSurfaces->push_back( LWO::Surface () );
LWO::Surface& surf = mSurfaces->back();
LWO::Texture* pTex = NULL;
GetS0(surf.mName,size);
bool running = true;
while (running) {
if (mFileBuffer + 6 >= end)
break;
IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
/* A single test file (sonycam.lwo) seems to have invalid surface chunks.
* I'm assuming it's the fault of a single, unknown exporter so there are
* probably THOUSANDS of them. Here's a dirty workaround:
*
* We don't break if the chunk limit is exceeded. Instead, we're computing
* how much storage is actually left and work with this value from now on.
*/
if (mFileBuffer + head.length > end) {
ASSIMP_LOG_ERROR("LWOB: Invalid surface chunk length. Trying to continue.");
head.length = (uint16_t) (end - mFileBuffer);
}
uint8_t* const next = mFileBuffer+head.length;
switch (head.type)
{
// diffuse color
case AI_LWO_COLR:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,3);
surf.mColor.r = GetU1() / 255.0f;
surf.mColor.g = GetU1() / 255.0f;
surf.mColor.b = GetU1() / 255.0f;
break;
}
// diffuse strength ...
case AI_LWO_DIFF:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,2);
surf.mDiffuseValue = GetU2() / 255.0f;
break;
}
// specular strength ...
case AI_LWO_SPEC:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,2);
surf.mSpecularValue = GetU2() / 255.0f;
break;
}
// luminosity ...
case AI_LWO_LUMI:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LUMI,2);
surf.mLuminosity = GetU2() / 255.0f;
break;
}
// transparency
case AI_LWO_TRAN:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,2);
surf.mTransparency = GetU2() / 255.0f;
break;
}
// surface flags
case AI_LWO_FLAG:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,FLAG,2);
uint16_t flag = GetU2();
if (flag & 0x4 ) surf.mMaximumSmoothAngle = 1.56207f;
if (flag & 0x8 ) surf.mColorHighlights = 1.f;
if (flag & 0x100) surf.bDoubleSided = true;
break;
}
// maximum smoothing angle
case AI_LWO_SMAN:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4);
surf.mMaximumSmoothAngle = std::fabs( GetF4() );
break;
}
// glossiness
case AI_LWO_GLOS:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,2);
surf.mGlossiness = (float)GetU2();
break;
}
// color texture
case AI_LWO_CTEX:
{
pTex = SetupNewTextureLWOB(surf.mColorTextures,
head.length);
break;
}
// diffuse texture
case AI_LWO_DTEX:
{
pTex = SetupNewTextureLWOB(surf.mDiffuseTextures,
head.length);
break;
}
// specular texture
case AI_LWO_STEX:
{
pTex = SetupNewTextureLWOB(surf.mSpecularTextures,
head.length);
break;
}
// bump texture
case AI_LWO_BTEX:
{
pTex = SetupNewTextureLWOB(surf.mBumpTextures,
head.length);
break;
}
// transparency texture
case AI_LWO_TTEX:
{
pTex = SetupNewTextureLWOB(surf.mOpacityTextures,
head.length);
break;
}
// texture path
case AI_LWO_TIMG:
{
if (pTex) {
GetS0(pTex->mFileName,head.length);
} else {
ASSIMP_LOG_WARN("LWOB: Unexpected TIMG chunk");
}
break;
}
// texture strength
case AI_LWO_TVAL:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TVAL,1);
if (pTex) {
pTex->mStrength = (float)GetU1()/ 255.f;
} else {
ASSIMP_LOG_ERROR("LWOB: Unexpected TVAL chunk");
}
break;
}
// texture flags
case AI_LWO_TFLG:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TFLG,2);
if (nullptr != pTex) {
const uint16_t s = GetU2();
if (s & 1)
pTex->majorAxis = LWO::Texture::AXIS_X;
else if (s & 2)
pTex->majorAxis = LWO::Texture::AXIS_Y;
else if (s & 4)
pTex->majorAxis = LWO::Texture::AXIS_Z;
if (s & 16) {
ASSIMP_LOG_WARN("LWOB: Ignoring \'negate\' flag on texture");
}
}
else {
ASSIMP_LOG_WARN("LWOB: Unexpected TFLG chunk");
}
break;
}
}
mFileBuffer = next;
}
}
#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER

703
thirdparty/assimp/code/LWO/LWOFileData.h vendored Normal file
View File

@@ -0,0 +1,703 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file LWOFileData.h
* @brief Defines chunk constants used by the LWO file format
The chunks are taken from the official LightWave SDK headers.
*/
#ifndef AI_LWO_FILEDATA_INCLUDED
#define AI_LWO_FILEDATA_INCLUDED
// STL headers
#include <vector>
#include <list>
// public ASSIMP headers
#include <assimp/mesh.h>
// internal headers
#include "Common/IFF.h"
#include "LWO/LWOAnimation.h"
namespace Assimp {
namespace LWO {
#define AI_LWO_FOURCC_LWOB AI_IFF_FOURCC('L','W','O','B')
#define AI_LWO_FOURCC_LWO2 AI_IFF_FOURCC('L','W','O','2')
#define AI_LWO_FOURCC_LXOB AI_IFF_FOURCC('L','X','O','B')
// chunks specific to the LWOB format
#define AI_LWO_SRFS AI_IFF_FOURCC('S','R','F','S')
#define AI_LWO_FLAG AI_IFF_FOURCC('F','L','A','G')
#define AI_LWO_VLUM AI_IFF_FOURCC('V','L','U','M')
#define AI_LWO_VDIF AI_IFF_FOURCC('V','D','I','F')
#define AI_LWO_VSPC AI_IFF_FOURCC('V','S','P','C')
#define AI_LWO_RFLT AI_IFF_FOURCC('R','F','L','T')
#define AI_LWO_BTEX AI_IFF_FOURCC('B','T','E','X')
#define AI_LWO_CTEX AI_IFF_FOURCC('C','T','E','X')
#define AI_LWO_DTEX AI_IFF_FOURCC('D','T','E','X')
#define AI_LWO_LTEX AI_IFF_FOURCC('L','T','E','X')
#define AI_LWO_RTEX AI_IFF_FOURCC('R','T','E','X')
#define AI_LWO_STEX AI_IFF_FOURCC('S','T','E','X')
#define AI_LWO_TTEX AI_IFF_FOURCC('T','T','E','X')
#define AI_LWO_TFLG AI_IFF_FOURCC('T','F','L','G')
#define AI_LWO_TSIZ AI_IFF_FOURCC('T','S','I','Z')
#define AI_LWO_TCTR AI_IFF_FOURCC('T','C','T','R')
#define AI_LWO_TFAL AI_IFF_FOURCC('T','F','A','L')
#define AI_LWO_TVEL AI_IFF_FOURCC('T','V','E','L')
#define AI_LWO_TCLR AI_IFF_FOURCC('T','C','L','R')
#define AI_LWO_TVAL AI_IFF_FOURCC('T','V','A','L')
#define AI_LWO_TAMP AI_IFF_FOURCC('T','A','M','P')
#define AI_LWO_TIMG AI_IFF_FOURCC('T','I','M','G')
#define AI_LWO_TAAS AI_IFF_FOURCC('T','A','A','S')
#define AI_LWO_TREF AI_IFF_FOURCC('T','R','E','F')
#define AI_LWO_TOPC AI_IFF_FOURCC('T','O','P','C')
#define AI_LWO_SDAT AI_IFF_FOURCC('S','D','A','T')
#define AI_LWO_TFP0 AI_IFF_FOURCC('T','F','P','0')
#define AI_LWO_TFP1 AI_IFF_FOURCC('T','F','P','1')
/* top-level chunks */
#define AI_LWO_LAYR AI_IFF_FOURCC('L','A','Y','R')
#define AI_LWO_TAGS AI_IFF_FOURCC('T','A','G','S')
#define AI_LWO_PNTS AI_IFF_FOURCC('P','N','T','S')
#define AI_LWO_BBOX AI_IFF_FOURCC('B','B','O','X')
#define AI_LWO_VMAP AI_IFF_FOURCC('V','M','A','P')
#define AI_LWO_VMAD AI_IFF_FOURCC('V','M','A','D')
#define AI_LWO_POLS AI_IFF_FOURCC('P','O','L','S')
#define AI_LWO_PTAG AI_IFF_FOURCC('P','T','A','G')
#define AI_LWO_ENVL AI_IFF_FOURCC('E','N','V','L')
#define AI_LWO_CLIP AI_IFF_FOURCC('C','L','I','P')
#define AI_LWO_SURF AI_IFF_FOURCC('S','U','R','F')
#define AI_LWO_DESC AI_IFF_FOURCC('D','E','S','C')
#define AI_LWO_TEXT AI_IFF_FOURCC('T','E','X','T')
#define AI_LWO_ICON AI_IFF_FOURCC('I','C','O','N')
/* polygon types */
#define AI_LWO_FACE AI_IFF_FOURCC('F','A','C','E')
#define AI_LWO_CURV AI_IFF_FOURCC('C','U','R','V')
#define AI_LWO_PTCH AI_IFF_FOURCC('P','T','C','H')
#define AI_LWO_MBAL AI_IFF_FOURCC('M','B','A','L')
#define AI_LWO_BONE AI_IFF_FOURCC('B','O','N','E')
#define AI_LWO_SUBD AI_IFF_FOURCC('S','U','B','D')
/* polygon tags */
#define AI_LWO_SURF AI_IFF_FOURCC('S','U','R','F')
#define AI_LWO_PART AI_IFF_FOURCC('P','A','R','T')
#define AI_LWO_SMGP AI_IFF_FOURCC('S','M','G','P')
/* envelopes */
#define AI_LWO_PRE AI_IFF_FOURCC('P','R','E',' ')
#define AI_LWO_POST AI_IFF_FOURCC('P','O','S','T')
#define AI_LWO_KEY AI_IFF_FOURCC('K','E','Y',' ')
#define AI_LWO_SPAN AI_IFF_FOURCC('S','P','A','N')
#define AI_LWO_TCB AI_IFF_FOURCC('T','C','B',' ')
#define AI_LWO_HERM AI_IFF_FOURCC('H','E','R','M')
#define AI_LWO_BEZI AI_IFF_FOURCC('B','E','Z','I')
#define AI_LWO_BEZ2 AI_IFF_FOURCC('B','E','Z','2')
#define AI_LWO_LINE AI_IFF_FOURCC('L','I','N','E')
#define AI_LWO_STEP AI_IFF_FOURCC('S','T','E','P')
/* clips */
#define AI_LWO_STIL AI_IFF_FOURCC('S','T','I','L')
#define AI_LWO_ISEQ AI_IFF_FOURCC('I','S','E','Q')
#define AI_LWO_ANIM AI_IFF_FOURCC('A','N','I','M')
#define AI_LWO_XREF AI_IFF_FOURCC('X','R','E','F')
#define AI_LWO_STCC AI_IFF_FOURCC('S','T','C','C')
#define AI_LWO_TIME AI_IFF_FOURCC('T','I','M','E')
#define AI_LWO_CONT AI_IFF_FOURCC('C','O','N','T')
#define AI_LWO_BRIT AI_IFF_FOURCC('B','R','I','T')
#define AI_LWO_SATR AI_IFF_FOURCC('S','A','T','R')
#define AI_LWO_HUE AI_IFF_FOURCC('H','U','E',' ')
#define AI_LWO_GAMM AI_IFF_FOURCC('G','A','M','M')
#define AI_LWO_NEGA AI_IFF_FOURCC('N','E','G','A')
#define AI_LWO_IFLT AI_IFF_FOURCC('I','F','L','T')
#define AI_LWO_PFLT AI_IFF_FOURCC('P','F','L','T')
/* surfaces */
#define AI_LWO_COLR AI_IFF_FOURCC('C','O','L','R')
#define AI_LWO_LUMI AI_IFF_FOURCC('L','U','M','I')
#define AI_LWO_DIFF AI_IFF_FOURCC('D','I','F','F')
#define AI_LWO_SPEC AI_IFF_FOURCC('S','P','E','C')
#define AI_LWO_GLOS AI_IFF_FOURCC('G','L','O','S')
#define AI_LWO_REFL AI_IFF_FOURCC('R','E','F','L')
#define AI_LWO_RFOP AI_IFF_FOURCC('R','F','O','P')
#define AI_LWO_RIMG AI_IFF_FOURCC('R','I','M','G')
#define AI_LWO_RSAN AI_IFF_FOURCC('R','S','A','N')
#define AI_LWO_TRAN AI_IFF_FOURCC('T','R','A','N')
#define AI_LWO_TROP AI_IFF_FOURCC('T','R','O','P')
#define AI_LWO_TIMG AI_IFF_FOURCC('T','I','M','G')
#define AI_LWO_RIND AI_IFF_FOURCC('R','I','N','D')
#define AI_LWO_TRNL AI_IFF_FOURCC('T','R','N','L')
#define AI_LWO_BUMP AI_IFF_FOURCC('B','U','M','P')
#define AI_LWO_SMAN AI_IFF_FOURCC('S','M','A','N')
#define AI_LWO_SIDE AI_IFF_FOURCC('S','I','D','E')
#define AI_LWO_CLRH AI_IFF_FOURCC('C','L','R','H')
#define AI_LWO_CLRF AI_IFF_FOURCC('C','L','R','F')
#define AI_LWO_ADTR AI_IFF_FOURCC('A','D','T','R')
#define AI_LWO_SHRP AI_IFF_FOURCC('S','H','R','P')
#define AI_LWO_LINE AI_IFF_FOURCC('L','I','N','E')
#define AI_LWO_LSIZ AI_IFF_FOURCC('L','S','I','Z')
#define AI_LWO_ALPH AI_IFF_FOURCC('A','L','P','H')
#define AI_LWO_AVAL AI_IFF_FOURCC('A','V','A','L')
#define AI_LWO_GVAL AI_IFF_FOURCC('G','V','A','L')
#define AI_LWO_BLOK AI_IFF_FOURCC('B','L','O','K')
#define AI_LWO_VCOL AI_IFF_FOURCC('V','C','O','L')
/* texture layer */
#define AI_LWO_TYPE AI_IFF_FOURCC('T','Y','P','E')
#define AI_LWO_CHAN AI_IFF_FOURCC('C','H','A','N')
#define AI_LWO_NAME AI_IFF_FOURCC('N','A','M','E')
#define AI_LWO_ENAB AI_IFF_FOURCC('E','N','A','B')
#define AI_LWO_OPAC AI_IFF_FOURCC('O','P','A','C')
#define AI_LWO_FLAG AI_IFF_FOURCC('F','L','A','G')
#define AI_LWO_PROJ AI_IFF_FOURCC('P','R','O','J')
#define AI_LWO_STCK AI_IFF_FOURCC('S','T','C','K')
#define AI_LWO_TAMP AI_IFF_FOURCC('T','A','M','P')
/* texture coordinates */
#define AI_LWO_TMAP AI_IFF_FOURCC('T','M','A','P')
#define AI_LWO_AXIS AI_IFF_FOURCC('A','X','I','S')
#define AI_LWO_CNTR AI_IFF_FOURCC('C','N','T','R')
#define AI_LWO_SIZE AI_IFF_FOURCC('S','I','Z','E')
#define AI_LWO_ROTA AI_IFF_FOURCC('R','O','T','A')
#define AI_LWO_OREF AI_IFF_FOURCC('O','R','E','F')
#define AI_LWO_FALL AI_IFF_FOURCC('F','A','L','L')
#define AI_LWO_CSYS AI_IFF_FOURCC('C','S','Y','S')
/* image map */
#define AI_LWO_IMAP AI_IFF_FOURCC('I','M','A','P')
#define AI_LWO_IMAG AI_IFF_FOURCC('I','M','A','G')
#define AI_LWO_WRAP AI_IFF_FOURCC('W','R','A','P')
#define AI_LWO_WRPW AI_IFF_FOURCC('W','R','P','W')
#define AI_LWO_WRPH AI_IFF_FOURCC('W','R','P','H')
#define AI_LWO_VMAP AI_IFF_FOURCC('V','M','A','P')
#define AI_LWO_AAST AI_IFF_FOURCC('A','A','S','T')
#define AI_LWO_PIXB AI_IFF_FOURCC('P','I','X','B')
/* procedural */
#define AI_LWO_PROC AI_IFF_FOURCC('P','R','O','C')
#define AI_LWO_COLR AI_IFF_FOURCC('C','O','L','R')
#define AI_LWO_VALU AI_IFF_FOURCC('V','A','L','U')
#define AI_LWO_FUNC AI_IFF_FOURCC('F','U','N','C')
#define AI_LWO_FTPS AI_IFF_FOURCC('F','T','P','S')
#define AI_LWO_ITPS AI_IFF_FOURCC('I','T','P','S')
#define AI_LWO_ETPS AI_IFF_FOURCC('E','T','P','S')
/* gradient */
#define AI_LWO_GRAD AI_IFF_FOURCC('G','R','A','D')
#define AI_LWO_GRST AI_IFF_FOURCC('G','R','S','T')
#define AI_LWO_GREN AI_IFF_FOURCC('G','R','E','N')
#define AI_LWO_PNAM AI_IFF_FOURCC('P','N','A','M')
#define AI_LWO_INAM AI_IFF_FOURCC('I','N','A','M')
#define AI_LWO_GRPT AI_IFF_FOURCC('G','R','P','T')
#define AI_LWO_FKEY AI_IFF_FOURCC('F','K','E','Y')
#define AI_LWO_IKEY AI_IFF_FOURCC('I','K','E','Y')
/* shader */
#define AI_LWO_SHDR AI_IFF_FOURCC('S','H','D','R')
#define AI_LWO_DATA AI_IFF_FOURCC('D','A','T','A')
/* VMAP types */
#define AI_LWO_TXUV AI_IFF_FOURCC('T','X','U','V')
#define AI_LWO_RGB AI_IFF_FOURCC('R','G','B',' ')
#define AI_LWO_RGBA AI_IFF_FOURCC('R','G','B','A')
#define AI_LWO_WGHT AI_IFF_FOURCC('W','G','H','T')
#define AI_LWO_MNVW AI_IFF_FOURCC('M','N','V','W')
#define AI_LWO_MORF AI_IFF_FOURCC('M','O','R','F')
#define AI_LWO_SPOT AI_IFF_FOURCC('S','P','O','T')
#define AI_LWO_PICK AI_IFF_FOURCC('P','I','C','K')
// MODO extension - per-vertex normal vectors
#define AI_LWO_MODO_NORM AI_IFF_FOURCC('N', 'O', 'R', 'M')
// ---------------------------------------------------------------------------
/** \brief Data structure for a face in a LWO file
*
* \note We can't use the code in SmoothingGroups.inl here - the mesh
* structures of 3DS/ASE and LWO are too different.
*/
struct Face : public aiFace {
//! Default construction
Face() AI_NO_EXCEPT
: surfaceIndex( 0 )
, smoothGroup( 0 )
, type( AI_LWO_FACE ) {
// empty
}
//! Construction from given type
explicit Face(uint32_t _type)
: surfaceIndex (0)
, smoothGroup (0)
, type (_type)
{}
//! Copy construction
Face(const Face& f) : aiFace() {
*this = f;
}
//! Zero-based index into tags chunk
unsigned int surfaceIndex;
//! Smooth group this face is assigned to
unsigned int smoothGroup;
//! Type of face
uint32_t type;
//! Assignment operator
Face& operator=(const LWO::Face& f) {
aiFace::operator =(f);
surfaceIndex = f.surfaceIndex;
smoothGroup = f.smoothGroup;
type = f.type;
return *this;
}
};
// ---------------------------------------------------------------------------
/** \brief Base structure for all vertex map representations
*/
struct VMapEntry
{
explicit VMapEntry(unsigned int _dims)
: dims(_dims)
{}
virtual ~VMapEntry() {}
//! allocates memory for the vertex map
virtual void Allocate(unsigned int num)
{
if (!rawData.empty())
return; // return if already allocated
const unsigned int m = num*dims;
rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs
rawData.resize(m,0.f);
abAssigned.resize(num,false);
}
std::string name;
unsigned int dims;
std::vector<float> rawData;
std::vector<bool> abAssigned;
};
// ---------------------------------------------------------------------------
/** \brief Represents an extra vertex color channel
*/
struct VColorChannel : public VMapEntry
{
VColorChannel()
: VMapEntry(4)
{}
//! need to overwrite this function - the alpha channel must
//! be initialized to 1.0 by default
virtual void Allocate(unsigned int num)
{
if (!rawData.empty())
return; // return if already allocated
unsigned int m = num*dims;
rawData.reserve(m + (m>>2u)); // 25% as extra storage for VMADs
rawData.resize(m);
for (aiColor4D* p = (aiColor4D*)&rawData[0]; p < (aiColor4D*)&rawData[m-1]; ++p)
p->a = 1.f;
abAssigned.resize(num,false);
}
};
// ---------------------------------------------------------------------------
/** \brief Represents an extra vertex UV channel
*/
struct UVChannel : public VMapEntry
{
UVChannel()
: VMapEntry(2)
{}
};
// ---------------------------------------------------------------------------
/** \brief Represents a weight map
*/
struct WeightChannel : public VMapEntry
{
WeightChannel()
: VMapEntry(1)
{}
};
// ---------------------------------------------------------------------------
/** \brief Represents a vertex-normals channel (MODO extension)
*/
struct NormalChannel : public VMapEntry
{
NormalChannel()
: VMapEntry(3)
{}
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO file texture
*/
struct Texture
{
// we write the enum values out here to make debugging easier ...
enum BlendType
{
Normal = 0,
Subtractive = 1,
Difference = 2,
Multiply = 3,
Divide = 4,
Alpha = 5,
TextureDispl = 6,
Additive = 7
};
enum MappingMode
{
Planar = 0,
Cylindrical = 1,
Spherical = 2,
Cubic = 3,
FrontProjection = 4,
UV = 5
};
enum Axes
{
AXIS_X = 0,
AXIS_Y = 1,
AXIS_Z = 2
};
enum Wrap
{
RESET = 0,
REPEAT = 1,
MIRROR = 2,
EDGE = 3
};
Texture()
: mClipIdx(UINT_MAX)
, mStrength (1.0f)
, type()
, mUVChannelIndex ("unknown")
, mRealUVIndex (UINT_MAX)
, enabled (true)
, blendType (Additive)
, bCanUse (true)
, mapMode (UV)
, majorAxis (AXIS_X)
, wrapAmountH (1.0f)
, wrapAmountW (1.0f)
, wrapModeWidth (REPEAT)
, wrapModeHeight (REPEAT)
, ordinal ("\x00")
{}
//! File name of the texture
std::string mFileName;
//! Clip index
unsigned int mClipIdx;
//! Strength of the texture - blend factor
float mStrength;
uint32_t type; // type of the texture
//! Name of the corresponding UV channel
std::string mUVChannelIndex;
unsigned int mRealUVIndex;
//! is the texture enabled?
bool enabled;
//! blend type
BlendType blendType;
//! are we able to use the texture?
bool bCanUse;
//! mapping mode
MappingMode mapMode;
//! major axis for planar, cylindrical, spherical projections
Axes majorAxis;
//! wrap amount for cylindrical and spherical projections
float wrapAmountH,wrapAmountW;
//! wrapping mode for the texture
Wrap wrapModeWidth,wrapModeHeight;
//! ordinal string of the texture
std::string ordinal;
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO file clip
*/
struct Clip
{
enum Type
{
STILL, SEQ, REF, UNSUPPORTED
} type;
Clip()
: type (UNSUPPORTED)
, clipRef()
, idx (0)
, negate (false)
{}
//! path to the base texture -
std::string path;
//! reference to another CLIP
unsigned int clipRef;
//! index of the clip
unsigned int idx;
//! Negate the clip?
bool negate;
};
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO file shader
*
* Later
*/
struct Shader
{
Shader()
: ordinal ("\x00")
, functionName ("unknown")
, enabled (true)
{}
std::string ordinal;
std::string functionName;
bool enabled;
};
typedef std::list < Texture > TextureList;
typedef std::list < Shader > ShaderList;
// ---------------------------------------------------------------------------
/** \brief Data structure for a LWO file surface (= material)
*/
struct Surface
{
Surface()
: mColor (0.78431f,0.78431f,0.78431f)
, bDoubleSided (false)
, mDiffuseValue (1.f)
, mSpecularValue (0.f)
, mTransparency (0.f)
, mGlossiness (0.4f)
, mLuminosity (0.f)
, mColorHighlights (0.f)
, mMaximumSmoothAngle (0.f) // 0 == not specified, no smoothing
, mVCMap ("")
, mVCMapType (AI_LWO_RGBA)
, mIOR (1.f) // vakuum
, mBumpIntensity (1.f)
, mWireframe (false)
, mAdditiveTransparency (0.f)
{}
//! Name of the surface
std::string mName;
//! Color of the surface
aiColor3D mColor;
//! true for two-sided materials
bool bDoubleSided;
//! Various material parameters
float mDiffuseValue,mSpecularValue,mTransparency,mGlossiness,mLuminosity,mColorHighlights;
//! Maximum angle between two adjacent triangles
//! that they can be smoothed - in degrees
float mMaximumSmoothAngle;
//! Vertex color map to be used to color the surface
std::string mVCMap;
uint32_t mVCMapType;
//! Names of the special shaders to be applied to the surface
ShaderList mShaders;
//! Textures - the first entry in the list is evaluated first
TextureList mColorTextures, // color textures are added to both diffuse and specular texture stacks
mDiffuseTextures,
mSpecularTextures,
mOpacityTextures,
mBumpTextures,
mGlossinessTextures,
mReflectionTextures;
//! Index of refraction
float mIOR;
//! Bump intensity scaling
float mBumpIntensity;
//! Wireframe flag
bool mWireframe;
//! Intensity of additive blending
float mAdditiveTransparency;
};
// ---------------------------------------------------------------------------
#define AI_LWO_VALIDATE_CHUNK_LENGTH(length,name,size) \
if (length < size) \
{ \
throw DeadlyImportError("LWO: "#name" chunk is too small"); \
} \
// some typedefs ... to make life with loader monsters like this easier
typedef std::vector < aiVector3D > PointList;
typedef std::vector < LWO::Face > FaceList;
typedef std::vector < LWO::Surface > SurfaceList;
typedef std::vector < std::string > TagList;
typedef std::vector < unsigned int > TagMappingTable;
typedef std::vector < unsigned int > ReferrerList;
typedef std::vector < WeightChannel > WeightChannelList;
typedef std::vector < VColorChannel > VColorChannelList;
typedef std::vector < UVChannel > UVChannelList;
typedef std::vector < Clip > ClipList;
typedef std::vector < Envelope > EnvelopeList;
typedef std::vector < unsigned int > SortedRep;
// ---------------------------------------------------------------------------
/** \brief Represents a layer in the file
*/
struct Layer
{
Layer()
: mFaceIDXOfs (0)
, mPointIDXOfs (0)
, mParent (0x0)
, mIndex (0xffff)
, skip (false)
{}
/** Temporary point list from the file */
PointList mTempPoints;
/** Lists for every point the index of another point
that has been copied from *this* point or UINT_MAX if
no copy of the point has been made */
ReferrerList mPointReferrers;
/** Weight channel list from the file */
WeightChannelList mWeightChannels;
/** Subdivision weight channel list from the file */
WeightChannelList mSWeightChannels;
/** Vertex color list from the file */
VColorChannelList mVColorChannels;
/** UV channel list from the file */
UVChannelList mUVChannels;
/** Normal vector channel from the file */
NormalChannel mNormals;
/** Temporary face list from the file*/
FaceList mFaces;
/** Current face indexing offset from the beginning of the buffers*/
unsigned int mFaceIDXOfs;
/** Current point indexing offset from the beginning of the buffers*/
unsigned int mPointIDXOfs;
/** Parent index */
uint16_t mParent;
/** Index of the layer */
uint16_t mIndex;
/** Name of the layer */
std::string mName;
/** Pivot point of the layer */
aiVector3D mPivot;
/** Skip this layer? */
bool skip;
};
typedef std::list<LWO::Layer> LayerList;
}}
#endif // !! AI_LWO_FILEDATA_INCLUDED

1490
thirdparty/assimp/code/LWO/LWOLoader.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

490
thirdparty/assimp/code/LWO/LWOLoader.h vendored Normal file
View File

@@ -0,0 +1,490 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the LWO importer class. */
#ifndef AI_LWOLOADER_H_INCLUDED
#define AI_LWOLOADER_H_INCLUDED
#include <assimp/types.h>
#include <assimp/material.h>
#include <assimp/DefaultLogger.hpp>
#include "LWOFileData.h"
#include <assimp/BaseImporter.h>
#include <map>
struct aiTexture;
struct aiNode;
struct aiMaterial;
namespace Assimp {
using namespace LWO;
// ---------------------------------------------------------------------------
/** Class to load LWO files.
*
* @note Methods named "xxxLWO2[xxx]" are used with the newer LWO2 format.
* Methods named "xxxLWOB[xxx]" are used with the older LWOB format.
* Methods named "xxxLWO[xxx]" are used with both formats.
* Methods named "xxx" are used to preprocess the loaded data -
* they aren't specific to one format version
*/
// ---------------------------------------------------------------------------
class LWOImporter : public BaseImporter
{
public:
LWOImporter();
~LWOImporter();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details.
*/
bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
bool checkSig) const;
// -------------------------------------------------------------------
/** Called prior to ReadFile().
* The function is a request to the importer to update its configuration
* basing on the Importer's configuration property list.
*/
void SetupProperties(const Importer* pImp);
protected:
// -------------------------------------------------------------------
// Get list of supported extensions
const aiImporterDesc* GetInfo () const;
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
private:
// -------------------------------------------------------------------
/** Loads a LWO file in the older LWOB format (LW < 6)
*/
void LoadLWOBFile();
// -------------------------------------------------------------------
/** Loads a LWO file in the newer LWO2 format (LW >= 6)
*/
void LoadLWO2File();
// -------------------------------------------------------------------
/** Parsing functions used for all file format versions
*/
inline void GetS0(std::string& out,unsigned int max);
inline float GetF4();
inline uint32_t GetU4();
inline uint16_t GetU2();
inline uint8_t GetU1();
// -------------------------------------------------------------------
/** Loads a surface chunk from an LWOB file
* @param size Maximum size to be read, in bytes.
*/
void LoadLWOBSurface(unsigned int size);
// -------------------------------------------------------------------
/** Loads a surface chunk from an LWO2 file
* @param size Maximum size to be read, in bytes.
*/
void LoadLWO2Surface(unsigned int size);
// -------------------------------------------------------------------
/** Loads a texture block from a LWO2 file.
* @param size Maximum size to be read, in bytes.
* @param head Header of the SUF.BLOK header
*/
void LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head,
unsigned int size );
// -------------------------------------------------------------------
/** Loads a shader block from a LWO2 file.
* @param size Maximum size to be read, in bytes.
* @param head Header of the SUF.BLOK header
*/
void LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* head,
unsigned int size );
// -------------------------------------------------------------------
/** Loads an image map from a LWO2 file
* @param size Maximum size to be read, in bytes.
* @param tex Texture object to be filled
*/
void LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex );
void LoadLWO2Gradient(unsigned int size, LWO::Texture& tex );
void LoadLWO2Procedural(unsigned int size, LWO::Texture& tex );
// loads the header - used by thethree functions above
void LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex );
// -------------------------------------------------------------------
/** Loads the LWO tag list from the file
* @param size Maximum size to be read, in bytes.
*/
void LoadLWOTags(unsigned int size);
// -------------------------------------------------------------------
/** Load polygons from a POLS chunk
* @param length Size of the chunk
*/
void LoadLWO2Polygons(unsigned int length);
void LoadLWOBPolygons(unsigned int length);
// -------------------------------------------------------------------
/** Load polygon tags from a PTAG chunk
* @param length Size of the chunk
*/
void LoadLWO2PolygonTags(unsigned int length);
// -------------------------------------------------------------------
/** Load a vertex map from a VMAP/VMAD chunk
* @param length Size of the chunk
* @param perPoly Operate on per-polygon base?
*/
void LoadLWO2VertexMap(unsigned int length, bool perPoly);
// -------------------------------------------------------------------
/** Load polygons from a PNTS chunk
* @param length Size of the chunk
*/
void LoadLWOPoints(unsigned int length);
// -------------------------------------------------------------------
/** Load a clip from a CLIP chunk
* @param length Size of the chunk
*/
void LoadLWO2Clip(unsigned int length);
// -------------------------------------------------------------------
/** Load an envelope from an EVL chunk
* @param length Size of the chunk
*/
void LoadLWO2Envelope(unsigned int length);
// -------------------------------------------------------------------
/** Count vertices and faces in a LWOB/LWO2 file
*/
void CountVertsAndFacesLWO2(unsigned int& verts,
unsigned int& faces,
uint16_t*& cursor,
const uint16_t* const end,
unsigned int max = UINT_MAX);
void CountVertsAndFacesLWOB(unsigned int& verts,
unsigned int& faces,
LE_NCONST uint16_t*& cursor,
const uint16_t* const end,
unsigned int max = UINT_MAX);
// -------------------------------------------------------------------
/** Read vertices and faces in a LWOB/LWO2 file
*/
void CopyFaceIndicesLWO2(LWO::FaceList::iterator& it,
uint16_t*& cursor,
const uint16_t* const end);
// -------------------------------------------------------------------
void CopyFaceIndicesLWOB(LWO::FaceList::iterator& it,
LE_NCONST uint16_t*& cursor,
const uint16_t* const end,
unsigned int max = UINT_MAX);
// -------------------------------------------------------------------
/** Resolve the tag and surface lists that have been loaded.
* Generates the mMapping table.
*/
void ResolveTags();
// -------------------------------------------------------------------
/** Resolve the clip list that has been loaded.
* Replaces clip references with real clips.
*/
void ResolveClips();
// -------------------------------------------------------------------
/** Add a texture list to an output material description.
*
* @param pcMat Output material
* @param in Input texture list
* @param type Type identifier of the texture list
*/
bool HandleTextures(aiMaterial* pcMat, const TextureList& in,
aiTextureType type);
// -------------------------------------------------------------------
/** Adjust a texture path
*/
void AdjustTexturePath(std::string& out);
// -------------------------------------------------------------------
/** Convert a LWO surface description to an ASSIMP material
*/
void ConvertMaterial(const LWO::Surface& surf,aiMaterial* pcMat);
// -------------------------------------------------------------------
/** Get a list of all UV/VC channels required by a specific surface.
*
* @param surf Working surface
* @param layer Working layer
* @param out Output list. The members are indices into the
* UV/VC channel lists of the layer
*/
void FindUVChannels(/*const*/ LWO::Surface& surf,
LWO::SortedRep& sorted,
/*const*/ LWO::Layer& layer,
unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS]);
// -------------------------------------------------------------------
char FindUVChannels(LWO::TextureList& list,
LWO::Layer& layer,LWO::UVChannel& uv, unsigned int next);
// -------------------------------------------------------------------
void FindVCChannels(const LWO::Surface& surf,
LWO::SortedRep& sorted,
const LWO::Layer& layer,
unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS]);
// -------------------------------------------------------------------
/** Generate the final node graph
* Unused nodes are deleted.
* @param apcNodes Flat list of nodes
*/
void GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes);
// -------------------------------------------------------------------
/** Add children to a node
* @param node Node to become a father
* @param parent Index of the node
* @param apcNodes Flat list of nodes - used nodes are set to NULL.
*/
void AddChildren(aiNode* node, uint16_t parent,
std::vector<aiNode*>& apcNodes);
// -------------------------------------------------------------------
/** Read a variable sized integer
* @param inout Input and output buffer
*/
int ReadVSizedIntLWO2(uint8_t*& inout);
// -------------------------------------------------------------------
/** Assign a value from a VMAP to a vertex and all vertices
* attached to it.
* @param base VMAP destination data
* @param numRead Number of float's to be read
* @param idx Absolute index of the first vertex
* @param data Value of the VMAP to be assigned - read numRead
* floats from this array.
*/
void DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead,
unsigned int idx, float* data);
// -------------------------------------------------------------------
/** Compute normal vectors for a mesh
* @param mesh Input mesh
* @param smoothingGroups Smoothing-groups-per-face array
* @param surface Surface for the mesh
*/
void ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
const LWO::Surface& surface);
// -------------------------------------------------------------------
/** Setup a new texture after the corresponding chunk was
* encountered in the file.
* @param list Texture list
* @param size Maximum number of bytes to be read
* @return Pointer to new texture
*/
LWO::Texture* SetupNewTextureLWOB(LWO::TextureList& list,
unsigned int size);
protected:
/** true if the file is a LWO2 file*/
bool mIsLWO2;
/** true if the file is a LXOB file*/
bool mIsLXOB;
/** Temporary list of layers from the file */
LayerList* mLayers;
/** Pointer to the current layer */
LWO::Layer* mCurLayer;
/** Temporary tag list from the file */
TagList* mTags;
/** Mapping table to convert from tag to surface indices.
UINT_MAX indicates that a no corresponding surface is available */
TagMappingTable* mMapping;
/** Temporary surface list from the file */
SurfaceList* mSurfaces;
/** Temporary clip list from the file */
ClipList mClips;
/** Temporary envelope list from the file */
EnvelopeList mEnvelopes;
/** file buffer */
uint8_t* mFileBuffer;
/** Size of the file, in bytes */
unsigned int fileSize;
/** Output scene */
aiScene* pScene;
/** Configuration option: speed flag set? */
bool configSpeedFlag;
/** Configuration option: index of layer to be loaded */
unsigned int configLayerIndex;
/** Configuration option: name of layer to be loaded */
std::string configLayerName;
/** True if we have a named layer */
bool hasNamedLayer;
};
// ------------------------------------------------------------------------------------------------
inline float LWOImporter::GetF4()
{
float f;
::memcpy(&f, mFileBuffer, 4);
mFileBuffer += 4;
AI_LSWAP4(f);
return f;
}
// ------------------------------------------------------------------------------------------------
inline uint32_t LWOImporter::GetU4()
{
uint32_t f;
::memcpy(&f, mFileBuffer, 4);
mFileBuffer += 4;
AI_LSWAP4(f);
return f;
}
// ------------------------------------------------------------------------------------------------
inline uint16_t LWOImporter::GetU2()
{
uint16_t f;
::memcpy(&f, mFileBuffer, 2);
mFileBuffer += 2;
AI_LSWAP2(f);
return f;
}
// ------------------------------------------------------------------------------------------------
inline uint8_t LWOImporter::GetU1()
{
return *mFileBuffer++;
}
// ------------------------------------------------------------------------------------------------
inline int LWOImporter::ReadVSizedIntLWO2(uint8_t*& inout)
{
int i;
int c = *inout;inout++;
if(c != 0xFF)
{
i = c << 8;
c = *inout;inout++;
i |= c;
}
else
{
c = *inout;inout++;
i = c << 16;
c = *inout;inout++;
i |= c << 8;
c = *inout;inout++;
i |= c;
}
return i;
}
// ------------------------------------------------------------------------------------------------
inline void LWOImporter::GetS0(std::string& out,unsigned int max)
{
unsigned int iCursor = 0;
const char*sz = (const char*)mFileBuffer;
while (*mFileBuffer)
{
if (++iCursor > max)
{
ASSIMP_LOG_WARN("LWO: Invalid file, string is is too long");
break;
}
++mFileBuffer;
}
size_t len = (size_t) ((const char*)mFileBuffer-sz);
out = std::string(sz,len);
mFileBuffer += (len&0x1 ? 1 : 2);
}
} // end of namespace Assimp
#endif // AI_LWOIMPORTER_H_INCLUDED

View File

@@ -0,0 +1,898 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the material oart of the LWO importer class */
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
// internal headers
#include "LWOLoader.h"
#include <assimp/ByteSwapper.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
template <class T>
T lerp(const T& one, const T& two, float val)
{
return one + (two-one)*val;
}
// ------------------------------------------------------------------------------------------------
// Convert a lightwave mapping mode to our's
inline aiTextureMapMode GetMapMode(LWO::Texture::Wrap in)
{
switch (in)
{
case LWO::Texture::REPEAT:
return aiTextureMapMode_Wrap;
case LWO::Texture::MIRROR:
return aiTextureMapMode_Mirror;
case LWO::Texture::RESET:
ASSIMP_LOG_WARN("LWO2: Unsupported texture map mode: RESET");
// fall though here
case LWO::Texture::EDGE:
return aiTextureMapMode_Clamp;
}
return (aiTextureMapMode)0;
}
// ------------------------------------------------------------------------------------------------
bool LWOImporter::HandleTextures(aiMaterial* pcMat, const TextureList& in, aiTextureType type)
{
ai_assert(NULL != pcMat);
unsigned int cur = 0, temp = 0;
aiString s;
bool ret = false;
for (const auto &texture : in) {
if (!texture.enabled || !texture.bCanUse)
continue;
ret = true;
// Convert lightwave's mapping modes to ours. We let them
// as they are, the GenUVcoords step will compute UV
// channels if they're not there.
aiTextureMapping mapping;
switch (texture.mapMode)
{
case LWO::Texture::Planar:
mapping = aiTextureMapping_PLANE;
break;
case LWO::Texture::Cylindrical:
mapping = aiTextureMapping_CYLINDER;
break;
case LWO::Texture::Spherical:
mapping = aiTextureMapping_SPHERE;
break;
case LWO::Texture::Cubic:
mapping = aiTextureMapping_BOX;
break;
case LWO::Texture::FrontProjection:
ASSIMP_LOG_ERROR("LWO2: Unsupported texture mapping: FrontProjection");
mapping = aiTextureMapping_OTHER;
break;
case LWO::Texture::UV:
{
if( UINT_MAX == texture.mRealUVIndex ) {
// We have no UV index for this texture, so we can't display it
continue;
}
// add the UV source index
temp = texture.mRealUVIndex;
pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_UVWSRC(type,cur));
mapping = aiTextureMapping_UV;
}
break;
default:
ai_assert(false);
};
if (mapping != aiTextureMapping_UV) {
// Setup the main axis
aiVector3D v;
switch (texture.majorAxis) {
case Texture::AXIS_X:
v = aiVector3D(1.0,0.0,0.0);
break;
case Texture::AXIS_Y:
v = aiVector3D(0.0,1.0,0.0);
break;
default: // case Texture::AXIS_Z:
v = aiVector3D(0.0,0.0,1.0);
break;
}
pcMat->AddProperty(&v,1,AI_MATKEY_TEXMAP_AXIS(type,cur));
// Setup UV scalings for cylindric and spherical projections
if (mapping == aiTextureMapping_CYLINDER || mapping == aiTextureMapping_SPHERE) {
aiUVTransform trafo;
trafo.mScaling.x = texture.wrapAmountW;
trafo.mScaling.y = texture.wrapAmountH;
static_assert(sizeof(aiUVTransform)/sizeof(ai_real) == 5, "sizeof(aiUVTransform)/sizeof(ai_real) == 5");
pcMat->AddProperty(&trafo,1,AI_MATKEY_UVTRANSFORM(type,cur));
}
ASSIMP_LOG_DEBUG("LWO2: Setting up non-UV mapping");
}
// The older LWOB format does not use indirect references to clips.
// The file name of a texture is directly specified in the tex chunk.
if (mIsLWO2) {
// find the corresponding clip (take the last one if multiple
// share the same index)
ClipList::iterator end = mClips.end(), candidate = end;
temp = texture.mClipIdx;
for (ClipList::iterator clip = mClips.begin(); clip != end; ++clip) {
if ((*clip).idx == temp) {
candidate = clip;
}
}
if (candidate == end) {
ASSIMP_LOG_ERROR("LWO2: Clip index is out of bounds");
temp = 0;
// fixme: apparently some LWO files shipping with Doom3 don't
// have clips at all ... check whether that's true or whether
// it's a bug in the loader.
s.Set("$texture.png");
//continue;
}
else {
if (Clip::UNSUPPORTED == (*candidate).type) {
ASSIMP_LOG_ERROR("LWO2: Clip type is not supported");
continue;
}
AdjustTexturePath((*candidate).path);
s.Set((*candidate).path);
// Additional image settings
int flags = 0;
if ((*candidate).negate) {
flags |= aiTextureFlags_Invert;
}
pcMat->AddProperty(&flags,1,AI_MATKEY_TEXFLAGS(type,cur));
}
}
else
{
std::string ss = texture.mFileName;
if (!ss.length()) {
ASSIMP_LOG_WARN("LWOB: Empty file name");
continue;
}
AdjustTexturePath(ss);
s.Set(ss);
}
pcMat->AddProperty(&s,AI_MATKEY_TEXTURE(type,cur));
// add the blend factor
pcMat->AddProperty<float>(&texture.mStrength,1,AI_MATKEY_TEXBLEND(type,cur));
// add the blend operation
switch (texture.blendType)
{
case LWO::Texture::Normal:
case LWO::Texture::Multiply:
temp = (unsigned int)aiTextureOp_Multiply;
break;
case LWO::Texture::Subtractive:
case LWO::Texture::Difference:
temp = (unsigned int)aiTextureOp_Subtract;
break;
case LWO::Texture::Divide:
temp = (unsigned int)aiTextureOp_Divide;
break;
case LWO::Texture::Additive:
temp = (unsigned int)aiTextureOp_Add;
break;
default:
temp = (unsigned int)aiTextureOp_Multiply;
ASSIMP_LOG_WARN("LWO2: Unsupported texture blend mode: alpha or displacement");
}
// Setup texture operation
pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_TEXOP(type,cur));
// setup the mapping mode
int mapping_ = static_cast<int>(mapping);
pcMat->AddProperty<int>(&mapping_, 1, AI_MATKEY_MAPPING(type, cur));
// add the u-wrapping
temp = (unsigned int)GetMapMode(texture.wrapModeWidth);
pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_U(type,cur));
// add the v-wrapping
temp = (unsigned int)GetMapMode(texture.wrapModeHeight);
pcMat->AddProperty<int>((int*)&temp,1,AI_MATKEY_MAPPINGMODE_V(type,cur));
++cur;
}
return ret;
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::ConvertMaterial(const LWO::Surface& surf,aiMaterial* pcMat)
{
// copy the name of the surface
aiString st;
st.Set(surf.mName);
pcMat->AddProperty(&st,AI_MATKEY_NAME);
const int i = surf.bDoubleSided ? 1 : 0;
pcMat->AddProperty(&i,1,AI_MATKEY_TWOSIDED);
// add the refraction index and the bump intensity
pcMat->AddProperty(&surf.mIOR,1,AI_MATKEY_REFRACTI);
pcMat->AddProperty(&surf.mBumpIntensity,1,AI_MATKEY_BUMPSCALING);
aiShadingMode m;
if (surf.mSpecularValue && surf.mGlossiness)
{
float fGloss;
if (mIsLWO2) {
fGloss = std::pow( surf.mGlossiness*ai_real( 10.0 )+ ai_real( 2.0 ), ai_real( 2.0 ) );
}
else
{
if (16.0 >= surf.mGlossiness)
fGloss = 6.0;
else if (64.0 >= surf.mGlossiness)
fGloss = 20.0;
else if (256.0 >= surf.mGlossiness)
fGloss = 50.0;
else fGloss = 80.0;
}
pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
pcMat->AddProperty(&fGloss,1,AI_MATKEY_SHININESS);
m = aiShadingMode_Phong;
}
else m = aiShadingMode_Gouraud;
// specular color
aiColor3D clr = lerp( aiColor3D(1.0,1.0,1.0), surf.mColor, surf.mColorHighlights );
pcMat->AddProperty(&clr,1,AI_MATKEY_COLOR_SPECULAR);
pcMat->AddProperty(&surf.mSpecularValue,1,AI_MATKEY_SHININESS_STRENGTH);
// emissive color
// luminosity is not really the same but it affects the surface in a similar way. Some scaling looks good.
clr.g = clr.b = clr.r = surf.mLuminosity*ai_real( 0.8 );
pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
// opacity ... either additive or default-blended, please
if (0.0 != surf.mAdditiveTransparency) {
const int add = aiBlendMode_Additive;
pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
} else if (10e10f != surf.mTransparency) {
const int def = aiBlendMode_Default;
const float f = 1.0f-surf.mTransparency;
pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY);
pcMat->AddProperty(&def,1,AI_MATKEY_BLEND_FUNC);
}
// ADD TEXTURES to the material
// TODO: find out how we can handle COLOR textures correctly...
bool b = HandleTextures(pcMat,surf.mColorTextures,aiTextureType_DIFFUSE);
b = (b || HandleTextures(pcMat,surf.mDiffuseTextures,aiTextureType_DIFFUSE));
HandleTextures(pcMat,surf.mSpecularTextures,aiTextureType_SPECULAR);
HandleTextures(pcMat,surf.mGlossinessTextures,aiTextureType_SHININESS);
HandleTextures(pcMat,surf.mBumpTextures,aiTextureType_HEIGHT);
HandleTextures(pcMat,surf.mOpacityTextures,aiTextureType_OPACITY);
HandleTextures(pcMat,surf.mReflectionTextures,aiTextureType_REFLECTION);
// Now we need to know which shader to use .. iterate through the shader list of
// the surface and search for a name which we know ...
for (const auto &shader : surf.mShaders) {
if (shader.functionName == "LW_SuperCelShader" || shader.functionName == "AH_CelShader") {
ASSIMP_LOG_INFO("LWO2: Mapping LW_SuperCelShader/AH_CelShader to aiShadingMode_Toon");
m = aiShadingMode_Toon;
break;
}
else if (shader.functionName == "LW_RealFresnel" || shader.functionName == "LW_FastFresnel") {
ASSIMP_LOG_INFO("LWO2: Mapping LW_RealFresnel/LW_FastFresnel to aiShadingMode_Fresnel");
m = aiShadingMode_Fresnel;
break;
}
else
{
ASSIMP_LOG_WARN_F("LWO2: Unknown surface shader: ", shader.functionName);
}
}
if (surf.mMaximumSmoothAngle <= 0.0)
m = aiShadingMode_Flat;
int m_ = static_cast<int>(m);
pcMat->AddProperty(&m_, 1, AI_MATKEY_SHADING_MODEL);
// (the diffuse value is just a scaling factor)
// If a diffuse texture is set, we set this value to 1.0
clr = (b && false ? aiColor3D(1.0,1.0,1.0) : surf.mColor);
clr.r *= surf.mDiffuseValue;
clr.g *= surf.mDiffuseValue;
clr.b *= surf.mDiffuseValue;
pcMat->AddProperty<aiColor3D>(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
}
// ------------------------------------------------------------------------------------------------
char LWOImporter::FindUVChannels(LWO::TextureList& list,
LWO::Layer& /*layer*/,LWO::UVChannel& uv, unsigned int next)
{
char ret = 0;
for (auto &texture : list) {
// Ignore textures with non-UV mappings for the moment.
if (!texture.enabled || !texture.bCanUse || texture.mapMode != LWO::Texture::UV) {
continue;
}
if (texture.mUVChannelIndex == uv.name) {
ret = 1;
// got it.
if (texture.mRealUVIndex == UINT_MAX || texture.mRealUVIndex == next)
{
texture.mRealUVIndex = next;
}
else {
// channel mismatch. need to duplicate the material.
ASSIMP_LOG_WARN("LWO: Channel mismatch, would need to duplicate surface [design bug]");
// TODO
}
}
}
return ret;
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::FindUVChannels(LWO::Surface& surf,
LWO::SortedRep& sorted,LWO::Layer& layer,
unsigned int out[AI_MAX_NUMBER_OF_TEXTURECOORDS])
{
unsigned int next = 0, extra = 0, num_extra = 0;
// Check whether we have an UV entry != 0 for one of the faces in 'sorted'
for (unsigned int i = 0; i < layer.mUVChannels.size();++i) {
LWO::UVChannel& uv = layer.mUVChannels[i];
for (LWO::SortedRep::const_iterator it = sorted.begin(); it != sorted.end(); ++it) {
LWO::Face& face = layer.mFaces[*it];
for (unsigned int n = 0; n < face.mNumIndices; ++n) {
unsigned int idx = face.mIndices[n];
if (uv.abAssigned[idx] && ((aiVector2D*)&uv.rawData[0])[idx] != aiVector2D()) {
if (extra >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
ASSIMP_LOG_ERROR("LWO: Maximum number of UV channels for "
"this mesh reached. Skipping channel \'" + uv.name + "\'");
}
else {
// Search through all textures assigned to 'surf' and look for this UV channel
char had = 0;
had |= FindUVChannels(surf.mColorTextures,layer,uv,next);
had |= FindUVChannels(surf.mDiffuseTextures,layer,uv,next);
had |= FindUVChannels(surf.mSpecularTextures,layer,uv,next);
had |= FindUVChannels(surf.mGlossinessTextures,layer,uv,next);
had |= FindUVChannels(surf.mOpacityTextures,layer,uv,next);
had |= FindUVChannels(surf.mBumpTextures,layer,uv,next);
had |= FindUVChannels(surf.mReflectionTextures,layer,uv,next);
// We have a texture referencing this UV channel so we have to take special care
// and are willing to drop unreferenced channels in favour of it.
if (had != 0) {
if (num_extra) {
for (unsigned int a = next; a < std::min( extra, AI_MAX_NUMBER_OF_TEXTURECOORDS-1u ); ++a) {
out[a+1] = out[a];
}
}
++extra;
out[next++] = i;
}
// Bah ... seems not to be used at all. Push to end if enough space is available.
else {
out[extra++] = i;
++num_extra;
}
}
it = sorted.end()-1;
break;
}
}
}
}
if (extra < AI_MAX_NUMBER_OF_TEXTURECOORDS) {
out[extra] = UINT_MAX;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::FindVCChannels(const LWO::Surface& surf, LWO::SortedRep& sorted, const LWO::Layer& layer,
unsigned int out[AI_MAX_NUMBER_OF_COLOR_SETS])
{
unsigned int next = 0;
// Check whether we have an vc entry != 0 for one of the faces in 'sorted'
for (unsigned int i = 0; i < layer.mVColorChannels.size();++i) {
const LWO::VColorChannel& vc = layer.mVColorChannels[i];
if (surf.mVCMap == vc.name) {
// The vertex color map is explicitly requested by the surface so we need to take special care of it
for (unsigned int a = 0; a < std::min(next,AI_MAX_NUMBER_OF_COLOR_SETS-1u); ++a) {
out[a+1] = out[a];
}
out[0] = i;
++next;
}
else {
for (LWO::SortedRep::iterator it = sorted.begin(); it != sorted.end(); ++it) {
const LWO::Face& face = layer.mFaces[*it];
for (unsigned int n = 0; n < face.mNumIndices; ++n) {
unsigned int idx = face.mIndices[n];
if (vc.abAssigned[idx] && ((aiColor4D*)&vc.rawData[0])[idx] != aiColor4D(0.0,0.0,0.0,1.0)) {
if (next >= AI_MAX_NUMBER_OF_COLOR_SETS) {
ASSIMP_LOG_ERROR("LWO: Maximum number of vertex color channels for "
"this mesh reached. Skipping channel \'" + vc.name + "\'");
}
else {
out[next++] = i;
}
it = sorted.end()-1;
break;
}
}
}
}
}
if (next != AI_MAX_NUMBER_OF_COLOR_SETS) {
out[next] = UINT_MAX;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2ImageMap(unsigned int size, LWO::Texture& tex )
{
LE_NCONST uint8_t* const end = mFileBuffer + size;
while (true)
{
if (mFileBuffer + 6 >= end)break;
LE_NCONST IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
if (mFileBuffer + head.length > end)
throw DeadlyImportError("LWO2: Invalid SURF.BLOCK chunk length");
uint8_t* const next = mFileBuffer+head.length;
switch (head.type)
{
case AI_LWO_PROJ:
tex.mapMode = (Texture::MappingMode)GetU2();
break;
case AI_LWO_WRAP:
tex.wrapModeWidth = (Texture::Wrap)GetU2();
tex.wrapModeHeight = (Texture::Wrap)GetU2();
break;
case AI_LWO_AXIS:
tex.majorAxis = (Texture::Axes)GetU2();
break;
case AI_LWO_IMAG:
tex.mClipIdx = GetU2();
break;
case AI_LWO_VMAP:
GetS0(tex.mUVChannelIndex,head.length);
break;
case AI_LWO_WRPH:
tex.wrapAmountH = GetF4();
break;
case AI_LWO_WRPW:
tex.wrapAmountW = GetF4();
break;
}
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2Procedural(unsigned int /*size*/, LWO::Texture& tex )
{
// --- not supported at the moment
ASSIMP_LOG_ERROR("LWO2: Found procedural texture, this is not supported");
tex.bCanUse = false;
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2Gradient(unsigned int /*size*/, LWO::Texture& tex )
{
// --- not supported at the moment
ASSIMP_LOG_ERROR("LWO2: Found gradient texture, this is not supported");
tex.bCanUse = false;
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2TextureHeader(unsigned int size, LWO::Texture& tex )
{
LE_NCONST uint8_t* const end = mFileBuffer + size;
// get the ordinal string
GetS0( tex.ordinal, size);
// we could crash later if this is an empty string ...
if (!tex.ordinal.length())
{
ASSIMP_LOG_ERROR("LWO2: Ill-formed SURF.BLOK ordinal string");
tex.ordinal = "\x00";
}
while (true)
{
if (mFileBuffer + 6 >= end)break;
const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
if (mFileBuffer + head.length > end)
throw DeadlyImportError("LWO2: Invalid texture header chunk length");
uint8_t* const next = mFileBuffer+head.length;
switch (head.type)
{
case AI_LWO_CHAN:
tex.type = GetU4();
break;
case AI_LWO_ENAB:
tex.enabled = GetU2() ? true : false;
break;
case AI_LWO_OPAC:
tex.blendType = (Texture::BlendType)GetU2();
tex.mStrength = GetF4();
break;
}
mFileBuffer = next;
}
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2TextureBlock(LE_NCONST IFF::SubChunkHeader* head, unsigned int size )
{
ai_assert(!mSurfaces->empty());
LWO::Surface& surf = mSurfaces->back();
LWO::Texture tex;
// load the texture header
LoadLWO2TextureHeader(head->length,tex);
size -= head->length + 6;
// now get the exact type of the texture
switch (head->type)
{
case AI_LWO_PROC:
LoadLWO2Procedural(size,tex);
break;
case AI_LWO_GRAD:
LoadLWO2Gradient(size,tex);
break;
case AI_LWO_IMAP:
LoadLWO2ImageMap(size,tex);
}
// get the destination channel
TextureList* listRef = NULL;
switch (tex.type)
{
case AI_LWO_COLR:
listRef = &surf.mColorTextures;break;
case AI_LWO_DIFF:
listRef = &surf.mDiffuseTextures;break;
case AI_LWO_SPEC:
listRef = &surf.mSpecularTextures;break;
case AI_LWO_GLOS:
listRef = &surf.mGlossinessTextures;break;
case AI_LWO_BUMP:
listRef = &surf.mBumpTextures;break;
case AI_LWO_TRAN:
listRef = &surf.mOpacityTextures;break;
case AI_LWO_REFL:
listRef = &surf.mReflectionTextures;break;
default:
ASSIMP_LOG_WARN("LWO2: Encountered unknown texture type");
return;
}
// now attach the texture to the parent surface - sort by ordinal string
for (TextureList::iterator it = listRef->begin();it != listRef->end(); ++it) {
if (::strcmp(tex.ordinal.c_str(),(*it).ordinal.c_str()) < 0) {
listRef->insert(it,tex);
return;
}
}
listRef->push_back(tex);
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2ShaderBlock(LE_NCONST IFF::SubChunkHeader* /*head*/, unsigned int size )
{
LE_NCONST uint8_t* const end = mFileBuffer + size;
ai_assert(!mSurfaces->empty());
LWO::Surface& surf = mSurfaces->back();
LWO::Shader shader;
// get the ordinal string
GetS0( shader.ordinal, size);
// we could crash later if this is an empty string ...
if (!shader.ordinal.length())
{
ASSIMP_LOG_ERROR("LWO2: Ill-formed SURF.BLOK ordinal string");
shader.ordinal = "\x00";
}
// read the header
while (true)
{
if (mFileBuffer + 6 >= end)break;
const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
if (mFileBuffer + head.length > end)
throw DeadlyImportError("LWO2: Invalid shader header chunk length");
uint8_t* const next = mFileBuffer+head.length;
switch (head.type)
{
case AI_LWO_ENAB:
shader.enabled = GetU2() ? true : false;
break;
case AI_LWO_FUNC:
GetS0( shader.functionName, head.length );
}
mFileBuffer = next;
}
// now attach the shader to the parent surface - sort by ordinal string
for (ShaderList::iterator it = surf.mShaders.begin();it != surf.mShaders.end(); ++it) {
if (::strcmp(shader.ordinal.c_str(),(*it).ordinal.c_str()) < 0) {
surf.mShaders.insert(it,shader);
return;
}
}
surf.mShaders.push_back(shader);
}
// ------------------------------------------------------------------------------------------------
void LWOImporter::LoadLWO2Surface(unsigned int size)
{
LE_NCONST uint8_t* const end = mFileBuffer + size;
mSurfaces->push_back( LWO::Surface () );
LWO::Surface& surf = mSurfaces->back();
GetS0(surf.mName,size);
// check whether this surface was derived from any other surface
std::string derived;
GetS0(derived,(unsigned int)(end - mFileBuffer));
if (derived.length()) {
// yes, find this surface
for (SurfaceList::iterator it = mSurfaces->begin(), end = mSurfaces->end()-1; it != end; ++it) {
if ((*it).mName == derived) {
// we have it ...
surf = *it;
derived.clear();break;
}
}
if (derived.size())
ASSIMP_LOG_WARN("LWO2: Unable to find source surface: " + derived);
}
while (true)
{
if (mFileBuffer + 6 >= end)
break;
const IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
if (mFileBuffer + head.length > end)
throw DeadlyImportError("LWO2: Invalid surface chunk length");
uint8_t* const next = mFileBuffer+head.length;
switch (head.type)
{
// diffuse color
case AI_LWO_COLR:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,12);
surf.mColor.r = GetF4();
surf.mColor.g = GetF4();
surf.mColor.b = GetF4();
break;
}
// diffuse strength ... hopefully
case AI_LWO_DIFF:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,4);
surf.mDiffuseValue = GetF4();
break;
}
// specular strength ... hopefully
case AI_LWO_SPEC:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,4);
surf.mSpecularValue = GetF4();
break;
}
// transparency
case AI_LWO_TRAN:
{
// transparency explicitly disabled?
if (surf.mTransparency == 10e10f)
break;
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,4);
surf.mTransparency = GetF4();
break;
}
// additive transparency
case AI_LWO_ADTR:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,ADTR,4);
surf.mAdditiveTransparency = GetF4();
break;
}
// wireframe mode
case AI_LWO_LINE:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LINE,2);
if (GetU2() & 0x1)
surf.mWireframe = true;
break;
}
// glossiness
case AI_LWO_GLOS:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,4);
surf.mGlossiness = GetF4();
break;
}
// bump intensity
case AI_LWO_BUMP:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,BUMP,4);
surf.mBumpIntensity = GetF4();
break;
}
// color highlights
case AI_LWO_CLRH:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,CLRH,4);
surf.mColorHighlights = GetF4();
break;
}
// index of refraction
case AI_LWO_RIND:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,RIND,4);
surf.mIOR = GetF4();
break;
}
// polygon sidedness
case AI_LWO_SIDE:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SIDE,2);
surf.bDoubleSided = (3 == GetU2());
break;
}
// maximum smoothing angle
case AI_LWO_SMAN:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4);
surf.mMaximumSmoothAngle = std::fabs( GetF4() );
break;
}
// vertex color channel to be applied to the surface
case AI_LWO_VCOL:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,VCOL,12);
surf.mDiffuseValue *= GetF4(); // strength
ReadVSizedIntLWO2(mFileBuffer); // skip envelope
surf.mVCMapType = GetU4(); // type of the channel
// name of the channel
GetS0(surf.mVCMap, (unsigned int) (next - mFileBuffer ));
break;
}
// surface bock entry
case AI_LWO_BLOK:
{
AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,BLOK,4);
IFF::SubChunkHeader head2 = IFF::LoadSubChunk(mFileBuffer);
switch (head2.type)
{
case AI_LWO_PROC:
case AI_LWO_GRAD:
case AI_LWO_IMAP:
LoadLWO2TextureBlock(&head2, head.length);
break;
case AI_LWO_SHDR:
LoadLWO2ShaderBlock(&head2, head.length);
break;
default:
ASSIMP_LOG_WARN("LWO2: Found an unsupported surface BLOK");
};
break;
}
}
mFileBuffer = next;
}
}
#endif // !! ASSIMP_BUILD_NO_X_IMPORTER