private GameObject ImportSubObject(GameObject parentObj, DataSet.ObjectData objData, Dictionary <string, Material> mats) { bool conv2sided = buildOptions != null && buildOptions.convertToDoubleSided; GameObject go = new GameObject(); go.name = objData.name; int count = 0; if (parentObj.transform) { while (parentObj.transform.Find(go.name)) { count++; go.name = objData.name + count; } } go.transform.SetParent(parentObj.transform, false); if (objData.allFaces.Count == 0) { throw new InvalidOperationException("Failed to parse vertex and uv data. It might be that the file is corrupt or is not a valid wavefront OBJ file."); //Debug.LogWarning("Sub object: " + objData.name + " has no face defined. Creating empty game object."); //return go; } //Debug.Log( "Importing sub object:" + objData.Name ); // count vertices needed for all the faces and map face indices to new vertices Dictionary <string, int> vIdxCount = new Dictionary <string, int>(); int vcount = 0; foreach (DataSet.FaceIndices fi in objData.allFaces) { string key = DataSet.GetFaceIndicesKey(fi); int idx; // avoid duplicates if (!vIdxCount.TryGetValue(key, out idx)) { vIdxCount.Add(key, vcount); vcount++; } } int arraySize = conv2sided ? vcount * 2 : vcount; Vector3[] newVertices = new Vector3[arraySize]; Vector2[] newUVs = new Vector2[arraySize]; Vector3[] newNormals = new Vector3[arraySize]; Color32[] newColors = new Color32[arraySize]; bool hasColors = currDataSet.colorList.Count > 0; foreach (DataSet.FaceIndices fi in objData.allFaces) { string key = DataSet.GetFaceIndicesKey(fi); int k = vIdxCount[key]; newVertices[k] = currDataSet.vertList[fi.vertIdx]; if (conv2sided) { newVertices[vcount + k] = newVertices[k]; } if (hasColors) { newColors[k] = currDataSet.colorList[fi.vertIdx]; if (conv2sided) { newColors[vcount + k] = newColors[k]; } } if (currDataSet.uvList.Count > 0) { newUVs[k] = currDataSet.uvList[fi.uvIdx]; if (conv2sided) { newUVs[vcount + k] = newUVs[k]; } } if (currDataSet.normalList.Count > 0 && fi.normIdx >= 0) { newNormals[k] = currDataSet.normalList[fi.normIdx]; if (conv2sided) { newNormals[vcount + k] = -newNormals[k]; } } } bool objectHasNormals = (currDataSet.normalList.Count > 0 && objData.hasNormals); bool objectHasColors = (currDataSet.colorList.Count > 0 && objData.hasColors); bool objectHasUVs = (currDataSet.uvList.Count > 0); int n = objData.faceGroups[0].faces.Count; int numIndices = conv2sided ? n * 2 : n; MeshFilter meshFilter = go.AddComponent <MeshFilter>(); go.AddComponent <MeshRenderer>(); Mesh mesh = new Mesh(); #if UNITY_2017_3_OR_NEWER if (Using32bitIndices()) { if (arraySize > MAX_VERT_COUNT || numIndices > MAX_INDICES_LIMIT_FOR_A_MESH) { mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; } } #endif mesh.name = go.name; meshFilter.sharedMesh = mesh; mesh.vertices = newVertices; if (objectHasUVs) { mesh.uv = newUVs; } if (objectHasNormals) { mesh.normals = newNormals; } if (objectHasColors) { mesh.colors32 = newColors; } Material material; string matName = (objData.faceGroups[0].materialName != null) ? objData.faceGroups[0].materialName : "default"; Renderer renderer = go.GetComponent <Renderer>(); if (mats.ContainsKey(matName)) { material = mats[matName]; renderer.sharedMaterial = material; #if UNITY_5_6_OR_NEWER RendererExtensions.UpdateGIMaterials(renderer); #else DynamicGI.UpdateMaterials(renderer); #endif } else { if (mats.ContainsKey("default")) { material = mats["default"]; renderer.sharedMaterial = material; Debug.LogWarning("Material: " + matName + " not found. Using the default material."); } else { Debug.LogError("Material: " + matName + " not found."); } } int[] indices = new int[numIndices]; for (int s = 0; s < n; s++) { DataSet.FaceIndices fi = objData.faceGroups[0].faces[s]; string key = DataSet.GetFaceIndicesKey(fi); indices[s] = vIdxCount[key]; } if (conv2sided) { for (int s = 0; s < n; s++) { indices[s + n] = vcount + indices[s / 3 * 3 + 2 - s % 3]; } } mesh.SetTriangles(indices, 0); if (!objectHasNormals) { mesh.RecalculateNormals(); } if (objectHasUVs) { Solve(mesh); } if (buildOptions != null && buildOptions.buildColliders) { #if UNITY_2018_3_OR_NEWER BuildMeshCollider(go, buildOptions.colliderConvex, buildOptions.colliderTrigger); #else BuildMeshCollider(go, buildOptions.colliderConvex, buildOptions.colliderTrigger, buildOptions.colliderInflate, buildOptions.colliderSkinWidth); #endif } return(go); }
/// <summary> /// Build an object once at a time, to be reiterated until false is returned. /// </summary> /// <param name="parentObj">Game object to which the new objects will be attached</param> /// <param name="mats">Materials from the previously loaded library</param> /// <returns>Return true until no more objects can be added, then false.</returns> protected bool BuildNextObject(GameObject parentObj, Dictionary <string, Material> mats) { // if all the objects were built stop here if (buildStatus.objCount >= currDataSet.objectList.Count) { return(false); } // get the next object in the list DataSet.ObjectData objData = currDataSet.objectList[buildStatus.objCount]; if (buildStatus.newObject) { if (buildStatus.objCount == 0 && objData.name == "default") { buildStatus.currObjGameObject = parentObj; } else { buildStatus.currObjGameObject = new GameObject(); buildStatus.currObjGameObject.transform.parent = parentObj.transform; buildStatus.currObjGameObject.name = objData.name; // restore the scale if the parent was rescaled buildStatus.currObjGameObject.transform.localScale = Vector3.one; } buildStatus.subObjParent = buildStatus.currObjGameObject; //if (od.Name != "default") go.name = od.Name; //Debug.Log("Object: " + objData.name); buildStatus.newObject = false; buildStatus.subObjCount = 0; buildStatus.idxCount = 0; buildStatus.grpIdx = 0; buildStatus.grpFaceIdx = 0; buildStatus.meshPartIdx = 0; buildStatus.totFaceIdxCount = 0; buildStatus.numGroups = Mathf.Max(1, objData.faceGroups.Count); } bool splitLargeMeshes = true; #if UNITY_2017_3_OR_NEWER // GPU support for 32 bit indices is not guaranteed on all platforms; // for example Android devices with Mali-400 GPU do not support them. // This check is performed in Using32bitIndices(). // If nothing is rendered on your device problably Using32bitIndices() must be updated. if (Using32bitIndices()) { splitLargeMeshes = false; } #endif bool splitGrp = false; DataSet.FaceGroupData grp = new DataSet.FaceGroupData(); grp.name = objData.faceGroups[buildStatus.grpIdx].name; grp.materialName = objData.faceGroups[buildStatus.grpIdx].materialName; // data for sub-object DataSet.ObjectData subObjData = new DataSet.ObjectData(); subObjData.hasNormals = objData.hasNormals; subObjData.hasColors = objData.hasColors; HashSet <int> vertIdxSet = new HashSet <int>(); bool conv2sided = buildOptions != null && buildOptions.convertToDoubleSided; int maxIdx4mesh = conv2sided ? MAX_INDICES_LIMIT_FOR_A_MESH / 2 : MAX_INDICES_LIMIT_FOR_A_MESH; // copy blocks of face indices to each sub-object data for (int f = buildStatus.grpFaceIdx; f < objData.faceGroups[buildStatus.grpIdx].faces.Count; f++) { // if large meshed must be split and // if passed the max num of vertices and not at the last iteration if (splitLargeMeshes && (vertIdxSet.Count / 3 > MAX_VERT_COUNT / 3 || subObjData.allFaces.Count / 3 > maxIdx4mesh / 3)) { // split the group across more objects splitGrp = true; buildStatus.grpFaceIdx = f; Debug.LogWarningFormat("Maximum vertex number for a mesh exceeded.\nSplitting object {0} (group {1}, starting from index {2})...", grp.name, buildStatus.grpIdx, f); break; } DataSet.FaceIndices fi = objData.faceGroups[buildStatus.grpIdx].faces[f]; subObjData.allFaces.Add(fi); grp.faces.Add(fi); vertIdxSet.Add(fi.vertIdx); } if (splitGrp || buildStatus.meshPartIdx > 0) { buildStatus.meshPartIdx++; } // create an empty (group) object in case the group has been splitted if (buildStatus.meshPartIdx == 1) { GameObject grpObj = new GameObject(); grpObj.transform.SetParent(buildStatus.currObjGameObject.transform, false); grpObj.name = grp.name; buildStatus.subObjParent = grpObj; } // add a suffix to the group name in case the group has been splitted if (buildStatus.meshPartIdx > 0) { grp.name = buildStatus.subObjParent.name + "_MeshPart" + buildStatus.meshPartIdx; } subObjData.name = grp.name; // add the group to the sub object data subObjData.faceGroups.Add(grp); // update the start index buildStatus.idxCount += subObjData.allFaces.Count; if (!splitGrp) { buildStatus.grpFaceIdx = 0; buildStatus.grpIdx++; } buildStatus.totFaceIdxCount += subObjData.allFaces.Count; GameObject subobj = ImportSubObject(buildStatus.subObjParent, subObjData, mats); if (subobj == null) { Debug.LogWarningFormat("Error loading sub object n.{0}.", buildStatus.subObjCount); } //else Debug.LogFormat( "Imported face indices: {0} to {1}", buildStatus.totFaceIdxCount - sub_od.AllFaces.Count, buildStatus.totFaceIdxCount ); buildStatus.subObjCount++; if (buildStatus.totFaceIdxCount >= objData.allFaces.Count || buildStatus.grpIdx >= objData.faceGroups.Count) { if (buildStatus.totFaceIdxCount != objData.allFaces.Count) { Debug.LogWarningFormat("Imported face indices: {0} of {1}", buildStatus.totFaceIdxCount, objData.allFaces.Count); return(false); } buildStatus.objCount++; buildStatus.newObject = true; } return(true); }