コード例 #1
0
// ***************************************************************************
void	NLPACS::CZoneTessellation::build()
{
	sint	el;
	uint	i, j;

	NL3D::CLandscape	landscape;
	landscape.init();

	vector<CVector>				normals;

	vector<CVector>				vectorCheck;
	bool						useNoHmZones = true;

	{
		NL3D::CLandscape	landscapeNoHm;
		landscapeNoHm.init();

		//
		// load the 9 landscape zones
		//
		for (i=0; i<_ZoneIds.size(); ++i)
		{
			string	filename = getZoneNameById(_ZoneIds[i])+ZoneExt;
			CIFile	file(CPath::lookup(filename));
			CZone	zone;
			zone.serial(file);
			file.close();

			if (Verbose)
				nlinfo("use zone %s %d", filename.c_str(), zone.getZoneId());

			if (zone.getZoneId() != _ZoneIds[i])
			{
				nlwarning ("Zone %s ID is wrong. Abort.", filename.c_str());
				return;
			}
			landscape.addZone(zone);

			if (useNoHmZones)
			{
				string	filenameNH = getZoneNameById(_ZoneIds[i])+ZoneNHExt;
				string	loadZ = CPath::lookup(filenameNH, false, false);
				if (!loadZ.empty())
				{
					CIFile	fileNH(loadZ);
					CZone	zoneNH;
					zoneNH.serial(fileNH);
					fileNH.close();
					if (zoneNH.getZoneId() != _ZoneIds[i])
					{
						nlwarning ("Zone %s ID is wrong. Abort.", filenameNH.c_str());
						return;
					}
					landscapeNoHm.addZone(zoneNH);
				}
				else
				{
					useNoHmZones = false;
				}
			}

			_ZonePtrs.push_back(landscape.getZone(_ZoneIds[i]));
		}

		landscape.setNoiseMode(false);
		landscape.checkBinds();

		if (useNoHmZones)
		{
			landscapeNoHm.setNoiseMode(false);
			landscapeNoHm.checkBinds();
		}

		BestFittingBBox.setCenter(CVector::Null);
		BestFittingBBox.setHalfSize(CVector::Null);
		BestFittingBBoxSetuped= false;

		// Compute best fitting bbox
		for (i=0; i<_ZoneIds.size(); ++i)
		{
			if (_ZoneIds[i] == CentralZoneId)
			{
				if(_ZonePtrs[i]->getNumPatchs()>0)
				{
					BestFittingBBox = _ZonePtrs[i]->getZoneBB().getAABBox();
					BestFittingBBoxSetuped= true;
				}
			}
		}

		CAABBox	enlBBox = BestFittingBBox;
		enlBBox.setHalfSize(enlBBox.getHalfSize()+CVector(8.0f, 8.0f, 1000.0f));

		// Add neighbor patch
		for (i=0; i<_ZoneIds.size(); ++i)
		{
			if (_ZoneIds[i] == CentralZoneId)
			{
				for (j=0; (sint)j<_ZonePtrs[i]->getNumPatchs(); ++j)
				{
					landscape.excludePatchFromRefineAll(_ZoneIds[i], j, false);
					if (useNoHmZones)
						landscapeNoHm.excludePatchFromRefineAll(_ZoneIds[i], j, false);
				}
				if (Verbose)
					nlinfo(" - selected %d/%d patches for zone %d", _ZonePtrs[i]->getNumPatchs(), _ZonePtrs[i]->getNumPatchs(), _ZoneIds[i]);
			}
			else
			{
				uint	nump = 0;
				for (j=0; (sint)j<_ZonePtrs[i]->getNumPatchs(); ++j)
				{
					CAABBox	pbox = _ZonePtrs[i]->getPatch(j)->buildBBox();
					bool	inters = enlBBox.intersect(pbox);

					if (inters)
					{
						landscape.excludePatchFromRefineAll(_ZoneIds[i], j, false);
						if (useNoHmZones)
							landscapeNoHm.excludePatchFromRefineAll(_ZoneIds[i], j, false);
						++nump;
					}
					else
					{
						landscape.excludePatchFromRefineAll(_ZoneIds[i], j, true);
						if (useNoHmZones)
							landscapeNoHm.excludePatchFromRefineAll(_ZoneIds[i], j, true);
					}
				}
				if (Verbose)
					nlinfo(" - selected %d/%d patches for zone %d", nump, _ZonePtrs[i]->getNumPatchs(), _ZoneIds[i]);
			}
		}

		// tessellate the landscape, get the leaves (the tessellation faces), and convert them
		// into surf elements
		if (Verbose)
			nlinfo("Compute landscape tessellation");

		if (Verbose)
			nlinfo("   - tessellate landscape");

		if (useNoHmZones)
		{
			// Before tesselate, verify that the 2 landscape zones have at least the same binds!
			// Else there will be errors because of not the same tesselation
			checkSameLandscapeHmBinds(landscape, landscapeNoHm);
			
			// Tesselate
			landscapeNoHm.setThreshold(0.0f);
			landscapeNoHm.setTileMaxSubdivision(TessellateLevel);
			landscapeNoHm.refineAll(CVector::Null);
			landscapeNoHm.averageTesselationVertices();

			// get the faces
			vector<const CTessFace *>	leavesNoHm;
			landscapeNoHm.getTessellationLeaves(leavesNoHm);

			for (el=0; el<(sint)leavesNoHm.size(); ++el)
			{
				const CTessFace	*face = leavesNoHm[el];
				const CVector	*v[3];

				// get the vertices of the face
				v[0] = &(face->VBase->EndPos);
				v[1] = &(face->VLeft->EndPos);
				v[2] = &(face->VRight->EndPos);

				normals.push_back( ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed() );

				vectorCheck.push_back(*(v[0]));
				vectorCheck.push_back(*(v[1]));
				vectorCheck.push_back(*(v[2]));
			}
		}
	}

	// Build the lanscape with heightmap
	landscape.setThreshold(0.0f);
	landscape.setTileMaxSubdivision(TessellateLevel);
	landscape.refineAll(CVector::Null);
	landscape.averageTesselationVertices();

	vector<const CTessFace *>	leaves;
	landscape.getTessellationLeaves(leaves);
	if (Verbose)
	{
		if (useNoHmZones)
			nlinfo("      - used no height map zones");
		nlinfo("      - generated %d leaves", leaves.size());
	}

	// If don't use NoHm zones, build normals and vectorCheck directly from std landscape
	if (!useNoHmZones)
	{
		for (el=0; el<(sint)leaves.size(); ++el)
		{
			const CTessFace	*face = leaves[el];
			const CVector	*v[3];

			// get the vertices of the face
			v[0] = &(face->VBase->EndPos);
			v[1] = &(face->VLeft->EndPos);
			v[2] = &(face->VRight->EndPos);

			normals.push_back( ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed() );

			vectorCheck.push_back(*(v[0]));
			vectorCheck.push_back(*(v[1]));
			vectorCheck.push_back(*(v[2]));
		}
	}

	// check that there is the same number of faces from landscape with and without heightmap
	if (normals.size() != leaves.size())
	{
		nlwarning ("ERROR : The heightmaped landscape has not the same number of polygon than the nonheightmaped landscape: %d/%d.", 
			normals.size(), leaves.size());
		exit (0);
	}

	// generate a vector of vertices and of surf element
	CHashMap<const CVector *, uint32, CHashPtr<const CVector> >				vremap;
	CHashMap<const CVector *, uint32, CHashPtr<const CVector> >::iterator	vremapit;
	CHashMap<const CTessFace *, CSurfElement *, CHashPtr<const CTessFace> >	fremap;
	CHashMap<const CTessFace *, CSurfElement *, CHashPtr<const CTessFace> >::iterator	fremapit;
	_Vertices.clear();
	_Tessellation.resize(leaves.size());

	if (Verbose)
		nlinfo("   - make and remap surface elements");

	for (el=0; el<(sint)leaves.size(); ++el)
		fremap[leaves[el]] = &(_Tessellation[el]);

	uint	check = 0;

	float	dist, maxdist = 0.0f;

	for (el=0; el<(sint)leaves.size(); ++el)
	{
		const CTessFace	*face = leaves[el];
		const CVector	*v[3];

		CSurfElement	&element = _Tessellation[el];

		// setup zone id
		element.ZoneId = face->Patch->getZone()->getZoneId();

		// get the vertices of the face
		v[0] = &(face->VBase->EndPos);
		v[1] = &(face->VLeft->EndPos);
		v[2] = &(face->VRight->EndPos);

		{
			CVector	vcheck;

			vcheck = vectorCheck[check++] - *(v[0]);
			vcheck.z = 0;
			dist = vcheck.norm();
			if (dist > maxdist)	maxdist = dist;
			//nlassert(vcheck.norm() < 0.1f);

			vcheck = vectorCheck[check++] - *(v[1]);
			vcheck.z = 0;
			dist = vcheck.norm();
			if (dist > maxdist)	maxdist = dist;
			//nlassert(vcheck.norm() < 0.1f);

			vcheck = vectorCheck[check++] - *(v[2]);
			vcheck.z = 0;
			dist = vcheck.norm();
			if (dist > maxdist)	maxdist = dist;
			//nlassert(vcheck.norm() < 0.1f);
		}

		//element.Normal = ((*(v[1])-*(v[0])) ^ (*(v[2])-*(v[0]))).normed();
		element.Normal = normals[el];


		// search the vertices in the map
		for (i=0; i<3; ++i)
		{
			// if doesn't exist, create a new vertex
			if ((vremapit = vremap.find(v[i])) == vremap.end())
			{
				element.Tri[i] = (uint32)_Vertices.size();
				_Vertices.push_back(*(v[i]));
				vremap.insert(make_pair(v[i], element.Tri[i]));
			}
			// else use previous
			else
			{
				element.Tri[i] = vremapit->second;
			}
		}

		// setup the vertices pointer
		element.Vertices = &_Vertices;

		CTessFace		*edge[3];

		edge[0] = face->FBase;
		edge[1] = face->FRight;
		edge[2] = face->FLeft;

		for (i=0; i<3; ++i)
		{
			fremapit = fremap.find(edge[i]);
			element.EdgeLinks[i] = (fremapit != fremap.end() ? fremapit->second : NULL);
		}
	}

	for (el=0; el<(sint)_Tessellation.size(); ++el)
	{
		// add the element to the list of valid elements
		Elements.push_back(&(_Tessellation[el]));
	}

	landscape.clear();
}
/**  Check a zone and report the total number of errors
  */
static uint CheckZone(std::string middleZoneFile, float weldThreshold, float middleEdgeWeldThreshold)
{
	uint numErrors = 0;
	uint k, l, m, n, p, q;	// some loop counters	
	// This avoid reporting errors twice (for readability)
	std::set<CPatchIdentPair> errorPairs;

	////////////////////////////
	// Load the zones around  //
	////////////////////////////

		std::auto_ptr<CZone>		zones[9];
		std::string					zoneNames[9];
		CZoneInfo					zoneInfos[9];				
		uint16  xPos, yPos;
		const sint16 posOffs[][2] = { {0, 0}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1} };

		std::string middleZoneName = CFile::getFilenameWithoutExtension(middleZoneFile);
		::getZoneCoordByName(middleZoneName.c_str(), xPos, yPos);
		try
		{
			std::string ext = CFile::getExtension(middleZoneFile);
			zones[0].reset(::LoadZone(xPos, yPos, ext.empty() ? "" : "." + ext));
			if (zones[0].get() == NULL)
			{
				nlwarning("Can't load zone  %s", middleZoneName.c_str());
				return 0;
			}
			for (uint k = 1; k < 9; ++k)
			{
				zones[k].reset(::LoadZone(xPos + posOffs[k][0], yPos + posOffs[k][1], ext.empty() ? "" : "." + ext));
			}
		}
		catch (NLMISC::Exception &e)
		{
			nlinfo("Zones loading failed : %d", e.what());
			return 0;
		}
	
	///////////////////////////////
	// retrieve datas from zones //
	///////////////////////////////

		for (k = 0; k < 9; ++k)
		{
			::getZoneNameByCoord(xPos + posOffs[k][0], yPos + posOffs[k][1], zoneNames[k]);			
			if (zones[k].get() != NULL) zones[k]->retrieve(zoneInfos[k]);
		}	

		// fill the quad grid
		CAABBox zoneBBox = zones[0]->getZoneBB().getAABBox();
		float zoneSize = 2.f * weldThreshold + std::max(zoneBBox.getMax().x - zoneBBox.getMin().x,
														 zoneBBox.getMax().y - zoneBBox.getMin().y);
		TPVQuadGrid qg;
		const uint numQGElt = 128;
		qg.create(numQGElt, zoneSize / numQGElt);

		// insert vertices in quadgrid
		for (k = 0; k < 9; ++k)
		{		
			for (l = 0; l < zoneInfos[k].Patchs.size(); ++l)
			{
				CPatchInfo &patch = zoneInfos[k].Patchs[l];
				// for each base vertex of the patch
				for (m = 0; m < 4; ++m)
				{
					CVector &pos = patch.Patch.Vertices[m];
					CBSphere s(pos, weldThreshold);
					if (zoneBBox.intersect(s)) // does this vertex and its zone of influence intersect the bbox ?
					{
						CVector half(weldThreshold, weldThreshold, weldThreshold);
						// yes, insert it in the tree
						qg.insert(pos - half, pos + half, CPatchVertexInfo(k, l, m, pos));
					}			
				}
			}
		}

	////////////////////////////////////////////////
	// check wether each patch is correctly bound //
	////////////////////////////////////////////////

	for (l = 0; l < zoneInfos[0].Patchs.size(); ++l)
	{	
		CPatchInfo &patch = zoneInfos[0].Patchs[l];
		// deals with each border
		for (m = 0; m < 4; ++m)
		{			
			// if this border is said to be bound, no need to test..
			if (patch.BindEdges[m].NPatchs == 0)
			{
				// maps from an index to  a (s, t) couple
				static const float indexToST[][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};

				// index of this border vertices
				const uint vIndex[]  = { m, (m + 1) & 0x03 };

				bool errorFound = false;

				static TPVVect verts[2];

				// Get vertices from other patch that could be welded with this patch boder's vertices.
				for (q = 0; q < 2; ++q)
				{
					//nlinfo("pos = %f, %f, %f", patch.Patch.Vertices[vIndex[q]].x, patch.Patch.Vertices[vIndex[q]].y, patch.Patch.Vertices[vIndex[q]].z);
					::GetCandidateVertices(patch.Patch.Vertices[vIndex[q]], qg, verts[q], l, 0, weldThreshold);
				}

									
				///////////////////////////
				// 1 - 1 connectivity ?  //
				///////////////////////////	
				// If there is a patch that is present in the 2 lists, then this is a 1-1 error
				for (n = 0; n < verts[0].size() && !errorFound; ++n)
				{
					for (p = 0; p < verts[1].size() && !errorFound; ++p)
					{
						if (verts[0][n]->ZoneIndex == verts[1][p]->ZoneIndex
							&& verts[0][n]->PatchIndex == verts[1][p]->PatchIndex)
						{
							CPatchIdent pi1(0, l);
							CPatchIdent pi2(verts[0][n]->ZoneIndex, verts[0][n]->PatchIndex);
							CPatchIdentPair errPair = std::make_pair(pi1, pi2);
							//
							if (std::find(errorPairs.begin(), errorPairs.end(), errPair) == errorPairs.end()) // error already displayed ?
							{									
								nlinfo("**** Patch %d of zone %s has 1 - 1 connectivity error, try binding it with patch %d of zone %s",
										l + 1, middleZoneName.c_str(), verts[0][n]->PatchIndex + 1, zoneNames[verts[0][n]->ZoneIndex].c_str());										
								errorPairs.insert(std::make_pair(pi2, pi1));
								++numErrors;
							}
							errorFound = true;
						}
					}
				}
				if (errorFound) continue;

				//////////////////////////
				// 1 - 2 connectivity ? //
				//////////////////////////
			
				// get the position at the middle of that border
				CVector middlePos = patch.Patch.eval( 0.5f * (indexToST[vIndex[0]][0] + indexToST[vIndex[1]][0]),
													  0.5f * (indexToST[vIndex[0]][1] + indexToST[vIndex[1]][1]) );						

				// for each vertex of this border
				for (q = 0; q < 2 && !errorFound; ++q)
				{
					for (n = 0; n < verts[q].size() && !errorFound; ++n)
					{
						const CPatchVertexInfo &pv = *(verts[q][n]);
						// ref to the patch that share a vertex with this one
						const CBezierPatch &bPatch = zoneInfos[pv.ZoneIndex].Patchs[pv.PatchIndex].Patch;
						sint vertIndex = ::GetWeldableVertex(bPatch, pv.Pos, weldThreshold);
						nlassert(vertIndex != -1); // should found one..
						// Follow this patch edge and see if the next / previous vertex could be welded with the middle
						const CVector &nextVertPos = bPatch.Vertices[(vertIndex +  1) & 0x03];
						const CVector &prevVertPos = bPatch.Vertices[(vertIndex -  1) & 0x03];
						if (::CanWeld(nextVertPos, middlePos, middleEdgeWeldThreshold)
							|| ::CanWeld(prevVertPos, middlePos, middleEdgeWeldThreshold)
						   )
						{
							CPatchIdent pi1(0, l);
							CPatchIdent pi2(pv.ZoneIndex, pv.PatchIndex);
							CPatchIdentPair errPair = std::make_pair(pi1, pi2);
							//
							if (std::find(errorPairs.begin(), errorPairs.end(), errPair) == errorPairs.end()) // error already displayed ?
							{									
								nlinfo("**** Patch %d of zone %s has 1 - 2 connectivity error, try binding it with patch %d of zone %s",
										l + 1, middleZoneName.c_str(), pv.PatchIndex + 1, zoneNames[pv.ZoneIndex].c_str());										
								errorPairs.insert(std::make_pair(pi2, pi1));
								++numErrors;										
							}

							errorFound = true;
							break;
						}		
					}
				}
				if (errorFound) continue;						
					
				//////////////////////////
				// 1 - 4 connectivity ? //
				//////////////////////////
				
				// compute points along the border.
				CVector borderPos[5];
				float lambda = 0.f;
				for (n = 0; n < 5; ++n)
				{
					borderPos[n] = patch.Patch.eval((1.f - lambda) * indexToST[vIndex[0]][0] + lambda * indexToST[vIndex[1]][0],
													(1.f - lambda) * indexToST[vIndex[0]][1] + lambda * indexToST[vIndex[1]][1]);
					lambda += 0.25f;
				}						
				
				// Try to find a patch that shares 2 consecutives vertices
				for (k = 0; k < 4 && !errorFound; ++k)
				{
					::GetCandidateVertices(borderPos[k], qg, verts[0], l, 0, middleEdgeWeldThreshold); 
					for (p = 0; p < verts[0].size() && !errorFound; ++p)
					{
						const CPatchVertexInfo &pv = *(verts[0][p]);
						// ref to the patch that share a vertex with this one
						const CBezierPatch &bPatch = zoneInfos[pv.ZoneIndex].Patchs[pv.PatchIndex].Patch;
						sint vertIndex = ::GetWeldableVertex(bPatch, pv.Pos, weldThreshold);
						nlassert(vertIndex != -1); // should found one..
						// Follow this patch edge and see if the next/ previous  vertex could be welded with the next point
						const CVector &nextVertPos = bPatch.Vertices[(vertIndex +  1) & 0x03];
						const CVector &prevVertPos = bPatch.Vertices[(vertIndex -  1) & 0x03];

						if (::CanWeld(nextVertPos, borderPos[k + 1], middleEdgeWeldThreshold)
							|| 	::CanWeld(prevVertPos, borderPos[k + 1], middleEdgeWeldThreshold)
						   )
						{
							CPatchIdent pi1(0, l);
							CPatchIdent pi2(pv.ZoneIndex, pv.PatchIndex);
							CPatchIdentPair errPair = std::make_pair(pi1, pi2);
							//
							if (std::find(errorPairs.begin(), errorPairs.end(), errPair) == errorPairs.end()) // error already displayed ?
							{									
								nlinfo("**** Patch %d of zone %s has 1 - 4 connectivity error, try binding it with patch %d of zone %s",
									   l + 1, middleZoneName.c_str(), pv.PatchIndex + 1, zoneNames[pv.ZoneIndex].c_str());										
								++numErrors;
								errorPairs.insert(std::make_pair(pi2, pi1));
							}
							errorFound = true;
						}
					}
				}											
			}
		}
	}
	////////////////////////////////
	////////////////////////////////
	if (numErrors != 0)
	{
		nlinfo("%d errors found", numErrors);
	}
	return numErrors;
}