您的位置:首页 > 编程语言

u3d honey hex framework 代码解读记录(四)

2016-07-14 23:12 330 查看
// 接着上次baking函数中的循环开始

// 生成阴影贴图,源和高度贴图源是一样的,目标贴图小了很多,边长是2的4次方分之1
//Render shadow and light to scaled down texture and copy it to Texture2D
int scaleDownPower = 4; //scaling it down 4 powers will resize e.g.: from 2048 to 128 making it marginally small
RenderTexture shadowTarget = RenderTargetManager.GetNewTexture(Chunk.TextureSize >> scaleDownPower, Chunk.TextureSize >> scaleDownPower);
Graphics.Blit(shadowsAndHeightRT, shadowTarget);
RenderTexture.active = shadowTarget;
texture = new Texture2D(Chunk.TextureSize >> scaleDownPower, Chunk.TextureSize >> scaleDownPower, TextureFormat.RGB24, false);
texture.wrapMode = TextureWrapMode.Clamp;
texture.ReadPixels(new Rect(0, 0, Chunk.TextureSize >> scaleDownPower, Chunk.TextureSize >> scaleDownPower), 0, 0);
texture.Apply();

//  SaveImage.SaveJPG(texture, currentChunk.position.ToString() + "h", randomIndex.ToString());

texture.Compress(false); //rgb24 will compress to 4 bits per pixel.
texture.Apply();

//if this is chunk refresh we need to destroy old texture soon
// 后面为chunk生成mesh的时候会将无用的texture释放掉
if (currentChunk.shadows != null) currentChunk.texturesForCleanup.Add(currentChunk.shadows);
// chunk的阴影贴图就有了
currentChunk.shadows = texture;
RenderTexture.active = null;
shadowTarget.Release();

if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

// Copy Diffuse to Texture2D
// 将之前生成的diffuse render texture转换成texture
RenderTexture.active = diffuseRT;
texture = new Texture2D(Chunk.TextureSize, Chunk.TextureSize, TextureFormat.RGB24, false);
texture.wrapMode = TextureWrapMode.Clamp;
texture.ReadPixels(new Rect(0, 0, Chunk.TextureSize, Chunk.TextureSize), 0, 0);

texture.name = "Diffuse" + currentChunk.position;
//if this is chunk refresh we need to destroy old texture soon
if (currentChunk.diffuse != null) currentChunk.texturesForCleanup.Add(currentChunk.diffuse);
// chunk的diffuse贴图就有了
currentChunk.diffuse = texture;
//   SaveImage.SaveJPG(texture, currentChunk.position.ToString() + "d", randomIndex.ToString());
// 压缩diffuse贴图
currentChunk.CompressDiffuse();

RenderTexture.active = null;

if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

bakingCamera.targetTexture = null;
// 目前chunk已经有了高度贴图,阴影贴图和diffuse贴图,用这些贴图就可以生成一个mesh了
currentChunk.CreateGameObjectWithTextures();
// 从dirtychunks中移除当前chunk,准备处理下一个chunk
dirtyChunks.RemoveAt(0);

// 将当前chunk添加到world对象的chunksToPolish列表中,准备后续处理
World.GetInstance().ReadyToPolishChunk(currentChunk);
RenderTargetManager.ReleaseAll();

if (CoroutineHelper.CheckIfPassed(30)) { yield return null; CoroutineHelper.StartTimer(); }

// baking 函数中的循环结束 --------------------------------------------------------------------

/// <summary>
/// Produces chunk assets, and ensures their height is continues with neighbours
/// </summary>
/// <returns></returns>
// chunk 类中的函数,用来生成chunk的GameObject
public void CreateGameObjectWithTextures()
{
// 对齐相邻chunk的高度
WeldChunk();

// DX11模式,由于我的电脑是mac,所以不能用
if (MHGameSettings.GetDx11Mode())
{
if (chunkObject == null)
{
if (MHGameSettings.GetMarkersMode())
{
chunkObject = GameObject.Instantiate(worldOwner.chunkBaseDx11WithMarkers) as GameObject;
}
else
{
chunkObject = GameObject.Instantiate(worldOwner.chunkBaseDx11) as GameObject;
}
}
}
else
{
if (chunkObject == null)
{
// 判断是否使用marker模式,marker和非marker使用的材质和shader不一样
// marker模式下地图生成后,地图上点击鼠标,可以绘制路径,具体用途现在还不是很明白
if (MHGameSettings.GetMarkersMode())
{
chunkObject = GameObject.Instantiate(worldOwner.chunkBaseWithMarkers) as GameObject;
}
else
{
chunkObject = GameObject.Instantiate(worldOwner.chunkBase) as GameObject;
}
}

MeshFilter m = chunkObject.GetComponent<MeshFilter>();
if (m.sharedMesh != null)  { GameObject.Destroy(m.sharedMesh); }
// 产生地形的mesh
m.sharedMesh = ProduceTerrainMesh(this);
}

// 设置当前chunk的GameObject的位置
chunkObject.transform.parent = worldOwner.transform;
chunkObject.name = "Chunk" + position;

Vector2 center = GetRect().center;
// 使用y作为高度了
chunkObject.transform.localPosition = new Vector3(center.x, 0, center.y);
float scale = ChunkSizeInWorld / 10.0f;
chunkObject.transform.localScale = new Vector3(scale, scale, scale);

// 为mesh设置材质和贴图
MeshRenderer mr = chunkObject.GetComponent<MeshRenderer>();
mr.material.SetTexture("_MainTex", diffuse);
mr.material.SetTexture("_HeightTex", height);

chunkMaterial = mr.material;

// 如果是marker模式,给chunkMaterial添加marker的几个属性
SetMarkerMaterials();

MeshFilter mf = chunkObject.GetComponent<MeshFilter>();
Bounds b = mf.mesh.bounds;

if (MHGameSettings.GetDx11Mode() && b.size.y < 1.0f)
{
//extending bounds so that chunk is loaded before it enters screen, flat (plane) bounding box is not enough for our needs, and our world works as plane just before rendering
b.Expand(new Vector3(0, 2, 0));
mf.mesh.bounds = b;
}

ChunkBehaviour cb = chunkObject.GetComponent<ChunkBehaviour>();
if (cb == null)
{
cb = chunkObject.AddComponent<ChunkBehaviour>();
}
cb.owner = this;

// 清除掉不用的texture资源
if (texturesForCleanup.Count > 0)
{
foreach (Texture2D t in texturesForCleanup)
{
GameObject.Destroy(t);
}
texturesForCleanup.Clear();
}
}

/// <summary>
/// this function will build mesh used by chunks when not using dx11 tessellation.
/// </summary>
/// <returns></returns>
static public Mesh ProduceTerrainMesh(Chunk source)
{
// meshDensity是顶点密度
// 举个简单点的例子,Chunk.ChunkSizeInWorld为10,如果此处meshDensity为3,
// 表示边被切割成3段,则一共需要16个顶点:
// *---*---*---*
// |   |   |   |  点之间的距离是3.3
// *---*---*---*
// |   |   |   |
// *---*---*---*
// |   |   |   |
// *---*---*---*
// 此处meshDensity=75,也就是说需要 76 * 76 个顶点
int meshDensity = 75;
MeshPreparationData data = new MeshPreparationData();

float quadScale = Chunk.ChunkSizeInWorld / meshDensity;
// 设置偏移量,用于将最后生成的坐标范围(x:0~10,z:0~10)转换到(x:-5~+5,z:-5~+5)
Vector3 offset = new Vector3(-Chunk.ChunkSizeInWorld / 2f, 0, -Chunk.ChunkSizeInWorld / 2f);

// 边上的顶点个数
int vertexRowCount = meshDensity +1;

//build vertex map
// 下面依次生成每个顶点的坐标和对应的贴图的uv值
for (int y = 0; y < vertexRowCount; y++)
{
for (int x = 0; x < vertexRowCount; x++)
{
float u = 1 - x / (float)meshDensity;
float v = 1 - y / (float)meshDensity;
// 高度贴图里面的alpha8的值代表高度,范围是0~1,这里转换为-0.5~0.5,再扩大1.6倍
float h =(source.height.GetPixelBilinear(u, v).a - 0.5f) * 1.6f;

data.vertexList.Add(new Vector3(x * quadScale, h, y * quadScale) + offset);

data.uvList.Add(new Vector2(u, v));
}
}

//build normal map
// 生成法线信息
for (int y = 0; y < vertexRowCount; y++)
{
for (int x = 0; x < vertexRowCount; x++)
{
//we take value of the vertex height in center and 4 neighbour vertices clamped to mesh size
// 取出当前顶点和上下左右四个顶点的索引值
int center = y * vertexRowCount + x;
int top = y > 0 ? (y - 1) * vertexRowCount + x : center;
int bottom = y < meshDensity ? (y + 1) * vertexRowCount + x : center;
int left = x > 0 ? y * vertexRowCount + x - 1 : center;
int right     = x < meshDensity   ? y * vertexRowCount + x +1     : center;

//now define normal direction based on (top vs bottom) and (left vs right)
// 根据周围四个点的高度决定法线向量的值
Vector3 normal = Vector3.up * 0.1f
+ Vector3.left * (data.vertexList[left].y - data.vertexList[right].y)
+ Vector3.forward * (data.vertexList[top].y - data.vertexList[bottom].y);
normal.Normalize();

data.normalsList.Add(normal);
}
}

//index vertices to build triangles.
//Note that we do not iterate here over extended size (we use meshDensity instead of vertexRowCount,
//to ensure we have one vertex more on right size to take for the last quads)
// 使用顶点来构成三角形,他的注释也比较清楚了,如下图就由两个三角形构成(v1,v3,v4)和(v1,v4,v2)。保证都是逆时针方向。
for (int y = 0; y < meshDensity; y++)
{
for (int x = 0; x < meshDensity; x++)
{
int row1 = y * vertexRowCount + x;
int row2 = (y + 1) * vertexRowCount + x;

// v1      v2
// X--------X
// |        |
// |        |
// |        |
// X--------X
// v3      v4

int v1 = row1;
int v2 = row1 + 1;
int v3 = row2;
int v4 = row2 + 1;

data.indexList.Add(v1);
data.indexList.Add(v3);
data.indexList.Add(v4);

data.indexList.Add(v1);
data.indexList.Add(v4);
data.indexList.Add(v2);

}
}

// 返回生成好的mesh
Mesh m = new Mesh();
m.name = "ChunkMesh_" + meshDensity + "x" + meshDensity;
m.vertices = data.vertexList.ToArray();
m.uv = data.uvList.ToArray();
m.triangles = data.indexList.ToArray();
m.normals = data.normalsList.ToArray();

return m;
}

// 在baking函数中的循环结束之后的最后一段代码:
// 此时所有的chunk都已经处理完成,加入到了world对象的chunksToPolish列表中
if (World.GetInstance().StartPolishingWorld())
{

MeshRenderer[] rArray = GetComponentsInChildren<MeshRenderer>(true) as MeshRenderer[];
foreach (MeshRenderer r in rArray)
{
if (r.material != null)
{
GameObject.Destroy(r.material);
}
}

RenderTargetManager.DestroyAllUnusedTextures();
Cleanup();
DestroyObject(gameObject);
instance = null;
}

public bool StartPolishingWorld()
{
if (chunks.Count > 0)
{
// 新的协程调用函数FinishingWorld
StartCoroutine("FinishingWorld");
return true;
}
return false;
}

/// <summary>
/// Coroutine which ensures height compression check (which knows itself if is required or not)
/// It as well do processing foreground data and generation of the mesh for it
/// </summary>
/// <returns></returns>
IEnumerator FinishingWorld()
{
status = Status.Finishing;

//we will try to compress chunks. They will ignore this command if they are already compressed

CoroutineHelper.StartTimer();
foreach (Chunk c in chunksToPolish)
{
// 这个函数实际上没有做任何事,代码都被注释掉了
c.CompressHeight();
if (CoroutineHelper.CheckIfPassed(20)) { yield return null; CoroutineHelper.StartTimer(); }
}

//now start preparation for trees
status = Status.Foreground;

foreach (Hex h in hexesToPolish )
{
// 为每一个hex生成树的数据
h.GenerateForegroundData();
if (CoroutineHelper.CheckIfPassed(20)) { yield return null; CoroutineHelper.StartTimer(); }
}
hexesToPolish.Clear();

foreach (Chunk c in chunksToPolish)
{
c.CleanupForeground(false);
// 渲染树,下次再解读
c.GetForegroundData();
if (CoroutineHelper.CheckIfPassed(20)) { yield return null; CoroutineHelper.StartTimer(); }
}
chunksToPolish.Clear();

status = Status.Ready;
}

/// <summary>
/// Generates foreground data of this single hex, later chunks will take control of foreground which have covered them but production occurs here.
/// </summary>
/// <returns></returns>
public void GenerateForegroundData()
{
// 清空树的信息
foregroundData.Clear();

// 获取到当前hex的位置
Vector2 flatHexPosition = GetWorldPosition();
Vector3 hexPosition = new Vector3(flatHexPosition.x, 0.0f, flatHexPosition.y);

// fgTypes就是当前地形类型上树的定义
foreach (MHSimpleCounter type in terrainType.source.fgTypes)
{
// type.count就是树的个数
for (int i = 0; i < type.count; i++)
{
ForegroundData data = new ForegroundData();

//produces value focused around center of the range
// 随机设置树离中心点的距离,foregroundRadius为1.1,所以rad的期望值为0.825
float rad = (UnityEngine.Random.Range(0, foregroundRadius) + UnityEngine.Random.Range(0, foregroundRadius)) * 0.5f;
// 随机选择角度
Quaternion q = Quaternion.Euler(0.0f, UnityEngine.Random.Range(0.0f, 360.0f), 0.0f);
// 树的位置就为距离中心点半径为rad,角度为q的点上面
data.position = q * new Vector3(rad, 0.0f, 0.0f) + hexPosition;

// 根据基础颜色,随机一些偏差作为树的颜色
data.color = TerrainDefinition.GetRandomizedColor(terrainType.source.foregroundColor, 30);
data.colorFinal = data.color;
data.name = type.name;
// 一定范围随机树的大小
data.scale = UnityEngine.Random.Range(0.002f * Hex.hexRadius, 0.0026f * Hex.hexRadius);
//data.horizontalInverse = Random.Range(0, 2) < 1 ? false : true;

foregroundData.Add(data);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: