private static Contact? GetClosestContact(OpenCog.Map.OCMap map, CharacterController controller) {
		Vector3 pos = controller.transform.position;
		int x1 = Mathf.FloorToInt(pos.x-controller.radius);
		int y1 = Mathf.FloorToInt(pos.y);
		int z1 = Mathf.FloorToInt(pos.z-controller.radius);
		
		int x2 = Mathf.CeilToInt(pos.x+controller.radius);
		int y2 = Mathf.CeilToInt(pos.y+controller.height);
		int z2 = Mathf.CeilToInt(pos.z+controller.radius);
		
		Contact? contact = null;
		for(int x=x1; x<=x2; x++) {
			for(int y=y1; y<=y2; y++) {
				for(int z=z1; z<=z2; z++) {
					OpenCog.Map.OCBlockData block = map.GetBlock(x, y, z);
					if(block.IsSolid()) {
						Contact? _newContact = BlockCharacterCollision.GetContactBlockCharacter(new Vector3i(x, y, z), pos, controller);
						if(_newContact.HasValue && _newContact.Value.delta.magnitude > float.Epsilon) {
							Contact newContact = _newContact.Value;
							if(!contact.HasValue || newContact.sqrDistance > contact.Value.sqrDistance) {
								contact = newContact;
							}
						}
					}
				}
			}
		}

		return contact;
//		return null;
	}
	public static UnityEngine.Color GetSmoothVertexLight(OpenCog.Map.OCMap map, Vector3i pos, UnityEngine.Vector3 vertex, OpenCog.BlockSet.BaseBlockSet.OCCubeBlock.CubeFace face) {
		// pos - позиция блока 
		// vertex позиция вершины относительно блока т.е. от -0.5 до 0.5
		int dx = (int)Mathf.Sign( vertex.x );
		int dy = (int)Mathf.Sign( vertex.y );
		int dz = (int)Mathf.Sign( vertex.z );
		
		Vector3i a, b, c, d;
		if(face == OCCubeBlock.CubeFace.Left || face == OCCubeBlock.CubeFace.Right) { // X
			a = pos + new Vector3i(dx, 0,  0);
			b = pos + new Vector3i(dx, dy, 0);
			c = pos + new Vector3i(dx, 0,  dz);
			d = pos + new Vector3i(dx, dy, dz);
		} else 
		if(face == OCCubeBlock.CubeFace.Bottom || face == OCCubeBlock.CubeFace.Top) { // Y
			a = pos + new Vector3i(0,  dy, 0);
			b = pos + new Vector3i(dx, dy, 0);
			c = pos + new Vector3i(0,  dy, dz);
			d = pos + new Vector3i(dx, dy, dz);
		} else { // Z
			a = pos + new Vector3i(0,  0,  dz);
			b = pos + new Vector3i(dx, 0,  dz);
			c = pos + new Vector3i(0,  dy, dz);
			d = pos + new Vector3i(dx, dy, dz);
		}
		
		if(map.GetBlock(b).IsAlpha() || map.GetBlock(c).IsAlpha()) {
			Color c1 = GetBlockLight(map, a);
			Color c2 = GetBlockLight(map, b);
			Color c3 = GetBlockLight(map, c);
			Color c4 = GetBlockLight(map, d);
			return (c1 + c2 + c3 + c4)/4f;
		} else {
			Color c1 = GetBlockLight(map, a);
			Color c2 = GetBlockLight(map, b);
			Color c3 = GetBlockLight(map, c);
			return (c1 + c2 + c3)/3f;
		}
	}
	//---------------------------------------------------------------------------

	#region Private Member Data

	//---------------------------------------------------------------------------
	

			
	//---------------------------------------------------------------------------

	#endregion

	//---------------------------------------------------------------------------

	#region Accessors and Mutators

	//---------------------------------------------------------------------------
		

			
	//---------------------------------------------------------------------------

	#endregion

	//---------------------------------------------------------------------------

	#region Public Member Functions

	//---------------------------------------------------------------------------

	public static void Build(Vector3i localPos, Vector3i worldPos, OpenCog.Map.OCMap map, OpenCog.Builder.OCMeshBuilder mesh, bool onlyLight)
	{
		OCFluidBlock fluid = (OCFluidBlock)map.GetBlock(worldPos).block;
		
		for(int i=0; i<6; i++)
		{
			OCCubeBlock.CubeFace face = OpenCog.Builder.OCCubeBuilder.faces[i];
			Vector3i dir = OpenCog.Builder.OCCubeBuilder.directions[i];
			Vector3i nearPos = worldPos + dir;
			if(IsFaceVisible(map, nearPos, face))
			{
				if(!onlyLight)
				{
					BuildFace(face, fluid, (UnityEngine.Vector3)localPos, mesh);
				}
				BuildFaceLight(face, map, worldPos, mesh);
			}
		}
	}
	public static Vector3? Intersection(OpenCog.Map.OCMap map, Ray ray, float distance) {
		Vector3 start = ray.origin;
		Vector3 end = ray.origin+ray.direction*distance;
		int startX = Mathf.RoundToInt(start.x);
		int startY = Mathf.RoundToInt(start.y);
		int startZ = Mathf.RoundToInt(start.z);
		int endX = Mathf.RoundToInt(end.x);
		int endY = Mathf.RoundToInt(end.y);
		int endZ = Mathf.RoundToInt(end.z);
		
		if(startX>endX) {
			int tmp = startX;
			startX = endX;
			endX = tmp;
		}
		
		if(startY>endY) {
			int tmp = startY;
			startY = endY;
			endY = tmp;
		}
		
		if(startZ>endZ) {
			int tmp = startZ;
			startZ = endZ;
			endZ = tmp;
		}
		
		float minDistance = distance;
		for(int z=startZ; z<=endZ; z++) {
			for(int y=startY; y<=endY; y++) {
				for(int x=startX; x<=endX; x++) {
					OpenCog.Map.OCBlockData block = map.GetBlock(x, y, z);
					if(block == null || block.IsEmpty() || block.IsFluid()) continue;
					float dis = BlockRayIntersection(new Vector3(x, y, z), ray);
					minDistance = Mathf.Min(minDistance, dis);
				}
			}
		}
		
		if(minDistance != distance) return ray.origin + ray.direction * minDistance;
		return null;
	}
	private static OpenCog.BlockSet.BaseBlockSet.OCBlock DrawInventory(OpenCog.BlockSet.OCBlockSet blockSet, ref UnityEngine.Vector2 scrollPosition, OpenCog.BlockSet.BaseBlockSet.OCBlock selected) {
		scrollPosition = GUILayout.BeginScrollView(scrollPosition);
		for(int i=0, y=0; i<blockSet.BlockCount; y++) {
			GUILayout.BeginHorizontal();
			for(int x=0; x<8; x++, i++) {
				OpenCog.BlockSet.BaseBlockSet.OCBlock block = blockSet.GetBlock(i);
				if( DrawBlock(block, block == selected && selected != null) ) {
					selected = block;
				}
			}
			GUILayout.EndHorizontal();
		}
		GUILayout.EndScrollView();
		return selected;
	}
	//---------------------------------------------------------------------------

	#endregion

	//---------------------------------------------------------------------------

	#region Private Member Functions

	//---------------------------------------------------------------------------
	
	private static bool IsFaceVisible(OpenCog.Map.OCMap map, Vector3i nearPos, OpenCog.BlockSet.BaseBlockSet.OCCubeBlock.CubeFace face)
	{
		if(face == OCCubeBlock.CubeFace.Top)
		{
			OpenCog.Map.OCBlockData block = map.GetBlock(nearPos);
			return block.IsEmpty() || !block.IsFluid();
		}
		else
		{
			return map.GetBlock(nearPos).IsEmpty();
		}
	}
	//---------------------------------------------------------------------------

	#endregion

	//---------------------------------------------------------------------------

	#region Private Member Functions

	//---------------------------------------------------------------------------
	
	private static UnityEngine.Color ComputeSmoothLight(OpenCog.Map.OCMap map, Vector3i a, Vector3i b, Vector3i c, Vector3i d) {
		if(map.GetBlock(b).IsAlpha() || map.GetBlock(c).IsAlpha()) {
			UnityEngine.Color c1 = GetBlockLight(map, a);
			UnityEngine.Color c2 = GetBlockLight(map, b);
			UnityEngine.Color c3 = GetBlockLight(map, c);
			UnityEngine.Color c4 = GetBlockLight(map, d);
			return (c1 + c2 + c3 + c4)/4f;
		} else {
			UnityEngine.Color c1 = GetBlockLight(map, a);
			UnityEngine.Color c2 = GetBlockLight(map, b);
			UnityEngine.Color c3 = GetBlockLight(map, c);
			return (c1 + c2 + c3)/3f;
		}
	}
	private static bool IsFaceVisible(OpenCog.Map.OCMap map, OpenCog.BlockSet.BaseBlockSet.OCCubeBlock.CubeFace face, Vector3i nearPos) {
		if(face == OpenCog.BlockSet.BaseBlockSet.OCCubeBlock.CubeFace.Bottom || face == OpenCog.BlockSet.BaseBlockSet.OCCubeBlock.CubeFace.Top) {
			OCBlock block = map.GetBlock(nearPos).block;
			if(block is OCCubeBlock && !block.IsAlpha()) return false;
			if(block is OCCactusBlock) return false;
		}
		return true;
	}
	private static void BuildCross(Vector3 localPos, Vector3i worldPos, OpenCog.Map.OCMap map, OpenCog.Builder.OCMeshBuilder mesh)
	{
		OCCrossBlock cross = (OCCrossBlock)map.GetBlock(worldPos).block;
		
		mesh.AddIndices(cross.AtlasID, indices);
		mesh.AddVertices(vertices, localPos);
		mesh.AddNormals(normals);
		mesh.AddTexCoords(cross.GetFaceUV());
		mesh.AddTexCoords(cross.GetFaceUV());
		mesh.AddTexCoords(cross.GetFaceUV());
		mesh.AddTexCoords(cross.GetFaceUV());
	}
	//---------------------------------------------------------------------------

	#endregion

	//---------------------------------------------------------------------------

	#region Private Member Functions

	//---------------------------------------------------------------------------
	
	private static bool IsFaceVisible(OpenCog.Map.OCMap map, Vector3i nearPos)
	{
		OCBlockData blockData = map.GetBlock(nearPos);
		if(blockData == null) return false;
		OCBlock block = blockData.block;
		return !(block is OCCubeBlock) || block.IsAlpha();
	}
	public static void Build(Vector3i localPos, Vector3i worldPos, OpenCog.Map.OCMap map, OpenCog.Builder.OCMeshBuilder mesh, bool onlyLight)
	{
		OpenCog.Map.OCBlockData block = map.GetBlock(worldPos);
		OCCubeBlock cube = (OCCubeBlock)block.block;
		OpenCog.Map.OCBlockDirection direction = block.GetDirection();
		
		for(int i=0; i<6; i++)
		{
			OCCubeBlock.CubeFace face = faces[i];
			Vector3i dir = directions[i];
			Vector3i nearPos = worldPos + dir;
			if(IsFaceVisible(map, nearPos))
			{
				if(!onlyLight)
				{
					BuildFace(face, cube, direction, localPos, mesh);
				}
				BuildFaceLight(face, map, worldPos, mesh);
			}
		}
	}
	//---------------------------------------------------------------------------

	#endregion

	//---------------------------------------------------------------------------

	#region Private Member Functions

	//---------------------------------------------------------------------------
	
	private static void Build(OpenCog.Map.OCChunk chunk, bool onlyLight) {
		OpenCog.Map.OCMap map = chunk.GetMap();
		_meshData.Clear();
		for(int z=0; z<OpenCog.Map.OCChunk.SIZE_Z; z++) {
			for(int y=0; y<OpenCog.Map.OCChunk.SIZE_Y; y++) {
				for(int x=0; x<OpenCog.Map.OCChunk.SIZE_X; x++) {
					OCBlockData blockData = chunk.GetBlock(x, y, z);
					if(blockData != null)
					{
						OCBlock block = blockData.block;
						if(block != null) 
						{
							Vector3i localPos = new Vector3i(x, y, z);
							Vector3i worldPos = OpenCog.Map.OCChunk.ToWorldPosition(chunk.GetPosition(), localPos);
							if(worldPos.y > 0)
								block.Build(localPos, worldPos, map, _meshData, onlyLight);
						}
					}
				}
			}
		}
	}