📄 export.cpp
字号:
VirtoolsMat = VirtoolsExporter->GetMaterialByKey(mtlKey);
if (PatchChannelCount>0) {
VirtoolsPMesh->AddChannel(VirtoolsMat);
}
VirtoolsPMesh->SetTVPatchCount(pmesh->numPatches,PatchChannelCount-1);
VirtoolsPMesh->SetTVCount(pmesh->getNumMapVerts(mp),PatchChannelCount-1);
for (i=0;i<pmesh->numPatches;++i) {
CKTVPatch Virtoolstvp;
TVPatch tvp = pmesh->getMapPatch(mp,i);
int MatID = (pmesh->patches[i].flags >> PATCH_MATID_SHIFT) & PATCH_MATID_MASK;
// No need to research the associated Virtools material if material index is the same
if (MatID != PreviousMatId) {
PatchMtl = GetMaterialByIndex(nodeMtl,MatID,mp,mtlKey);
VirtoolsMat = VirtoolsExporter->GetMaterialByKey(mtlKey);
useUVGen = GetTextureUvGen(PatchMtl,uvgen);
}
PreviousMatId = MatID;
if (tvp.tv) {
int nb = (pmesh->patches[i].type == PATCH_TRI) ? 3 : 4;
for (int j=0;j<nb;++j) {
VxUV uv;
CopyUV(&uv,&pmesh->getMapVert(mp,tvp.tv[j]));
ApplyUvGen(uv,uvgen,useUVGen);
Virtoolstvp.tv[j] = tvp.tv[j];
VirtoolsPMesh->SetTV(tvp.tv[j],uv.u,uv.v,PatchChannelCount-1);
}
VirtoolsPMesh->SetTVPatch(i,&Virtoolstvp,PatchChannelCount-1);
}
}
PatchChannelCount++;
}
}
ent->SetCurrentMesh(VirtoolsPMesh);
InsertNewMeshInstance(os.obj,nodeMtl,VirtoolsPMesh);
if (needDel) {
delete patch;
}
}
Point3 Max2Nmo::GetVertexNormal(Mesh* mesh, int faceNo, RVertex* rv)
{
Face* f = &mesh->faces[faceNo];
DWORD smGroup = f->smGroup;
int numNormals;
Point3 vertexNormal;
// Is normal specified
// SPCIFIED is not currently used, but may be used in future versions.
if (rv->rFlags & SPECIFIED_NORMAL) {
vertexNormal = rv->rn.getNormal();
}
// If normal is not specified it's only available if the face belongs
// to a smoothing group
else if ((numNormals = rv->rFlags & NORCT_MASK) && smGroup) {
// If there is only one vertex is found in the rn member.
if (numNormals == 1) {
vertexNormal = rv->rn.getNormal();
}
else {
// If two or more vertices are there you need to step through them
// and find the vertex with the same smoothing group as the current face.
// You will find multiple normals in the ern member.
for (int i = 0; i < numNormals; i++) {
if (rv->ern[i].getSmGroup() & smGroup) {
vertexNormal = rv->ern[i].getNormal();
}
}
}
}
else {
// Get the normal from the Face if no smoothing groups are there
vertexNormal = mesh->getFaceNormal(faceNo);
}
return vertexNormal;
}
/****************************************************************************
Material and Texture Export
****************************************************************************/
void Max2Nmo::ExportMaterialList()
{
//--- Initialize the lightmap base materials hash table
m_lightmapBaseMaterials.Clear();
//--- Parse all 3dsmax materials
int numMtls = mtlList.Count();
for (int i=0; i<numMtls; i++) {
DumpMaterial(mtlList.GetMtl(i));
}
}
void Max2Nmo::DumpMaterial(Mtl* mtl)
{
Report(REPORT_HLEVEL,"%s...",mtl->GetName());
if (!mtl) return;
if (VirtoolsExporter->GetMaterialByKey(mtl)) return ;
TimeValue t = GetStaticFrame();
// We know the Standard material, so we can get some extra info
if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) {
StdMat* std = (StdMat*)mtl;
VirtoolsMaterial NMat;
TextureUVGen uvgen;
BOOL useUVGen = FALSE;
Color Ambient = std->GetAmbient(t);
Color Diffuse = std->GetDiffuse(t);
Color Specular = std->GetSpecular(t);
float Shininess = std->GetShininess(t);
float ShinStr = std->GetShinStr(t);
float Transparency = std->GetXParency(t);
float selfIllum = std->GetSelfIllum(0);
BOOL selfIllumColorOn=FALSE;
Color Emissive;
useUVGen = GetTextureUvGen(mtl,uvgen);
//--- Get self Illumn color information
StdMat2* stdmat2=(StdMat2*)std;
if (stdmat2->SupportsShaders()) {
Shader* shader=stdmat2->GetShader();
if (shader) {
selfIllumColorOn=shader->IsSelfIllumClrOn();
Emissive=shader->GetSelfIllumClr(0);
}
}
NMat.m_Diffuse = VxColor(Diffuse.r,Diffuse.g,Diffuse.b,1.0f-Transparency);
NMat.m_Ambient = VxColor(Ambient.r,Ambient.g,Ambient.b);
NMat.m_Specular = VxColor(Specular.r,Specular.g,Specular.b);
NMat.m_Emissive = selfIllum*NMat.m_Diffuse;
NMat.m_TwoSided = std->GetTwoSided();
NMat.m_FillMode = std->GetWire() ? VXFILL_WIREFRAME : VXFILL_SOLID;
NMat.m_ShadeMode = std->GetShading() == SHADE_CONST ? VXSHADE_FLAT : VXSHADE_GOURAUD;
if (selfIllumColorOn)
NMat.m_Emissive = VxColor(Emissive.r,Emissive.g,Emissive.b);
if (Transparency) {
NMat.m_AlphaBlending = TRUE;
NMat.m_SrcBlendFactor = VXBLEND_SRCALPHA;
NMat.m_DstBlendFactor = VXBLEND_INVSRCALPHA;
NMat.m_ZWriteEnable = FALSE;
}
// TODO
switch (std->GetTransparencyType()) {
case TRANSP_FILTER: break;
case TRANSP_SUBTRACTIVE: break;
case TRANSP_ADDITIVE: break;
}
//--- Look for texture (diffuse only)
Texmap* pTexmap;
BitmapTex* pBmpTex;
BOOL TexValid = FALSE;
BOOL OpacityValid = FALSE;
int TexWidth = 0;
int TexHeight = 0;
#ifdef MAX51
//--- Araya: If the material has a lightmap shader in the viewport
///// then use the lightmap base texture instead of the diffusemap texture
///// and add a new material/texture, the lightmap.
if( LM::ShaderEnabled(mtl) ){
VxColor white( 1.0f, 1.0f, 1.0f, 1.0f );
VxColor black( 0.0f, 0.0f, 0.0f, 1.0f );
BOOL baseMaterialAlreadyExists = FALSE;
//--- Add base texture
if( LM::BaseTextureEnabled(mtl) ){
XString baseTextureFilename = LM::BaseTextureFilename(mtl);
CKPathSplitter psplitter( baseTextureFilename.Str() );
//--- Note: the texture is added only if there's no texture with
///// the same filename already added.
NMat.m_Texture = VirtoolsExporter->AddTexture(
baseTextureFilename.CStr(),
(const char *)psplitter.GetName() );
NMat.m_Emissive = white;
//--- If there's a similar material already added, then use it,
///// it must be the first created lightmap diffuse map
///// refering to the same texture
XHashTable< Mtl*, XString, XHashFun<XString> >::Iterator it =
m_lightmapBaseMaterials.Find( baseTextureFilename );
if( it!=m_lightmapBaseMaterials.End() ){
baseMaterialAlreadyExists = TRUE;
} else {
//--- Else, add this material to the list of unique lightmap base textures
m_lightmapBaseMaterials.Insert( baseTextureFilename, mtl );
}
}
//--- Add lightmap texture
if( LM::LightmapTextureEnabled(mtl) ){
VirtoolsMaterial LMMat;
XString lightTextureFilename = LM::LightmapTextureFilename(mtl);
CKPathSplitter psplitter( lightTextureFilename.Str() );
LMMat.m_Texture = VirtoolsExporter->AddTexture(
lightTextureFilename.CStr(),
(const char *)psplitter.GetName() );
LMMat.m_TextureBlendMode = VXTEXTUREBLEND_MODULATEALPHA;
LMMat.m_Diffuse = white;
LMMat.m_Ambient = white;
LMMat.m_Specular = black;
LMMat.m_Emissive = white;
LMMat.m_TwoSided = NMat.m_TwoSided;
LMMat.m_FillMode = NMat.m_FillMode;
LMMat.m_ShadeMode = NMat.m_ShadeMode;
//--- ~ little trick for lightmap material
void* keyToFindBackMaterial = (void*)~(DWORD)mtl;
CKMaterial* mat = VirtoolsExporter->AddMaterial( &LMMat,
(const char *)psplitter.GetName(),
keyToFindBackMaterial );
mat->SetTextureAddressMode( VXTEXTURE_ADDRESSCLAMP );
}
if( baseMaterialAlreadyExists ) return;
//--- Material is standard (without any shader effect)
} else
#endif
{
if(std->MapEnabled(ID_DI) || std->MapEnabled(ID_SI)) {
int MapToRetrieve = std->MapEnabled(ID_DI) ? ID_DI : ID_SI;
float MixAmount = std->GetTexmapAmt(MapToRetrieve,0);
// If Amount is 100 % the diffuse color is not taken into account
// (equivalent to DECAL_MODE) in Virtools
// 14.11.200 Removed since people are most of the time
// waiting for a modulate mode
// if (MixAmount<1) NMat.m_TextureBlendMode = VXTEXTUREBLEND_MODULATEALPHA;
// else NMat.m_TextureBlendMode = VXTEXTUREBLEND_DECAL;
NMat.m_TextureBlendMode = VXTEXTUREBLEND_MODULATEALPHA;
if(pTexmap = std->GetSubTexmap(MapToRetrieve)) {
if(pTexmap->ClassID()==Class_ID(BMTEX_CLASS_ID,0)) {
pBmpTex = (BitmapTex*)pTexmap;
Bitmap* bm=pBmpTex->GetBitmap(0);
XString diffuseFileName=pBmpTex->GetMapName();
CKPathSplitter psplitter(diffuseFileName.Str());
NMat.m_Texture = VirtoolsExporter->AddTexture(diffuseFileName.CStr(),(const char *)psplitter.GetName(),pBmpTex);
// Check if texture is valid for Virtools
int flg=0;
if(bm==NULL) {
Report(REPORT_HLEVEL,"\r\n%s : file not found...\r\n",pBmpTex->GetMapName());
} else { // Test power of 2
TexValid = TRUE;
TexWidth = bm->Width();
TexHeight = bm->Height();
DWORD w=(DWORD)bm->Width();
DWORD h=(DWORD)bm->Height();
DWORD mask;
mask=0xFFFFFFFF;
mask>>=(32-GetMSB(w));
mask=w&mask;
if(mask) flg=1;
mask=0xFFFFFFFF;
mask>>=(32-GetMSB(h));
mask=h&mask;
if(mask) flg=1;
if(flg) {
Report(REPORT_HLEVEL,"\r\nWarning %s :w:%d - h:%d\r\nThe width and height of the bitmap must be a power of 2 (2,4,8 ... 256,512)\r\n",pBmpTex->GetMapName(),w,h);
}
}
}
}
}
if(NMat.m_Texture && TexValid && std->MapEnabled(ID_OP)) {
if (pTexmap = std->GetSubTexmap(ID_OP)) {
if(pTexmap->ClassID()==Class_ID(BMTEX_CLASS_ID,0)) {
pBmpTex = (BitmapTex*)pTexmap;
Bitmap* bm = pBmpTex->GetBitmap(0);
if (bm) {
Report(REPORT_LLEVEL,"\r\nGenerating alpha from opacity map : %s\r\n",pBmpTex->GetMapName());
int BmpWidth = bm->Width();
int BmpHeight = bm->Height();
if ((BmpWidth == TexWidth) && (BmpHeight == TexHeight)) {
int Stride = BmpWidth*sizeof(DWORD);
BYTE* ptr = NMat.m_Texture->LockSurfacePtr();
if (ptr) {
// Go to alpha value
ptr+=3;
XArray<WORD> Pixels;
Pixels.Resize(BmpWidth);
for (int i=0; i< BmpHeight; ++i,ptr+=Stride) {
int res = bm->Get16Gray(0,i,BmpWidth,Pixels.Begin());
BYTE* pix = (BYTE *)Pixels.Begin();
++pix;
BYTE* tptr = ptr;
for (int j=0;j<BmpWidth;++j,pix+=2,tptr+=4) {
*tptr = *pix;
}
}
OpacityValid = TRUE;
NMat.m_AlphaBlending = TRUE;
NMat.m_SrcBlendFactor = VXBLEND_SRCALPHA;
NMat.m_DstBlendFactor = VXBLEND_INVSRCALPHA;
NMat.m_Diffuse.a = 1.0f; // Opacity value is not taken when
// giving a opacity map
}
} else {
Report(REPORT_HLEVEL,"\r\nOpacity map : %s must be the same size than the diffuse texture...\r\n",pBmpTex->GetMapName());
}
} else {
Report(REPORT_HLEVEL,"\r\nOpacity map : %s not found...\r\n",pBmpTex->GetMapName());
}
}
}
}
}
CKMaterial* mat = VirtoolsExporter->AddMaterial(&NMat,mtl->GetName(),mtl);
//if (!(uvgen.MirrorU || uvgen.TileU || uvgen.MirrorV || uvgen.TileV))
// mat->SetTextureAddressMode(VXTEXTURE_ADDRESSCLAMP);
//else
if( useUVGen ){
if (uvgen.MirrorU && uvgen.MirrorV)
mat->SetTextureAddressMode(VXTEXTURE_ADDRESSMIRROR); // Will this be supported by video card in Virtools ????
if (uvgen.UOffset!=0 || uvgen.UScale!=1.0f || uvgen.VOffset!=0 || uvgen.VScale!=1.0f) {
if (uvgen.Cropping && (uvgen.UOffset!=0 || uvgen.UScale!=1.0f || uvgen.VOffset!=0 || uvgen.VScale!=1.0)) {
// Cropping and (tiling/mirroring) are used together warn the
// user that the conversion may not work
Report(REPORT_HLEVEL,"\r\nCropping and (tiling or mirroring) are used on this texture\r\n and may generate incorrect results in Virtools\r\n");
}
}
}
// Opacity map is valid , we enable Alpha testing
//
if (OpacityValid) {
mat->EnableAlphaTest(TRUE);
mat->SetAlphaRef(1);
mat->SetAlphaFunc(VXCMP_GREATER);
// At least 1 bit of alpha should be here for
// the display to be correct
NMat.m_Texture->SetDesiredVideoFormat(_16_ARGB1555);
}
}
Report(REPORT_HLEVEL,"Done\r\n");
#ifdef MAX51
// Baked Shell Material
if( mtl->ClassID() == Class_ID(BAKE_SHELL_CLASS_ID, 0)){
if( mtl->NumSubMtls() >= 2 ){
Mtl* bakedMaterial = mtl->GetSubMtl(1);
//If baked material is only configured to use
//self illum we must certainly also consider the first material
if (bakedMaterial && bakedMaterial->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) {
StdMat* std = (StdMat*)bakedMaterial;
if (!std->MapEnabled(ID_DI) && std->MapEnabled(ID_SI)) {
DumpMaterial( mtl->GetSubMtl(0));
}
}
if( bakedMaterial ){
DumpMaterial( bakedMaterial );
}
}
} else
#endif
// Sub Materials
if (mtl->NumSubMtls() > 0) {
for (int i=0; i<mtl->NumSubMtls(); i++) {
Mtl* subMtl = mtl->GetSubMtl(i);
if (subMtl) {
DumpMaterial(subMtl);
}
}
}
}
/****************************************************************************
Misc Utility functions
****************************************************************************/
// Determine is the node has negative scaling.
// This is used for mirrored objects for example. They have a negative scale factor
// so when calculating the normal we should take the vertices counter clockwise.
// If we don't compensate for this the objects will be 'inverted'.
// But People should never use Mirror !!!!
BOOL Max2Nmo::TMNegParity(Matrix3 &m)
{
return (DotProd(CrossProd(m.GetRow(0),m.GetRow(1)),m.GetRow(2))<0.0)?1:0;
}
// Return a pointer to a TriObject given an INode or return NULL
// if the node cannot be converted to a TriObject
TriObject* Max2Nmo::GetTriObjectFromNode(INode *node, TimeValue t, int &deleteIt)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -