// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdafx.h" #include "export_nel.h" #include "../tile_utility/tile_utility.h" #include "nel/3d/texture_file.h" #include "nel/3d/texture_multi_file.h" #include "nel/3d/texture_cube.h" #include "nel/3d/tangent_space_build.h" #include "nel/3d/meshvp_per_pixel_light.h" #include #include using namespace NLMISC; using namespace NL3D; using namespace std; // Name of animatable values #define BMTEX_CROP_APPLY "apply" #define BMTEX_CROP_U_NAME "clipu" #define BMTEX_CROP_V_NAME "clipv" #define BMTEX_CROP_W_NAME "clipw" #define BMTEX_CROP_H_NAME "cliph" #define MAT_SLOTS 4 #define SHADER_NORMAL 1 #define SHADER_BUMP 2 #define SHADER_USER_COLOR 3 #define SHADER_LIGHTMAP 4 #define SHADER_SPECULAR 5 #define SHADER_WATER 6 #define SHADER_PER_PIXEL_LIGHTING 7 #define SHADER_PER_PIXEL_LIGHTING_NO_SPEC 8 #define NEL_BITMAP_TEXTURE_CLASS_ID_A 0x5a8003f9 #define NEL_BITMAP_TEXTURE_CLASS_ID_B 0x43e0955 /** Test wether the given max material is a water material. A water object should only have one material, and must have planar, convex geometry. * Morevover, the mapping should only have scale and offsets, no rotation */ bool CExportNel::hasWaterMaterial(INode& node, TimeValue time) { // Get primary material pointer of the node Mtl* pNodeMat = node.GetMtl(); // If NULL, no material at all at this node if (pNodeMat == NULL) return false; if (pNodeMat->NumSubMtls() != 0) return 0; // subMaterials not supported for water int bWater = 0; // false CExportNel::getValueByNameUsingParamBlock2 (*pNodeMat, "bWater", (ParamType2)TYPE_BOOL, &bWater, time); return bWater != 0; } ///=================================================================================================== bool CExportNel::hasMaterialWithShaderForVP(INode &node, TimeValue time, NL3D::CMaterial::TShader &shader) { // Get primary material pointer of the node Mtl* pNodeMat = node.GetMtl(); // If NULL, no material at all at this node if (pNodeMat == NULL) return false; return CExportNel::needVP(*pNodeMat, time, shader); } ///=================================================================================================== bool CExportNel::needVP(Mtl &mat, TimeValue time, NL3D::CMaterial::TShader &shader) { if (mat.NumSubMtls() != 0) // look at sub materials { for (uint k = 0; k < (uint) mat.NumSubMtls(); ++k) { if (CExportNel::needVP(*(mat.GetSubMtl(k)), time, shader)) return true; } return false; } int shaderType = 0; // false CExportNel::getValueByNameUsingParamBlock2 (mat, "iShaderType", (ParamType2)TYPE_INT, &shaderType, time); switch (shaderType) { case 7: // per pixel lighting (shader 7) need a specific VP shader = CMaterial::PerPixelLighting; return true; break; case 8: // per pixel lighting, no specular shader = CMaterial::PerPixelLightingNoSpec; return true; break; default: return false; break; } } ///=================================================================================================== /// Build a per-pixel vertex program static NL3D::CMeshVPPerPixelLight *BuildPerPixelLightingVP(NL3D::CMesh::CMeshBuild *mb, bool wantSpecular) { // We need at least one texture set to be able to build the tangent space information. // In this version, we support only one texture coordinate for the input. If there are more the result is undefined. NL3D::CMesh::CMeshBuild otherMb; if (NL3D::BuildTangentSpace(otherMb, *mb)) // build succesful ? { *mb = otherMb; CMeshVPPerPixelLight *vp = new CMeshVPPerPixelLight; vp->SpecularLighting = wantSpecular; return vp; } else { return NULL; } return NULL; } ///=================================================================================================== IMeshVertexProgram *CExportNel::buildMeshMaterialShaderVP(NL3D::CMaterial::TShader shader, NL3D::CMesh::CMeshBuild *mb) { nlassert(shader < CMaterial::shaderCount); switch (shader) { case CMaterial::PerPixelLighting: return BuildPerPixelLightingVP(mb, true /* with specular */); break; case CMaterial::PerPixelLightingNoSpec: return BuildPerPixelLightingVP(mb, false /* no spec */); break; default: return NULL; // no need for a vp } } // Build an array of NeL material corresponding with max material at this node. Return the number of material exported. // Fill an array to remap the 3ds vertexMap channels for each materials. maxBaseBuild.RemapChannel.size() must be == to materials.size(). // maxBaseBuild.RemapChannel[mat].size() is the final count of NeL vertexMap channels used for the material nb mat. // For each NeL channel of a material, copy the 3ds channel maxBaseBuild.RemapChannel[nelChannel]._IndexInMaxMaterial using the transformation matrix // maxBaseBuild.RemapChannel[nelChannel]._UVMatrix. // maxBaseBuild.NeedVertexColor will be true if at least one material need vertex color. Forced to true if lightmaps are used. // maxBaseBuild.AlphaVertex[mat] will be true if the material use per vertex alpha. // maxBaseBuild.AlphaVertexChannel[mat] will be the channel to use to get the alpha if the material use per vertex alpha. // This method append the node material to the vector passed. void CExportNel::buildMaterials (std::vector& materials, CMaxMeshBaseBuild& maxBaseBuild, INode& node, TimeValue time) { // Material count maxBaseBuild.FirstMaterial=materials.size(); int nMaterialCount=0; // Get primary material pointer of the node Mtl* pNodeMat=node.GetMtl(); // If NULL, no material at all at this node if (pNodeMat!=NULL) { // Number of sub material at in this material nMaterialCount=pNodeMat->NumSubMtls(); // If it is a multisub object, export all its sub materials if (nMaterialCount>0) { // Resize the destination array materials.resize (materials.size()+nMaterialCount); // Resize the vertMap remap table maxBaseBuild.MaterialInfo.resize (nMaterialCount); // Export all the sub materials for (int nSub=0; nSubGetSubMtl(nSub); // Should not be NULL nlassert (pSub); // Export it buildAMaterial (materials[maxBaseBuild.FirstMaterial+nSub], maxBaseBuild.MaterialInfo[nSub], *pSub, time); // Need vertex color ? maxBaseBuild.NeedVertexColor |= maxBaseBuild.MaterialInfo[nSub].AlphaVertex | maxBaseBuild.MaterialInfo[nSub].ColorVertex; } } // Else export only this material, so, count is 1 else { // Only one material nMaterialCount=1; // Resize the destination array materials.resize (materials.size()+1); // Resize the vertMap remap table maxBaseBuild.MaterialInfo.resize (1); // Export the main material buildAMaterial (materials[maxBaseBuild.FirstMaterial], maxBaseBuild.MaterialInfo[0], *pNodeMat, time); // Need vertex color ? maxBaseBuild.NeedVertexColor |= maxBaseBuild.MaterialInfo[0].AlphaVertex | maxBaseBuild.MaterialInfo[0].ColorVertex; } } // Normalize UVRouting uint i; for (i=0; i0)) break; if ((iShaderType==SHADER_LIGHTMAP) && (i>0)) break; if ((iShaderType==SHADER_SPECULAR) && (i>1)) break; // Get the enable flag name char enableSlotName[100]; smprintf (enableSlotName, 100, "bEnableSlot_%d", i+1); // Get the enable flag int bEnableSlot = 0; CExportNel::getValueByNameUsingParamBlock2 (mtl, enableSlotName, (ParamType2)TYPE_BOOL, &bEnableSlot, time); if (bEnableSlot) { // Get the texture arg name char textureName[100]; smprintf (textureName, 100, "tTexture_%d", i+1); // Get the texture pointer Texmap *pTexmap = NULL; CExportNel::getValueByNameUsingParamBlock2 (mtl, textureName, (ParamType2)TYPE_TEXMAP, &pTexmap, time); if (pTexmap) { // Pointer on the diffuse texture static ITexture* pTexture=NULL; // Ok export the texture in NeL format CMaterialDesc materialDesc; pTexture=buildATexture (*pTexmap, materialDesc, time, (i==1) && (iShaderType==SHADER_SPECULAR) ); // Get the gen texture coord flag char texGenName[100]; smprintf (texGenName, 100, "bTexGen_%d", i+1); int bTexGen = 0; CExportNel::getValueByNameUsingParamBlock2 (mtl, texGenName, (ParamType2)TYPE_BOOL, &bTexGen, time); // If specular shader, set uv channel to reflexion if ( ((i==1) && (iShaderType==SHADER_SPECULAR)) || bTexGen ) { materialDesc._IndexInMaxMaterial = UVGEN_REFLEXION; } materialInfo.RemapChannel.push_back (materialDesc); // Add flags if mapping coodinates are used.. if (materialDesc._IndexInMaxMaterial>=0) { uint j; for (j=0; j 1) materialInfo.RemapChannel[size] = materialInfo.RemapChannel[0]; // Source lightmap mapping channel materialInfo.RemapChannel[size]._IndexInMaxMaterial = iLightMapChannel; // Backup old mapping channel materialInfo.RemapChannel[size]._IndexInMaxMaterialAlternative = materialInfo.RemapChannel[0]._IndexInMaxMaterial; } // For each slot uint stage; for (stage=0; stage1) { // Enable and setup it material.enableTexAddrMode (); material.setTexAddressingMode (stage, (CMaterial::TTexAddressingMode)(texEnvMode-2)); } } } // Set material name TSTR name=mtl.GetName(); materialInfo.MaterialName = (const char*)name; } else { // Not a nel material, try to get something near... // Init the material lighted material.initLighted (); // *** *************** // *** Export Textures // *** *************** /// TODO: Only one texture for the time. Add multitexture support, and shaders support. // Look for a diffuse texmap vector mapEnables; CExportNel::getValueByNameUsingParamBlock2 (mtl, "mapEnables", (ParamType2)TYPE_BOOL_TAB, &mapEnables, time, false); Texmap *pDifTexmap = NULL; Texmap *pOpaTexmap = NULL; Texmap *pSpeTexmap = NULL; if (mapEnables[ID_DI]) pDifTexmap = mtl.GetSubTexmap (ID_DI); if (mapEnables[ID_OP]) pOpaTexmap = mtl.GetSubTexmap (ID_OP); if (mapEnables[ID_SP]) pSpeTexmap = mtl.GetSubTexmap (ID_SP); // Is there a lightmap handling wanted int bLightMap = 0; // false int bAlphaTest = 1; // true int bForceZWrite = 0; // false int bForceNoZWrite = 0; // false CExportNel::getValueByNameUsingParamBlock2 (mtl, "bForceZWrite", (ParamType2)TYPE_BOOL, &bForceZWrite, time, false); CExportNel::getValueByNameUsingParamBlock2 (mtl, "bForceNoZWrite", (ParamType2)TYPE_BOOL, &bForceNoZWrite, time, false); CExportNel::getValueByNameUsingParamBlock2 (mtl, "bAlphaTest", (ParamType2)TYPE_BOOL, &bAlphaTest, time, false); if( pSpeTexmap != NULL ) { material.setShader (CMaterial::Specular); } else { CExportNel::getValueByNameUsingParamBlock2 (mtl, "bLightMap", (ParamType2)TYPE_BOOL, &bLightMap, time, false); if (bLightMap) { material.setShader (CMaterial::LightMap); } else { material.setShader (CMaterial::Normal); } } int bStainedGlassWindow = 0; CExportNel::getValueByNameUsingParamBlock2 (mtl, "bStainedGlassWindow", (ParamType2)TYPE_BOOL, &bStainedGlassWindow, time, false); material.setStainedGlassWindow( bStainedGlassWindow!=0 ); material.setAlphaTest(false); // By default set blend to false material.setBlend (false); // Diffuse texmap is present ? if (pDifTexmap) { // Pointer on the diffuse texture static ITexture* pTexture=NULL; // Is it a simple file ? if (isClassIdCompatible(*pDifTexmap, Class_ID (BMTEX_CLASS_ID, 0)) || isClassIdCompatible(*pDifTexmap, Class_ID (NEL_BITMAP_TEXTURE_CLASS_ID_A, NEL_BITMAP_TEXTURE_CLASS_ID_B)) ) { // List of channels used by this texture CMaterialDesc _3dsTexChannel; // Ok export the texture in NeL format pTexture=buildATexture (*pDifTexmap, _3dsTexChannel, time); // For this shader, only need a texture channel. materialInfo.RemapChannel.resize (1); // Need an explicit channel, not generated if ( _3dsTexChannel._IndexInMaxMaterial < 0 ) { materialInfo.RemapChannel[0]._IndexInMaxMaterial=UVGEN_MISSING; materialInfo.RemapChannel[0]._UVMatrix.IdentityMatrix(); } // Else copy it else materialInfo.RemapChannel[0]=_3dsTexChannel; // Add the texture if it exist material.setTexture(0, pTexture); // Active blend if texture in opacity if( pOpaTexmap!=NULL ) { if( bAlphaTest ) { // If Alpha Test enabled no blend required just check if we are forced to NOT write in the ZBuffer material.setAlphaTest(true); if( bForceNoZWrite ) material.setZWrite( false ); else material.setZWrite( true ); } else { // No Alpha Test so we have to blend and check if we are forced to write in the ZBuffer material.setBlend( true ); if( bForceZWrite ) material.setZWrite( true ); else material.setZWrite( false ); } } // Add flags if mapping coodinates are used.. if (_3dsTexChannel._IndexInMaxMaterial>=0) { // Channel routing materialInfo.UVRouting[0] = 0; } // Export mapping channel 2 if lightmap asked. if ( bLightMap ) // lightmap enabled ? { materialInfo.RemapChannel.resize( 2 ); // Copy information from channel 0 materialInfo.RemapChannel[1] = materialInfo.RemapChannel[0]; materialInfo.RemapChannel[1]._IndexInMaxMaterial = 2; materialInfo.RemapChannel[1]._IndexInMaxMaterialAlternative = materialInfo.RemapChannel[0]._IndexInMaxMaterial; // Add flags if mapping coodinates are used.. materialInfo.UVRouting[1] |= 1; } } } if (pSpeTexmap) { // Pointer to the specular reflection texture CTextureCube* pTextureCube = NULL; if (isClassIdCompatible(*pSpeTexmap, Class_ID(ACUBIC_CLASS_ID, 0))) { pTextureCube = buildTextureCubeFromReflectRefract(*pSpeTexmap, time); } else if (isClassIdCompatible(*pSpeTexmap, Class_ID(COMPOSITE_CLASS_ID, 0))) { pTextureCube = buildTextureCubeFromComposite(*pSpeTexmap, time); } else if (isClassIdCompatible(*pSpeTexmap, Class_ID(BMTEX_CLASS_ID, 0))) { pTextureCube = buildTextureCubeFromTexture(*pSpeTexmap, time); } // Add the texture if it exist material.setTexture(1, pTextureCube); } // Blend mode int opacityType = 0; // 0-filter 1-substractive 2-additive CExportNel::getValueByNameUsingParamBlock2 (mtl, "opacityType", (ParamType2)TYPE_INT, &opacityType, time, false); if( opacityType == 0 ) // Filter material.setBlendFunc (CMaterial::srcalpha, CMaterial::invsrcalpha); else material.setBlendFunc (CMaterial::srcalpha, CMaterial::one); // Z function by default. TODO. material.setZFunc (CMaterial::lessequal); // Z bias by default. TODO. material.setZBias (0.f); // Is the mtl a std material ? // The class Id can be the StdMat one, or StdMat2 // It can be the superClassId if the mtl is derived from StdMat or StdMat2. if ( isClassIdCompatible (mtl, Class_ID(DMTL_CLASS_ID, 0)) || isClassIdCompatible (mtl, Class_ID(DMTL2_CLASS_ID, 0)) ) { // Get a pointer on a stdmat //StdMat2* stdmat=(StdMat2*)&mtl; // ***************************************** // *** Colors, self illumination and opacity // ***************************************** // Get the diffuse color of the max material //Color color=stdmat->GetDiffuse (time); Point3 maxDiffuse; CRGBA nelDiffuse; CExportNel::getValueByNameUsingParamBlock2 (mtl, "diffuse", (ParamType2)TYPE_RGBA, &maxDiffuse, time, false); // Convert to NeL color convertColor (nelDiffuse, maxDiffuse); // Get the opacity value from the material // float fOp=stdmat->GetOpacity (time); float fOp = 0.0f; CExportNel::getValueByNameUsingParamBlock2 (mtl, "opacity", (ParamType2)TYPE_PCNT_FRAC, &fOp, time, false); // Add alpha to the value float fA=(fOp*255.f+0.5f); clamp (fA, 0.f, 255.f); nelDiffuse.A=(uint8)fA; // Set le NeL diffuse color material material.setColor (nelDiffuse); // Set the blend mode on if opacity is not 1.f if( fOp < 0.99f ) { if( bAlphaTest ) { // If Alpha Test enabled no blend required just check if we are forced to NOT write in the ZBuffer material.setAlphaTest(true); if( bForceNoZWrite ) material.setZWrite( false ); else material.setZWrite( true ); } else { // No Alpha Test so we have to blend and check if we are forced to write in the ZBuffer material.setBlend( true ); if( bForceZWrite ) material.setZWrite( true ); else material.setZWrite( false ); } } // Get colors of 3dsmax material CRGBA nelEmissive; CRGBA nelAmbient; CRGBA nelSpecular; //if (stdmat->GetSelfIllumColorOn()) // convertColor (emissiveColor, stdmat->GetSelfIllumColor (time)); //else // convertColor (emissiveColor, stdmat->GetDiffuse (time)*stdmat->GetSelfIllum (time)); int bSelfIllumColorOn; Point3 maxSelfIllum; float fTemp; CExportNel::getValueByNameUsingParamBlock2 (mtl, "useSelfIllumColor", (ParamType2)TYPE_BOOL, &bSelfIllumColorOn, time, false); if( bSelfIllumColorOn ) { CExportNel::getValueByNameUsingParamBlock2 (mtl, "selfIllumColor", (ParamType2)TYPE_RGBA, &maxSelfIllum, time, false); } else { CExportNel::getValueByNameUsingParamBlock2 (mtl, "selfIllumAmount", (ParamType2)TYPE_PCNT_FRAC, &fTemp, time, false); maxSelfIllum = maxDiffuse * fTemp; } convertColor( nelEmissive, maxSelfIllum ); Point3 maxAmbient; CExportNel::getValueByNameUsingParamBlock2 (mtl, "ambient", (ParamType2)TYPE_RGBA, &maxAmbient, time, false); convertColor (nelAmbient, maxAmbient); Point3 maxSpecular; CExportNel::getValueByNameUsingParamBlock2 (mtl, "specular", (ParamType2)TYPE_RGBA, &maxSpecular, time, false); convertColor (nelSpecular, maxSpecular); // Specular level float shininess; //=stdmat->GetShinStr(time); CExportNel::getValueByNameUsingParamBlock2 (mtl, "specularLevel", (ParamType2)TYPE_PCNT_FRAC, &shininess, time, false); CRGBAF fColor = nelSpecular; fColor *= shininess; nelSpecular = fColor; // Shininess CExportNel::getValueByNameUsingParamBlock2 (mtl, "glossiness", (ParamType2)TYPE_PCNT_FRAC, &shininess, time, false); //shininess=stdmat->GetShader()->GetGlossiness(time); shininess=(float)pow(2.0, shininess * 10.0) * 4.f; // Light parameters material.setLighting (true, nelEmissive, nelAmbient, nelDiffuse, nelSpecular, shininess); // Double sided int bDoubleSided; CExportNel::getValueByNameUsingParamBlock2 (mtl, "twoSided", (ParamType2)TYPE_BOOL, &bDoubleSided, time, false); //material.setDoubleSided (stdmat->GetTwoSided()!=FALSE); material.setDoubleSided ( bDoubleSided!=0 ); } if( ! bLightMap ) { int bUnlighted = 0; // false CExportNel::getValueByNameUsingParamBlock2 (mtl, "bUnlighted", (ParamType2)TYPE_BOOL, &bUnlighted, 0, false); if( bUnlighted ) { material.setLighting( false ); } } // Use alpha vertex ? int bAlphaVertex = 0; // false // Get the scripted value CExportNel::getValueByNameUsingParamBlock2 (mtl, "bAlphaVertex", (ParamType2)TYPE_BOOL, &bAlphaVertex, time, false); // Find ? if ( bAlphaVertex ) { // Ok, alpha vertex materialInfo.AlphaVertex = true; if( bAlphaTest ) { // If Alpha Test enabled no blend required just check if we are forced to NOT write in the ZBuffer material.setAlphaTest(true); if( bForceNoZWrite ) material.setZWrite( false ); else material.setZWrite( true ); } else { // No Alpha Test so we have to blend and check if we are forced to write in the ZBuffer material.setBlend( true ); if( bForceZWrite ) material.setZWrite( true ); else material.setZWrite( false ); } // Get the channel for alpha vertex materialInfo.AlphaVertexChannel = 0; CExportNel::getValueByNameUsingParamBlock2 (mtl, "iAlphaVertexChannel", (ParamType2)TYPE_INT, &materialInfo.AlphaVertexChannel, time, false); } // Use color vertex ? int bColorVertex = 0; // false // Get the scripted value CExportNel::getValueByNameUsingParamBlock2 (mtl, "bColorVertex", (ParamType2)TYPE_BOOL, &bColorVertex, time, false); // Find ? if ( bColorVertex ) { // Ok, color vertex materialInfo.ColorVertex = true; // Active vertex color in lighted mode material.setLightedVertexColor (material.isLighted()); } // Set material name TSTR name=mtl.GetName(); materialInfo.MaterialName = (const char*)name; } } // Get 3ds channels uv used by a texmap and make a good index channel int CExportNel::getVertMapChannel (Texmap& texmap, Matrix3& channelMatrix, TimeValue time) { // *** Get the channel matrix // Set to identity because deafult implementation of texmapGetUVTransform make nothing channelMatrix.IdentityMatrix(); // Get UV channel matrix texmap.GetUVTransform(channelMatrix); // Return the map channel return texmap.GetMapChannel(); } // get the absolute or relative path from a texture filename static std::string ConvertTexFileName(const char *src, bool _AbsolutePath) { // File name, maxlen 256 under windows char sFileName[512]; strcpy (sFileName, src); // Let absolute path ? if (!_AbsolutePath) { // Decompose bitmap file name char sName[256]; char sExt[256]; _splitpath (sFileName, NULL, NULL, sName, sExt); // Make the final path _makepath (sFileName, NULL, NULL, sName, sExt); } return std::string(sFileName); } // Build a NeL texture corresponding with a max Texmap. // Fill an array with the 3ds vertexMap used by this texture. // Texture file uses only 1 channel. ITexture* CExportNel::buildATexture (Texmap& texmap, CMaterialDesc &remap3dsTexChannel, TimeValue time, bool forceCubic) { /// TODO: support other texmap than Bitmap // By default, not build ITexture* pTexture=NULL; // Is it a bitmap texture file ? if (isClassIdCompatible(texmap, Class_ID (BMTEX_CLASS_ID,0)) || isClassIdCompatible(texmap, Class_ID(NEL_BITMAP_TEXTURE_CLASS_ID_A, NEL_BITMAP_TEXTURE_CLASS_ID_B)) ) { // Cast the pointer BitmapTex* pBitmap=(BitmapTex*)&texmap; // Get the apply crop value int bApply; bool bRes=getValueByNameUsingParamBlock2 (texmap, BMTEX_CROP_APPLY, (ParamType2)TYPE_BOOL, &bApply, time); nlassert (bRes); // If a crop is applyed if (bApply) { // Get the crop value U bRes=getValueByNameUsingParamBlock2 (texmap, BMTEX_CROP_U_NAME, (ParamType2)TYPE_FLOAT, &(remap3dsTexChannel._CropU), time); nlassert (bRes); // Get the crop value V bRes=getValueByNameUsingParamBlock2 (texmap, BMTEX_CROP_V_NAME, (ParamType2)TYPE_FLOAT, &(remap3dsTexChannel._CropV), time); nlassert (bRes); // Get the crop value W bRes=getValueByNameUsingParamBlock2 (texmap, BMTEX_CROP_W_NAME, (ParamType2)TYPE_FLOAT, &(remap3dsTexChannel._CropW), time); nlassert (bRes); // Get the crop value H bRes=getValueByNameUsingParamBlock2 (texmap, BMTEX_CROP_H_NAME, (ParamType2)TYPE_FLOAT, &(remap3dsTexChannel._CropH), time); nlassert (bRes); } // 1) build a texture that can either be a file texture or a multifile texture ITexture *srcTex; const uint numNelTextureSlots = 8; static const char *fileNamesTab[] = { "bitmap1FileName", "bitmap2FileName", "bitmap3FileName", "bitmap4FileName", "bitmap5FileName", "bitmap6FileName", "bitmap7FileName", "bitmap8FileName" }; if (isClassIdCompatible(texmap, Class_ID(NEL_BITMAP_TEXTURE_CLASS_ID_A, NEL_BITMAP_TEXTURE_CLASS_ID_B))) // is it a set of textures ? { uint k; std::string fileName[numNelTextureSlots]; for (k = 0; k < numNelTextureSlots; ++k) // copy each texture of the set { nlassert(k < sizeof(fileNamesTab) / sizeof(const char *)); /// get the file name from the parameter block bRes=getValueByNameUsingParamBlock2 (texmap, fileNamesTab[k], (ParamType2)TYPE_STRING, &fileName[k], time); nlassert (bRes); } uint numUsedSlots = 0; uint l; for (l = 0; l < numNelTextureSlots; ++l) { if (!fileName[l].empty()) numUsedSlots = l + 1; } // if only the first slot is used we create a CTextureFile... if (l == 1 && !fileName[0].empty()) { srcTex = new CTextureFile; static_cast(srcTex)->setFileName (ConvertTexFileName(fileName[0].c_str(), _AbsolutePath)); } else { srcTex = new CTextureMultiFile(numUsedSlots); for (k = 0; k < numUsedSlots; ++k) // copy each texture of the set { if (!fileName[k].empty()) { /// set the name of the texture after converting it static_cast(srcTex)->setFileName(k, ConvertTexFileName(fileName[k].c_str(), _AbsolutePath).c_str()); } } } } else // standard texture { srcTex = new CTextureFile; static_cast(srcTex)->setFileName (ConvertTexFileName(pBitmap->GetMapName(), _AbsolutePath)); } // 2) Use this texture 'as it', or duplicate it to create the faces of a cube map // Force cubic if (forceCubic) { // Cube side const static CTextureCube::TFace tfNewOrder[6] = { CTextureCube::positive_z, CTextureCube::negative_z, CTextureCube::negative_x, CTextureCube::positive_x, CTextureCube::positive_y, CTextureCube::negative_y }; // Alloc a cube texture CTextureCube* pTextureCube = new CTextureCube; for (uint side=0; side<6; side++) { // Set a copy of the source texture in the cube if (dynamic_cast(srcTex)) { pTextureCube->setTexture (tfNewOrder[side], new CTextureMultiFile(*static_cast(srcTex))); } else { pTextureCube->setTexture (tfNewOrder[side], new CTextureFile(*static_cast(srcTex))); } } // Ok, good texture pTexture=pTextureCube; delete srcTex; } else { /// just use the source texture pTexture = srcTex; } } else if (isClassIdCompatible(texmap, Class_ID(ACUBIC_CLASS_ID, 0))) { pTexture = buildTextureCubeFromReflectRefract(texmap, time); } else if (isClassIdCompatible(texmap, Class_ID(COMPOSITE_CLASS_ID, 0))) { pTexture = buildTextureCubeFromComposite(texmap, time); } // Get the UVs channel and the channel matrix Matrix3 channelMatrix; int nChannel=getVertMapChannel (texmap, channelMatrix, time); // Add the UVs channel remap3dsTexChannel._IndexInMaxMaterial=nChannel; remap3dsTexChannel._UVMatrix=channelMatrix; // check for tiling if (pTexture != NULL) // avoid possible crash { if (!(texmap.GetTextureTiling() & U_WRAP)) pTexture->setWrapS(ITexture::Clamp); if (!(texmap.GetTextureTiling() & V_WRAP)) pTexture->setWrapT(ITexture::Clamp); } // Return the texture pointer return pTexture; } /// Build a NeL texture cube from a Reflect/refract map containing 6 textures. NL3D::CTextureCube *CExportNel::buildTextureCubeFromReflectRefract(Texmap &texmap, TimeValue time) { if (isClassIdCompatible(texmap, Class_ID(ACUBIC_CLASS_ID, 0))) { // texture cube CTextureCube *pTextureCube = new CTextureCube(); // set settings pTextureCube->setWrapS(ITexture::Clamp); pTextureCube->setWrapT(ITexture::Clamp); pTextureCube->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); // face order const static CTextureCube::TFace tfNewOrder[6] = { CTextureCube::positive_z, CTextureCube::negative_z, // up dn CTextureCube::negative_x, CTextureCube::positive_x, // lf rt // CTextureCube::positive_y, CTextureCube::negative_y }; CTextureCube::negative_y, CTextureCube::positive_y }; // fr bk // vector of bitmap names vector names; CExportNel::getValueByNameUsingParamBlock2(texmap, "bitmapName", (ParamType2)TYPE_STRING_TAB, &names, time); uint nNbSubMap = std::min((uint)names.size(), 6); for (uint i = 0; i < nNbSubMap; ++i) { // Create a texture file CTextureFile *pT = new CTextureFile(); // Set the file name pT->setFileName(ConvertTexFileName(names[i].c_str(), _AbsolutePath)); // Set the texture pTextureCube->setTexture(tfNewOrder[i], pT); } // return the texture cube return pTextureCube; } else return NULL; } /// Build a NeL texture cube from a Composite map containing 6 textures (note: no re-ordering is done, because it didn't reorder composite cube in previous version either). NL3D::CTextureCube *CExportNel::buildTextureCubeFromComposite(Texmap &texmap, TimeValue time) { if (isClassIdCompatible(texmap, Class_ID(COMPOSITE_CLASS_ID, 0))) { // texture cube CTextureCube *pTextureCube = new CTextureCube(); // set settings pTextureCube->setWrapS(ITexture::Clamp); pTextureCube->setWrapT(ITexture::Clamp); pTextureCube->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); // get number of maps uint nNbSubMap = std::min((uint)texmap.NumSubTexmaps(), 6); for (uint i = 0; i < nNbSubMap; ++i) { std::vector _3dsTexChannel; Texmap *pSubMap = texmap.GetSubTexmap(i); if (pSubMap != NULL && isClassIdCompatible(*pSubMap, Class_ID(BMTEX_CLASS_ID, 0))) { CMaterialDesc _3dsTexChannel; ITexture *pTexture = buildATexture(*pSubMap, _3dsTexChannel, time); pTextureCube->setTexture((CTextureCube::TFace)i, pTexture); } } // return the texture cube return pTextureCube; } else return NULL; } /// Build a NeL texture cube from a single texture. NL3D::CTextureCube *CExportNel::buildTextureCubeFromTexture(Texmap &texmap, TimeValue time) { if (isClassIdCompatible(texmap, Class_ID(BMTEX_CLASS_ID, 0))) { // texture cube CTextureCube *pTextureCube = new CTextureCube(); // set settings pTextureCube->setWrapS(ITexture::Clamp); pTextureCube->setWrapT(ITexture::Clamp); pTextureCube->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); // List of channels used by this texture CMaterialDesc _3dsTexChannel; // Ok export the texture in NeL format ITexture *pTexture = buildATexture(texmap, _3dsTexChannel, time); pTextureCube->setTexture(CTextureCube::positive_x, pTexture); return pTextureCube; } else return NULL; } #pragma optimize("", on)