 92c59963f8
			
		
	
	
		92c59963f8
		
	
	
	
	
		
			
			git-svn-id: http://mc-server.googlecode.com/svn/trunk@606 0a769ca7-a7f5-676a-18bf-c427514a06d6
		
			
				
	
	
		
			465 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			465 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| // StructGen.h
 | |
| 
 | |
| #include "Globals.h"
 | |
| #include "StructGen.h"
 | |
| #include "BlockID.h"
 | |
| #include "Trees.h"
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // cStructGenOreNests configuration:
 | |
| 
 | |
| const int MAX_HEIGHT_COAL = 127;
 | |
| const int NUM_NESTS_COAL = 60;
 | |
| const int NEST_SIZE_COAL = 10;
 | |
| 
 | |
| const int MAX_HEIGHT_IRON = 70;
 | |
| const int NUM_NESTS_IRON = 30;
 | |
| const int NEST_SIZE_IRON = 6;
 | |
| 
 | |
| const int MAX_HEIGHT_REDSTONE = 40;
 | |
| const int NUM_NESTS_REDSTONE = 10;
 | |
| const int NEST_SIZE_REDSTONE = 6;
 | |
| 
 | |
| const int MAX_HEIGHT_GOLD = 35;
 | |
| const int NUM_NESTS_GOLD = 6;
 | |
| const int NEST_SIZE_GOLD = 6;
 | |
| 
 | |
| const int MAX_HEIGHT_DIAMOND = 16;
 | |
| const int NUM_NESTS_DIAMOND = 6;
 | |
| const int NEST_SIZE_DIAMOND = 5;
 | |
| 
 | |
| const int MAX_HEIGHT_LAPIS = 30;
 | |
| const int NUM_NESTS_LAPIS = 6;
 | |
| const int NEST_SIZE_LAPIS = 5;
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| template <typename T> T Clamp(T a_Value, T a_Min, T a_Max)
 | |
| {
 | |
| 	return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // cStructGenTrees:
 | |
| 
 | |
| void cStructGenTrees::GenStructures(
 | |
| 	int a_ChunkX, int a_ChunkZ, 
 | |
| 	cChunkDef::BlockTypes & a_BlockTypes,    // Block types to read and change
 | |
| 	cChunkDef::BlockNibbles & a_BlockMetas,  // Block meta to read and change
 | |
| 	cChunkDef::HeightMap & a_HeightMap,      // Height map to read and change by the current data
 | |
| 	cEntityList & a_Entities,                // Entities may be added or deleted
 | |
| 	cBlockEntityList & a_BlockEntities       // Block entities may be added or deleted
 | |
| )
 | |
| {
 | |
| 	cChunkDef::BlockTypes   WorkerBlockTypes;
 | |
| 	cChunkDef::BlockNibbles WorkerBlockMeta;
 | |
| 	cChunkDef::HeightMap    WorkerHeight;
 | |
| 
 | |
| 	cEntityList Entities;
 | |
| 	cBlockEntityList BlockEntities;
 | |
| 	
 | |
| 	// Generate trees:
 | |
| 	for (int x = 0; x <= 2; x++)
 | |
| 	{
 | |
| 		int BaseX = a_ChunkX + x - 1;
 | |
| 		for (int z = 0; z <= 2; z++)
 | |
| 		{
 | |
| 			int BaseZ = a_ChunkZ + z - 1;
 | |
| 			sSetBlockVector Outside;
 | |
| 			
 | |
| 			cChunkDef::BlockTypes *   BlT;
 | |
| 			cChunkDef::BlockNibbles * BlM;
 | |
| 			cChunkDef::HeightMap *    Hei;
 | |
| 			
 | |
| 			cChunkDef::BiomeMap Biomes;
 | |
| 			m_BiomeGen->GenBiomes(BaseX, BaseZ, Biomes);
 | |
| 
 | |
| 			if ((x != 1) || (z != 1))
 | |
| 			{
 | |
| 				BlT = &WorkerBlockTypes;
 | |
| 				BlM = &WorkerBlockMeta;
 | |
| 				Hei = &WorkerHeight;
 | |
| 				
 | |
| 				m_HeightGen->GenHeightMap       (BaseX, BaseZ, *Hei);
 | |
| 				m_CompositionGen->ComposeTerrain(BaseX, BaseZ, *BlT, *BlM, *Hei, Biomes, Entities, BlockEntities);
 | |
| 				// TODO: Free the entity lists
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				BlT = &a_BlockTypes;
 | |
| 				BlM = &a_BlockMetas;
 | |
| 				Hei = &a_HeightMap;
 | |
| 			}
 | |
| 
 | |
| 			int NumTrees = GetNumTrees(BaseX, BaseZ, Biomes);
 | |
| 
 | |
| 			for (int i = 0; i < NumTrees; i++)
 | |
| 			{
 | |
| 				GenerateSingleTree(BaseX, BaseZ, i, *BlT, *BlM, *Hei, Biomes, Outside);
 | |
| 			}
 | |
| 
 | |
| 			// Integrate blocks in Outside into chunk:
 | |
| 			for (sSetBlockVector::const_iterator itr = Outside.begin(); itr != Outside.end(); ++itr)
 | |
| 			{
 | |
| 				if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ))
 | |
| 				{
 | |
| 					continue;
 | |
| 				}
 | |
| 				switch (cChunkDef::GetBlock(a_BlockTypes, itr->x, itr->y, itr->z))
 | |
| 				{
 | |
| 					CASE_TREE_OVERWRITTEN_BLOCKS:
 | |
| 					{
 | |
| 						cChunkDef::SetBlock (a_BlockTypes, itr->x, itr->y, itr->z, itr->BlockType);
 | |
| 						cChunkDef::SetNibble(a_BlockMetas, itr->x, itr->y, itr->z, itr->BlockMeta);
 | |
| 						break;
 | |
| 					}
 | |
| 				}  // switch (GetBlock())
 | |
| 			}  // for itr - Outside[]
 | |
| 		}  // for z
 | |
| 	}  // for x
 | |
| 	
 | |
| 	// Update the heightmap:
 | |
| 	for (int x = 0; x < cChunkDef::Width; x++)
 | |
| 	{
 | |
| 		for (int z = 0; z < cChunkDef::Width; z++)
 | |
| 		{
 | |
| 			for (int y = cChunkDef::Height - 1; y >= 0; y--)
 | |
| 			{
 | |
| 				if (cChunkDef::GetBlock(a_BlockTypes, x, y, z) != E_BLOCK_AIR)
 | |
| 				{
 | |
| 					cChunkDef::SetHeight(a_HeightMap, x, z, y);
 | |
| 					break;
 | |
| 				}
 | |
| 			}  // for y
 | |
| 		}  // for z
 | |
| 	}  // for x
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| void cStructGenTrees::GenerateSingleTree(
 | |
| 	int a_ChunkX, int a_ChunkZ, int a_Seq,
 | |
| 	cChunkDef::BlockTypes & a_BlockTypes,
 | |
| 	cChunkDef::BlockNibbles & a_BlockMetas,
 | |
| 	const cChunkDef::HeightMap & a_Height,
 | |
| 	const cChunkDef::BiomeMap & a_Biomes,
 | |
| 	sSetBlockVector & a_Blocks
 | |
| )
 | |
| {
 | |
| 	int x = m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) % cChunkDef::Width;
 | |
| 	int z = m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) % cChunkDef::Width;
 | |
| 	
 | |
| 	int Height = a_Height[x + cChunkDef::Width * z];
 | |
| 	
 | |
| 	if ((Height <= 0) || (Height > 240))
 | |
| 	{
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	// Check the block underneath the tree:
 | |
| 	BLOCKTYPE TopBlock = cChunkDef::GetBlock(a_BlockTypes, x, Height, z);
 | |
| 	if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_SOIL))
 | |
| 	{
 | |
| 		return;
 | |
| 	}
 | |
| 	
 | |
| 	sSetBlockVector TreeBlocks;
 | |
| 	GetTreeImageByBiome(a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z, m_Noise, a_Seq, a_Biomes[x + cChunkDef::Width * z], TreeBlocks);
 | |
| 
 | |
| 	// Check if the generated image fits the terrain:
 | |
| 	for (sSetBlockVector::const_iterator itr = TreeBlocks.begin(); itr != TreeBlocks.end(); ++itr)
 | |
| 	{
 | |
| 		if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ) || (itr->BlockType != E_BLOCK_LOG))
 | |
| 		{
 | |
| 			// Outside the chunk, or not a log (we don't check non-logs)
 | |
| 			continue;
 | |
| 		}
 | |
| 
 | |
| 		BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, itr->x, itr->y, itr->z);
 | |
| 		switch (Block)
 | |
| 		{
 | |
| 			CASE_TREE_ALLOWED_BLOCKS:
 | |
| 			{
 | |
| 				break;
 | |
| 			}
 | |
| 			default:
 | |
| 			{
 | |
| 				// There's something in the way, abort this tree altogether
 | |
| 				return;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
 | |
| 	for (sSetBlockVector::const_iterator itr = TreeBlocks.begin(); itr != TreeBlocks.end(); ++itr)
 | |
| 	{
 | |
| 		if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ))
 | |
| 		{
 | |
| 			// Inside this chunk, integrate into a_BlockTypes:
 | |
| 			switch (cChunkDef::GetBlock(a_BlockTypes, itr->x, itr->y, itr->z))
 | |
| 			{
 | |
| 				CASE_TREE_OVERWRITTEN_BLOCKS:
 | |
| 				{
 | |
| 					cChunkDef::SetBlock (a_BlockTypes, itr->x, itr->y, itr->z, itr->BlockType);
 | |
| 					cChunkDef::SetNibble(a_BlockMetas, itr->x, itr->y, itr->z, itr->BlockMeta);
 | |
| 					break;
 | |
| 				}
 | |
| 			}  // switch (GetBlock())
 | |
| 			continue;
 | |
| 		}
 | |
| 		
 | |
| 		// Outside the chunk, push into a_Blocks; check if already present there:
 | |
| 		bool Found = false;
 | |
| 		for (sSetBlockVector::iterator itrB = a_Blocks.begin(); itrB != a_Blocks.end(); ++itrB)
 | |
| 		{
 | |
| 			if (
 | |
| 				(itr->ChunkX == itrB->ChunkX) &&
 | |
| 				(itr->ChunkZ == itrB->ChunkZ) &&
 | |
| 				(itr->x == itrB->x) &&
 | |
| 				(itr->y == itrB->y) &&
 | |
| 				(itr->z == itrB->z)
 | |
| 			)
 | |
| 			{
 | |
| 				// Same coords already found in a_Blocks, overwrite with wood, if requested:
 | |
| 				if (itr->BlockType == E_BLOCK_LOG)
 | |
| 				{
 | |
| 					itrB->BlockType = itr->BlockType;
 | |
| 					itrB->BlockMeta = itr->BlockMeta;
 | |
| 				}
 | |
| 				Found = true;
 | |
| 				break;
 | |
| 			}
 | |
| 		}  // for itrB - a_Blocks[]
 | |
| 		if (!Found)
 | |
| 		{
 | |
| 			a_Blocks.push_back(*itr);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| int cStructGenTrees::GetNumTrees(
 | |
| 	int a_ChunkX, int a_ChunkZ,
 | |
| 	const cChunkDef::BiomeMap & a_Biomes
 | |
| )
 | |
| {
 | |
| 	int NumTrees = 0;
 | |
| 	for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
 | |
| 	{
 | |
| 		int Add = 0;
 | |
| 		switch (a_Biomes[x + cChunkDef::Width * z])
 | |
| 		{
 | |
| 			case biPlains:           Add = 1;  break;
 | |
| 			case biExtremeHills:     Add = 3;  break;
 | |
| 			case biForest:           Add = 30; break;
 | |
| 			case biTaiga:            Add = 30; break;
 | |
| 			case biSwampland:        Add = 8;  break;
 | |
| 			case biIcePlains:        Add = 1;  break;
 | |
| 			case biIceMountains:     Add = 1;  break;
 | |
| 			case biMushroomIsland:   Add = 3;  break;
 | |
| 			case biMushroomShore:    Add = 3;  break;
 | |
| 			case biForestHills:      Add = 20; break;
 | |
| 			case biTaigaHills:       Add = 20; break;
 | |
| 			case biExtremeHillsEdge: Add = 5;  break;
 | |
| 			case biJungle:           Add = 120; break;
 | |
| 			case biJungleHills:      Add = 90; break;
 | |
| 		}
 | |
| 		NumTrees += Add;
 | |
| 	}
 | |
| 	return NumTrees / 1024;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // cStructGenMarbleCaves:
 | |
| 
 | |
| static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
 | |
| {
 | |
| 	static const float PI_2 = 1.57079633f;
 | |
| 	float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4;
 | |
| 
 | |
| 	oct1 = oct1 * oct1 * oct1;
 | |
| 	if (oct1 < 0.f)  oct1 = PI_2;
 | |
| 	if (oct1 > PI_2) oct1 = PI_2;
 | |
| 
 | |
| 	return oct1;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| void cStructGenMarbleCaves::GenStructures(
 | |
| 	int a_ChunkX, int a_ChunkZ, 
 | |
| 	cChunkDef::BlockTypes & a_BlockTypes,   // Block types to read and change
 | |
| 	cChunkDef::BlockNibbles & a_BlockMeta,  // Block meta to read and change
 | |
| 	cChunkDef::HeightMap & a_HeightMap,     // Height map to read and change by the current data
 | |
| 	cEntityList & a_Entities,               // Entities may be added or deleted
 | |
| 	cBlockEntityList & a_BlockEntities      // Block entities may be added or deleted
 | |
| )
 | |
| {
 | |
| 	cNoise Noise(m_Seed);
 | |
| 	for (int z = 0; z < cChunkDef::Width; z++) 
 | |
| 	{
 | |
| 		const float zz = (float)(a_ChunkZ * cChunkDef::Width + z);
 | |
| 		for (int x = 0; x < cChunkDef::Width; x++)
 | |
| 		{
 | |
| 			const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
 | |
| 			
 | |
| 			int Top = cChunkDef::GetHeight(a_HeightMap, x, z);
 | |
| 			for (int y = 1; y < Top; ++y )
 | |
| 			{
 | |
| 				if (cChunkDef::GetBlock(a_BlockTypes, x, y, z) != E_BLOCK_STONE)
 | |
| 				{
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				const float yy = (float)y;
 | |
| 				const float WaveNoise = 1;
 | |
| 				if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
 | |
| 				{
 | |
| 					if (y > 4)
 | |
| 					{
 | |
| 						cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_STATIONARY_LAVA);
 | |
| 					}
 | |
| 				}
 | |
| 			}  // for y
 | |
| 		}  // for x
 | |
| 	}  // for z
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | |
| // cStructGenOreNests:
 | |
| 
 | |
| void cStructGenOreNests::GenStructures(
 | |
| 	int a_ChunkX, int a_ChunkZ,
 | |
| 	cChunkDef::BlockTypes & a_BlockTypes,   // Block types to read and change
 | |
| 	cChunkDef::BlockNibbles & a_BlockMeta,  // Block meta to read and change
 | |
| 	cChunkDef::HeightMap & a_HeightMap,     // Height map to read and change by the current data
 | |
| 	cEntityList & a_Entities,               // Entities may be added or deleted
 | |
| 	cBlockEntityList & a_BlockEntities      // Block entities may be added or deleted
 | |
| )
 | |
| {
 | |
| 	GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_COAL_ORE,     MAX_HEIGHT_COAL,     NUM_NESTS_COAL,     NEST_SIZE_COAL,     a_BlockTypes, 1);
 | |
| 	GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_IRON_ORE,     MAX_HEIGHT_IRON,     NUM_NESTS_IRON,     NEST_SIZE_IRON,     a_BlockTypes, 2);
 | |
| 	GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, a_BlockTypes, 3);
 | |
| 	GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_GOLD_ORE,     MAX_HEIGHT_GOLD,     NUM_NESTS_GOLD,     NEST_SIZE_GOLD,     a_BlockTypes, 4);
 | |
| 	GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_DIAMOND_ORE,  MAX_HEIGHT_DIAMOND,  NUM_NESTS_DIAMOND,  NEST_SIZE_DIAMOND,  a_BlockTypes, 5);
 | |
| 	GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_LAPIS_ORE,    MAX_HEIGHT_LAPIS,    NUM_NESTS_LAPIS,    NEST_SIZE_LAPIS,    a_BlockTypes, 6);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq)
 | |
| {
 | |
| 	// This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
 | |
| 	// It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
 | |
| 	// Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).
 | |
| 	
 | |
| 	for (int i = 0; i < a_NumNests; i++)
 | |
| 	{
 | |
| 		int rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8;
 | |
| 		int BaseX = rnd % cChunkDef::Width;
 | |
| 		rnd /= cChunkDef::Width;
 | |
| 		int BaseZ = rnd % cChunkDef::Width;
 | |
| 		rnd /= cChunkDef::Width;
 | |
| 		int BaseY = rnd % a_MaxHeight;
 | |
| 		rnd /= a_MaxHeight;
 | |
| 		int NestSize = a_NestSize + (rnd % (a_NestSize / 4));  // The actual nest size may be up to 1/4 larger
 | |
| 		int Num = 0;
 | |
| 		while (Num < NestSize)
 | |
| 		{
 | |
| 			// Put a cuboid around [BaseX, BaseY, BaseZ]
 | |
| 			int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8;
 | |
| 			int xsize = rnd % 2;
 | |
| 			int ysize = (rnd / 4) % 2;
 | |
| 			int zsize = (rnd / 16) % 2;
 | |
| 			rnd >>= 8;
 | |
| 			for (int x = xsize; x >= 0; --x)
 | |
| 			{
 | |
| 				int BlockX = BaseX + x;
 | |
| 				if ((BlockX < 0) || (BlockX >= cChunkDef::Width))
 | |
| 				{
 | |
| 					Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
 | |
| 					continue;
 | |
| 				}
 | |
| 				for (int y = ysize; y >= 0; --y)
 | |
| 				{
 | |
| 					int BlockY = BaseY + y;
 | |
| 					if ((BlockY < 0) || (BlockY >= cChunkDef::Height))
 | |
| 					{
 | |
| 						Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
 | |
| 						continue;
 | |
| 					}
 | |
| 					for (int z = zsize; z >= 0; --z)
 | |
| 					{
 | |
| 						int BlockZ = BaseZ + z;
 | |
| 						if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width))
 | |
| 						{
 | |
| 							Num++;  // So that the cycle finishes even if the base coords wander away from the chunk
 | |
| 							continue;
 | |
| 						}
 | |
| 
 | |
| 						int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ);
 | |
| 						if (a_BlockTypes[Index] == E_BLOCK_STONE)
 | |
| 						{
 | |
| 							a_BlockTypes[Index] = a_OreType;
 | |
| 						}
 | |
| 						Num++;
 | |
| 					}  // for z
 | |
| 				}  // for y
 | |
| 			}  // for x
 | |
| 			
 | |
| 			// Move the base to a neighbor voxel
 | |
| 			switch (rnd % 4)
 | |
| 			{
 | |
| 				case 0: BaseX--; break;
 | |
| 				case 1: BaseX++; break;
 | |
| 			}
 | |
| 			switch ((rnd >> 3) % 4)
 | |
| 			{
 | |
| 				case 0: BaseY--; break;
 | |
| 				case 1: BaseY++; break;
 | |
| 			}
 | |
| 			switch ((rnd >> 6) % 4)
 | |
| 			{
 | |
| 				case 0: BaseZ--; break;
 | |
| 				case 1: BaseZ++; break;
 | |
| 			}
 | |
| 		}  // while (Num < NumBlocks)
 | |
| 	}  // for i - NumNests
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 |