private static PhysicsConvexShape DecomposeConvex(MeshLOD lod, bool useSingleConvex = false) { using (var vhacd = new VHACD()) { return(vhacd.Compute(lod, useSingleConvex)); } }
private static MeshLOD BoxCylPrismToMesh(this ObjectPart.PrimitiveShape.Decoded shape) { ProfileDetails path; switch (shape.ShapeType) { case PrimitiveShapeType.Box: path = CalcBoxProfile(shape); break; case PrimitiveShapeType.Cylinder: path = CalcCylinderProfile(shape); break; case PrimitiveShapeType.Prism: path = CalcPrismProfile(shape); break; default: throw new NotImplementedException(); } double cut = shape.PathBegin; double cutBegin = cut; double cutEnd = shape.PathEnd; double cutStep = (cutEnd - cut) / 10f; double twistBegin = shape.TwistBegin * Math.PI; double twistEnd = shape.TwistEnd * Math.PI; double neededSteps = Math.Max(1, Math.Ceiling((shape.TwistEnd - shape.TwistBegin) / (5 * Math.PI / 180) * (cutEnd - cut))); cutStep /= neededSteps; var mesh = new MeshLOD(); if (double.Epsilon <= Math.Abs(shape.TwistBegin - shape.TwistEnd)) { for (; cut < cutEnd; cut += cutStep) { mesh.Vertices.AddRange(path.ExtrudeBasic(shape, twistBegin, twistEnd, cut)); } } else { mesh.Vertices.AddRange(path.ExtrudeBasic(shape, twistBegin, twistEnd, cutBegin)); } mesh.Vertices.AddRange(path.ExtrudeBasic(shape, twistBegin, twistEnd, cutEnd)); shape.BuildBasicTriangles(mesh, path, cutBegin, cutEnd); return(mesh); }
private static MeshLOD AdvancedToMesh(this ObjectPart.PrimitiveShape.Decoded shape) { ProfileDetails profile; switch (shape.ShapeType) { case PrimitiveShapeType.Ring: profile = CalcPrismProfile(shape); break; case PrimitiveShapeType.Torus: profile = CalcCylinderProfile(shape); break; case PrimitiveShapeType.Tube: profile = CalcBoxProfile(shape); break; case PrimitiveShapeType.Sphere: profile = CalcSphereProfile(shape); break; default: throw new NotImplementedException(); } double cut = shape.PathBegin; double cutBegin = cut; double cutEnd = shape.PathEnd; double cutStep = (cutEnd - cut) / 36f / shape.Revolutions; double twistBegin = shape.TwistBegin * Math.PI * 2; double twistEnd = shape.TwistEnd * Math.PI * 2; double neededSteps = Math.Max(1, Math.Ceiling((shape.TwistEnd - shape.TwistBegin) / (5 * Math.PI / 180) * (cutEnd - cut))); cutStep /= neededSteps; var mesh = new MeshLOD(); for (; cut < cutEnd; cut += cutStep) { mesh.Vertices.AddRange(profile.ExtrudeAdvanced(shape, twistBegin, twistEnd, cut)); } mesh.Vertices.AddRange(profile.ExtrudeAdvanced(shape, twistBegin, twistEnd, cutEnd)); shape.BuildAdvancedTriangles(mesh, profile, cutBegin, cutEnd); return(mesh); }
public void UpdateTerrainChunk() { if (mapDataReceived) { //give me the smalles way to next egde of the bounding box float viewerDstFromNearestEdge = Mathf.Sqrt(new Bounds(position, Vector2.one * size).SqrDistance(viewerPosition)); bool visible = viewerDstFromNearestEdge <= maxViewDst; if (visible) { int index = 0; for (int i = 0; i < detailLevels.Length; i++) { if (viewerDstFromNearestEdge > detailLevels[i].visibleThresholdDst) { index = i + 1; } else { break; } } if (index != previewLODIndex) { MeshLOD meshLOD = lodMeshes [index]; if (meshLOD.hasMesh) { previewLODIndex = index; meshFilter.mesh = meshLOD.mesh; } else if (!meshLOD.hasRequestofmesh) { meshLOD.RequestofMesh(mapData); } } } SetVisible(visible); } }
public string LODToString(MeshLOD lod) { StringBuilder sb = new StringBuilder(); sb.AppendFormat(".BoneDataCount : {0}\n", lod.BoneDataCount); sb.AppendFormat(".Unknown02 : 0x{0}\n", lod.Unknown02.ToString("X8")); sb.AppendFormat(".NumSections : {0}\n", lod.NumSubObjects); sb.AppendFormat(".SectionsOffset : 0x{0}\n", lod.SectionsOffset.ToString("X16")); sb.AppendFormat(".Unknown03 : 0x{0}\n", lod.Unknown03.ToString("X8")); for (int i = 0; i < 4; i++) { sb.AppendFormat(".Data Entry[{0}] : 0x{1} - 0x{2}\n", i, lod.DataOffsets[i].ToString("X16"), lod.DataValues[i].ToString("X8")); } sb.AppendFormat(".Unknown04 : 0x{0}\n", lod.Unknown04.ToString("X8")); sb.AppendFormat(".IndexBufferSize : 0x{0}\n", lod.IndexBufferSize.ToString("X8")); sb.AppendFormat(".VertexBufferSize : 0x{0}\n", lod.VertexBufferSize.ToString("X8")); sb.AppendFormat(".Unknown05 : 0x{0}\n", lod.Unknown05.ToString("X8")); sb.AppendFormat(".ChunkID : 0x{0}\n", Helpers.ByteArrayToHexString(lod.ChunkID)); sb.AppendFormat(".InlineDataOffset : 0x{0}\n", lod.InlineDataOffset.ToString("X8")); sb.AppendFormat(".Unknown07 : 0x{0}\n", lod.Unknown07.ToString("X8")); sb.AppendFormat(".Unknown08 : 0x{0}\n", lod.Unknown08.ToString("X8")); sb.AppendFormat(".String01 : {0}\n", lod.String01); sb.AppendFormat(".String02 : {0}\n", lod.String02); sb.AppendFormat(".String03 : {0}\n", lod.String03); sb.AppendFormat(".Unknown09 : 0x{0}\n", lod.Unknown09.ToString("X8")); sb.AppendFormat(".Unknown10 : 0x{0}\n", lod.Unknown10.ToString("X8")); sb.AppendFormat(".Unknown11 : 0x{0}\n", lod.Unknown11.ToString("X8")); sb.AppendFormat(".BoneCount : {0}\n", lod.BoneCount); sb.AppendFormat(".BoneData : "); for (int i = 0; i < lod.BoneData.Count / 2; i++) { sb.AppendFormat("[0x" + lod.BoneData[i * 2].ToString("X") + " - 0x" + lod.BoneData[i * 2 + 1].ToString("X") + "] "); } sb.AppendFormat("\n.Unknown03 : 0x{0}\n", lod.Unknown13.ToString("X8")); int count = 0; foreach (MeshSection sec in lod.Sections) { sb.AppendFormat(".[Section {0}]:\n{1}", count++, SectionToString(sec)); } return(sb.ToString()); }
private static void ProcessMeshes(AnArray meshList, MeshInventoryItem item) { int idx = 0; foreach (IValue iv in meshList) { var newasset = new AssetData { ID = UUID.Random, Type = AssetType.Mesh, Name = item.Name + " - Mesh " + (idx + 1).ToString() }; using (var meshstream = new MemoryStream()) { Map meshData; /* add the version tag */ using (var inputstream = new MemoryStream((BinaryData)iv)) { meshData = (Map)LlsdBinary.Deserialize(inputstream); meshData["version"] = new Integer(1); LlsdBinary.Serialize(meshData, meshstream); inputstream.CopyTo(meshstream); } newasset.Data = meshstream.ToArray(); } item.Assets.Add(newasset); item.MeshMap.Add(idx, newasset.ID); var m = new LLMesh(newasset); MeshLOD lod = m.GetLOD(LLMesh.LodLevel.LOD3); if (lod.NumFaces >= 1 && lod.NumFaces <= 9) { item.MeshFaces.Add(idx, lod.NumFaces); } ++idx; } }
public TerrainChunk(Vector2 coord, int size, LODInfo[] detailLevels, Transform parent, Material material) { this.detailLevels = detailLevels; this.size = size; position = coord * size; //meshObject = GameObject.CreatePrimitive(PrimitiveType.Plane); //add new GameObjekt meshObject = new GameObject("Terrain"); meshRenderer = meshObject.AddComponent <MeshRenderer>(); meshFilter = meshObject.AddComponent <MeshFilter>(); meshRenderer.material = material; meshObject.transform.position = new Vector3(position.x, 0, position.y); //meshObject.transform.localScale = Vector3.one * size /10f; meshObject.transform.parent = parent; SetVisible(false); lodMeshes = new MeshLOD[detailLevels.Length]; for (int i = 0; i < detailLevels.Length; i++) { lodMeshes[i] = new MeshLOD(this.detailLevels[i].LOD, UpdateTerrainChunk); } mapGenerator.RequestMapData(position, OnMapDataReceived); }
void OnPostprocessModel(GameObject go) { if (enabled && !go.GetComponentInChildren <LODGroup>() && meshSimplifierType != null) { if (go.GetComponentsInChildren <SkinnedMeshRenderer>().Any()) { Debug.Log("Automatic LOD generation on does not currently support skinned meshes on import"); return; } var originalMeshFilters = go.GetComponentsInChildren <MeshFilter>(); uint polyCount = 0; foreach (var mf in originalMeshFilters) { var m = mf.sharedMesh; for (int i = 0; i < m.subMeshCount; i++) { var topology = m.GetTopology(i); var indexCount = m.GetIndexCount(i); switch (topology) { case MeshTopology.Quads: indexCount /= 4; break; case MeshTopology.Triangles: indexCount /= 3; break; case MeshTopology.Lines: case MeshTopology.LineStrip: indexCount /= 2; break; } polyCount += indexCount; } } var meshLODs = new List <MeshLOD>(); var preprocessMeshes = new HashSet <int>(); var lodData = AssetDatabase.LoadAssetAtPath <LODData>(GetLODDataPath(assetPath)); if (!lodData) { lodData = ScriptableObject.CreateInstance <LODData>(); } var overrideDefaults = lodData.overrideDefaults; var importSettings = lodData.importSettings; if (importSettings == null) { importSettings = new LODImportSettings(); lodData.importSettings = importSettings; } if (!overrideDefaults) { importSettings.generateOnImport = true; importSettings.meshSimplifier = meshSimplifierType.AssemblyQualifiedName; importSettings.maxLODGenerated = maxLOD; importSettings.initialLODMaxPolyCount = initialLODMaxPolyCount; } if (importSettings.generateOnImport && importSettings.maxLODGenerated == 0 && polyCount <= importSettings.initialLODMaxPolyCount) { return; } if (!overrideDefaults || importSettings.generateOnImport) { if (polyCount > importSettings.initialLODMaxPolyCount) { foreach (var mf in originalMeshFilters) { var inputMesh = mf.sharedMesh; var outputMesh = new Mesh(); outputMesh.name = inputMesh.name; outputMesh.bounds = inputMesh.bounds; mf.sharedMesh = outputMesh; var meshLOD = new MeshLOD(); meshLOD.inputMesh = inputMesh; meshLOD.outputMesh = outputMesh; meshLOD.quality = (float)importSettings.initialLODMaxPolyCount / (float)polyCount; meshLODs.Add(meshLOD); preprocessMeshes.Add(outputMesh.GetInstanceID()); } } // Clear out previous LOD data in case the number of LODs has been reduced for (int i = 0; i <= LODData.MaxLOD; i++) { lodData[i] = null; } lodData[0] = originalMeshFilters.Select(mf => mf.GetComponent <Renderer>()).ToArray(); for (int i = 1; i <= importSettings.maxLODGenerated; i++) { var lodMeshes = new List <Renderer>(); foreach (var mf in originalMeshFilters) { var inputMesh = mf.sharedMesh; var lodTransform = EditorUtility.CreateGameObjectWithHideFlags(mf.name, k_DefaultHideFlags, typeof(MeshFilter), typeof(MeshRenderer)).transform; lodTransform.parent = mf.transform; lodTransform.localPosition = Vector3.zero; var lodMF = lodTransform.GetComponent <MeshFilter>(); var lodRenderer = lodTransform.GetComponent <MeshRenderer>(); AppendLODNameToRenderer(lodRenderer, i); var outputMesh = new Mesh(); outputMesh.name = string.Format("{0} LOD{1}", inputMesh.name, i); outputMesh.bounds = inputMesh.bounds; lodMF.sharedMesh = outputMesh; lodMeshes.Add(lodRenderer); EditorUtility.CopySerialized(mf.GetComponent <MeshRenderer>(), lodRenderer); var meshLOD = new MeshLOD(); meshLOD.inputMesh = inputMesh; meshLOD.outputMesh = outputMesh; meshLOD.quality = Mathf.Pow(0.5f, i); meshLODs.Add(meshLOD); } lodData[i] = lodMeshes.ToArray(); } // Change the name of the original renderers last, so the name change doesn't end up in the clones for other LODs AppendLODNameToRenderers(go.transform, 0); if (meshLODs.Count > 0) { if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(lodData))) { AssetDatabase.CreateAsset(lodData, GetLODDataPath(assetPath)); } else { var objects = AssetDatabase.LoadAllAssetsAtPath(GetLODDataPath(assetPath)); foreach (var o in objects) { var mesh = o as Mesh; if (mesh) { UnityObject.DestroyImmediate(mesh, true); } } EditorUtility.SetDirty(lodData); } meshLODs.ForEach(ml => AssetDatabase.AddObjectToAsset(ml.outputMesh, lodData)); AssetDatabase.SaveAssets(); foreach (var ml in meshLODs) { GenerateMeshLOD(ml, preprocessMeshes); } } } else { // Don't allow overriding LOD0 lodData[0] = originalMeshFilters.Select(mf => { var r = mf.GetComponent <Renderer>(); AppendLODNameToRenderer(r, 0); return(r); }).ToArray(); for (int i = 1; i <= LODData.MaxLOD; i++) { var renderers = lodData[i]; for (int j = 0; j < renderers.Length; j++) { var r = renderers[j]; var lodTransform = EditorUtility.CreateGameObjectWithHideFlags(r.name, k_DefaultHideFlags, typeof(MeshFilter), typeof(MeshRenderer)).transform; lodTransform.parent = go.transform; lodTransform.localPosition = Vector3.zero; var lodMF = lodTransform.GetComponent <MeshFilter>(); var lodRenderer = lodTransform.GetComponent <MeshRenderer>(); EditorUtility.CopySerialized(r.GetComponent <MeshFilter>(), lodMF); EditorUtility.CopySerialized(r, lodRenderer); AppendLODNameToRenderer(lodRenderer, i); renderers[j] = lodRenderer; } } } List <LOD> lods = new List <LOD>(); var maxLODFound = -1; for (int i = 0; i <= LODData.MaxLOD; i++) { var renderers = lodData[i]; if (renderers == null || renderers.Length == 0) { break; } maxLODFound++; } for (int i = 0; i <= maxLODFound; i++) { var lod = new LOD(); lod.renderers = lodData[i]; lod.screenRelativeTransitionHeight = i == maxLODFound ? 0.01f : Mathf.Pow(0.5f, i + 1); lods.Add(lod); } var lodGroup = go.AddComponent <LODGroup>(); lodGroup.SetLODs(lods.ToArray()); lodGroup.RecalculateBounds(); s_ModelAssetsProcessed.Add(assetPath); } }
static void GenerateMeshLOD(MeshLOD meshLOD, HashSet <int> preprocessMeshes) { // A NOP to make sure we have an instance before launching into threads that may need to execute on the main thread MonoBehaviourHelper.ExecuteOnMainThread(() => { }); WorkingMesh inputMesh = null; var inputMeshID = meshLOD.inputMesh.GetInstanceID(); if (!preprocessMeshes.Contains(inputMeshID)) { inputMesh = meshLOD.inputMesh.ToWorkingMesh(); } var meshSimplifier = (IMeshSimplifier)Activator.CreateInstance(meshSimplifierType); #if !SINGLE_THREADED var worker = new BackgroundWorker(); worker.DoWork += (sender, args) => { // If this mesh is dependent on another mesh, then let it complete first if (inputMesh == null) { while (preprocessMeshes.Contains(inputMeshID)) { Thread.Sleep(100); } MonoBehaviourHelper.ExecuteOnMainThread(() => inputMesh = meshLOD.inputMesh.ToWorkingMesh()); } #endif var outputMesh = new WorkingMesh(); #if UNITY_2017_3_OR_NEWER outputMesh.indexFormat = inputMesh.indexFormat; #endif meshSimplifier.Simplify(inputMesh, outputMesh, meshLOD.quality); #if !SINGLE_THREADED args.Result = outputMesh; }; #endif #if !SINGLE_THREADED worker.RunWorkerCompleted += (sender, args) => #endif { var outMesh = meshLOD.outputMesh; Debug.Log("Completed LOD " + outMesh.name); #if !SINGLE_THREADED var resultMesh = (WorkingMesh)args.Result; #else var resultMesh = outputMesh; #endif resultMesh.name = outMesh.name; resultMesh.ApplyToMesh(outMesh); outMesh.RecalculateBounds(); var outputMeshID = outMesh.GetInstanceID(); if (preprocessMeshes.Remove(outputMeshID)) { Debug.Log("Pre-process mesh complete: " + outputMeshID); } }; #if !SINGLE_THREADED worker.RunWorkerAsync(); #endif }
private MeshLOD ReadMeshLOD(Stream s) { MeshLOD r = new MeshLOD(); r.BoneDataCount = Helpers.ReadInt(s); r.Unknown02 = Helpers.ReadInt(s); r.NumSubObjects = Helpers.ReadInt(s); r.SectionsOffset = Helpers.ReadLong(s); r.Unknown03 = Helpers.ReadInt(s); r.DataOffsets = new long[4]; r.DataValues = new int[4]; for (int i = 0; i < 4; i++) { r.DataOffsets[i] = Helpers.ReadLong(s); r.DataValues[i] = Helpers.ReadInt(s); } r.Unknown04 = Helpers.ReadInt(s); r.IndexBufferSize = Helpers.ReadInt(s); r.VertexBufferSize = Helpers.ReadInt(s); r.Unknown05 = Helpers.ReadInt(s); r.ChunkID = new byte[16]; for (int i = 0; i < 16; i++) { r.ChunkID[i] = (byte)s.ReadByte(); } r.InlineDataOffset = Helpers.ReadInt(s); r.Unknown07 = Helpers.ReadInt(s); r.Unknown08 = Helpers.ReadInt(s); r.String01 = SerializeString(s); r.String02 = SerializeString(s); r.String03 = SerializeString(s); r.Unknown09 = Helpers.ReadInt(s); r.Unknown10 = Helpers.ReadInt(s); r.Unknown11 = Helpers.ReadInt(s); r.BoneCount = Helpers.ReadInt(s); r.BoneData = new List <long>(); for (int i = 0; i < r.BoneDataCount; i++) { r.BoneData.Add(Helpers.ReadLong(s)); r.BoneData.Add(Helpers.ReadLong(s)); } r.Unknown13 = Helpers.ReadInt(s); r.Sections = new List <MeshSection>(); if (r.NumSubObjects > 0) { s.Seek(r.SectionsOffset, SeekOrigin.Begin); for (int i = 0; i < r.NumSubObjects; i++) { r.Sections.Add(ReadMeshSection(s)); } } for (int i = 0; i < r.NumSubObjects; i++) { if (r.Sections[i].Unknown05 > 0) { int NumSubObjectBones = (r.Sections[i].Unknown04 >> 24); r.Sections[i].SubBoneList = new ushort[NumSubObjectBones]; s.Seek(r.Sections[i].Unknown05, 0); for (int j = 0; j < NumSubObjectBones; j++) { r.Sections[i].SubBoneList[j] = Helpers.ReadUShort(s); } } } return(r); }
public void LoadChunkData(MeshLOD lod, Stream s) { foreach (MeshSection sec in lod.Sections) { s.Seek(sec.VertexBufferOffset, 0); sec.VertexBuffer = new List <Vertex>(); for (int i = 0; i < sec.VertexCount; i++) { Vertex v = new Vertex(); long pos = s.Position; foreach (VertexDescriptor desc in sec.VertexEntries) { s.Seek(pos + desc.Offset, 0); switch (desc.VertexType) { case 0x301: v.Position.x = Helpers.ReadFloat(s); v.Position.y = Helpers.ReadFloat(s); v.Position.z = Helpers.ReadFloat(s); break; case 0x701: v.Position.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Position.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Position.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x806: v.Normals.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Normals.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Normals.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x807: v.Tangents.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Tangents.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Tangents.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x808: v.Bitangents.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Bitangents.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Bitangents.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x621: v.TexCoords.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.TexCoords.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0xd04: v.BoneWeights = new float[4]; for (int k = 0; k < 4; k++) { v.BoneWeights[k] = ((float)s.ReadByte()) / 255f; } break; case 0xc02: v.BoneIndices = new int[4]; for (int m = 0; m < 4; m++) { v.BoneIndices[m] = s.ReadByte(); } break; } s.Seek(pos + sec.VertexStride, 0); } sec.VertexBuffer.Add(v); } sec.IndexBuffer = new List <Triangle>(); s.Seek(lod.VertexBufferSize + sec.StartIndex * 2, 0); for (int i = 0; i < sec.TriangleCount; i++) { sec.IndexBuffer.Add(new Triangle(Helpers.ReadUShort(s), Helpers.ReadUShort(s), Helpers.ReadUShort(s))); } } }
public string LODToString(MeshLOD lod) { StringBuilder sb = new StringBuilder(); sb.AppendFormat(".BoneDataCount : {0}\n", lod.BoneDataCount); sb.AppendFormat(".Unknown02 : 0x{0}\n", lod.Unknown02.ToString("X8")); sb.AppendFormat(".NumSections : {0}\n", lod.NumSubObjects); sb.AppendFormat(".SectionsOffset : 0x{0}\n", lod.SectionsOffset.ToString("X16")); sb.AppendFormat(".Unknown03 : 0x{0}\n", lod.Unknown03.ToString("X8")); for (int i = 0; i < 4; i++) sb.AppendFormat(".Data Entry[{0}] : 0x{1} - 0x{2}\n", i, lod.DataOffsets[i].ToString("X16"), lod.DataValues[i].ToString("X8")); sb.AppendFormat(".Unknown04 : 0x{0}\n", lod.Unknown04.ToString("X8")); sb.AppendFormat(".IndexBufferSize : 0x{0}\n", lod.IndexBufferSize.ToString("X8")); sb.AppendFormat(".VertexBufferSize : 0x{0}\n", lod.VertexBufferSize.ToString("X8")); sb.AppendFormat(".Unknown05 : 0x{0}\n", lod.Unknown05.ToString("X8")); sb.AppendFormat(".ChunkID : 0x{0}\n", Helpers.ByteArrayToHexString(lod.ChunkID)); sb.AppendFormat(".InlineDataOffset : 0x{0}\n", lod.InlineDataOffset.ToString("X8")); sb.AppendFormat(".Unknown07 : 0x{0}\n", lod.Unknown07.ToString("X8")); sb.AppendFormat(".Unknown08 : 0x{0}\n", lod.Unknown08.ToString("X8")); sb.AppendFormat(".String01 : {0}\n", lod.String01); sb.AppendFormat(".String02 : {0}\n", lod.String02); sb.AppendFormat(".String03 : {0}\n", lod.String03); sb.AppendFormat(".Unknown09 : 0x{0}\n", lod.Unknown09.ToString("X8")); sb.AppendFormat(".Unknown10 : 0x{0}\n", lod.Unknown10.ToString("X8")); sb.AppendFormat(".Unknown11 : 0x{0}\n", lod.Unknown11.ToString("X8")); sb.AppendFormat(".BoneCount : {0}\n", lod.BoneCount); sb.AppendFormat(".BoneData : "); for (int i = 0; i < lod.BoneData.Count / 2; i++) sb.AppendFormat("[0x" + lod.BoneData[i * 2].ToString("X") + " - 0x" + lod.BoneData[i * 2 + 1].ToString("X") + "] "); sb.AppendFormat("\n.Unknown03 : 0x{0}\n", lod.Unknown13.ToString("X8")); int count = 0; foreach (MeshSection sec in lod.Sections) sb.AppendFormat(".[Section {0}]:\n{1}", count++, SectionToString(sec)); return sb.ToString(); }
private MeshLOD ReadMeshLOD(Stream s) { MeshLOD r = new MeshLOD(); r.BoneDataCount = Helpers.ReadInt(s); r.Unknown02 = Helpers.ReadInt(s); r.NumSubObjects = Helpers.ReadInt(s); r.SectionsOffset = Helpers.ReadLong(s); r.Unknown03 = Helpers.ReadInt(s); r.DataOffsets = new long[4]; r.DataValues = new int[4]; for (int i = 0; i < 4; i++) { r.DataOffsets[i] = Helpers.ReadLong(s); r.DataValues[i] = Helpers.ReadInt(s); } r.Unknown04 = Helpers.ReadInt(s); r.IndexBufferSize = Helpers.ReadInt(s); r.VertexBufferSize = Helpers.ReadInt(s); r.Unknown05 = Helpers.ReadInt(s); r.ChunkID = new byte[16]; for (int i = 0; i < 16; i++) r.ChunkID[i] = (byte)s.ReadByte(); r.InlineDataOffset = Helpers.ReadInt(s); r.Unknown07 = Helpers.ReadInt(s); r.Unknown08 = Helpers.ReadInt(s); r.String01 = SerializeString(s); r.String02 = SerializeString(s); r.String03 = SerializeString(s); r.Unknown09 = Helpers.ReadInt(s); r.Unknown10 = Helpers.ReadInt(s); r.Unknown11 = Helpers.ReadInt(s); r.BoneCount = Helpers.ReadInt(s); r.BoneData = new List<long>(); for (int i = 0; i < r.BoneDataCount; i++) { r.BoneData.Add(Helpers.ReadLong(s)); r.BoneData.Add(Helpers.ReadLong(s)); } r.Unknown13 = Helpers.ReadInt(s); r.Sections = new List<MeshSection>(); if (r.NumSubObjects > 0) { s.Seek(r.SectionsOffset, SeekOrigin.Begin); for (int i = 0; i < r.NumSubObjects; i++) r.Sections.Add(ReadMeshSection(s)); } for (int i = 0; i < r.NumSubObjects; i++) if (r.Sections[i].Unknown05 > 0) { int NumSubObjectBones = (r.Sections[i].Unknown04 >> 24); r.Sections[i].SubBoneList = new ushort[NumSubObjectBones]; s.Seek(r.Sections[i].Unknown05, 0); for (int j = 0; j < NumSubObjectBones; j++) r.Sections[i].SubBoneList[j] = Helpers.ReadUShort(s); } return r; }
public PhysicsConvexShape Compute(MeshLOD m, bool useSingleConvex = false) { if (m_VHacd == IntPtr.Zero) { throw new ObjectDisposedException("VHACD"); } var points = new double[m.Vertices.Count * 3]; var tris = new int[m.Triangles.Count * 3]; int idx = 0; int vertices = m.Vertices.Count; foreach (Vector3 v in m.Vertices) { points[idx++] = v.X; points[idx++] = v.Y; points[idx++] = v.Z; } idx = 0; foreach (Triangle t in m.Triangles) { tris[idx++] = t.Vertex1; tris[idx++] = t.Vertex2; tris[idx++] = t.Vertex3; if (t.Vertex1 >= vertices || t.Vertex2 >= vertices || t.Vertex3 >= vertices) { throw new InputDataException("Invalid triangle found"); } } if (idx == 0) { throw new InputDataException("No triangles found"); } Parameters p = Parameters.Defaults; if (useSingleConvex) { p.Depth = 0; } if (!VHacd_Compute(m_VHacd, points, 3, (uint)m.Vertices.Count, tris, 3, (uint)m.Triangles.Count, ref p)) { throw new InputDataException("Decompose failed"); } var shape = new PhysicsConvexShape(); int numhulls = VHacd_GetNConvexHulls(m_VHacd); for (uint hullidx = 0; hullidx < numhulls; ++hullidx) { var hull = new ConvexHull(); VHacd_GetConvexHull(m_VHacd, hullidx, ref hull); var resPoints = new double[hull.NumPoints * 3]; Marshal.Copy(hull.Points, resPoints, 0, hull.NumPoints * 3); var resTris = new int[hull.NumTriangles * 3]; Marshal.Copy(hull.Triangles, resTris, 0, hull.NumTriangles * 3); var cHull = new PhysicsConvexShape.ConvexHull(); for (int vertidx = 0; vertidx < hull.NumPoints * 3; vertidx += 3) { cHull.Vertices.Add(new Vector3( resPoints[vertidx + 0], resPoints[vertidx + 1], resPoints[vertidx + 2])); } int vCount = cHull.Vertices.Count; for (int triidx = 0; triidx < hull.NumTriangles * 3; ++triidx) { int tri = resTris[triidx]; if (tri >= vCount || tri < 0) { m_Log.ErrorFormat("Tri Index out of range"); throw new InputDataException("Tri index out of range"); } cHull.Triangles.Add(resTris[triidx]); } shape.Hulls.Add(cHull); } return(shape); }
public bool Run() { DumpParams(m_Shape); MeshLOD mesh = m_Shape.ToMesh(m_AssetService); var checkList = new List <string>(); var usedVerts = new List <int>(); bool success = true; foreach (Triangle tri in mesh.Triangles) { var tridx = new int[3] { tri.Vertex1, tri.Vertex2, tri.Vertex3 }; Array.Sort(tridx); string k = string.Join(",", tridx); if (checkList.Contains(k)) { m_Log.WarnFormat("Duplicate triangle found: {0} {1} {2}", tri.Vertex1, tri.Vertex2, tri.Vertex3); success = false; } if (tri.Vertex1 == tri.Vertex3 || tri.Vertex1 == tri.Vertex2 || tri.Vertex2 == tri.Vertex3) { m_Log.WarnFormat("Degenerate triangle found: {0} {1} {2}", tri.Vertex1, tri.Vertex2, tri.Vertex3); success = false; } else if ((mesh.Vertices[tri.Vertex1] == mesh.Vertices[tri.Vertex2] || mesh.Vertices[tri.Vertex1] == mesh.Vertices[tri.Vertex3] || mesh.Vertices[tri.Vertex2] == mesh.Vertices[tri.Vertex3]) && (m_Shape.ShapeType != PrimitiveShapeType.Sculpt || m_Shape.SculptType == PrimitiveSculptType.Mesh)) { m_Log.WarnFormat("Degenerate triangle found: {0} {1} {2}", tri.Vertex1, tri.Vertex2, tri.Vertex3); } if (!usedVerts.Contains(tri.Vertex1)) { usedVerts.Add(tri.Vertex1); } if (!usedVerts.Contains(tri.Vertex2)) { usedVerts.Add(tri.Vertex2); } if (!usedVerts.Contains(tri.Vertex3)) { usedVerts.Add(tri.Vertex3); } checkList.Add(k); } checkList.Clear(); foreach (Vector3 v in mesh.Vertices) { string k = v.ToString(); if (checkList.Contains(k)) { m_Log.WarnFormat("Duplicate vertex found: {0}", k); } checkList.Add(k); } for (int i = 0; i < mesh.Vertices.Count; ++i) { if (!usedVerts.Contains(i)) { m_Log.WarnFormat("Unused vertex found: {0}: {1}", i, mesh.Vertices[i]); } } m_Log.InfoFormat("Generated vertices: {0}", mesh.Vertices.Count); m_Log.InfoFormat("Generated triangles: {0}", mesh.Triangles.Count); /* write a blender .raw */ mesh.DumpToBlenderRaw(m_OutputFileName); return(success); }
internal static MeshLOD SculptMeshToMesh(this Bitmap bitmap, ObjectPart.PrimitiveShape.Decoded shape, bool generate_uv = false) { bool mirror = shape.IsSculptMirrored; var mesh = new MeshLOD(); bool reverse_horizontal = shape.IsSculptInverted ? !mirror : mirror; PrimitiveSculptType sculptType = shape.SculptType; int sculptSizeS; int sculptSizeT; int sculptVerts = bitmap.Width * bitmap.Height / 4; if (sculptVerts > 32 * 32) { sculptVerts = 32 * 32; } double ratio = (double)bitmap.Width / bitmap.Height; sculptSizeS = (int)Math.Sqrt(sculptVerts / ratio); sculptSizeS = Math.Max(sculptSizeS, 4); sculptSizeT = sculptVerts / sculptSizeS; sculptSizeT = Math.Max(sculptSizeT, 4); sculptSizeS = sculptVerts / sculptSizeT; /* generate vertex map */ for (int s = 0; s < sculptSizeS; ++s) { for (int t = 0; t < sculptSizeT; ++t) { int reversed_t = t; if (reverse_horizontal) { reversed_t = sculptSizeT - t - 1; } var x = (int)((double)reversed_t / (sculptSizeT - 1) * bitmap.Width); var y = (int)((double)s / (sculptSizeS - 1) * bitmap.Height); if (y == 0) { if (sculptType == PrimitiveSculptType.Sphere) { x = bitmap.Width / 2; } } else if (y == bitmap.Height) { y = (sculptType == PrimitiveSculptType.Torus) ? 0 : bitmap.Height - 1; if (sculptType == PrimitiveSculptType.Sphere) { x = bitmap.Width / 2; } } if (x == bitmap.Width) { switch (sculptType) { case PrimitiveSculptType.Sphere: case PrimitiveSculptType.Torus: case PrimitiveSculptType.Cylinder: x = 0; break; default: x = bitmap.Width - 1; break; } } Vector3 v = bitmap.GetVertex(x, y, mirror); mesh.Vertices.Add(v); if (generate_uv) { var uv = new UVCoord { U = (float)reversed_t / (sculptSizeS - t), V = (float)s / (sculptSizeS - 1) }; mesh.UVCoords.Add(uv); } } } /* generate triangles */ for (int row = 0; row < sculptSizeS - 1; ++row) { int rowIndex = row * sculptSizeT; int row2Index = rowIndex + sculptSizeT; for (int col = 0; col < sculptSizeT - 1; ++col) { var tri = new Triangle { Vertex1 = rowIndex + col, Vertex2 = row2Index + col + 1, Vertex3 = rowIndex + col + 1 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = rowIndex + col, Vertex2 = row2Index + col, Vertex3 = row2Index + col + 1 }; mesh.Triangles.Add(tri); } } return(mesh); }
void OnPostprocessModel(GameObject go) { if (!go.GetComponentInChildren <LODGroup>() && meshSimplifierType != null) { if (go.GetComponentsInChildren <SkinnedMeshRenderer>().Any()) { Debug.Log("Automatic LOD generation on does not currently support skinned meshes on import"); return; } var originalMeshFilters = go.GetComponentsInChildren <MeshFilter>(); uint polyCount = 0; foreach (var mf in originalMeshFilters) { var m = mf.sharedMesh; for (int i = 0; i < m.subMeshCount; i++) { var topology = m.GetTopology(i); var indexCount = m.GetIndexCount(i); switch (topology) { case MeshTopology.Quads: indexCount /= 4; break; case MeshTopology.Triangles: indexCount /= 3; break; case MeshTopology.Lines: case MeshTopology.LineStrip: indexCount /= 2; break; } polyCount += indexCount; } } var meshLODs = new List <MeshLOD>(); var preprocessMeshes = new HashSet <int>(); var lodData = GetLODData(assetPath); var overrideDefaults = lodData.overrideDefaults; var importSettings = lodData.importSettings; // It's possible to override defaults to either generate on import or to not generate and use specified // LODs in the override, but in the case where we are not overriding and globally we are not generating // on import, then there should be no further processing. if (!overrideDefaults && !enabled) { return; } if (importSettings.generateOnImport) { if (importSettings.maxLODGenerated == 0 && polyCount <= importSettings.initialLODMaxPolyCount) { return; } var simplifierType = Type.GetType(importSettings.meshSimplifier) ?? meshSimplifierType; if (polyCount > importSettings.initialLODMaxPolyCount) { foreach (var mf in originalMeshFilters) { var inputMesh = mf.sharedMesh; var outputMesh = new Mesh(); outputMesh.name = inputMesh.name; outputMesh.bounds = inputMesh.bounds; mf.sharedMesh = outputMesh; var meshLOD = new MeshLOD(); meshLOD.inputMesh = inputMesh; meshLOD.outputMesh = outputMesh; meshLOD.quality = (float)importSettings.initialLODMaxPolyCount / (float)polyCount; meshLOD.meshSimplifierType = simplifierType; meshLODs.Add(meshLOD); preprocessMeshes.Add(outputMesh.GetInstanceID()); } } // Clear out previous LOD data in case the number of LODs has been reduced for (int i = 0; i <= LODData.MaxLOD; i++) { lodData[i] = null; } lodData[0] = originalMeshFilters.Select(mf => mf.GetComponent <Renderer>()).ToArray(); for (int i = 1; i <= importSettings.maxLODGenerated; i++) { var lodMeshes = new List <Renderer>(); foreach (var mf in originalMeshFilters) { var inputMesh = mf.sharedMesh; var lodTransform = EditorUtility.CreateGameObjectWithHideFlags(mf.name, k_DefaultHideFlags, typeof(MeshFilter), typeof(MeshRenderer)).transform; lodTransform.parent = mf.transform; lodTransform.localPosition = Vector3.zero; lodTransform.localRotation = Quaternion.identity; var lodMF = lodTransform.GetComponent <MeshFilter>(); var lodRenderer = lodTransform.GetComponent <MeshRenderer>(); AppendLODNameToRenderer(lodRenderer, i); var outputMesh = new Mesh(); outputMesh.name = string.Format("{0} LOD{1}", inputMesh.name, i); outputMesh.bounds = inputMesh.bounds; lodMF.sharedMesh = outputMesh; lodMeshes.Add(lodRenderer); EditorUtility.CopySerialized(mf.GetComponent <MeshRenderer>(), lodRenderer); var meshLOD = new MeshLOD(); meshLOD.inputMesh = inputMesh; meshLOD.outputMesh = outputMesh; meshLOD.quality = Mathf.Pow(0.5f, i); meshLOD.meshSimplifierType = simplifierType; meshLODs.Add(meshLOD); } lodData[i] = lodMeshes.ToArray(); } // Change the name of the original renderers last, so the name change doesn't end up in the clones for other LODs AppendLODNameToRenderers(go.transform, 0); if (meshLODs.Count > 0) { if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(lodData))) { AssetDatabase.CreateAsset(lodData, GetLODDataPath(assetPath)); } else { var objects = AssetDatabase.LoadAllAssetsAtPath(GetLODDataPath(assetPath)); foreach (var o in objects) { var mesh = o as Mesh; if (mesh) { UnityObject.DestroyImmediate(mesh, true); } } EditorUtility.SetDirty(lodData); } meshLODs.ForEach(ml => AssetDatabase.AddObjectToAsset(ml.outputMesh, lodData)); AssetDatabase.SaveAssets(); foreach (var ml in meshLODs) { GenerateMeshLOD(ml, preprocessMeshes); } } } else { // Don't allow overriding LOD0 lodData[0] = originalMeshFilters.Select(mf => { var r = mf.GetComponent <Renderer>(); AppendLODNameToRenderer(r, 0); return(r); }).ToArray(); for (int i = 1; i <= LODData.MaxLOD; i++) { var renderers = lodData[i]; for (int j = 0; j < renderers.Length; j++) { var r = renderers[j]; var lodTransform = EditorUtility.CreateGameObjectWithHideFlags(r.name, k_DefaultHideFlags, typeof(MeshFilter), typeof(MeshRenderer)).transform; lodTransform.parent = go.transform; lodTransform.localPosition = Vector3.zero; var lodMF = lodTransform.GetComponent <MeshFilter>(); var lodRenderer = lodTransform.GetComponent <MeshRenderer>(); EditorUtility.CopySerialized(r.GetComponent <MeshFilter>(), lodMF); EditorUtility.CopySerialized(r, lodRenderer); AppendLODNameToRenderer(lodRenderer, i); renderers[j] = lodRenderer; } } } List <LOD> lods = new List <LOD>(); var maxLODFound = -1; for (int i = 0; i <= LODData.MaxLOD; i++) { var renderers = lodData[i]; if (renderers == null || renderers.Length == 0) { break; } maxLODFound++; } var importerRef = new SerializedObject(assetImporter); var importerLODLevels = importerRef.FindProperty("m_LODScreenPercentages"); for (int i = 0; i <= maxLODFound; i++) { var lod = new LOD(); lod.renderers = lodData[i]; var screenPercentage = i == maxLODFound ? 0.01f : Mathf.Pow(0.5f, i + 1); // Use the model importer percentages if they exist if (i < importerLODLevels.arraySize) { var element = importerLODLevels.GetArrayElementAtIndex(i); screenPercentage = element.floatValue; } lod.screenRelativeTransitionHeight = screenPercentage; lods.Add(lod); } var lodGroup = go.AddComponent <LODGroup>(); lodGroup.SetLODs(lods.ToArray()); lodGroup.RecalculateBounds(); // Keep model importer in sync importerLODLevels.ClearArray(); for (int i = 0; i < lods.Count; i++) { var lod = lods[i]; importerLODLevels.InsertArrayElementAtIndex(i); var element = importerLODLevels.GetArrayElementAtIndex(i); element.floatValue = lod.screenRelativeTransitionHeight; } importerRef.ApplyModifiedPropertiesWithoutUndo(); s_ModelAssetsProcessed.Add(assetPath); } }
void GenerateLODs() { int maxLOD = 1; var go = gameObject; var hlodLayer = LayerMask.NameToLayer(HLODLayer); var lodGroup = go.GetComponent <LODGroup>(); if (lodGroup) { var lods = new LOD[maxLOD + 1]; var lod0 = lodGroup.GetLODs()[0]; lod0.screenRelativeTransitionHeight = 0.5f; lods[0] = lod0; var meshes = new List <Mesh>(); var totalMeshCount = maxLOD * lod0.renderers.Length; for (int l = 1; l <= maxLOD; l++) { var lodRenderers = new List <MeshRenderer>(); foreach (var mr in lod0.renderers) { var mf = mr.GetComponent <MeshFilter>(); var sharedMesh = mf.sharedMesh; var lodTransform = EditorUtility.CreateGameObjectWithHideFlags(string.Format("{0} LOD{1}", sharedMesh.name, l), k_DefaultHideFlags, typeof(MeshFilter), typeof(MeshRenderer)).transform; lodTransform.gameObject.layer = hlodLayer; lodTransform.SetPositionAndRotation(mf.transform.position, mf.transform.rotation); lodTransform.localScale = mf.transform.lossyScale; lodTransform.SetParent(mf.transform, true); var lodMF = lodTransform.GetComponent <MeshFilter>(); var lodRenderer = lodTransform.GetComponent <MeshRenderer>(); lodRenderers.Add(lodRenderer); EditorUtility.CopySerialized(mf, lodMF); EditorUtility.CopySerialized(mf.GetComponent <MeshRenderer>(), lodRenderer); var simplifiedMesh = new Mesh(); simplifiedMesh.name = sharedMesh.name + string.Format(" LOD{0}", l); lodMF.sharedMesh = simplifiedMesh; meshes.Add(simplifiedMesh); var meshLOD = MeshLOD.GetGenericInstance(meshSimplifierType); meshLOD.InputMesh = sharedMesh; meshLOD.OutputMesh = simplifiedMesh; meshLOD.Quality = Mathf.Pow(0.5f, l); } var lod = lods[l]; lod.renderers = lodRenderers.ToArray(); lod.screenRelativeTransitionHeight = l == maxLOD ? 0.01f : Mathf.Pow(0.5f, l + 1); lods[l] = lod; } lodGroup.ForceLOD(0); lodGroup.SetLODs(lods.ToArray()); lodGroup.RecalculateBounds(); lodGroup.ForceLOD(-1); #if UNITY_2018_2_OR_NEWER var prefab = PrefabUtility.GetCorrespondingObjectFromSource(go); #else var prefab = PrefabUtility.GetPrefabParent(go); #endif if (prefab) { var assetPath = AssetDatabase.GetAssetPath(prefab); var pathPrefix = Path.GetDirectoryName(assetPath) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(assetPath); var lodsAssetPath = pathPrefix + "_lods.asset"; ObjectUtils.CreateAssetFromObjects(meshes.ToArray(), lodsAssetPath); } } }
public void LoadChunkData(MeshLOD lod, Stream s) { foreach (MeshSection sec in lod.Sections) { s.Seek(sec.VertexBufferOffset, 0); sec.VertexBuffer = new List<Vertex>(); for (int i = 0; i < sec.VertexCount; i++) { Vertex v = new Vertex(); long pos = s.Position; foreach (VertexDescriptor desc in sec.VertexEntries) { s.Seek(pos + desc.Offset, 0); switch (desc.VertexType) { case 0x301: v.Position.x = Helpers.ReadFloat(s); v.Position.y = Helpers.ReadFloat(s); v.Position.z = Helpers.ReadFloat(s); break; case 0x701: v.Position.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Position.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Position.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x806: v.Normals.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Normals.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Normals.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x807: v.Tangents.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Tangents.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Tangents.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x808: v.Bitangents.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Bitangents.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.Bitangents.z = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0x621: v.TexCoords.x = HalfUtils.Unpack(Helpers.ReadUShort(s)); v.TexCoords.y = HalfUtils.Unpack(Helpers.ReadUShort(s)); break; case 0xd04: v.BoneWeights = new float[4]; for (int k = 0; k < 4; k++) v.BoneWeights[k] = ((float)s.ReadByte()) / 255f; break; case 0xc02: v.BoneIndices = new int[4]; for (int m = 0; m < 4; m++) v.BoneIndices[m] = s.ReadByte(); break; } s.Seek(pos + sec.VertexStride, 0); } sec.VertexBuffer.Add(v); } sec.IndexBuffer = new List<Triangle>(); s.Seek(lod.VertexBufferSize + sec.StartIndex * 2, 0); for (int i = 0; i < sec.TriangleCount; i++) sec.IndexBuffer.Add(new Triangle(Helpers.ReadUShort(s), Helpers.ReadUShort(s), Helpers.ReadUShort(s))); } }
private static PhysicsConvexShape GenerateDefaultAvatarShape() { var meshLod = new MeshLOD(); meshLod.Vertices.Add(new Vector3(-0.5, -0.5, 0)); meshLod.Vertices.Add(new Vector3(0.5, -0.5, 0)); meshLod.Vertices.Add(new Vector3(0.5, 0.5, 0)); meshLod.Vertices.Add(new Vector3(-0.5, 0.5, 0)); meshLod.Vertices.Add(new Vector3(-0.1, -0.1, -0.5)); meshLod.Vertices.Add(new Vector3(0.1, -0.1, -0.5)); meshLod.Vertices.Add(new Vector3(0.1, 0.1, -0.5)); meshLod.Vertices.Add(new Vector3(-0.1, 0.1, -0.5)); meshLod.Vertices.Add(new Vector3(-0.5, -0.5, 0.5)); meshLod.Vertices.Add(new Vector3(0.5, -0.5, 0.5)); meshLod.Vertices.Add(new Vector3(0.5, 0.5, 0.5)); meshLod.Vertices.Add(new Vector3(-0.5, 0.5, 0.5)); #region Top meshLod.Triangles.Add(new Triangle(8, 9, 10)); meshLod.Triangles.Add(new Triangle(11, 8, 10)); #endregion #region Bottom meshLod.Triangles.Add(new Triangle(5, 4, 6)); meshLod.Triangles.Add(new Triangle(6, 4, 7)); #endregion #region Lower Sides A meshLod.Triangles.Add(new Triangle(1, 4, 5)); meshLod.Triangles.Add(new Triangle(1, 0, 4)); #endregion #region Lower Sides B meshLod.Triangles.Add(new Triangle(1, 5, 6)); meshLod.Triangles.Add(new Triangle(2, 1, 6)); #endregion #region Lower Sides C meshLod.Triangles.Add(new Triangle(2, 6, 7)); meshLod.Triangles.Add(new Triangle(3, 2, 7)); #endregion #region Lower Sides D meshLod.Triangles.Add(new Triangle(4, 3, 7)); meshLod.Triangles.Add(new Triangle(4, 0, 3)); #endregion #region Upper Sides A meshLod.Triangles.Add(new Triangle(0, 1, 8)); meshLod.Triangles.Add(new Triangle(8, 1, 9)); #endregion #region Upper Sides B meshLod.Triangles.Add(new Triangle(1, 2, 9)); meshLod.Triangles.Add(new Triangle(9, 2, 10)); #endregion #region Upper Sides C meshLod.Triangles.Add(new Triangle(2, 3, 10)); meshLod.Triangles.Add(new Triangle(10, 3, 11)); #endregion #region Upper Sides D meshLod.Triangles.Add(new Triangle(3, 0, 8)); meshLod.Triangles.Add(new Triangle(3, 8, 11)); #endregion return(DecomposeConvex(meshLod)); }
private static void BuildBasicTriangles(this ObjectPart.PrimitiveShape.Decoded shape, MeshLOD mesh, ProfileDetails path, double cutBegin, double cutEnd) { double twistBegin = shape.TwistBegin * Math.PI; double twistEnd = shape.TwistEnd * Math.PI; int verticeRowCount = path.Vertices.Count; int verticeTotalCount = mesh.Vertices.Count; int verticeRowEndCount = verticeRowCount; if (!shape.IsOpen && shape.IsHollow) { --verticeRowEndCount; } /* generate z-triangles */ for (int l = 0; l < verticeRowEndCount; ++l) { if (!shape.IsOpen && shape.IsHollow && l == verticeRowCount / 2 - 1) { continue; } for (int z = 0; z < verticeTotalCount - verticeRowCount; z += verticeRowCount) { /* p0 ___ p1 */ /* | | */ /* |___| */ /* p3 p2 */ /* tris: p0, p1, p2 and p0, p3, p2 */ /* p2 and p3 are on next row */ int z2 = z + verticeRowCount; int l2 = (l + 1) % verticeRowCount; /* loop closure */ var tri = new Triangle { Vertex1 = z + l, Vertex2 = z2 + l2, Vertex3 = z + l2 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = z + l, Vertex2 = z2 + l, Vertex3 = z2 + l2 }; mesh.Triangles.Add(tri); } } /* generate top and bottom triangles */ if (shape.IsHollow) { /* simpler just close neighboring dots */ /* no need for uneven check here. * The path generator always generates two pathes here which are connected and therefore always a multiple of two */ int bottomIndex = verticeTotalCount - verticeRowCount; for (int l = 0; l < verticeRowCount / 2; ++l) { int l2 = verticeRowCount - l - 1; var tri = new Triangle { Vertex1 = l, Vertex2 = l2, Vertex3 = l + 1 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + 1, Vertex2 = l2, Vertex3 = l2 - 1 }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + bottomIndex, Vertex2 = l2 + bottomIndex, Vertex3 = l + 1 + bottomIndex }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + 1 + bottomIndex, Vertex2 = l2 + bottomIndex, Vertex3 = l2 - 1 + bottomIndex }; mesh.Triangles.Add(tri); } } else { /* build a center point and connect all vertexes with triangles */ int centerpointTop = mesh.Vertices.Count; int bottomIndex = verticeTotalCount - verticeRowCount; mesh.Vertices.Add(ApplyTortureParams(shape, new Vector3(0, 0, 0), twistBegin, twistEnd, cutBegin)); int centerpointBottom = mesh.Vertices.Count; mesh.Vertices.Add(ApplyTortureParams(shape, new Vector3(0, 0, 0), twistBegin, twistEnd, cutEnd)); for (int l = 0; l < verticeRowCount; ++l) { int l2 = (l + 1) % verticeRowCount; var tri = new Triangle { Vertex1 = l, Vertex2 = l2, Vertex3 = centerpointTop }; mesh.Triangles.Add(tri); tri = new Triangle { Vertex1 = l + bottomIndex, Vertex2 = l2 + bottomIndex, Vertex3 = centerpointBottom }; mesh.Triangles.Add(tri); } } }
private PhysicsConvexShape ConvertToMesh(PrimitivePhysicsShapeType physicsShape, ObjectPart.PrimitiveShape shape) { PhysicsConvexShape convexShape = null; bool hasHullList = false; if (shape.Type == PrimitiveShapeType.Sculpt && shape.SculptType == PrimitiveSculptType.Mesh) { var m = new LLMesh(m_AssetService[shape.SculptMap]); if (physicsShape == PrimitivePhysicsShapeType.Convex) { #if DEBUG m_Log.DebugFormat("Selected convex of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif if (m.HasConvexPhysics()) { try { #if DEBUG m_Log.DebugFormat("Using convex of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif convexShape = m.GetConvexPhysics(false); hasHullList = convexShape.HasHullList; return(convexShape); } catch (NoSuchMeshDataException) { /* no shape */ #if DEBUG m_Log.DebugFormat("No convex in asset of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif } catch (Exception e) { m_Log.Warn($"Failed to get convex data of {shape.SculptType} {shape.SculptMap}", e); } } #if DEBUG else { m_Log.DebugFormat("No convex shape in {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); } #endif if (convexShape == null) { #if DEBUG m_Log.DebugFormat("Using decompose to single convex for {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif MeshLOD lod = m.GetLOD(LLMesh.LodLevel.LOD3); lod.Optimize(); convexShape = DecomposeConvex(lod, true); } } else { #if DEBUG m_Log.DebugFormat("Selected detailed physics of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif if (m.HasLOD(LLMesh.LodLevel.Physics)) { /* check for physics mesh before giving out the single hull */ #if DEBUG m_Log.DebugFormat("Using detailed physics of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif MeshLOD lod = m.GetLOD(LLMesh.LodLevel.Physics); lod.Optimize(); convexShape = DecomposeConvex(lod); } else if (m.HasConvexPhysics()) { #if DEBUG m_Log.DebugFormat("Using convex of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif try { convexShape = m.GetConvexPhysics(true); hasHullList = convexShape.HasHullList; } catch (NoSuchMeshDataException) { /* no shape */ #if DEBUG m_Log.DebugFormat("No suitable convex in asset of {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif } catch (Exception e) { m_Log.Warn($"Failed to get convex data of {shape.Type}/{shape.SculptType}/{shape.SculptMap}", e); } if (convexShape == null) { /* this way we keep convex hull type functional by having it only get active on PrimitivePhysicsShapeType.Prim */ #if DEBUG m_Log.DebugFormat("Using decompose to convex for {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif MeshLOD lod = m.GetLOD(LLMesh.LodLevel.LOD3); lod.Optimize(); convexShape = DecomposeConvex(lod); } } } } else { #if DEBUG m_Log.DebugFormat("Using decompose to convex for {0}/{1}/{2}", shape.Type, shape.SculptType, shape.SculptMap); #endif MeshLOD m = shape.ToMesh(m_AssetService); m.Optimize(); convexShape = DecomposeConvex(m, physicsShape == PrimitivePhysicsShapeType.Convex); } return(convexShape); }
private void GetObjectMatches(RayData ray, RayTestHitFlags flags, List <RayResult> results) { foreach (ObjectGroup grp in m_Scene.ObjectGroups) { if (grp.IsAttached) { /* ignore attachments */ continue; } /* flag checks are cheap so do those first */ if (((flags & RayTestHitFlags.NonPhantom) != 0 && !grp.IsPhantom) || ((flags & RayTestHitFlags.Phantom) != 0 && grp.IsPhantom) || ((flags & RayTestHitFlags.NonPhysical) != 0 && !grp.IsPhysics) || ((flags & RayTestHitFlags.Physical) != 0 && grp.IsPhysics)) { /* found a flag match */ } else { continue; } BoundingBox bbox; grp.GetBoundingBox(out bbox); bbox.CenterOffset = grp.GlobalPosition; bbox.Size *= grp.GlobalRotation; bbox.Size = bbox.Size.ComponentMax(-bbox.Size); double distance = IntersectBox(ray, ref bbox); if (distance < 0) { /* only process if linkset bounding box is hit */ continue; } foreach (ObjectPart part in grp.ValuesByKey1) { part.GetBoundingBox(out bbox); distance = IntersectBox(ray, ref bbox); if (distance < 0) { /* skip if not hit */ continue; } var res = new RayResult { ObjectId = grp.ID, PartId = part.ID }; /* calculate actual HitPoint and HitNormal */ ObjectPart.PrimitiveShape shape = part.Shape; MeshLOD lod = null; if (shape.Type == PrimitiveShapeType.Sculpt && shape.SculptType == PrimitiveSculptType.Mesh) { var m = new LLMesh(m_Scene.AssetService[shape.SculptMap]); foreach (LLMesh.LodLevel level in LodOrder) { if (m.HasLOD(level)) { lod = m.GetLOD(level); break; } } } else { lod = shape.ToMesh(m_Scene.AssetService); } if (lod != null) { lod.Optimize(); Vector3 normal; foreach (Triangle tri in lod.Triangles) { double dist = IntersectTri(ray, lod.Vertices[tri.Vertex1], lod.Vertices[tri.Vertex2], lod.Vertices[tri.Vertex3], out normal); if (dist >= 0) { res.HitNormalWorld = normal; res.HitPointWorld = ray.Origin + ray.Direction * dist; results.Add(res); break; } } } } } }