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