void NifImporter::SetNormals(Mesh& mesh, const vector<Niflib::Triangle>& tris, const vector<Niflib::Vector3>& n)
{
	mesh.checkNormals(TRUE);
	if (n.size() > 0)
	{
		bool needNormals = false;
		for (unsigned int i=0; i<n.size(); i++){
			Vector3 v = n[i];
			Point3 norm(v.x, v.y, v.z);
			if (norm != mesh.getNormal(i)) {
				needNormals = true;
				break;
			}
		}
		if (needNormals)
		{
#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 5
			mesh.SpecifyNormals();
			MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals ();
			if (NULL != specNorms)
			{
				specNorms->ClearAndFree();
				specNorms->SetNumFaces(tris.size());
				specNorms->SetNumNormals(n.size());

				Point3* norms = specNorms->GetNormalArray();
				for (unsigned int i=0; i<n.size(); i++){
					Vector3 v = n[i];
					norms[i] = Point3(v.x, v.y, v.z);
				}
				MeshNormalFace* pFaces = specNorms->GetFaceArray();
				for (unsigned int i=0; i<tris.size(); i++){
					const Triangle& tri = tris[i];
					pFaces[i].SpecifyNormalID(0, tri.v1);
					pFaces[i].SpecifyNormalID(1, tri.v2);
					pFaces[i].SpecifyNormalID(2, tri.v3);
				}
#if VERSION_3DSMAX > ((7000<<16)+(15<<8)+0) // Version 7+
				specNorms->SetAllExplicit(true);
#else
				for (int i=0; i<specNorms->GetNumNormals(); ++i) {
					specNorms->SetNormalExplicit(i, true);
				}
#endif
				specNorms->CheckNormals();
			}
#endif
		}
	}
}
Exemple #2
0
bool Exporter::splitMesh(INode *node, Mesh& mesh, FaceGroups &grps, TimeValue t, vector<Color4>& vertColors, bool noSplit)
{
	Mtl* nodeMtl = node->GetMtl();
	Matrix3 tm = node->GetObjTMAfterWSM(t);

	// Order of the vertices. Get 'em counter clockwise if the objects is
	// negatively scaled.
	int vi[3];
	if (TMNegParity(tm)) {
		vi[0] = 2; vi[1] = 1; vi[2] = 0;
	} else {
		vi[0] = 0; vi[1] = 1; vi[2] = 2;
	}

	Matrix3 flip;
	flip.IdentityMatrix();
	flip.Scale(Point3(1, -1, 1));

   int nv = mesh.getNumVerts();
   int nf = mesh.getNumFaces();

	if (noSplit)
	{
		int nv = mesh.getNumVerts();
		int nf = mesh.getNumFaces();
		// Dont split the mesh at all.  For debugging purposes.
		FaceGroup& grp = grps[0];
		grp.vidx.resize(nv, -1);
		grp.verts.resize(nv);
		grp.faces.resize(nf);
		grp.uvs.resize(nv);
		grp.vnorms.resize(nv);
      grp.fidx.resize(nf);

		Matrix3 texm;
		getTextureMatrix(texm, getMaterial(node, 0));
		texm *= flip;

		for (int face=0; face<nf; ++face) {
         grp.fidx[face] = face;
			for (int vi=0; vi<3; ++vi) {
				int idx = mesh.faces[face].getVert(vi);
				grp.faces[face][vi] = idx;

				// Calculate normal
				Point3 norm;
#if VERSION_3DSMAX <= ((5000<<16)+(15<<8)+0) // Version 5
				norm = getVertexNormal(&mesh, face, mesh.getRVertPtr(idx));
#else
				MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals ();
				if (NULL != specNorms && specNorms->GetNumNormals() != 0)
					norm = specNorms->GetNormal(face, vi);
				else
					norm = getVertexNormal(&mesh, face, mesh.getRVertPtr(idx));
#endif
				Point3 uv;
				if (mesh.tVerts && mesh.tvFace) {
					uv = mesh.tVerts[ mesh.tvFace[ face ].t[ vi ]] * texm;
					uv.y += 1.0f;
				}

				if (grp.vidx[idx] == idx){
					ASSERT(grp.verts[idx] == TOVECTOR3(mesh.getVert(idx)));
					//ASSERT(vg.norm == norm);
					//Point3 uv = mesh.getTVert(idx);
					//if (mesh.getNumTVerts() > 0)
					//{
					//	ASSERT(grp.uvs[idx].u == uv.x && grp.uvs[idx].v == uv.y);
					//}
				} else {
					grp.vidx[idx] = idx;
					grp.verts[idx] = TOVECTOR3(mesh.getVert(idx));
					//grp.uvs[idx].u = uv.x;
					//grp.uvs[idx].v = uv.y;
					grp.vnorms[idx] = TOVECTOR3(norm);
				}
			}
		}
		for (int i=0; i<nv; ++i) {
			ASSERT(grp.vidx[i] != -1);
		}
	}
	else
	{
		int face, numSubMtls = nodeMtl?nodeMtl->NumSubMtls():0;
		for (face=0; face<mesh.getNumFaces(); face++) 
		{
			int mtlID = (numSubMtls!=0) ? (mesh.faces[face].getMatID() % numSubMtls) : 0;
         Mtl *mtl = getMaterial(node, mtlID);
			Matrix3 texm;
			getTextureMatrix(texm, mtl);
			texm *= flip;

         FaceGroup& grp = grps[mtlID];

		 if (grp.uvMapping.size() == 0) // Only needs to be done once per face group
		 {
			 int nmaps = 0;
			 int nmapsStart = max(1, mesh.getNumMaps() - (mesh.mapSupport(0) ? 1 : 0)); // Omit vertex color map.
			 for (int ii = 1; ii <= nmapsStart; ii++) // Winnow out the unsupported maps.
			 {
				 if (!mesh.mapSupport(ii)) continue;
				 grp.uvMapping[ii] = nmaps++;
			 }
			 grp.uvs.resize(nmaps == 0 ? 1 : nmaps);
		 }
         if (nv > int(grp.verts.capacity()))
         {
            grp.vgrp.reserve(nv);
            grp.verts.reserve(nv);
            grp.vnorms.reserve(nv);
            for (int i=0; i<grp.uvs.size(); ++i)
               grp.uvs[i].reserve(nv);
            grp.vcolors.reserve(nv);
            grp.vidx.reserve(nv);
         }
         if (nf > int(grp.faces.capacity()))
         {
            grp.faces.reserve(nf);
            grp.fidx.reserve(nf);
         }

         Triangle tri;
         for (int i=0; i<3; i++)
            tri[i] = addVertex(grp, face, vi[i], &mesh, texm, vertColors);
         grp.faces.push_back(tri);

         if (grp.fidx.size() < nf)
            grp.fidx.resize(nf,-1);
         grp.fidx[face] = grp.faces.size() - 1;
		}
	}

	return true;
}
Exemple #3
0
int Exporter::addVertex(FaceGroup &grp, int face, int vi, Mesh *mesh, const Matrix3 &texm, vector<Color4>& vertColors)
{
   VertexGroup vg;
   int vidx;
   vidx = vg.idx = mesh->faces[ face ].v[ vi ];
	vg.pt = mesh->verts[ vidx ];
#if VERSION_3DSMAX <= ((5000<<16)+(15<<8)+0) // Version 5
   vg.norm = getVertexNormal(mesh, face, mesh->getRVertPtr(vidx));
#else
   MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
   if (NULL != specNorms && specNorms->GetNumNormals() != 0)
      vg.norm = specNorms->GetNormal(face, vi);
   else
      vg.norm = getVertexNormal(mesh, face, mesh->getRVertPtr(vidx));
#endif

   int nmaps = grp.uvMapping.size();
   map<int,int>::iterator UVSetIter;
   vg.uvs.resize(nmaps > 0 ? nmaps : 1);
   if (nmaps > 0) {
	  for (UVSetIter=grp.uvMapping.begin() ; UVSetIter != grp.uvMapping.end(); UVSetIter++ )
      {
		  int maxUVIdx = (*UVSetIter).first;
		  int nifUVIdx = (*UVSetIter).second;
         TexCoord& uvs = vg.uvs[nifUVIdx];
         UVVert *uv = mesh->mapVerts(maxUVIdx); 
         TVFace *tv = mesh->mapFaces(maxUVIdx);
         if (uv && tv) {
            Point3 uvw = uv[ tv[ face ].t[ vi ]] * texm;
            uvs.u = uvw[0];
            uvs.v = uvw[1] + 1.0f;
         }
      }
   } else {
      if (mesh->tVerts && mesh->tvFace) {
         Point3 uvw = mesh->tVerts[ mesh->tvFace[ face ].t[ vi ]] * texm;
         vg.uvs[0].u = uvw[0];
         vg.uvs[0].v = uvw[1] + 1.0f;
      }
   }
   vg.color = Color4(1.0f, 1.0f, 1.0f);
   if (mVertexColors && !vertColors.empty()){
      TVFace *vcFace = mesh->vcFaceData ? mesh->vcFaceData : mesh->vcFace;
      if (vcFace) {
         int vidx = vcFace[ face ].t[ vi ];
         vg.color = vertColors[vidx];
      }
   }

   VertexCompare vc(grp, Exporter::mWeldThresh, Exporter::mNormThresh, Exporter::mUVWThresh);
   int n = grp.verts.size();
#if 0
   IntRange range = std::equal_range(grp.vmap.begin(), grp.vmap.end(), vg, vc);
   if (range.first != range.second) {
      return (*range.first);
   } 
   grp.vmap.insert(range.first, n);
#else
   for (int i=0; i<grp.vgrp.size(); i++) {
      if ( vc.compare(vg, i) == 0 )
         return i;
   }
   grp.vmap.push_back(n);
#endif
   grp.vidx.push_back(vidx);
   grp.vgrp.push_back(vg);
   grp.verts.push_back(TOVECTOR3(vg.pt));
   grp.vnorms.push_back(TOVECTOR3(vg.norm));
   for (int i=0; i< grp.uvs.size(); ++i) {
      TexCoords& uvs = grp.uvs[i];
      uvs.push_back(vg.uvs[i]);
   }
   grp.vcolors.push_back(vg.color);
   return n;
}
Exemple #4
0
Exporter::Result Exporter::exportMesh(NiNodeRef &ninode, INode *node, TimeValue t)
{
	ObjectState os = node->EvalWorldState(t);

	bool local = !mFlattenHierarchy;

	TriObject *tri = (TriObject *)os.obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0));
	if (!tri)
		return Skip;

	Mesh *copymesh = NULL;
	Mesh *mesh = &tri->GetMesh();

	Matrix3 mtx(true), rtx(true);
	if (Exporter::mCollapseTransforms)
	{
		mtx = GetNodeLocalTM(node, t);
		mtx.NoTrans();
		Quat q(mtx);
		q.MakeMatrix(rtx);
		mesh = copymesh = new Mesh(*mesh);
		{
			int n = mesh->getNumVerts();
			for ( unsigned int i = 0; i < n; ++i ) {
				Point3& vert = mesh->getVert(i);
				vert = mtx * vert;
			}
			mesh->checkNormals(TRUE);
#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 6+
			MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
			if (NULL != specNorms) {
				specNorms->CheckNormals();
				for ( unsigned int i = 0; i < specNorms->GetNumNormals(); ++i ) {
					Point3& norm = specNorms->Normal(i);
					norm = (rtx * norm).Normalize();
				}
			}
#endif
		}
	}
	// Note that calling setVCDisplayData will clear things like normals so we set this up first
	vector<Color4> vertColors;
	if (mVertexColors)
	{
		bool hasvc = false;
		if (mesh->mapSupport(MAP_ALPHA))
		{
			mesh->setVCDisplayData(MAP_ALPHA);         int n = mesh->getNumVertCol();
			if (n > vertColors.size())
				vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
			VertColor *vertCol = mesh->vertColArray;
			if (vertCol) {
				for (int i=0; i<n; ++i) {
					VertColor c = vertCol[ i ];
					float a = (c.x + c.y + c.z) / 3.0f;
					vertColors[i].a = a;
					hasvc |= (a != 1.0f);
				}
			}
		}
		if (mesh->mapSupport(0))
		{
			mesh->setVCDisplayData(0);
			VertColor *vertCol = mesh->vertColArray;
			int n = mesh->getNumVertCol();
			if (n > vertColors.size())
				vertColors.assign(n, Color4(1.0f, 1.0f, 1.0f, 1.0f));
			if (vertCol) {
				for (int i=0; i<n; ++i) {
					VertColor col = vertCol[ i ];
					vertColors[i] = Color4(col.x, col.y, col.z, vertColors[i].a);
					hasvc |= (col.x != 1.0f || col.y != 1.0f || col.z != 1.0f);
				}
			}
		}
		if (!hasvc) vertColors.clear();
	}

#if VERSION_3DSMAX <= ((5000<<16)+(15<<8)+0) // Version 5
	mesh->checkNormals(TRUE);
#else
	MeshNormalSpec *specNorms = mesh->GetSpecifiedNormals ();
	if (NULL != specNorms) {
		specNorms->CheckNormals();
		if (specNorms->GetNumNormals() == 0)
			mesh->checkNormals(TRUE);
	} else {
		mesh->checkNormals(TRUE);
	}
#endif

	Result result = Ok;

	Modifier* geomMorpherMod = GetMorpherModifier(node);
	bool noSplit = FALSE;
//	bool noSplit = (NULL != geomMorpherMod);

	while (1)
	{
		FaceGroups grps;
		if (!splitMesh(node, *mesh, grps, t, vertColors, noSplit))
		{
			result = Error;
			break;
		}
		bool exportStrips = mTriStrips && (Exporter::mNifVersionInt > VER_4_2_2_0);

		Matrix44 tm = Matrix44::IDENTITY;
		if ( mExportExtraNodes || (mExportType != NIF_WO_ANIM && isNodeKeyed(node) ) ) {
			tm = TOMATRIX4(getObjectTransform(node, t, false) * Inverse(getNodeTransform(node, t, false)));
		} else {
			Matrix33 rot; Vector3 trans;
			objectTransform(rot, trans, node, t, local);
			tm = Matrix44(trans, rot, 1.0f);
		}
		tm = TOMATRIX4(Inverse(mtx)) * tm;

		TSTR basename = node->NodeName();
		TSTR format = (!basename.isNull() && grps.size() > 1) ? "%s:%d" : "%s";

		int i=1;
		FaceGroups::iterator grp;
		for (grp=grps.begin(); grp!=grps.end(); ++grp, ++i)
		{
			string name = FormatString(format, basename.data(), i);
			NiTriBasedGeomRef shape = makeMesh(ninode, getMaterial(node, grp->first), grp->second, exportStrips);
			if (shape == NULL)
			{
				result = Error;
				break;
			}

			if (node->IsHidden())
				shape->SetVisibility(false);

			shape->SetName(name);
			shape->SetLocalTransform(tm);

			if (Exporter::mZeroTransforms) {
				shape->ApplyTransforms();
			}

			makeSkin(shape, node, grp->second, t);

			if (geomMorpherMod) {
				vector<Vector3> verts = shape->GetData()->GetVertices();
				exportGeomMorpherControl(geomMorpherMod, verts, shape->GetData()->GetVertexIndices(), shape);
				shape->GetData()->SetConsistencyFlags(CT_VOLATILE);
			}

		}

		break;
	}

	if (tri != os.obj)
		tri->DeleteMe();

	if (copymesh)
		delete copymesh;

	return result;
}
Exemple #5
0
void RenderMesh::ConvertFaces(Mesh *Mesh, int MatIndex, Tab<Vert3> &Verts, Tab<Face3> &Faces, bool NegScale)
{
    Face3			TmpFace;
    Vert3			TmpVert;
	BitArray		Written;
	int				i,j,k,NumFace;
	int				NumUV,UVCount,Index;
	int				NumVert,Count,VIndex;
	Face			*aFace;
	Tab<BasisVert>	FNormals;
	Tab<VNormal>	Normals;
	UVVert			*UVVert;
	TVFace			*UVFace;
	Point3			S,T,SxT;
	unsigned long	Sg;

	bool useMeshNorms = false;


	if(NegScale)
	{
		gVIndex[0] = 2;
		gVIndex[1] = 1;
		gVIndex[2] = 0;
	}
	else
	{
		gVIndex[0] = 0;
		gVIndex[1] = 1;
		gVIndex[2] = 2;
	}

	// Do we have an EditNormal modifier present - if so we use those normals instead.
	// We only use this if they have been applied on a face with smoothing groups, otherwise
	// it messes up the tangent space calculation.  Probably not the most obtmized route, but it
	// works...

	MeshNormalSpec * meshNorm = Mesh->GetSpecifiedNormals();
	if(meshNorm && meshNorm->GetNumNormals())
		useMeshNorms = true;

	NumFace = 0;

	for(i=0; i < Mesh->getNumFaces(); i++) 
	{
		if(!Mesh->faces[i].Hidden())
		{
			Index = Mesh->getFaceMtlIndex(i) + 1;

			if(Index == MatIndex || MatIndex == 0)
			{
				NumFace++;
			}
		}

	}

	NumVert = Mesh->getNumVerts();
    Verts.SetCount(NumVert);

    Faces.SetCount(NumFace);

	if(NumVert == 0 || NumFace == 0)
	{
		return;
	}

	ComputeVertexNormals(Mesh,FNormals,Normals,NegScale);

    Written.SetSize(Mesh->getNumVerts());
    Written.ClearAll();

	NumUV = Mesh->getNumMaps();	

	if(NumUV)
	{	
		Count = 0;

		if(NumUV > MAX_TMUS + 1)
		{
			NumUV = MAX_TMUS + 1;
		}

		for(i=0; i < Mesh->getNumFaces(); i++) 
		{
			aFace = &Mesh->faces[i];

			TmpFace.m_Num[0] = aFace->v[gVIndex[0]];
			TmpFace.m_Num[1] = aFace->v[gVIndex[1]];
			TmpFace.m_Num[2] = aFace->v[gVIndex[2]];


			Sg = aFace->smGroup;

			for(j=0; j < 3; j++) 
			{
				VIndex			 = aFace->v[gVIndex[j]];
				TmpVert.m_Pos	 = Mesh->verts[VIndex];

				if(Sg)
				{
					if(useMeshNorms)
					{
						int normID = meshNorm->Face(i).GetNormalID(gVIndex[j]);
						TmpVert.m_Normal = meshNorm->Normal(normID).Normalize();
						Normals[VIndex].GetNormal(Sg,S,T,SxT);
					}
					else
					        TmpVert.m_Normal = Normals[VIndex].GetNormal(Sg,S,T,SxT);
					
					TmpVert.m_S		 = S;
					TmpVert.m_T		 = T;
					TmpVert.m_SxT	 = SxT;

				}
				else
				{
					TmpVert.m_Normal = FNormals[i].m_Normal;
					TmpVert.m_S		 = FNormals[i].m_S;
					TmpVert.m_T		 = FNormals[i].m_T;
					TmpVert.m_SxT	 = FNormals[i].m_SxT;
				}

				UVCount		 = 0;
				TmpVert.m_Sg = Sg;

				for(k=0;k<m_MapChannels.Count();k++)
				{	
					int index = m_MapChannels[k];

					if(Mesh->getNumMapVerts(index))
					{
						UVVert = Mesh->mapVerts(index);
						UVFace = Mesh->mapFaces(index);

						TmpVert.m_UV[k].x = UVVert[UVFace[i].t[gVIndex[j]]].x;
						TmpVert.m_UV[k].y = UVVert[UVFace[i].t[gVIndex[j]]].y;

	
					}
					else
					{
						TmpVert.m_UV[k].x = 0.0f;
						TmpVert.m_UV[k].y = 0.0f;
					}
				}
				
		
				if(Written[VIndex]) 
				{
					if((Sg == 0) || 
					   (Verts[VIndex].m_Sg != TmpVert.m_Sg) ||	
					   (!UVVertEqual(Verts[VIndex].m_UV[0],TmpVert.m_UV[0]))) 
					{
						TmpFace.m_Num[j] = Verts.Count();
						Verts.Append(1,&TmpVert,10);
					}
				} 
				else 
				{
					Verts[VIndex] = TmpVert;
					Written.Set(VIndex);
				}

			}

			if(!Mesh->faces[i].Hidden())
			{
				Index = Mesh->getFaceMtlIndex(i) + 1;

				if(Index == MatIndex || MatIndex == 0)
				{
					Faces[Count++] = TmpFace;
				}

			}

		}

	}
	else
	{
		for(i=0; i < Mesh->getNumFaces(); i++) 
		{
			aFace = &Mesh->faces[i];

			Faces[i].m_Num[0] = aFace->v[gVIndex[0]];
			Faces[i].m_Num[1] = aFace->v[gVIndex[1]];
			Faces[i].m_Num[2] = aFace->v[gVIndex[2]];

			for(j=0; j < 3; j++) 
			{
				VIndex					= aFace->v[gVIndex[j]];
				Verts[VIndex].m_Pos		= Mesh->verts[VIndex];
				Verts[VIndex].m_Normal	= Normals[VIndex].GetNormal(aFace->smGroup,S,T,SxT);
				Verts[VIndex].m_S		= Point3(0.0f,0.0f,0.0f);
				Verts[VIndex].m_T		= Point3(0.0f,0.0f,0.0f);
				Verts[VIndex].m_SxT		= Point3(0.0f,0.0f,0.0f);

				for(k=0; k < MAX_TMUS; k++)
				{
					Verts[VIndex].m_UV[k].x = 0.0f;
					Verts[VIndex].m_UV[k].y = 0.0f;
				}

			}

		}

	}
	Verts.Shrink();
	

}
void SolidifyPW::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) 
{

	//TODO: Add the code for actually modifying the object
	meshInfo.Free();

	if (os->obj->IsSubClassOf(triObjectClassID)) {
		TriObject *tobj = (TriObject*)os->obj;
		Mesh &mesh = tobj->GetMesh();
		Interval iv = FOREVER;
		float a,oa;
		
		pblock->GetValue(pb_amount,t,a,iv);
		pblock->GetValue(pb_oamount,t,oa,iv);

		if (a == oa)
			oa += 0.00001f;

		BOOL overrideMatID;
		int matid;

		pblock->GetValue(pb_overridematid,t,overrideMatID,iv);
		pblock->GetValue(pb_matid,t,matid,iv);
		matid--;

		if (!overrideMatID) matid = -1;


		BOOL overridesg;
		int sg;
		pblock->GetValue(pb_overridesg,t,overridesg,iv);
		pblock->GetValue(pb_sg,t,sg,iv);
		

		if (!overridesg) sg = -1;

		int edgeMap;
		pblock->GetValue(pb_edgemap,t,edgeMap,iv);

		float tvOffset;
		pblock->GetValue(pb_tvoffset,t,tvOffset,iv);


		BOOL ioverrideMatID;
		int imatid;

		pblock->GetValue(pb_overrideinnermatid,t,ioverrideMatID,iv);
		pblock->GetValue(pb_innermatid,t,imatid,iv);
		imatid--;

		if (!ioverrideMatID) imatid = -1;


		BOOL ooverrideMatID;
		int omatid;

		pblock->GetValue(pb_overrideoutermatid,t,ooverrideMatID,iv);
		pblock->GetValue(pb_outermatid,t,omatid,iv);
		omatid--;

		if (!ooverrideMatID) omatid = -1;


		BOOL selEdges, selInner,selOuter;

		static BOOL selEdgesPrev = FALSE;
		static BOOL selInnerPrev = FALSE;
		static BOOL selOuterPrev = FALSE;
		
		BOOL updateUI = FALSE;
		
		pblock->GetValue(pb_seledges,t,selEdges,iv);
		pblock->GetValue(pb_selinner,t,selInner,iv);
		pblock->GetValue(pb_selouter,t,selOuter,iv);
		
		if (selEdges && (!selEdgesPrev))
			updateUI = TRUE;
		if (selInner && (!selInnerPrev))
			updateUI = TRUE;
		if (selOuter && (!selOuterPrev))
			updateUI = TRUE;
			
		selEdgesPrev = selEdges;
		selInnerPrev = selInner;			
		selOuterPrev = selOuter;			

		if (selEdges || selInner|| selOuter)
			{
			mesh.dispFlags = DISP_SELFACES;
			mesh.selLevel = MESH_FACE;
			}



		int segments = 1;

		pblock->GetValue(pb_segments,t,segments,iv);
		if (segments < 1) segments = 1;


		BOOL fixupCorners;
		pblock->GetValue(pb_fixupcorners,t,fixupCorners,iv);


		BOOL autoSmooth;
		float smoothAngle;
		pblock->GetValue(pb_autosmooth,t,autoSmooth,iv);
		pblock->GetValue(pb_autosmoothangle,t,smoothAngle,iv);

		BOOL bevel;
		INode *node;
		pblock->GetValue(pb_bevel,t,bevel,iv);
		pblock->GetValue(pb_bevelshape,t,node,iv);

		PolyShape shape;
		if ((bevel) && node)
		{
			ObjectState nos = node->EvalWorldState(t);
			if (nos.obj->IsShapeObject()) 
			{
				ShapeObject *pathOb = (ShapeObject*)nos.obj;

				if (!pathOb->NumberOfCurves()) 
				{
					bevel = FALSE;
				}
				else
				{
					pathOb->MakePolyShape(t, shape,PSHAPE_BUILTIN_STEPS,TRUE);
					if (shape.lines[0].IsClosed())
						bevel = FALSE;

;
				}
			
			}
		}


		DWORD selLevel = mesh.selLevel;
		
		mesh.faceSel.ClearAll();

		if (bevel)
			meshInfo.MakeSolid(&mesh,segments, a,oa,matid, sg,edgeMap,tvOffset,imatid,omatid,selEdges,selInner,selOuter,fixupCorners,autoSmooth,smoothAngle,&shape.lines[0]);
		else meshInfo.MakeSolid(&mesh,segments, a,oa,matid, sg,edgeMap,tvOffset,imatid,omatid,selEdges,selInner,selOuter,fixupCorners,autoSmooth,smoothAngle,NULL);

		
		mesh.selLevel = selLevel;

		mesh.InvalidateTopologyCache ();


		for (int i = 0; i < mesh.numFaces; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				int index = mesh.faces[j].v[j];
				if ((index < 0) || (index >= mesh.numVerts))
					DebugPrint(_T("Invalid face %d(%d) %d\n"),i,j,index);
			}
		}
		
		int numMaps = mesh.getNumMaps();


		for (int mp = -NUM_HIDDENMAPS; mp < numMaps; mp++)
		{

			if (!mesh.mapSupport(mp)) continue;
			Point3 *uvw = mesh.mapVerts(mp);
			TVFace *uvwFace = mesh.mapFaces(mp);

			if ((uvw) && (uvwFace))
			{
				int numberTVVerts = mesh.getNumMapVerts(mp);
				for (int i = 0; i < mesh.numFaces; i++)
				{
					for (int j = 0; j < 3; j++)
					{
						int index = uvwFace[i].t[j];
						if ((index < 0) || (index >= numberTVVerts))
							DebugPrint(_T("Invalid Map %d tvface %d(%d) %d\n"),mp,i,j,index);
					}
				}

			}
		}



		os->obj->UpdateValidity(GEOM_CHAN_NUM,iv);
		os->obj->UpdateValidity(TOPO_CHAN_NUM,iv);
		
		MeshNormalSpec *pNormSpec = (MeshNormalSpec *) mesh.GetInterface (MESH_NORMAL_SPEC_INTERFACE);
		if (pNormSpec && pNormSpec->GetNumFaces() > 0)
		{
			pNormSpec->SetParent(&mesh);
			pNormSpec->BuildNormals();
			pNormSpec->ComputeNormals();
		}

		if ((updateUI) && (ip))
		{
			ip->PipeSelLevelChanged();
		}
		}
	EnableUIControls();

}
bool NifImporter::ImportMultipleGeometry(NiNodeRef parent, vector<NiTriBasedGeomRef>& glist)
{
   bool ok = true;
   if (glist.empty()) return false;

   ImpNode *node = i->CreateNode();
   if(!node) return false;

   INode *inode = node->GetINode();
   TriObject *triObject = CreateNewTriObject();
   node->Reference(triObject);

   string name = parent->GetName();
   node->SetName(wide(name).c_str());

   // Texture
   Mesh& mesh = triObject->GetMesh();

   vector< pair<int, int> > vert_range, tri_range;
   vector<Triangle> tris;
   vector<Vector3> verts;
   int submats = glist.size();

   // Build list of vertices and triangles.  Optional components like normals will be handled later.
   for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr) {
      NiTriBasedGeomDataRef triGeomData = StaticCast<NiTriBasedGeomData>((*itr)->GetData());

      // Get verts and collapse local transform into them
      int nVertices = triGeomData->GetVertexCount();
      vector<Vector3> subverts = triGeomData->GetVertices();
      Matrix44 transform = (*itr)->GetLocalTransform();
      //Apply the transformations
      if (transform != Matrix44::IDENTITY) {
         for ( unsigned int i = 0; i < subverts.size(); ++i )
            subverts[i] = transform * subverts[i];
      }
      vert_range.push_back( pair<int,int>( verts.size(), verts.size() + subverts.size()) );
      verts.insert(verts.end(), subverts.begin(), subverts.end());

      vector<Triangle> subtris = triGeomData->GetTriangles();
      for (vector<Triangle>::iterator itr = subtris.begin(), end = subtris.end(); itr != end; ++itr) {
         (*itr).v1 += nVertices, (*itr).v2 += nVertices, (*itr).v3 += nVertices;
      }
      tri_range.push_back( pair<int,int>( tris.size(), tris.size() + subtris.size()) );
      tris.insert(tris.end(), subtris.begin(), subtris.end());
   }

   // Transform up-to-parent
   Matrix44 baseTM = (importBones) ? Matrix44::IDENTITY : parent->GetWorldTransform();
   node->SetTransform(0,TOMATRIX3(baseTM));  

   // Set vertices and triangles
   mesh.setNumVerts(verts.size());
   mesh.setNumTVerts(verts.size(), TRUE);
   for (int i=0, n=verts.size(); i < n; ++i){
      Vector3 &v = verts[i];
      mesh.verts[i].Set(v.x, v.y, v.z);
   }
   mesh.setNumFaces(tris.size());
   mesh.setNumTVFaces(tris.size());
   for (int submat=0; submat<submats; ++submat) {
      int t_start = tri_range[submat].first, t_end = tri_range[submat].second;
      for (int i=t_start; i<t_end; ++i) {
         Triangle& t = tris[i];
         Face& f = mesh.faces[i];
         f.setVerts(t.v1, t.v2, t.v3);
         f.Show();
         f.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);
         f.setMatID(-1);
         TVFace& tf = mesh.tvFace[i];
         tf.setTVerts(t.v1, t.v2, t.v3);
      }
   }
   mesh.buildNormals();
   bool bSpecNorms = false;

   MultiMtl *mtl = NULL;
   int igeom = 0;
   for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr, ++igeom) 
   {
      NiTriBasedGeomDataRef triGeomData = StaticCast<NiTriBasedGeomData>((*itr)->GetData());

      int v_start = vert_range[igeom].first, v_end = vert_range[igeom].second;
      int t_start = tri_range[igeom].first, t_end = tri_range[igeom].second;

      // Normals
      vector<Vector3> subnorms = triGeomData->GetNormals();
      Matrix44 rotation = (*itr)->GetLocalTransform().GetRotation();
      if (rotation != Matrix44::IDENTITY) {
         for ( unsigned int i = 0; i < subnorms.size(); ++i )
            subnorms[i] = rotation * subnorms[i];
      }
      if (!subnorms.empty())
      {
#if VERSION_3DSMAX > ((5000<<16)+(15<<8)+0) // Version 5
         // Initialize normals if necessary
         if (!bSpecNorms) {
            bSpecNorms = true;
            mesh.SpecifyNormals();
            MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals();
            if (NULL != specNorms) {
               specNorms->BuildNormals();
               //specNorms->ClearAndFree();
               //specNorms->SetNumFaces(tris.size());
               //specNorms->SetNumNormals(n.size());
            }
         }
         MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals();
         if (NULL != specNorms)
         {
            Point3* norms = specNorms->GetNormalArray();
            for (int i=0, n=subnorms.size(); i<n; i++){
               Vector3& v = subnorms[i];
               norms[i+v_start] = Point3(v.x, v.y, v.z);
            }
            //MeshNormalFace* pFaces = specNorms->GetFaceArray();
            //for (int i=0; i<tris.size(); i++){
            //   Triangle& tri = tris[i];
            //   MeshNormalFace& face = pFaces[i+t_start];
            //   face.SpecifyNormalID(0, tri.v1);
            //   face.SpecifyNormalID(1, tri.v2);
            //   face.SpecifyNormalID(2, tri.v3);
            //}
#if VERSION_3DSMAX > ((7000<<16)+(15<<8)+0) // Version 7+
			specNorms->SetAllExplicit(true);
#endif
            specNorms->CheckNormals();
         }
#endif
      }
      // uv texture info
      if (triGeomData->GetUVSetCount() > 0) {
         vector<TexCoord> texCoords = triGeomData->GetUVSet(0);
         for (int i=0, n = texCoords.size(); i<n; ++i) {
            TexCoord& texCoord = texCoords[i];
            mesh.tVerts[i+v_start].Set(texCoord.u, (flipUVTextures) ? 1.0f-texCoord.v : texCoord.v, 0);
         }
      }
      vector<Color4> cv = triGeomData->GetColors();
      ImportVertexColor(inode, triObject, tris, cv, v_start);

      if ( StdMat2* submtl = ImportMaterialAndTextures(node, (*itr)) )
      {
         if (mtl == NULL) {
            mtl = NewDefaultMultiMtl();
            gi->GetMaterialLibrary().Add(mtl);
            inode->SetMtl(mtl);
         }
         // SubMatIDs do not have to be contiguous so we just use the offset
         mtl->SetSubMtlAndName(igeom, submtl, submtl->GetName());
         for (int i=t_start; i<t_end; ++i)
            mesh.faces[i].setMatID(igeom);
      }
      if (enableSkinSupport)
         ImportSkin(node, (*itr));
   }

   this->i->AddNodeToScene(node);   

   inode = node->GetINode();
   inode->EvalWorldState(0);

   for (vector<NiTriBasedGeomRef>::iterator itr = glist.begin(), end = glist.end(); itr != end; ++itr) 
   {
      // attach child
      if (INode *parent = GetNode((*itr)->GetParent()))
         parent->AttachChild(inode, 1);
      inode->Hide((*itr)->GetVisibility() ? FALSE : TRUE);
   }
   if (removeDegenerateFaces)
      mesh.RemoveDegenerateFaces();
   if (removeIllegalFaces)
      mesh.RemoveIllegalFaces();
   if (weldVertices)
	   WeldVertices(mesh);
   if (enableAutoSmooth)
      mesh.AutoSmooth(TORAD(autoSmoothAngle), FALSE, FALSE);
   return ok;
}
void MaxAWDExporter::ExportTriGeom(AWDTriGeom *awdGeom, Object *obj, INode *node, ISkin *skin, IGameMesh * igame_mesh)
{
    if (awdGeom != NULL) {
        if (awdGeom->get_is_created())
            return;
        else {
            awdGeom->set_is_created(true);

            // Extract skinning information (returns number of joints per vertex),
            // and writes the weights and joints array according to the jpv
            int jpv=0;
            awd_float64 *weights=NULL;
            awd_uint32 *joints=NULL;
            jpv = ExportSkin(node, skin, &weights, &joints);
            // Calculate offset matrix from the object TM (which includes geometry offset)
            // this will be used to transform all vertices into node space.
            int time=maxInterface->GetTime();

            // get the neutral pose time from the vertex (using a dedcated cache for this)
            bool hasVertexAnim=vetexAnimNeutralPosecache->hasKey(node);
            if(hasVertexAnim)
                time=vetexAnimNeutralPosecache->Get(node)*GetTicksPerFrame();
            else{
                // get the neutral pose time from the skeleton, if any is used
                if (opts->ExportSkin() && skin && jpv>0) {
                    SkeletonCacheItem *skel = skeletonCache->GetFromBone(skin->GetBone(0));
                    time=skel->awdSkel->get_neutralPose();
                }
            }

            Matrix3 offsMtx = node->GetObjectTM(time) * Inverse(node->GetNodeTM(time));
            bool isInSkinPoseMode=false;
            double *mtxData = (double *)malloc(12*sizeof(double));
            SerializeMatrix3(offsMtx, mtxData);
            /*if (skin && jpv>0) {
                // if the mesh is not in "Skin Pose Mode",
                // we add the bind-matrix to the offset matrix (so that all points are moved accoridingly)
                // and later we set the transform matrix of the mesh to the identity-matrix (no transform)
                ISkinPose * skinPose;
                skinPose=skinPose->GetISkinPose(*node);
                //isInSkinPoseMode=skinPose->SkinPoseMode();
                Matrix3 mtx;
                mtx.IdentityMatrix();
                if (!isInSkinPoseMode){
                    Matrix3 bm;
                    bm.IdentityMatrix();
                    //skin->GetSkinInitTM(node, bm, true);
                    //offsMtx *= bm;
                    //SerializeMatrix3(mtx, mtxData);
                }
                skinPose=NULL;
            }*/

            ObjectState os;
            // Flatten entire modifier stack
            os = node->EvalWorldState(time);
            obj = os.obj;
            // its allready been taken care that the correct obj is submitted to this function, so we can directly convert to TriObject (?)
            TriObject *triObject = (TriObject*)obj->ConvertToType(time, Class_ID(TRIOBJ_CLASS_ID, 0));

            Mesh mesh = triObject->mesh;
            int numTris = mesh.getNumFaces();
            int numVerts = mesh.getNumVerts();
            awdGeom->set_originalPointCnt(numVerts);

            // This could happen for example with splines (No!, this should never happen, because we check for this earlier (?))
            if (numTris==0)
                return;
            /*
            //TODO: optional reorder exported faces, so that quads are stored first
            //we than can store one uint32 as offset into the triangle-list, to reconstruct quads on import

            int * quadDic=(int *)malloc(sizeof(int) * numTris);
            int * isExportedDic=(int *)malloc(sizeof(int) * numTris);
            int cnt1=0;
            for(cnt1=0;cnt1<numTris;cnt1++){
                quadDic[cnt1]=0;
                isExportedDic[cnt1]=0;
            }
            PolyObject *polyObject = (PolyObject*)obj->ConvertToType(time, Class_ID(POLYOBJ_CLASS_ID, 0));
            if (polyObject!=NULL){
                MNMesh mnMesh = polyObject->GetMesh();
                int numFaces = mnMesh.FNum();
                int numtri = mnMesh.TriNum();
                int idxCnt=0;
                for(idxCnt=0;idxCnt<numFaces;idxCnt++){
                    MNFace thisface=mnMesh.f[idxCnt];
                    if(thisface.TriNum()==2){
                        Tab<int> intList;
                        thisface.GetTriangles(intList);
                        //quadDic[intList[0]]=intList[1];
                        //quadDic[intList[1]]=intList[0];
                    }
                }
                //if (polyObject!=obj)
                //    polyObject->DeleteMe();
            }
            */
            bool force_split=opts->SplitByMatID();// if true, the GeomUtils will create a SubGeo for each MaterialID used by a face, no matter if they share materials or not
            bool useUV=opts->ExportUVs();// TODO: check if uvs exists
            bool useSecUVs=useUV; // TODO: check if second UVs exists (and are requested)
            bool useNormals=opts->ExportNormals();

            // ATTENTION:
            // we have collected all meshintsances that are using this geometry.
            // but some material-settings ( UV / SecondUV / explode) can force us to create multiple geometries...

            // the IGAmeMesh gives acces to some handy functions for exporting meshes
            // we still need to use the mesh from the standart api, to have access to the correct UV (?)

            MeshNormalSpec *specificNormals = NULL;
            if(igame_mesh!=NULL){
                int numTrisGameMesh = igame_mesh->GetNumberOfFaces();
                if (numTrisGameMesh!=numTris){
                    return; //ERROR: faceCount of game-mesh is not facecount of api-mesh - should not happen
                }
                AWDBlockList * meshInstanceList = awdGeom->get_mesh_instance_list();
                if (meshInstanceList==NULL){
                    return; //ERROR: faceCount of game-mesh is not facecount of api-mesh - should not happen
                }
                int numMeshInstances=meshInstanceList->get_num_blocks();
                if ((meshInstanceList!=NULL)&&(numMeshInstances==0)){
                    return; //ERROR: faceCount of game-mesh is not facecount of api-mesh - should not happen
                }
                // check if the first UVChannel is available for this mesh ( numTVFaces must be equal to numTris)
                if (useUV) {
                    try{
                        if (mesh.mapSupport(1)){
                            MeshMap * mesh_map;
                            mesh_map = &(mesh.Map(1));
                            int numTrisMap = mesh_map->getNumFaces();
                            if (numTrisMap!=numTris){
                                useUV=false;
                                useSecUVs=false;
                            }
                        }
                        else{
                            useUV=false;
                            useSecUVs=false;
                        }
                    }
                    catch(...){
                        useUV=false;
                        useSecUVs=false;
                    }
                }
                // check if any normals are available for the mesh
                if (useNormals) {
                    mesh.SpecifyNormals();
                    specificNormals = mesh.GetSpecifiedNormals();
                    int  specificNormalCount = specificNormals->GetNumNormals();// for me, this is allways been 0
                    if (specificNormalCount==0){
                        specificNormals=NULL;
                        int numNorms = igame_mesh->GetNumberOfNormals();
                        if (numNorms==0){
                            useNormals=false;
                        }
                    }
                }

                AWD_field_type precision_geo=AWD_FIELD_FLOAT32;
                if (opts->StorageGeometry()==1)
                    precision_geo=AWD_FIELD_FLOAT64;
                AWDGeomUtil * geomUtil=new AWDGeomUtil(awdGeom->get_split_faces(), force_split, useUV, useSecUVs, useNormals, 0.0, jpv, precision_geo);
                // create a list of GUGeom for each Mesh instance.
                // before collecting the actual geom-data,
                // we will reduce the number of GUGeoms to the minimum needed to display all mesh-instances correctly
                geomUtil->createPreGeometries(meshInstanceList);
                Tab<int> MatIDList=igame_mesh->GetActiveMatIDs();
                int matIDCnt;
                Tab<FaceEx *> facelist;
                // for each submesh do:
                for (matIDCnt=0; matIDCnt< MatIDList.Count(); matIDCnt++){
                    facelist=igame_mesh->GetFacesFromMatID(MatIDList[matIDCnt]);
                    int idx;
                    int faceCnt=facelist.Count();
                    if (faceCnt>0){
                        // if the submesh will be used (if the matID is used by any face):
                        AWDBlockList * subMaterialList = new AWDBlockList();
                        int meshInstCnt=0;
                        // for each mesh instance, apply the material
                        for (meshInstCnt=0;meshInstCnt<numMeshInstances; meshInstCnt++){
                            AWDMeshInst * awdMesh=(AWDMeshInst *)meshInstanceList->getByIndex(meshInstCnt);
                            if (awdMesh==NULL){
                                return;
                            }
                            else{
                                AWDBlockList * preMaterials = awdMesh->get_pre_materials();
                                if (preMaterials==NULL){
                                    return;
                                }
                                else{
                                    bool createDefault = false;
                                    AWDMaterial * thisMatBlock = (AWDMaterial *)preMaterials->getByIndex(MatIDList[matIDCnt]);
                                    if (thisMatBlock==NULL){
                                        thisMatBlock=(AWDMaterial *)awdMesh->get_defaultMat();
                                    }
                                    if (useUV){
                                        if (thisMatBlock->get_mappingChannel()>0)
                                            thisMatBlock->set_mappingChannel(checkIfUVMapExists(mesh, numTris, thisMatBlock->get_mappingChannel()));
                                    }
                                    if (useSecUVs){
                                        if (thisMatBlock->get_secondMappingChannel()>0)
                                            thisMatBlock->set_secondMappingChannel(checkIfUVMapExists(mesh, numTris, thisMatBlock->get_secondMappingChannel()));
                                    }
                                    subMaterialList->force_append(thisMatBlock);
                                }
                            }
                        }
                        geomUtil->add_new_sub_geo_to_preGUgeoms(subMaterialList, MatIDList[matIDCnt]);
                        delete subMaterialList;
                    }
                }
                geomUtil->createGeometries();

                for (matIDCnt=0; matIDCnt<MatIDList.Count(); matIDCnt++){
                    facelist=igame_mesh->GetFacesFromMatID(MatIDList[matIDCnt]);
                    int idx;
                    int faceCnt=facelist.Count();
                    if (faceCnt>0){
                        int numGeoms=geomUtil->get_geoList()->get_num_blocks();
                        int geoCnt;
                        for(geoCnt=0; geoCnt<numGeoms; geoCnt++){
                            GUGeo * thisGUGeo = geomUtil->get_geoList()->get_by_idx(geoCnt);
                            // get the uvs using the index stored in the geomUtil
                            // get the secondUV using the index stored in the geomUtil
                            // get the explode (normals) using the index stored in the geomUtil

                            // ATTENTION: its not a Subgeo but a SubgeoGroup, so it contain several Subgeos if the face or vert lists are to big for one subgeo
                            int thisSubGeoIdx=geomUtil->getSubGeoIdxForMatIdx(MatIDList[matIDCnt]);
                            GUSubGeoGroup *thisSubGeoGroup = thisGUGeo->get_subGeomList()->get_by_idx(thisSubGeoIdx);
                            if (thisSubGeoGroup==NULL){
                                return;
                            }
                            AWDMaterial * thisAWDMat=(AWDMaterial *)thisSubGeoGroup->materials->getByIndex(0);
                            bool explode=thisAWDMat->get_is_faceted();

                            MeshMap * mainUVMeshMap=NULL;
                            MeshMap * secondUVMeshMap=NULL;

                            if (thisSubGeoGroup->include_uv){
                                if (thisAWDMat->get_mappingChannel()>0)
                                    mainUVMeshMap = &(mesh.Map(thisAWDMat->get_mappingChannel()));
                            }
                            if (thisSubGeoGroup->include_suv){
                                if (thisAWDMat->get_secondMappingChannel()>0)
                                    secondUVMeshMap = &(mesh.Map(thisAWDMat->get_secondMappingChannel()));
                            }
                            // for each face in the list do:
                            bool hasMultipleUV=false;
                            for (idx=0;idx<faceCnt;idx++){
                                /*if(isExportedDic[idx]==0){
                                    isExportedDic[idx]=1;
                                    if(quadDic[idx]>0){
                                        int yes=0;
                                    }*/
                                    // this will create a new SubGeo inside the SubgeoGroup, if the limits are reached
                                thisSubGeoGroup->check_limits();
                                // create a new vert
                                FaceEx * f=facelist[idx];
                                int apiFaceIdx=f->meshFaceIndex;
                                TVFace tvface;
                                TVFace tvFaceSecond;
                                Face face = mesh.faces[apiFaceIdx];
                                DWORD *inds = face.getAllVerts();
                                if (thisSubGeoGroup->include_uv){
                                    if (mainUVMeshMap!=NULL)
                                        tvface = mainUVMeshMap->tf[apiFaceIdx];
                                    else
                                        tvface = mesh.tvFace[apiFaceIdx];
                                }
                                if (thisSubGeoGroup->include_suv){
                                    if (secondUVMeshMap!=NULL)
                                        tvFaceSecond = secondUVMeshMap->tf[apiFaceIdx];
                                    else
                                        tvFaceSecond = mesh.tvFace[apiFaceIdx];
                                }
                                Point3 faceNormal;
                                if (geomUtil->include_normals) {
                                    // if we want to export normals, but no normals was read, than we need to calculate ourself,
                                    // using the face-normal for the angle-calulation
                                    if ((igame_mesh==NULL && specificNormals==NULL)||(explode)){
                                        // faceNormal = mesh.getFaceNormal(t); // this crashes 3dsmax (why?), so calulate the facenormal manually:
                                            Point3 v0, v1, v2;
                                            Tab<Point3> fnorms;
                                            v0 = mesh.getVert(face.getVert(0));
                                            v1 = mesh.getVert(face.getVert(1));
                                            v2 = mesh.getVert(face.getVert(2));
                                            faceNormal = (v1-v0)^(v2-v1);
                                            faceNormal = Normalize(faceNormal);
                                    }
                                }
                                int v;
                                for (v=2; v>=0; v--) {
                                    int vIdx = face.getVert(v);
                                    Point3 vtx = offsMtx * mesh.getVert(vIdx);

                                    vdata *vd = (vdata *)malloc(sizeof(vdata));
                                    vd->orig_idx = vIdx;
                                    vd->x = vtx.x;
                                    vd->y = vtx.z;
                                    vd->z = vtx.y;
                                    // Might not have UV coords
                                    if (geomUtil->include_uv) {
                                        int tvIdx;
                                        Point3 tvtx;
                                        Point3 stvtx;
                                        if (mainUVMeshMap!=NULL)
                                            tvtx=mainUVMeshMap->tv[tvface.t[v]];
                                        else{
                                            tvIdx = tvface.getTVert(v);
                                            tvtx = mesh.getTVert(tvIdx);
                                        }

                                        if (secondUVMeshMap!=NULL)
                                            stvtx=secondUVMeshMap->tv[tvFaceSecond.t[v]];
                                        else{
                                            tvIdx = tvface.getTVert(v);
                                            stvtx = mesh.getTVert(tvIdx);
                                        }
                                        vd->u = tvtx.x;
                                        vd->v = 1.0-tvtx.y;
                                        vd->su = stvtx.x;
                                        vd->sv = 1.0-stvtx.y;
                                    }

                                    if (geomUtil->include_normals) {
                                        Point3 normal;
                                        // if specific vertex-normals was found, we use it, if the subgeo is not set to explode
                                        if ((specificNormals!=NULL) && (!explode)){
                                            normal = specificNormals->GetNormal(apiFaceIdx, v);
                                        }
                                        // else if a (not specific) vertex-normals was found (on the igame-mesh), we use it, if the subgeo is not set to explode
                                        else if ((igame_mesh!=NULL) && (!explode)){
                                            igame_mesh->GetNormal(f->norm[v], normal, true);
                                        }
                                        // else: since we still want normals exported, we use the face-normal (using facenormal with threshold of 0 will explode the mesh
                                        else{
                                            // i dont think this should really get executed anymore (since the igame-object allways should give access to the normals)
                                            normal=faceNormal;
                                        }
                                        // if the object is skinned, we get the global normals
                                        if (jpv>0){
                                            if (normal)
                                                normal=offsMtx*normal;
                                        }

                                        vd->nx = normal.x;
                                        vd->ny = normal.z;
                                        vd->nz = normal.y;
                                    }

                                    // If there is skinning information, copy it from the weight
                                    // and joint index arrays returned by ExportSkin() above.
                                    vd->num_bindings = jpv;
                                    if (jpv > 0) {
                                        vd->weights = (awd_float64*)malloc(jpv*sizeof(awd_float64));
                                        vd->joints = (awd_uint32*)malloc(jpv*sizeof(awd_uint32));

                                        int memoffs = jpv*vIdx;
                                        memcpy(vd->weights, weights+memoffs, jpv*sizeof(awd_float64));
                                        memcpy(vd->joints, joints+memoffs, jpv*sizeof(awd_uint32));
                                    }

                                    vd->force_hard = false;

                                    // add the new vertex to the subgeo
                                    thisSubGeoGroup->append_vdata(vd);
                                }
                            }
                        }
                    }
                }
                AWDBlockList * returned_geoms =  geomUtil->build_geom(awdGeom);

                AWDBlockIterator * it=NULL;
                AWDMeshInst * block;
                int maxCount=0;
                int geomCnt=0;
                for (geomCnt=0; geomCnt<returned_geoms->get_num_blocks();geomCnt++){
                    AWDTriGeom * thisAWDGeom=(AWDTriGeom *)returned_geoms->getByIndex(geomCnt);
                    it = new AWDBlockIterator(thisAWDGeom->get_mesh_instance_list());
                    AWDSubGeom *sub;
                    sub = thisAWDGeom->get_first_sub();
                    while (sub) {
                        AWDBlockList *subGeoGroupMatList=sub->get_materials();
                        int thisIdx=0;
                        it->reset();
                        while ((block = (AWDMeshInst*)it->next()) != NULL) {
                            block->set_geom(thisAWDGeom);
                            //if (!isInSkinPoseMode){
                            //    block->set_transform(mtxData);
                            //}
                            AWDMaterial * thisMat=(AWDMaterial *)subGeoGroupMatList->getByIndex(thisIdx);
                            if (thisMat==NULL){
                                int test=0;
                                //ERROR - this should never happen
                            }
                            else{
                                AWDLightPicker * lightPicker=(AWDLightPicker *)block->get_lightPicker();
                                AWDBlock * thisAnimator=(AWDBlock *)block->get_animator();
                                if ((lightPicker!=NULL)||(thisAnimator!=NULL)){
                                    thisMat=thisMat->get_unique_material(lightPicker, thisAnimator, NULL);
                                    if(lightPicker!=NULL){
                                        if(opts->SetMultiPass()){
                                            // multipass using the number of lights, that the lightpicker uses
                                            if (lightPicker->get_lights()->get_num_blocks()>4){
                                                thisMat->set_multiPass(true);
                                            }
                                            else{
                                                thisMat->set_multiPass(false);
                                            }
                                        }
                                        if ((lightPicker->get_lights()->get_num_blocks()>4)&&(!thisMat->get_multiPass())){
                                            AWDMessageBlock * newWarning = new AWDMessageBlock(thisMat->get_name(), "AWDMaterial has more than 4 lights assigned, but is set to singlepass. this will cause problems on render.");
                                            awd->get_message_blocks()->append(newWarning);
                                        }
                                        if(opts->IncludeShadows()){
                                            if(thisMat->get_shadowMethod()!=NULL){
                                                bool shadowOK=lightPicker->check_shadowMethod((AWDShadowMethod *)(thisMat->get_shadowMethod()));
                                                if(!shadowOK){
                                                    AWDMessageBlock * newWarning = new AWDMessageBlock(thisMat->get_name(), "Could not find the ShadowMethod thats applied to the material on one of the lights that it assigned to the material.");
                                                    awd->get_message_blocks()->append(newWarning);
                                                    thisMat->set_shadowMethod(NULL);
                                                }
                                                }
                                            if(thisMat->get_shadowMethod()==NULL){
                                                thisMat->set_shadowMethod(lightPicker->get_shadowMethod());
                                            }
                                        }
                                    }
                                }
                                block->add_material((AWDMaterial*)thisMat);
                            }
                            thisIdx++;
                        }
                        sub = sub->next;
                    }
                    delete it;
                }
                delete returned_geoms;
                // If conversion created a new object, dispose it
                if (triObject != obj)
                    triObject->DeleteMe();
                delete geomUtil;
            }
            else{
            }
            //free(quadDic);
            //free(isExportedDic);
            free(mtxData);
            if (weights!=NULL)
                free(weights);
            if (joints!=NULL)
                free(joints);
        }
    }
    return;
}
int ExportQuake3Model(const TCHAR *filename, ExpInterface *ei, Interface *gi, int start_time, std::list<ExportNode> lTags, std::list<ExportNode> lMeshes)
{
	FILE *file;
	int i, j, totalTags, totalMeshes, current_time = 0;
	long pos_current, totalTris = 0, totalVerts = 0;
	std::list<FrameRange>::iterator range_i;
	std::vector<Point3> lFrameBBoxMin;
	std::vector<Point3> lFrameBBoxMax;
	long pos_tagstart;
	long pos_tagend;
	long pos_filesize;
	long pos_framestart;
	int lazynamesfixed = 0;
	const Point3 x_axis(1, 0, 0);
	const Point3 z_axis(0, 0, 1);

	SceneEnumProc checkScene(ei->theScene, start_time, gi);
	totalTags = (int)lTags.size();
	if (g_tag_for_pivot)
		totalTags++;
	totalMeshes = (int)lMeshes.size();

	// open file
	file = _tfopen(filename, _T("wb"));
	if (!file)
	{
		ExportError("Cannot open file '%s'.", filename);
		return FALSE;
	}
	ExportDebug("%s:", filename);

	// sync pattern and version
	putChars("IDP3", 4, file);
	put32(15, file);
	putChars("Darkplaces MD3 Exporter", 64, file);
	put32(0, file);   // flags
	
	// MD3 header
	ExportState("Writing MD3 header");
	put32(g_total_frames, file);      // how many frames
	put32(totalTags, file);	  // tagsnum
	put32(totalMeshes, file); // meshnum
	put32(1, file);   // maxskinnum
	put32(108, file); // headersize
	pos_tagstart = ftell(file); put32(0, file);   // tagstart
	pos_tagend	= ftell(file);  put32(256, file); // tagend
	pos_filesize = ftell(file); put32(512, file); // filesize
	ExportDebug("    %i frames, %i tags, %i meshes", g_total_frames, totalTags, totalMeshes);

	// frame info
	// bbox arrays get filled while exported mesh and written back then
	ExportState("Writing frame info");
	pos_framestart = ftell(file);
	lFrameBBoxMin.resize(g_total_frames);
	lFrameBBoxMax.resize(g_total_frames);
	for (i = 0; i < g_total_frames; i++)
	{
		// init frame data
		lFrameBBoxMin[i].Set(0, 0, 0);
		lFrameBBoxMax[i].Set(0, 0, 0);
		// put data
		putFloat(-1.0f, file); // bbox min vector
		putFloat(-1.0f, file);
		putFloat(-1.0f, file);	
		putFloat( 1.0f, file); // bbox max vector
		putFloat(1.0f, file);
		putFloat(1.0f, file);
		putFloat(0.0f, file);  // local origin (usually 0 0 0)
		putFloat(0.0f, file);
		putFloat(0.0f, file);
		putFloat(1.0f, file);  // radius of bounding sphere
		putChars("", 16, file);
	}

	// tags
	pos_current = ftell(file);
	fseek(file, pos_tagstart, SEEK_SET);
	put32(pos_current, file);
	fseek(file, pos_current, SEEK_SET);
	
	// for each frame range cycle all frames and write out each tag
	long pos_tags = pos_current;
	if (totalTags)
	{
		long current_frame = 0;
		ExportState("Writing %i tags", totalTags);
		for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
		{
			for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
			{
				SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi);
				current_time = current_scene.time;

				// write out tags
				if (lTags.size())
				{
					for (std::list<ExportNode>::iterator tag_i = lTags.begin(); tag_i != lTags.end(); tag_i++)
					{
						INode *node	= current_scene[tag_i->i]->node;
						Matrix3	tm = node->GetObjTMAfterWSM(current_time);

						ExportState("Writing '%s' frame %i of %i", tag_i->name, i, g_total_frames);

						// tagname
						putChars(tag_i->name, 64, file);
						// origin, rotation matrix
						Point3 row = tm.GetRow(3);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
						row = tm.GetRow(0);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
						row = tm.GetRow(1);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
						row = tm.GetRow(2);
						putFloat(row.x, file);
						putFloat(row.y, file);
						putFloat(row.z, file);
					}
				}

				// write the center of mass tag_pivot which is avg of all objects's pivots
				if (g_tag_for_pivot)
				{
					ExportState("Writing 'tag_pivot' frame %i of %i", i, g_total_frames);

					// write the null data as tag_pivot need to be written after actual geometry
					// (it needs information on frame bound boxes to get proper blendings)
					putChars("tag_pivot", 64, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(1, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(1, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(0, file);
					putFloat(1, file);
				}
			}
		}
	}

	// write the tag object offsets
	pos_current = ftell(file);
	fseek(file, pos_tagend, SEEK_SET);
	put32(pos_current, file);
	fseek(file, pos_current, SEEK_SET);

	// allocate the structs used to calculate tag_pivot
	std::vector<Point3> tag_pivot_origin;
	std::vector<double> tag_pivot_volume;
	if (g_tag_for_pivot)
	{
		tag_pivot_origin.resize(g_total_frames);
		tag_pivot_volume.resize(g_total_frames);
	}

	// mesh objects
	// for each mesh object write uv and frames
	SceneEnumProc scratch(ei->theScene, start_time, gi);
	ExportState("Writing %i meshes", (int)lMeshes.size());
	for (std::list<ExportNode>::iterator mesh_i = lMeshes.begin(); mesh_i != lMeshes.end(); mesh_i++)
	{
		bool needsDel;

		ExportState("Start mesh #%i", mesh_i);
		INode *node = checkScene[mesh_i->i]->node;
		Matrix3 tm	= node->GetObjTMAfterWSM(start_time);
		TriObject *tri = GetTriObjectFromNode(node, start_time, needsDel);
		if (!tri)
			continue;

		// get mesh, compute normals
		Mesh &mesh = tri->GetMesh();
		MeshNormalSpec *meshNormalSpec = mesh.GetSpecifiedNormals();
		if (meshNormalSpec)
		{
			if (!meshNormalSpec->GetNumFaces())
				meshNormalSpec = NULL;
			else
			{
				meshNormalSpec->SetParent(&mesh);
				meshNormalSpec->CheckNormals();
			}
		}
		mesh.checkNormals(TRUE);

		// fix lazy object names
		ExportState("Attempt to fix mesh name '%s'", mesh_i->name);
		char  meshname[64];
		size_t meshnamelen = min(63, strlen(mesh_i->name));
		memset(meshname, 0, 64);
		strncpy(meshname, mesh_i->name, meshnamelen);
		meshname[meshnamelen] = 0;
		if (!strncmp("Box", meshname, 3)    || !strncmp("Sphere", meshname, 6)  || !strncmp("Cylinder", meshname, 8) ||
            !strncmp("Torus", meshname, 5)  || !strncmp("Cone", meshname, 4)    || !strncmp("GeoSphere", meshname, 9) ||
			!strncmp("Tube", meshname, 4)   || !strncmp("Pyramid", meshname, 7) || !strncmp("Plane", meshname, 5) ||
			!strncmp("Teapot", meshname, 6) || !strncmp("Object", meshname, 6))
		{
name_conflict:
			lazynamesfixed++;
			if (lazynamesfixed == 1)
				strcpy(meshname, "base");
			else
				sprintf(meshname, "base%i", lazynamesfixed);

			// check if it's not used by another mesh
			for (std::list<ExportNode>::iterator m_i = lMeshes.begin(); m_i != lMeshes.end(); m_i++)
				if (!strncmp(m_i->name, meshname, strlen(meshname)))
					goto name_conflict;
			// approve name
			ExportWarning("Lazy object name '%s' (mesh renamed to '%s').", node->GetName(), meshname);
		}

		// special mesh check
		bool shadow_or_collision = false;
		if (g_mesh_special)
			  if (!strncmp("collision", meshname, 9) || !strncmp("shadow", meshname, 6))
				shadow_or_collision = true;

		// get material
		const char *shadername = NULL;
		Texmap *tex = 0;
		Mtl *mtl = 0;
		if (!shadow_or_collision)
		{
			mtl = node->GetMtl();
			if (mtl)
			{
				// check for multi-material
				if (mtl->IsMultiMtl())
				{
					// check if it's truly multi material
					// we do support multi-material with only one texture (some importers set it)
					bool multi_material = false;
					MtlID matId = mesh.faces[0].getMatID();
					for (i = 1; i < mesh.getNumFaces(); i++)
						if (mesh.faces[i].getMatID() != matId)
							multi_material = true;

					if (multi_material)
						if (g_mesh_multimaterials == MULTIMATERIALS_NONE)
							ExportWarning("Object '%s' is multimaterial and using multiple materials on its faces, that case is not yet supported (truncating to first submaterial).", node->GetName());
					
					// switch to submaterial
					mtl = mtl->GetSubMtl(matId);
				}

				// get shader from material if supplied
				char *materialname = GetChar(mtl->GetName());
				if (g_mesh_materialasshader && (strstr(materialname, "/") != NULL || strstr(materialname, "\\") != NULL))
					shadername = GetChar(mtl->GetName());
				else
				{
					// get texture
					tex = mtl->GetSubTexmap(ID_DI);
					if (tex)
					{
						if (tex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0x00))
						{
							shadername = GetChar(((BitmapTex *)tex)->GetMapName());
							if (shadername == NULL || !shadername[0])
								ExportWarning("Object '%s' material '%s' has no bitmap.", tex->GetName(), node->GetName());
						}
						else
						{
							tex = NULL;
							ExportWarning("Object '%s' has material with wrong texture type (only Bitmap are supported).", node->GetName());
						}
					}
					else
						ExportWarning("Object '%s' has material but no texture.", node->GetName());
				}
			}
			else
				ExportWarning("Object '%s' has no material.", node->GetName());
		}

		long pos_meshstart = ftell(file);

		// surface object
		ExportState("Writing mesh '%s' header", meshname);
		putChars("IDP3", 4, file);
		putChars(meshname, 64, file);
		put32(0, file); // flags
		put32(g_total_frames, file);                          // framecount
		put32(1, file);                                       // skincount
		long pos_vertexnum = ftell(file); put32(0, file);     // vertexcount
		put32(mesh.getNumFaces(), file);                      // trianglecount
		long pos_trianglestart = ftell(file); put32(0, file); // start triangles
		put32(108, file);                                     // header size
		long pos_texvecstart = ftell(file); put32(0, file);   // texvecstart
		long pos_vertexstart = ftell(file); put32(16, file);  // vertexstart
		long pos_meshsize = ftell(file); put32(32, file);	  // meshsize

		// write out a single 'skin'
		ExportState("Writing mesh %s texture", meshname);
		if (shadow_or_collision)
			putChars(meshname, 64, file);
		else if (shadername) 
			putMaterial(shadername, mtl, tex, file);
		else
			putChars("noshader", 64, file);
		put32(0, file); // flags

		// build geometry
		ExportState("Building vertexes/triangles");
		std::vector<ExportVertex>vVertexes;
		std::vector<ExportTriangle>vTriangles;
		vVertexes.resize(mesh.getNumVerts());
		int vExtraVerts = mesh.getNumVerts();
		for (i = 0; i < mesh.getNumVerts(); i++)
		{
			vVertexes[i].vert = i;
			vVertexes[i].normalfilled = false;
			// todo: check for coincident verts
		}
		int vNumExtraVerts = 0;

		// check normals
		if (!mesh.normalsBuilt && !shadow_or_collision)
			ExportWarning("Object '%s' does not have normals contructed.", node->GetName());

		// get info for triangles
		const float normal_epsilon = 0.01f;
		vTriangles.resize(mesh.getNumFaces());
		for (i = 0; i < mesh.getNumFaces(); i++)
		{
			DWORD smGroup = mesh.faces[i].getSmGroup();
			ExportState("Mesh %s: checking normals for face %i of %i", meshname, i, mesh.getNumFaces());
			for (j = 0; j < 3; j++)
			{
				int vert = mesh.faces[i].getVert(j);
				vTriangles[i].e[j] = vert;
				// find a right normal for this vertex and save its 'address'
				int vni;
				Point3 vn;
				if (!mesh.normalsBuilt || shadow_or_collision)
				{
					vn.Set(0, 0, 0);
					vni = 0;
				}
				else
				{
					int numNormals;
					RVertex *rv = mesh.getRVertPtr(vert);
					if (meshNormalSpec)
					{  
						ExportState("face %i vert %i have normal specified", i, j);
						// mesh have explicit normals (i.e. Edit Normals modifier)
						vn = meshNormalSpec->GetNormal(i, j);
						vni = meshNormalSpec->GetNormalIndex(i, j);
					}
					else if (rv && rv->rFlags & SPECIFIED_NORMAL)
					{
						ExportState("face %i vert %i have SPECIFIED_NORMAL flag", i, j);
						// SPECIFIED_NORMAL flag
						vn = rv->rn.getNormal();
						vni = 0;
					}
					else if (rv && (numNormals = rv->rFlags & NORCT_MASK) && smGroup)
					{
						// If there is only one vertex is found in the rn member.
						if (numNormals == 1)
						{
							ExportState("face %i vert %i have solid smooth group", i, j);
							vn = rv->rn.getNormal();
							vni = 0;
							
						}
						else
						{
							ExportState("face %i vert %i have mixed smoothing groups", i, j);
							// 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 k = 0; k < numNormals; k++)
							{
								if (rv->ern[k].getSmGroup() & smGroup)
								{
									vn = rv->ern[k].getNormal();
									vni = 1 + k;
								}
							}
						}
					}
					else
					{
						ExportState("face %i vert %i flat shaded", i, j);
						// Get the normal from the Face if no smoothing groups are there
						vn = mesh.getFaceNormal(i);
						vni = 0 - (i + 1);
					}
				}

				// subdivide to get all normals right
				if (!vVertexes[vert].normalfilled)
				{
					vVertexes[vert].normal = vn;
					vVertexes[vert].normalindex = vni;
					vVertexes[vert].normalfilled = true;
				}
				else if ((vVertexes[vert].normal - vn).Length() >= normal_epsilon)
				{
					// current vertex not matching normal - it was already filled by different smoothing group
					// find a vert in extra verts in case it was already created
					bool vert_found = false;
					for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++)
					{
						if (vVertexes[ev].vert == vert && (vVertexes[ev].normal - vn).Length() < normal_epsilon)
						{
							vert_found = true;
							vTriangles[i].e[j] = ev;
							break;
						}
					}
					// we havent found a vertex, create new
					if (!vert_found)
					{
						ExportVertex NewVert;
						NewVert.vert = vVertexes[vert].vert;
						NewVert.normal = vn;
						NewVert.normalindex = vni;
						NewVert.normalfilled = true;
						vTriangles[i].e[j] = (int)vVertexes.size();
						vVertexes.push_back(NewVert);
						vNumExtraVerts++;
					}
				}
			}
		}
		int vNumExtraVertsForSmoothGroups = vNumExtraVerts;

		// generate UV map
		// VorteX: use direct maps reading since getNumTVerts()/getTVert is deprecated
		//  max sets two default mesh maps: 0 - vertex color, 1 : UVW, 2 & up are custom ones
		ExportState("Building UV map");
		std::vector<ExportUV>vUVMap;
		vUVMap.resize(vVertexes.size());
		int meshMap = 1;
		if (!mesh.mapSupport(meshMap) || !mesh.getNumMapVerts(meshMap) || shadow_or_collision)
		{
			for (i = 0; i < mesh.getNumVerts(); i++)
			{
				vUVMap[i].u = 0.5;
				vUVMap[i].v = 0.5;
			}
			if (!shadow_or_collision)
				ExportWarning("No UV mapping was found on object '%s'.", node->GetName());
		}
		else
		{
			UVVert *meshUV = mesh.mapVerts(meshMap);
			for (i = 0; i < (int)vTriangles.size(); i++)
			{
				ExportState("Mesh %s: converting tvert for face %i of %i", meshname, i, (int)vTriangles.size());
				// for 3 face vertexes
				for (j = 0; j < 3; j++)
				{
					int vert = vTriangles[i].e[j];
					int tv = mesh.tvFace[i].t[j];
					UVVert &UV = meshUV[tv];

					if (!vUVMap[vert].filled)
					{
						// fill uvMap vertex
						vUVMap[vert].u = UV.x;
						vUVMap[vert].v = UV.y;
						vUVMap[vert].filled = true;
						vUVMap[vert].tvert = tv;
					}
					else if (tv != vUVMap[vert].tvert)
					{
						// uvMap slot for this vertex has been filled
						// we should arrange triangle to other vertex, which not filled and having same shading and uv
						// check if any of the extra vertices can fit
						bool vert_found = false;
						for (int ev = vExtraVerts; ev < (int)vVertexes.size(); ev++)
						{
							if (vVertexes[ev].vert == vert && vUVMap[vert].u == UV.x &&vUVMap[vert].v == UV.y  && (vVertexes[ev].normal - vVertexes[vert].normal).Length() < normal_epsilon)
							{
								vert_found = true;
								vTriangles[i].e[j] = vVertexes[ev].vert;
								break;
							}
						}
						if (!vert_found)
						{
							// create new vert
							ExportVertex NewVert;
							NewVert.vert = vVertexes[vert].vert;
							NewVert.normal = vVertexes[vert].normal;
							NewVert.normalindex = vVertexes[vert].normalindex;
							NewVert.normalfilled = vVertexes[vert].normalfilled;
							vTriangles[i].e[j] = (int)vVertexes.size();
							vVertexes.push_back(NewVert);
							vNumExtraVerts++;
							// create new TVert
							ExportUV newUV;
							newUV.filled = true;
							newUV.u = UV.x;
							newUV.v = UV.y;
							newUV.tvert = tv;
							vUVMap.push_back(newUV);
						}
					}
				}
			}
		}
		int vNumExtraVertsForUV = (vNumExtraVerts - vNumExtraVertsForSmoothGroups);

		// print some debug stats
		ExportDebug("    mesh %s: %i vertexes +%i %s +%i UV, %i triangles", meshname, ((int)vVertexes.size() - vNumExtraVerts), vNumExtraVertsForSmoothGroups, meshNormalSpec ? "EditNormals" : "SmoothGroups", vNumExtraVertsForUV, (int)vTriangles.size());

		// fill in triangle start
		pos_current = ftell(file);
		fseek(file, pos_trianglestart, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);

		// detect if object have negative scale (mirrored)
		// in this canse we should rearrange triangles counterclockwise
		// so stuff will not be inverted
		ExportState("Mesh %s: writing %i triangles", meshname, (int)vTriangles.size());
		if (DotProd(CrossProd(tm.GetRow(0), tm.GetRow(1)), tm.GetRow(2)) < 0.0)
		{
			ExportWarning("Object '%s' is mirrored (having negative scale on it's transformation)", node->GetName());
			for (i = 0; i < (int)vTriangles.size(); i++)
			{
				put32(vTriangles[i].b, file);	// vertex index
				put32(vTriangles[i].c, file);	// for 3 vertices
				put32(vTriangles[i].a, file);	// of triangle
			}
		}
		else
		{
			for (i = 0; i < (int)vTriangles.size(); i++)
			{
				put32(vTriangles[i].a, file);	// vertex index
				put32(vTriangles[i].c, file);	// for 3 vertices
				put32(vTriangles[i].b, file);	// of triangle
			}
		}

		// fill in texvecstart
		// write out UV mapping coords.
		ExportState("Mesh %s: writing %i UV vertexes", meshname, (int)vUVMap.size());
		pos_current = ftell(file);
		fseek(file, pos_texvecstart, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);
		for (i = 0; i < (int)vUVMap.size(); i++)
		{
			putFloat(vUVMap[i].u, file); // texture coord u,v
			putFloat(1.0f - vUVMap[i].v, file);	// for vertex
		}
		vUVMap.clear();

		// fill in vertexstart
		pos_current = ftell(file);
		fseek(file, pos_vertexstart, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);

		// fill in vertexnum
		pos_current = ftell(file);
		fseek(file, pos_vertexnum, SEEK_SET);
		put32((int)vVertexes.size(), file);
		fseek(file, pos_current, SEEK_SET);

		// write out for each frame the position of each vertex
		long current_frame = 0;
		ExportState("Mesh %s: writing %i frames", meshname, g_total_frames);
		for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
		{
			for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
			{
				bool _needsDel;

				// get triobject for current frame
				SceneEnumProc current_scene(ei->theScene, i * g_ticks_per_frame, gi);
				current_time = current_scene.time;
				INode *_node = current_scene[mesh_i->i]->node;
				TriObject *_tri	= GetTriObjectFromNode(_node, current_time, _needsDel);
				if (!_tri)
					continue;

				// get mesh, compute normals
				Mesh &_mesh	= _tri->GetMesh();
				MeshNormalSpec *_meshNormalSpec = _mesh.GetSpecifiedNormals();
				if (_meshNormalSpec)
				{
					if (!_meshNormalSpec->GetNumFaces())
						_meshNormalSpec = NULL;
					else
					{
						_meshNormalSpec->SetParent(&_mesh);
						_meshNormalSpec->CheckNormals();
					}
				}
				_mesh.checkNormals(TRUE);

				// get transformations for current frame
				Matrix3 _tm	= _node->GetObjTMAfterWSM(current_time);

				ExportState("Mesh %s: writing frame %i of %i", meshname, current_frame, g_total_frames);

				Point3 BoxMin(0, 0, 0);
				Point3 BoxMax(0, 0, 0);
				for (j = 0; j < (int)vVertexes.size(); j++) // number of vertices
				{
					ExportState("Mesh %s: transform vertex %i of %i", meshname, j, (int)vVertexes.size());

					int vert = vVertexes[j].vert;
					Point3 &v = _tm.PointTransform(_mesh.getVert(vert));
					
					// populate bbox data
					if (!shadow_or_collision)
					{
						BoxMin.x = min(BoxMin.x, v.x);
						BoxMin.y = min(BoxMin.y, v.y);
						BoxMin.z = min(BoxMin.z, v.z);
						BoxMax.x = max(BoxMax.x, v.x);
						BoxMax.y = max(BoxMax.y, v.y);
						BoxMax.z = max(BoxMax.z, v.z);
					}

					// write vertex
					double f;
					f = v.x * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file);
					f = v.y * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file);
					f = v.z * 64.0f; if (f < -32768.0) f = -32768.0; if (f > 32767.0) f = 32767.0; put16((short)f, file);

					// get normal
					ExportState("Mesh %s: transform vertex normal %i of %i", meshname, j, (int)vVertexes.size());
					Point3 n;
					if (_meshNormalSpec) // mesh have explicit normals (i.e. Edit Normals modifier)
						n = _meshNormalSpec->Normal(vVertexes[j].normalindex);
					else if (!vVertexes[j].normalfilled || !_mesh.normalsBuilt)
						n = _mesh.getNormal(vert);
					else
					{
						RVertex *rv = _mesh.getRVertPtr(vert);
						if (vVertexes[j].normalindex < 0)
							n = _mesh.getFaceNormal((0 - vVertexes[j].normalindex) - 1);
						else if (vVertexes[j].normalindex == 0)
							n = rv->rn.getNormal();
						else 
							n = rv->ern[vVertexes[j].normalindex - 1].getNormal();
					}

					// transform normal
					Point3 &nt = _tm.VectorTransform(n).Normalize();

					// encode a normal vector into a 16-bit latitude-longitude value
					double lng = acos(nt.z) * 255 / (2 * pi);
					double lat = atan2(nt.y, nt.x) * 255 / (2 * pi);
					put16((((int)lat & 0xFF) << 8) | ((int)lng & 0xFF), file);
				}

				// blend the pivot positions for tag_pivot using mesh's volumes for blending power
				if (g_tag_for_pivot && !shadow_or_collision)
				{
					ExportState("Mesh %s: writing tag_pivot", meshname);

					Point3 Size = BoxMax - BoxMin;
					double BoxVolume = pow(Size.x * Size.y * Size.z, 0.333f);

					// blend matrices
					float blend = (float)(BoxVolume / (BoxVolume + tag_pivot_volume[current_frame]));
					float iblend = 1 - blend;
					tag_pivot_volume[current_frame]   = tag_pivot_volume[current_frame] + BoxVolume;
					Point3 row = _tm.GetRow(3) - _node->GetObjOffsetPos();
					tag_pivot_origin[current_frame].x = tag_pivot_origin[current_frame].x * iblend + row.x * blend;
					tag_pivot_origin[current_frame].y = tag_pivot_origin[current_frame].y * iblend + row.y * blend;
					tag_pivot_origin[current_frame].z = tag_pivot_origin[current_frame].z * iblend + row.z * blend;
				}

				// populate bbox data for frames
				lFrameBBoxMin[current_frame].x = min(lFrameBBoxMin[current_frame].x, BoxMin.x);
				lFrameBBoxMin[current_frame].y = min(lFrameBBoxMin[current_frame].y, BoxMin.y);
				lFrameBBoxMin[current_frame].z = min(lFrameBBoxMin[current_frame].z, BoxMin.z);
				lFrameBBoxMax[current_frame].x = max(lFrameBBoxMax[current_frame].x, BoxMax.x);
				lFrameBBoxMax[current_frame].y = max(lFrameBBoxMax[current_frame].y, BoxMax.y);
				lFrameBBoxMax[current_frame].z = max(lFrameBBoxMax[current_frame].z, BoxMax.z);

				// delete the working object, if necessary.
				if (_needsDel)
					delete _tri;
			}
		}

		// delete if necessary
		if (needsDel)
			delete tri;

		// fill in meshsize
		pos_current = ftell(file);
		fseek(file, pos_meshsize, SEEK_SET);
		put32(pos_current - pos_meshstart, file);
		fseek(file, pos_current, SEEK_SET);  

		// reset back to first frame
		SceneEnumProc scratch(ei->theScene, start_time, gi);
		totalTris += (long)vTriangles.size();
		totalVerts += (long)vVertexes.size();
		vTriangles.clear();
		vVertexes.clear();
	}

	// write tag_pivot
	ExportState("Writing tag_pivot positions");
	if (g_tag_for_pivot)
	{
		pos_current = ftell(file);
		long current_frame = 0;
		for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
		{
			for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
			{
				fseek(file, pos_tags + totalTags*112*current_frame + (int)lTags.size()*112 + 64, SEEK_SET);
				// origin
				putFloat(tag_pivot_origin[current_frame].x, file);
				putFloat(tag_pivot_origin[current_frame].y, file);
				putFloat(tag_pivot_origin[current_frame].z, file);
			}
		}
		fseek(file, pos_current, SEEK_SET);
	}
	tag_pivot_volume.clear();
	tag_pivot_origin.clear();

	// write frame data
	ExportState("Writing culling info");
	long current_frame = 0;
	pos_current = ftell(file);
	for (range_i = g_frame_ranges.begin(); range_i != g_frame_ranges.end(); range_i++)
	{
		for (i = (*range_i).first; i <= (int)(*range_i).last; i++, current_frame++)
		{
			fseek(file, pos_framestart + current_frame*56, SEEK_SET);
			putFloat(lFrameBBoxMin[current_frame].x, file);	// bbox min vector
			putFloat(lFrameBBoxMin[current_frame].y, file);
			putFloat(lFrameBBoxMin[current_frame].z, file);	
			putFloat(lFrameBBoxMax[current_frame].x, file); // bbox max vector
			putFloat(lFrameBBoxMax[current_frame].y, file);
			putFloat(lFrameBBoxMax[current_frame].z, file);
			putFloat(0, file); // local origin (usually 0 0 0)
			putFloat(0, file);
			putFloat(0, file);
			putFloat(max(lFrameBBoxMin[current_frame].Length(), lFrameBBoxMax[current_frame].Length()) , file); // radius of bounding sphere
		}
	}
	fseek(file, pos_current, SEEK_SET);
	lFrameBBoxMin.clear();
	lFrameBBoxMax.clear();

	// fill in filesize
	pos_current = ftell(file);
	fseek(file, pos_filesize, SEEK_SET);
	put32(pos_current, file);
	fseek(file, pos_current, SEEK_SET);

	fclose(file);

	ExportDebug("    total: %i vertexes, %i triangles", totalVerts, totalTris);

	return TRUE;
}
Exemple #10
0
void WMOGroupImpl::buildMaxData()
{
	// Group Header Node
	INode* groupHeadNode = createGroupHeaderNode();
	groupHeadNode->SetGroupHead(TRUE);
	groupHeadNode->SetGroupMember(FALSE);

	// Geoset
	// 一个Render Batch构造一个Node, 并且加入到组中

	for (int i = 0; i < m_batchCount; ++i)
	{
		WMORenderBatch& renderBatch = m_batchData[i];

		m_wmoImporter->m_logStream << "Model Geoset " << i << " Vertex Info: " 
			<< renderBatch.vertexStart << " -- " << renderBatch.vertexEnd << endl;
		m_wmoImporter->m_logStream << "Model Geoset " << i << " Index Info: " 
			<< renderBatch.indexStart << " -- " << renderBatch.indexCount << endl;

		// Triangle Mesh Object
		// 基本的三角形模型对象
		TriObject* triObject = CreateNewTriObject();

		// 创建Node, 并且设为Group Header Node的子节点
		ImpNode* tmpImpNode = m_wmoImporter->m_impInterface->CreateNode();
		tmpImpNode->Reference(triObject);

		m_wmoImporter->m_impInterface->AddNodeToScene(tmpImpNode);
		INode* realINode = tmpImpNode->GetINode();
		realINode->SetGroupHead(FALSE);
		realINode->SetGroupMember(TRUE);
		groupHeadNode->AttachChild(realINode);

		m_geosetNodeList.push_back(realINode);

		TCHAR nodeName[256];
		sprintf(nodeName, "%s_part_%03d", m_groupName.c_str(), i);
		realINode->SetName(nodeName);

		// mesh
		unsigned short vertexCount = renderBatch.vertexEnd - renderBatch.vertexStart + 1;
		unsigned int trigangleCount = renderBatch.indexCount / 3;

		Mesh& mesh = triObject->GetMesh();
		mesh.setNumVerts(vertexCount);
		mesh.setNumTVerts(vertexCount, TRUE);

		mesh.setNumFaces(trigangleCount);
		mesh.setNumTVFaces(trigangleCount);

		// 顶点坐标和UV
		for (int i = 0; i < vertexCount; ++i)
		{
			mesh.verts[i] = *(Point3*)(&(m_vertexData[renderBatch.vertexStart + i]));

			// UV坐标反转
			mesh.tVerts[i].x = m_textureCoords[renderBatch.vertexStart + i].x;
			mesh.tVerts[i].y = 1.0f - m_textureCoords[renderBatch.vertexStart + i].y;
		}

		// 三角形
		for (unsigned int i = 0; i < trigangleCount; ++i)
		{
			Face& face = mesh.faces[i];
			int index1 = m_indexData[renderBatch.indexStart + i*3] - renderBatch.vertexStart;
			int index2 = m_indexData[renderBatch.indexStart + i*3+1] - renderBatch.vertexStart;
			int index3 = m_indexData[renderBatch.indexStart + i*3+2] - renderBatch.vertexStart;

			face.setVerts(index1, index2, index3);
			face.Show();
			face.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);

			TVFace& tface = mesh.tvFace[i];
			tface.setTVerts(index1, index2, index3);
		}

		// 法线
		mesh.SpecifyNormals();
		MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals();
		if (specNorms)
		{
			specNorms->ClearAndFree();
			specNorms->SetNumFaces(trigangleCount);
			specNorms->SetNumNormals(vertexCount);

			Point3* norms = specNorms->GetNormalArray();
			for (int i = 0; i < vertexCount; ++i)
			{
				norms[i] = *(Point3*)(&m_normalData[renderBatch.vertexStart + i]);
			}

			MeshNormalFace* pFaces = specNorms->GetFaceArray();
			for (unsigned int i = 0; i < trigangleCount; ++i)
			{
				int index1 = m_indexData[renderBatch.indexStart + i*3] - renderBatch.vertexStart;
				int index2 = m_indexData[renderBatch.indexStart + i*3+1] - renderBatch.vertexStart;
				int index3 = m_indexData[renderBatch.indexStart + i*3+2] - renderBatch.vertexStart;

				pFaces[i].SpecifyNormalID(0, index1);
				pFaces[i].SpecifyNormalID(1, index2);
				pFaces[i].SpecifyNormalID(2, index3);
			}

			specNorms->SetAllExplicit(true);
			specNorms->CheckNormals();
		}

		// 删除重复的和无效的面
		mesh.RemoveDegenerateFaces();
		mesh.RemoveIllegalFaces();

		realINode->SetMtl(m_wmoImporter->m_materialList[renderBatch.texture]);
		//realINode->BackCull(FALSE);			// 取消背面裁减 不是所有的Node都要取消背面裁减
		realINode->EvalWorldState(0);
	}
}
Exemple #11
0
void SymmetryMod::ModifyTriObject (TimeValue t, ModContext &mc, TriObject *tobj, INode *inode) {
	Mesh &mesh = tobj->GetMesh();
	Interval iv = FOREVER;
	int axis, slice, weld, flip;
	float threshold;

	mp_pblock->GetValue (kSymAxis, t, axis, iv);
	mp_pblock->GetValue (kSymFlip, t, flip, iv);
	mp_pblock->GetValue (kSymSlice, t, slice, iv);
	mp_pblock->GetValue (kSymWeld, t, weld, iv);
	mp_pblock->GetValue (kSymThreshold, t, threshold, iv);
	if (threshold<0) threshold=0;

	// Get transform from mirror controller:
	Matrix3 tm  = CompMatrix (t, NULL, &mc, &iv);
	Matrix3 itm = Inverse (tm);

	// Get DotProd(N,x)=offset plane definition from transform
	Point3 Axis(0,0,0);
	Axis[axis] = flip ? -1.0f : 1.0f;
	Point3 origin = tm.GetTrans();
	Point3 N = Normalize(tm*Axis - origin);
	float offset = DotProd (N, origin);

	// Slice operation does not handle NormalSpecs, but it handles mapping channels.
	// move our mesh normal data to a map channel
	MeshNormalSpec *pNormals = mesh.GetSpecifiedNormals ();
	int normalMapChannel = INVALID_NORMALMAPCHANNEL;
	if (pNormals && pNormals->GetNumFaces())
	{
		pNormals->SetParent(&mesh);
		//find an empty map channel
		for (int mp = 0; mp < mesh.getNumMaps(); mp++) 
		{			
			if (!mesh.mapSupport(mp)) 
			{
				normalMapChannel = mp;

				mesh.setMapSupport(normalMapChannel,TRUE);
				MeshMap& map = mesh.Map(normalMapChannel);
				for (int i = 0; i < map.fnum; i++)
				{
					for (int j = 0; j < 3; j++)
					{
						unsigned int newID = pNormals->Face(i).GetNormalID(j);
						map.tf[i].t[j] = newID;
					}
				}
				map.setNumVerts(pNormals->GetNumNormals());
				for (int i = 0; i < map.vnum; i++)
				{
					map.tv[i] = pNormals->Normal(i);
				}				

				// make sure nothing is done with MeshNormalSpec (until data is copied back) 
				pNormals->Clear();
				break;
			}
		}
	}
	
	// Slice off everything below the plane.
	if (slice) SliceTriObject (mesh, N, offset);
	MirrorTriObject (mesh, axis, tm, itm,normalMapChannel);
	if (weld) WeldTriObject (mesh, N, offset, threshold);

	//now move the normals back
	if (pNormals && normalMapChannel != -1)
	{
		MeshMap& map = mesh.Map(normalMapChannel);
		pNormals->SetNumFaces(map.fnum);

		pNormals->SetNumNormals(map.vnum);
		pNormals->SetAllExplicit(true);
		BitArray temp;
		temp.SetSize(map.vnum);
		temp.SetAll();
		pNormals->SpecifyNormals(TRUE,&temp);

		for (int i = 0; i < map.vnum; i++)
		{
			pNormals->GetNormalArray()[i] = map.tv[i];
			pNormals->SetNormalExplicit(i,true);
		}	

		for (int i = 0; i < map.fnum; i++)
		{
			for (int j = 0; j < 3; j++)
			{
				pNormals->SetNormalIndex(i,j,map.tf[i].t[j]);				
				MeshNormalFace& face = pNormals->Face(i);
				face.SpecifyAll(true);
			}
		}

		pNormals->SetFlag(MESH_NORMAL_MODIFIER_SUPPORT);

		for (int i = 0; i < pNormals->GetNumFaces(); i++)
		{
			for (int j = 0; j < 3; j++)
			{
				int id = pNormals->GetNormalIndex(i,j);	
			}
		}

		pNormals->CheckNormals();
		pNormals->SetParent(NULL);

		// Free the map channel
		mesh.setMapSupport(normalMapChannel,FALSE);
	}
	
	tobj->UpdateValidity (GEOM_CHAN_NUM, iv);
	tobj->UpdateValidity (TOPO_CHAN_NUM, iv);
	tobj->UpdateValidity (VERT_COLOR_CHAN_NUM, iv);
	tobj->UpdateValidity (TEXMAP_CHAN_NUM, iv);
	tobj->UpdateValidity (SELECT_CHAN_NUM, iv);
}
Exemple #12
0
// 1. 加载模型顶点数据
void M2Importer::importGeomObject()
{
	// Group Header Node
	INode* groupHeadNode = createGroupHeaderNode();
	groupHeadNode->SetGroupHead(TRUE);
	groupHeadNode->SetGroupMember(FALSE);

	if (m_modelHeader->nameLength > 1)
	{
		TCHAR* modelName = (TCHAR*)(m_m2FileData + m_modelHeader->nameOfs);
		groupHeadNode->SetName(modelName);
		m_logStream << "ModelName: " << modelName << endl;
	}
	else
		groupHeadNode->SetName("GeomGroup");


	// Geoset
	// 一个Geoset构造一个Node, 并且加入到组中

	unsigned short* verDataIndex = (unsigned short*)(m_m2FileData + m_modelView->ofsIndex);
	unsigned short* triData = (unsigned short*)(m_m2FileData + m_modelView->ofsTris);

	m_geosetNodeList.reserve(m_modelView->nSub);
	m_materialList.reserve(m_modelView->nSub);
	for (unsigned int i = 0; i < m_modelView->nSub; ++i)
		m_materialList.push_back(0);

	for (unsigned int i = 0; i < m_modelView->nSub; ++i)
	{
		ModelGeoset& geosetData = m_modelGeoset[i];

		// Triangle Mesh Object
		// 基本的三角形模型对象
		TriObject* triObject = CreateNewTriObject();

		// 创建Node, 并且设为Group Header Node的子节点
		ImpNode* tmpImpNode = m_impInterface->CreateNode();
		tmpImpNode->Reference(triObject);
		//tmpImpNode->SetPivot(*(Point3*)&(geosetData.v));

		m_impInterface->AddNodeToScene(tmpImpNode);
		INode* realINode = tmpImpNode->GetINode();
		realINode->SetGroupHead(FALSE);
		realINode->SetGroupMember(TRUE);
		groupHeadNode->AttachChild(realINode);

		m_geosetNodeList.push_back(realINode);

		TCHAR nodeName[256];
		sprintf(nodeName, "GeosetPart_%d", i);
		realINode->SetName(nodeName);

		// mesh
		Mesh& mesh = triObject->GetMesh();
		mesh.setNumVerts(geosetData.vcount);
		mesh.setNumTVerts(geosetData.vcount, TRUE);

		unsigned int triangeCount = geosetData.icount / 3;
		mesh.setNumFaces(triangeCount);
		mesh.setNumTVFaces(triangeCount);

		m_logStream << "Model Geoset " << i << " Vertex Count: " << geosetData.vcount << endl;
		m_logStream << "Model Geoset " << i << " Index Count: " << triangeCount << endl;

		// 顶点坐标和UV
		for (unsigned int i = 0; i < geosetData.vcount; ++i)
		{
			ModelVertex& vertexData = m_globalVertices[ verDataIndex[geosetData.vstart + i] ];

			mesh.verts[i] = *(Point3*)(&vertexData.pos);

			// UV坐标反转
			mesh.tVerts[i].x = vertexData.texcoords.x;
			mesh.tVerts[i].y = 1.0f - vertexData.texcoords.y;
		}

		// 三角形
		for (unsigned int i = 0; i < triangeCount; ++i)
		{
			Face& face = mesh.faces[i];
			face.setVerts(triData[geosetData.istart + i*3] - m_indexCount, 
						  triData[geosetData.istart + i*3+1] - m_indexCount, 
						  triData[geosetData.istart + i*3+2] - m_indexCount);
			face.Show();
			face.setEdgeVisFlags(EDGE_VIS, EDGE_VIS, EDGE_VIS);

			TVFace& tface = mesh.tvFace[i];
			tface.setTVerts(triData[geosetData.istart + i*3] - m_indexCount, 
							triData[geosetData.istart + i*3+1] - m_indexCount, 
							triData[geosetData.istart + i*3+2] - m_indexCount);
		}

		// 法线
		mesh.SpecifyNormals();
		MeshNormalSpec *specNorms = mesh.GetSpecifiedNormals();
		if (specNorms)
		{
			specNorms->ClearAndFree();
			specNorms->SetNumFaces(triangeCount);
			specNorms->SetNumNormals(geosetData.vcount);

			Point3* norms = specNorms->GetNormalArray();
			for (unsigned int i = 0; i < geosetData.vcount; ++i)
			{
				ModelVertex& vertexData = m_globalVertices[ verDataIndex[geosetData.vstart + i] ];
				norms[i] = *(Point3*)(&vertexData.normal);
			}

			MeshNormalFace* pFaces = specNorms->GetFaceArray();
			for (unsigned int i = 0; i < triangeCount; ++i)
			{
				pFaces[i].SpecifyNormalID(0, triData[geosetData.istart + i*3] - m_indexCount);
				pFaces[i].SpecifyNormalID(1, triData[geosetData.istart + i*3+1] - m_indexCount);
				pFaces[i].SpecifyNormalID(2, triData[geosetData.istart + i*3+2] - m_indexCount);
			}

			specNorms->SetAllExplicit(true);
			specNorms->CheckNormals();
		}

		// 删除重复的和无效的面
		mesh.RemoveDegenerateFaces();
		mesh.RemoveIllegalFaces();

		//realINode->BackCull(FALSE);			// 取消背面裁减 双面绘制与取消背面裁减一起设置
		realINode->EvalWorldState(0);

		// 索引值修正
		m_indexCount += geosetData.vcount;
	}

	// 加载材质
	unsigned short* texLookupData = (unsigned short*)(m_m2FileData + m_modelHeader->ofsTexLookup);
	ModelTextureDef* texUnitDefData = (ModelTextureDef*)(m_m2FileData + m_modelHeader->ofsTextures);
	ModelTexUnit* texUnitData = (ModelTexUnit*)(m_m2FileData + m_modelView->ofsTex);

	for (unsigned int i = 0; i < m_modelView->nTex; ++i)
	{
		ModelTexUnit& texUnit = texUnitData[i];
		unsigned short textureID = texLookupData[texUnit.textureid];
		ModelTextureDef& texDef = texUnitDefData[textureID];

		string textureName;

		if (texDef.type == 0)
			textureName = (LPCSTR)(m_m2FileData + texDef.nameOfs);
		else
			textureName = getReplacableTexture(texDef.type);

		StdMat2* material = m_materialList[texUnit.op];
		if (!material)
			material = createMaterial();

		// 根据混合属性决定加在第几层
		material->SetSubTexmap(ID_DI, createTexture(textureName.c_str()));
		material->EnableMap(ID_DI, TRUE);
		//material->SetTwoSided(TRUE);		// 双面 设置了此标志的才打开

		m_maxInterface->GetMaterialLibrary().Add(material);
		m_geosetNodeList[texUnit.op]->SetMtl(material);
	}

	m_maxInterface->RedrawViews(m_maxInterface->GetTime());
}
/*
*	Get vertex normal using smooth group with normal method.(using RVertex information)
*	FaceVertexIdx is between 0 and 2 local to a triangle
*/
Point3 SGP_MaxInterface::GetVertexNormal( Mesh* pMesh, int faceId, int vertexId, int FaceVertexIdx )
{
	//////////////////////////////////////////////////////////////////////////
	// Below is the way if someone has used "edit normals modifier" to change vertex normal
	MeshNormalSpec *pNormalSpec = pMesh->GetSpecifiedNormals();
	if( pNormalSpec )
	{
		const int NumFaces = pNormalSpec->GetNumFaces();
		const int NumNormals = pNormalSpec->GetNumNormals();

		if( NumFaces != 0 && NumNormals != 0 )
		{
			const int NormalID = pNormalSpec->Face(faceId).GetNormalID(FaceVertexIdx);
			return pNormalSpec->Normal(NormalID).Normalize();
		}
	}
	//////////////////////////////////////////////////////////////////////////

	RVertex *pRVertex;
	pRVertex = pMesh->getRVertPtr(vertexId);


	// get the face
	Face *pFace;
	pFace = &pMesh->faces[faceId];

	// get the smoothing group of the face
	DWORD smGroup;
	smGroup = pFace->smGroup;

	// get the number of normals
	int normalCount;
	normalCount = pRVertex->rFlags & NORCT_MASK;

	// check if the normal is specified ...
	if(pRVertex->rFlags & SPECIFIED_NORMAL)
	{
		return pRVertex->rn.getNormal();
	}
	// ... otherwise, check for a smoothing group
	else if((normalCount > 0) && (smGroup != 0))
	{
		// If there is only one vertex is found in the rn member.
		if(normalCount == 1)
		{
			return pRVertex->rn.getNormal();
		}
		else
		{
			int normalId;
			Point3 n(0,0,0);
			for(normalId = 0; normalId < normalCount; normalId++)
			{
				if(pRVertex->ern[normalId].getSmGroup() & smGroup)
				{
					n = n+pRVertex->ern[normalId].getNormal();
				}
			}
			n = n.Normalize();
			return n;
		}
	}

	// if all fails, return the face normal
	return pMesh->getFaceNormal(faceId);
}
BOOL GetVertexNormalUsingSmoothGroup(Point3& VN, Mesh& mesh, int faceId, int globalvertexId, int _FaceVertexIdx)
{
    //_FaceVertexIdx is between 0 and 2 local to a triangle

    //THIS IS WHAT YOU NEED IF SOMEONE HAS USED THE EDIT NORMAL MODIFIER
    MeshNormalSpec * normalspec = mesh.GetSpecifiedNormals();
    if (normalspec)
    {
        const int NumFaces      = normalspec->GetNumFaces   ();
        const int NumNormals   = normalspec->GetNumNormals   ();

        if (NumFaces && NumNormals)
        {
            const int normID = normalspec->Face(faceId).GetNormalID(_FaceVertexIdx);
            VN = normalspec->Normal(normID).Normalize();
            return TRUE;
        }
    }

    // get the "rendered" vertex
    RVertex *pRVertex = mesh.getRVertPtr(globalvertexId);
    if(! pRVertex)return FALSE;

    // get the face
    const Face& Face = mesh.faces[faceId];

    // get the smoothing group of the face
    const DWORD smGroup = Face.smGroup;

    // get the number of normals
    const int normalCount = pRVertex->rFlags & NORCT_MASK;

    // check if the normal is specified ...
    if(pRVertex->rFlags & SPECIFIED_NORMAL)
    {
        VN = pRVertex->rn.getNormal();
        return TRUE;
    }
    // ... otherwise, check for a smoothing group
    else if((normalCount > 0) && (smGroup != 0))
    {
        // If there is only one vertex is found in the rn member.
        if(normalCount == 1)
        {
            VN = pRVertex->rn.getNormal();
            return TRUE;
        }
        else
        {
            for(int normalId = 0; normalId < normalCount; normalId++)
            {
                if(pRVertex->ern[normalId].getSmGroup() & smGroup)
                {
                    VN = pRVertex->ern[normalId].getNormal();
                    return TRUE;
                }
            }
        }
    }

    // if all failed, return the face normal
    VN = mesh.getFaceNormal(faceId);
    return TRUE;
}