public bool Import(Transform terrainParent, Transform objectsParent, Texture2D atlas, Texture2D atlas_normal, Dictionary<string, Rect> atlasRectHash)
{
if (!m_isValid)
{
Debug.LogError("Cannot Import patch_" + this.m_name);
return false;
}
// Begin the real work
// Each 4 heights are connected together as a quad
// Each * below is a datapoint from HIM file representing height z
// (0,0) (1,0) (2,0)
// *----*----*
// | | |
// (0,1) *----*----* (2,1)
// | | |
// (0,2) *----*----* (2,2)
//
// Mesh components
// path = 16x16 tiles
// tile = 4x4 quads
// quad = 2 triangles
// triangle = 3 vertices
int nVertices = 64 * 64 * 4;
Vector3[] vertices = new Vector3[nVertices];
Vector2[] uvsBottom = new Vector2[nVertices];
Vector2[] uvsTop = new Vector2[nVertices];
Color[] uvsLight= new Color[nVertices];
int[] triangles = new int[(m_HIM.Length-1)*(m_HIM.Width-1)*6];
int i_v = 0; // vertex index
int i_t = 0; // triangle index
// TODO: move these hardcoded values to a more appropriate place
float m_xStride = 2.5f;
float m_yStride = 2.5f;
float heightScaler = 300.0f / (m_xStride * 1.2f);
float x_offset = this.m_Row * m_xStride * 64.0f;
float y_offset = this.m_Col * m_yStride * 64.0f;
center = new Vector2(x_offset + m_xStride * 32.0f, y_offset + m_yStride * 32.0f);
m_mesh = new Mesh();
// Uv mapping for tiles
// x%5 = 0 1 2 3 4
// (0,1) (.25,1)(.5,1)(.75,1)(1,1)
// *-----*-----*-----*-----*
// | / | / | / | / |
// | / | / | / | / |
// (0,.75) *-----*-----*-----*-----*
// | / | / | / | / |
// | / | / | / | / |
// (0,.5) *-----*-----*-----*-----*
// | / | / | / | / |
// | / | / | / | / |
// (0,.25) *-----*-----*-----*-----*
// | / | / | / | / |
// | / | / | / | / |
// (0,0) *-----*-----*-----*-----*
// (.25,0)(.5,0)(.75,0)(1,0)
Vector2[,] uvMatrix = new Vector2[5, 5];
Vector2[,] uvMatrixLR = new Vector2[5, 5];
Vector2[,] uvMatrixTB = new Vector2[5, 5];
Vector2[,] uvMatrixLRTB = new Vector2[5, 5];
Vector2[,] uvMatrixRotCW = new Vector2[5, 5]; // rotated 90 deg clockwise
Vector2[,] uvMatrixRotCCW = new Vector2[5, 5]; // rotated 90 counter clockwise
for (int uv_x = 0; uv_x < 5; uv_x++)
{
for (int uv_y = 0; uv_y < 5; uv_y++)
{
uvMatrix[uv_y, uv_x] = new Vector2(0.25f * (float)uv_x, 1.0f - 0.25f * (float)uv_y);
uvMatrixLR[uv_y, uv_x] = new Vector2(1.0f - 0.25f * (float)uv_x, 1.0f - 0.25f * (float)uv_y);
uvMatrixTB[uv_y, uv_x] = new Vector2(0.25f * (float)uv_x, 0.25f * (float)uv_y);
uvMatrixLRTB[uv_y, uv_x] = new Vector2(1.0f - 0.25f * (float)uv_x, 0.25f * (float)uv_y);
uvMatrixRotCCW[uv_x, uv_y] = new Vector2(0.25f * (float)uv_x, 1.0f - 0.25f * (float)uv_y);
uvMatrixRotCW[uv_x, uv_y] = new Vector2(0.25f * (float)uv_y, 1.0f - 0.25f * (float)uv_x);
}
}
m_tiles = new List<Tile>();
// Populate tiles with texture references
for (int t_x = 0; t_x < 16; t_x++)
{
for (int t_y = 0; t_y < 16; t_y++)
{
Tile tile = new Tile( );
int tileID = m_TIL.Tiles[t_y, t_x].TileID;
string texPath1 = m_ZON.Textures[m_ZON.Tiles[tileID].ID1].TexPath;
string texPath2 = m_ZON.Textures[m_ZON.Tiles[tileID].ID2].TexPath;
tile.bottomTex = texPath1;
tile.topTex = texPath2;
m_tiles.Add(tile);
}
}
//string lightTexPath = "Assets/3DDATA/MAPS/JUNON/JPT01/" + m_Col + "_" + m_Row + "/" + m_Col + "_" + m_Row + "_PLANELIGHTINGMAP.dds";
Texture2D lightTex = Utils.loadTex( ref groundLight );// Resources.LoadAssetAtPath<Texture2D>(lightTexPath); //Utils.loadTex(lightTexPath, "Assets/GameData/Textures/Lightmaps/");
//Utils.convertTex( lightTexPath, "Assets/GameData/Textures/Lightmaps/", ref lightTex);
// copy rects to tiles
foreach(Tile tile in m_tiles)
{
tile.bottomRect = atlasRectHash[tile.bottomTex];
tile.topRect = atlasRectHash[tile.topTex];
}
// Generate a material
Material material = null;
if(realTimeBaking)
{
material = (Material)AssetDatabase.LoadMainAssetAtPath("Assets/Materials/JPT01.mat"); // new Material(Shader.Find( "Custom/StandardTerrain"));
//material.SetTexture("_MainTex", atlas);
//material.SetTexture("_DetailAlbedoMap", atlas);
}
else
{
material = new Material(Shader.Find( "Custom/TerrainShader2"));
material.SetTexture("_BottomTex", atlas);
material.SetTexture("_TopTex", atlas);
material.SetTexture("_LightTex", lightTex);
}
float l = m_HIM.Length - 1;
float w = m_HIM.Width - 1;
int triangleID = 0;
// Generate vertices and triangles
for (int x = 0; x < m_HIM.Length - 1; x++)
{
for (int y = 0; y < m_HIM.Width - 1; y++)
{
// Each quad will be split into two triangles:
//
// a b
// *-----*
// | / |
// | / |
// | / |
// d *-----* c
//
// The triangles used are: adb and bdc
int a = i_v++;
int b = i_v++;
int c = i_v++;
int d = i_v++;
// Calculate lightmap UV's (placed in color because mesh only has uv and uv2)
uvsLight[a] = new Color((float)y / w, 1.0f - (float)x / l, 0.0f);
uvsLight[b] = new Color((float)y / w, 1.0f - (float)(x + 1) / l, 0.0f);
uvsLight[c] = new Color((float)(y + 1) / w, 1.0f - (float)(x + 1) / l, 0.0f);
uvsLight[d] = new Color((float)(y + 1) / w, 1.0f - (float)(x) / l, 0.0f);
// Calculate vertices
vertices[a] = new Vector3(x * m_xStride + x_offset, m_HIM.Heights[x, y] / heightScaler, y * m_yStride + y_offset);
vertices[b] = new Vector3((x + 1) * m_xStride + x_offset, m_HIM.Heights[x + 1, y] / heightScaler, y * m_yStride + y_offset);
vertices[c] = new Vector3((x + 1) * m_xStride + x_offset, m_HIM.Heights[x + 1, y + 1] / heightScaler, (y + 1) * m_yStride + y_offset);
vertices[d] = new Vector3(x * m_xStride + x_offset, m_HIM.Heights[x, y + 1] / heightScaler, (y + 1) * m_yStride + y_offset);
if (y == 0)
{
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), a);
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), b);
}
if (y == m_HIM.Width - 1)
{
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), d);
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), c);
}
if (x == 0)
{
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), a);
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), d);
}
if (x == m_HIM.Length - 1)
{
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), b);
Utils.addVertexToLookup(edgeVertexLookup, vertices[a].ToString(), c);
}
int tileX = x / 4;
int tileY = y / 4;
int tileID = tileY * 16 + tileX;
// Apply UV's
ZON.RotationType rotation = m_ZON.Tiles[m_TIL.Tiles[tileX, tileY].TileID].Rotation;
Vector2[,] rotMatrix;
if (rotation == ZON.RotationType.Rotate90Clockwise || rotation == ZON.RotationType.Rotate90CounterClockwise)
Debug.Log("Rotation: " + (int)rotation);
switch (rotation)
{
case ZON.RotationType.Normal:
rotMatrix = uvMatrix;
break;
case ZON.RotationType.LeftRight:
rotMatrix = uvMatrixLR;
break;
case ZON.RotationType.LeftRightTopBottom:
rotMatrix = uvMatrixLRTB;
break;
case ZON.RotationType.Rotate90Clockwise:
rotMatrix = uvMatrixRotCW;
break;
case ZON.RotationType.Rotate90CounterClockwise:
rotMatrix = uvMatrixRotCCW;
break;
case ZON.RotationType.TopBottom:
rotMatrix = uvMatrixTB;
break;
default:
rotMatrix = uvMatrix;
break;
}
// Get top and bottom UV's using texture atlas and rotation adjustments
uvsTop[a] = m_tiles[tileID].GetUVTop(rotMatrix[x % 4, y % 4]);
uvsTop[b] = m_tiles[tileID].GetUVTop(rotMatrix[(x % 4 + 1) % 5, y % 4]);
uvsTop[c] = m_tiles[tileID].GetUVTop(rotMatrix[(x % 4 + 1) % 5, (y % 4 + 1) % 5]);
uvsTop[d] = m_tiles[tileID].GetUVTop(rotMatrix[x % 4, (y % 4 + 1) % 5]);
uvsBottom[a] = m_tiles[tileID].GetUVBottom(rotMatrix[x % 4, y % 4]);
uvsBottom[b] = m_tiles[tileID].GetUVBottom(rotMatrix[(x % 4 + 1) % 5, y % 4]);
uvsBottom[c] = m_tiles[tileID].GetUVBottom(rotMatrix[(x % 4 + 1) % 5, (y % 4 + 1) % 5]);
uvsBottom[d] = m_tiles[tileID].GetUVBottom(rotMatrix[x % 4, (y % 4 + 1) % 5]);
triangles[triangleID ++] = a;
triangles[triangleID ++] = d;
triangles[triangleID ++] = b;
triangles[triangleID ++] = b;
triangles[triangleID ++] = d;
triangles[triangleID ++] = c;
} // for y
} // for x
m_mesh.vertices = vertices;
m_mesh.triangles = triangles;
m_mesh.uv = uvsBottom;
m_mesh.uv2 = uvsTop;
m_mesh.colors = uvsLight;
m_mesh.RecalculateNormals();
if(blendNormals)
{
// CalculateSharedNormals: fix all normals as follows:
// Several triangles share same vertex, but it is duplicated
// We want to:
// 1. search the vertex array for shared vertices
// 2. store each shared vertex id in a data structure comprising rows of shared vertices
// 3. go through each row of shared vertices and calculate the average normal
// 4. store the avg normal and all corresponding vertex id's in different data structure
// 5. traverse the new data structure and assign the new normal to all the vertices it belongs to
Vector3[] normals = new Vector3[m_mesh.vertexCount];
Dictionary<String, List<int>> vertexLookup = new Dictionary<String, List<int>>();
// 1. and 2.
for (int i = 0; i < m_mesh.vertexCount; i++)
Utils.addVertexToLookup(vertexLookup, m_mesh.vertices[i].ToString(), i);
// traverse the shared vertex list and calculate new normals
foreach (KeyValuePair<String, List<int>> entry in vertexLookup)
{
Vector3 avg = Vector3.zero;
foreach (int id in entry.Value)
{
avg += m_mesh.normals[id];
}
avg.Normalize();
foreach (int id in entry.Value)
normals[id] = avg;
}
m_mesh.normals = normals;
}
Utils.calculateMeshTangents(m_mesh);
m_mesh.RecalculateBounds();
m_mesh.Optimize();
//AssetDatabase.CreateAsset( m_mesh, "Assets/patch_" + this.m_name + ".mesh");
GameObject patchObject = new GameObject();
patchObject.name = "patch_" + this.m_name;
patchObject.AddComponent<MeshFilter>().mesh = m_mesh;
patchObject.AddComponent<MeshRenderer>();
patchObject.AddComponent<MeshCollider>();
MeshRenderer patchRenderer = patchObject.GetComponent<MeshRenderer>();
patchRenderer.material = material;
//patchRenderer.castShadows = false;
patchObject.transform.parent = terrainParent;
patchObject.layer = LayerMask.NameToLayer("Floor");
//================== TERRAIN OBJECTS==========================
GameObject deco = new GameObject();
deco.name = "deco_" + this.m_name;
deco.transform.parent = objectsParent;
deco.layer = LayerMask.NameToLayer("MapObjects");
//================= DECORATION ======================
for (int obj = 0; obj < m_IFO.Decoration.Count; obj++ )
{
IFO.BaseIFO ifo = m_IFO.Decoration[obj];
GameObject terrainObject = new GameObject();
terrainObject.layer = LayerMask.NameToLayer("MapObjects");
terrainObject.name = "Deco_" + ifo.MapPosition.x + "_" + ifo.MapPosition.y;
terrainObject.transform.parent = deco.transform;
terrainObject.transform.localPosition = (ifo.Position / 100.0f);
bool isAnimated = false;
AnimationClip clip = new AnimationClip();
clip.legacy = true;
for (int part = 0; part < m_ZSC_Deco.Objects[ifo.ObjectID].Models.Count; part++ )
{
ZSC.Object.Model model = m_ZSC_Deco.Objects[ifo.ObjectID].Models[part];
// load ZMS
string zmsPath = m_3dDataDir.Parent.FullName + "/" + m_ZSC_Deco.Models[model.ModelID].Replace("\\", "/");
string texPath = "Assets/" + m_ZSC_Deco.Textures[model.TextureID].Path;
string lightPath = null;
ZMS zms = null;
lightPath = Utils.FixPath(this.m_assetDir.Parent.FullName + "\\" + this.m_name + "\\LIGHTMAP\\" + m_LIT_Deco.Objects[obj].Parts[part].DDSName);
LIT.Object.Part lmData = m_LIT_Deco.Objects[obj].Parts[part];
// Calculate light map UV offset and scale
float objScale = 1.0f / (float) lmData.ObjectsPerWidth;
float rowNum = (float) Math.Floor( (double)( (double)lmData.MapPosition / (double)lmData.ObjectsPerWidth) );
float colNum = (float) lmData.MapPosition % lmData.ObjectsPerWidth;
Vector2 lmOffset = new Vector2(colNum * objScale, rowNum * objScale);
Vector2 lmScale = new Vector2(objScale, objScale);
zms = new ZMS(zmsPath, lmScale, lmOffset);
// Create material
Texture2D mainTex = Utils.loadTex( ref texPath);
Material mat = null;
if(realTimeBaking)
{
Texture2D normalMap = Utils.generateNormalMap( texPath );
mat = new Material(Shader.Find("Standard"));
mat.SetFloat("_Mode", 1.0f);
mat.SetTexture("_MainTex", mainTex);
mat.SetTexture("_OcclusionMap", mainTex);
mat.SetTexture("_BumpMap", normalMap);
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
mat.SetInt("_ZWrite", 1);
mat.EnableKeyword("_ALPHATEST_ON");
mat.DisableKeyword("_ALPHABLEND_ON");
mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mat.SetFloat("_OcclusionStrength", 0.5f);
mat.SetFloat("_BumpScale", 0.8f);
mat.SetFloat("_Glossiness", 0.1f);
//mat.renderQueue = 2450;
}
else
{
mat = new Material(Shader.Find("Custom/ObjectShader"));
mat.SetTexture("_MainTex", mainTex);
Texture2D lightTexture = Utils.loadTex ( ref lightPath);
mat.SetTexture("_LightTex", lightTexture);
}
GameObject modelObject = new GameObject();
modelObject.layer = LayerMask.NameToLayer("MapObjects");
modelObject.transform.parent = terrainObject.transform;
modelObject.transform.localScale = model.Scale;
modelObject.transform.localPosition = (model.Position / 100.0f);
modelObject.transform.rotation = model.Rotation;
modelObject.AddComponent<MeshFilter>().mesh = zms.getMesh();
modelObject.AddComponent<MeshRenderer>();
modelObject.name = new DirectoryInfo(zmsPath).Name;
MeshRenderer renderer = modelObject.GetComponent<MeshRenderer>();
renderer.material = mat;
//renderer.castShadows = false;
if(model.CollisionLevel != ZSC.CollisionLevelType.None)
modelObject.AddComponent<MeshCollider>();
string zmoPath = model.Motion;
if (zmoPath!= null && zmoPath.ToLower().Contains("zmo"))
{
isAnimated = true;
ZMO zmo = new ZMO("assets/" + model.Motion, false, true);
clip = zmo.buildAnimationClip(modelObject.name, clip);
}
else
modelObject.isStatic = true;
}
terrainObject.transform.rotation = ifo.Rotation;
terrainObject.transform.localScale = ifo.Scale;
if (isAnimated)
{
Animation animation = terrainObject.GetComponent<Animation>();
if (animation == null)
animation = terrainObject.AddComponent<Animation>();
clip.wrapMode = WrapMode.Loop;
animation.AddClip(clip, terrainObject.name);
animation.clip = clip;
}
else
{
terrainObject.isStatic = true;
}
}
GameObject cnst = new GameObject();
cnst.name = "cnst_" + this.m_name;
cnst.transform.parent = objectsParent;
cnst.layer = LayerMask.NameToLayer("MapObjects");
//================= CONSTRUCTION ======================
for (int obj = 0; obj < m_IFO.Construction.Count; obj++)
{
IFO.BaseIFO ifo = m_IFO.Construction[obj];
GameObject terrainObject = new GameObject();
terrainObject.layer = LayerMask.NameToLayer("MapObjects");
terrainObject.name = "Const_" + ifo.MapPosition.x + "_" + ifo.MapPosition.y;
terrainObject.transform.parent = deco.transform;
terrainObject.transform.localPosition = (ifo.Position / 100.0f);
bool isAnimated = false;
AnimationClip clip = new AnimationClip();
clip.legacy = true;
for (int part = 0; part < m_ZSC_Cnst.Objects[ifo.ObjectID].Models.Count; part++)
{
ZSC.Object.Model model = m_ZSC_Cnst.Objects[ifo.ObjectID].Models[part];
string zmsPath = m_3dDataDir.Parent.FullName + "/" + m_ZSC_Cnst.Models[model.ModelID].Replace("\\","/");
string texPath = "Assets/" + m_ZSC_Cnst.Textures[model.TextureID].Path;
string lightPath = null;
ZMS zms = null;
// load ZMS
lightPath = Utils.FixPath(this.m_assetDir.Parent.FullName + "\\" + this.m_name + "\\LIGHTMAP\\" + m_LIT_Cnst.Objects[obj].Parts[part].DDSName);
LIT.Object.Part lmData = m_LIT_Cnst.Objects[obj].Parts[part];
// Calculate light map UV offset and scale
float objScale = 1.0f / (float)lmData.ObjectsPerWidth;
float rowNum = (float)Math.Floor((double)((double)lmData.MapPosition / (double)lmData.ObjectsPerWidth));
float colNum = (float)lmData.MapPosition % lmData.ObjectsPerWidth;
Vector2 lmOffset = new Vector2(colNum * objScale, rowNum * objScale);
Vector2 lmScale = new Vector2(objScale, objScale);
zms = new ZMS(zmsPath, lmScale, lmOffset);
// Create material
Texture2D mainTex = Utils.loadTex(ref texPath);
Material mat = null;
if(realTimeBaking)
{
Texture2D normalMap = Utils.generateNormalMap( texPath );
mat = new Material(Shader.Find("Standard"));
mat.SetFloat("_Mode", 1.0f);
mat.SetTexture("_MainTex", mainTex);
mat.SetTexture("_OcclusionMap", mainTex);
mat.SetTexture("_BumpMap", normalMap);
mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
mat.SetInt("_ZWrite", 1);
mat.EnableKeyword("_ALPHATEST_ON");
mat.DisableKeyword("_ALPHABLEND_ON");
mat.DisableKeyword("_ALPHAPREMULTIPLY_ON");
mat.SetFloat("_OcclusionStrength", 0.5f);
mat.SetFloat("_BumpScale", 0.8f);
mat.SetFloat("_Glossiness", 0.1f);
//mat.renderQueue = 2450;
}
else
{
mat = new Material(Shader.Find("Custom/ObjectShader"));
mat.SetTexture("_MainTex", mainTex);
Texture2D lightTexture = Utils.loadTex(ref lightPath);
mat.SetTexture("_LightTex", lightTexture);
}
GameObject modelObject = new GameObject();
modelObject.layer = LayerMask.NameToLayer("MapObjects");
modelObject.transform.parent = terrainObject.transform;
modelObject.transform.localScale = model.Scale;
modelObject.transform.localPosition = (model.Position / 100.0f);
modelObject.transform.rotation = model.Rotation;
modelObject.AddComponent<MeshFilter>().mesh = zms.getMesh();
modelObject.AddComponent<MeshRenderer>();
modelObject.name = new DirectoryInfo(zmsPath).Name;
MeshRenderer renderer = modelObject.GetComponent<MeshRenderer>();
renderer.material = mat;
//renderer.castShadows = false;
modelObject.AddComponent<MeshCollider>();
string zmoPath = model.Motion;
if (zmoPath != null && zmoPath.ToLower().Contains("zmo"))
{
isAnimated = true;
ZMO zmo = new ZMO("assets/" + model.Motion, false, true);
clip = zmo.buildAnimationClip(modelObject.name, clip);
}
else
modelObject.isStatic = true;
}
terrainObject.transform.rotation = ifo.Rotation;
terrainObject.transform.localScale = ifo.Scale;
if (isAnimated)
{
Animation animation = terrainObject.GetComponent<Animation>();
if (animation == null)
animation = terrainObject.AddComponent<Animation>();
clip.wrapMode = WrapMode.Loop;
animation.AddClip(clip, terrainObject.name);
animation.clip = clip;
}
else
terrainObject.isStatic = false;
}
/*
// TODO: add any extra components here
AssetDatabase.CreateAsset( m_mesh, this.m_unityAssetDir.FullName);
AssetDatabase.SaveAssets();
*/
return true;
}