public void Awake() { #if UNITY_WEBGL if (workerThreadMode == WorkerThreadMode.Asynchronous) { Debug.LogWarning("Turbo Slicer will run synchronously because WebGL does not support threads.", this); workerThreadMode = WorkerThreadMode.Synchronous; } #endif if (preloadMeshes != null) { for (int i = 0; i < preloadMeshes.Length; i++) { var mesh = preloadMeshes[i]; var indices = new int[mesh.subMeshCount][]; for (int j = 0; j < mesh.subMeshCount; j++) { indices[j] = mesh.GetIndices(j); } //Note that this is NOT a usable mesh snapshot. It will need to be combined with live data at runtime. var rom = new MeshSnapshot(null, mesh.vertices, mesh.normals, mesh.uv, mesh.tangents, mesh.boneWeights, new Material[0], new BoneMetadata[0], null, indices); preloadedMeshes[mesh.GetInstanceID()] = rom; } } }
public void SeverByJoint(GameObject subject, string jointName, float rootTipProgression, Vector3?planeNormal) { //Sanity check: are we already slicing this? foreach (var extantState in jobStates) { if (ReferenceEquals(extantState.Specification.Subject, subject)) { //Debug.LogErrorFormat("Turbo Slicer was asked to slice '{0}' but this target is already enqueued.", subject.name); return; } } rootTipProgression = Mathf.Clamp01(rootTipProgression); //These here are in local space because they're only used to copy to the resultant meshes; they're not used //to transform the vertices. We expect a world-space slice input. Hackable hackable = null; { var hackables = subject.GetComponentsInChildren <Hackable>(); if (hackables.Length > 0) { if (hackables.Length > 1) { Debug.LogWarning("Limb Hacker found multiple slice configurations on object '" + subject.name + "'! Behavior is undefined."); } hackable = hackables[0]; } else { Debug.LogWarning("Limb Hacker found no slice configuration on object '" + subject.name + "'."); return; } } //We need information about which BONES are getting severed. var metadataByNodeName = new Dictionary <string, NodeMetadata>(); { var childTransformByName = new Dictionary <string, Transform>(); var parentKeyByKey = new Dictionary <string, string>(); foreach (Transform t in GetConcatenatedHierarchy(subject.transform)) { childTransformByName[t.name] = t; var parent = t.parent; if (t == subject.transform) { parent = null; } parentKeyByKey[t.name] = parent == null ? null : parent.name; } var severedByChildName = new Dictionary <string, bool>(); { foreach (string childName in childTransformByName.Keys) { severedByChildName[childName] = childName == jointName; } bool changesMade; do { changesMade = false; foreach (string childKey in childTransformByName.Keys) { bool severed = severedByChildName[childKey]; if (severed) { continue; } string parentKey = parentKeyByKey[childKey]; bool parentSevered; if (severedByChildName.TryGetValue(parentKey, out parentSevered) == false) { continue; } if (parentSevered) { severedByChildName[childKey] = true; changesMade = true; } } }while (changesMade); } foreach (var kvp in severedByChildName) { var t = childTransformByName[kvp.Key]; var isConsideredSevered = kvp.Value; metadataByNodeName[kvp.Key] = new NodeMetadata(t, isConsideredSevered); } } IEnumerable <MeshSnapshot> snapshots; var forwardPassAgent = subject.GetComponent <ForwardPassAgent>(); if (forwardPassAgent == null) { var snapshotBuilder = new List <MeshSnapshot>(); var skinnedMeshRenderers = subject.GetComponentsInChildren <SkinnedMeshRenderer>(true); foreach (var smr in skinnedMeshRenderers) { var mesh = smr.sharedMesh; var boneMetadata = new BoneMetadata[smr.bones.Length]; var bones = smr.bones; var bindPoses = mesh.bindposes; for (int i = 0; i < boneMetadata.Length; i++) { boneMetadata[i] = new BoneMetadata(i, bones[i].name, bindPoses[i]); } int?infillIndex = null; if (hackable.infillMaterial != null) { var mats = smr.sharedMaterials; for (int i = 0; i < mats.Length; i++) { if (hackable.infillMaterial == mats[i]) { infillIndex = i; break; } } } MeshSnapshot snapshot; MeshSnapshot preloadedFragment; if (preloadedMeshes.TryGetValue(mesh.GetInstanceID(), out preloadedFragment)) { //The preloaded fragments are missing data which is particular to the SMR. We'll combine it with such data here. snapshot = preloadedFragment.WithKey(smr.name).WithMaterials(smr.sharedMaterials).WithBoneMetadata(boneMetadata).WithInfillIndex(infillIndex); } else { var indices = new int[mesh.subMeshCount][]; for (int i = 0; i < mesh.subMeshCount; i++) { indices[i] = mesh.GetIndices(i); } snapshot = new MeshSnapshot( smr.name, mesh.vertices, mesh.normals, mesh.uv, mesh.tangents, mesh.boneWeights, smr.sharedMaterials, boneMetadata, infillIndex, indices); } snapshotBuilder.Add(snapshot); } snapshots = snapshotBuilder; } else { snapshots = forwardPassAgent.Snapshot; } var jobSpec = new JobSpecification(subject, hackable, snapshots, metadataByNodeName, hackable.infillMaterial, jointName, rootTipProgression, planeNormal, hackable.infillMode, true); var jobState = new JobState(jobSpec); try { switch (workerThreadMode) { case WorkerThreadMode.Asynchronous: jobStates.Add(jobState); #if NETFX_CORE && !UNITY_EDITOR System.Threading.Tasks.Task.Factory.StartNew(ThreadSafeHack.Slice, jobState); #else System.Threading.ThreadPool.QueueUserWorkItem(ThreadSafeHack.Slice, jobState); #endif break; case WorkerThreadMode.Synchronous: ThreadSafeHack.Slice(jobState); if (jobState.HasYield) { ConsumeJobYield(jobState); } else if (jobState.HasException) { throw jobState.Exception; } break; default: throw new System.NotImplementedException(); } } catch (System.Exception ex) { Debug.LogException(ex, subject); } }
public static MeshSnapshot EmbraceAndExtend(this MeshSnapshot source, ArrayBuilder <Vector3> newVertices, ArrayBuilder <Vector3> newNormals, ArrayBuilder <Vector2> newCoords, ArrayBuilder <BoneWeight> newWeights, ArrayBuilder <int>[] newIndicesBySubmesh) { const int Unassigned = -1; var vertexCount = newVertices.length; var submeshCount = newIndicesBySubmesh.Length; int[] transferTable; using (intArrayPool.Get(vertexCount, false, out transferTable)) { for (int i = 0; i < vertexCount; i++) { transferTable[i] = Unassigned; } var targetIndex = 0; var targetIndexArrays = new int[submeshCount][]; for (int submeshIndex = 0; submeshIndex < submeshCount; submeshIndex++) { var sourceIndices = newIndicesBySubmesh[submeshIndex]; var targetIndices = targetIndexArrays[submeshIndex] = new int[sourceIndices.length]; for (int i = 0; i < sourceIndices.length; i++) { int requestedVertex = sourceIndices.array[i]; int j = transferTable[requestedVertex]; if (j == Unassigned) { j = targetIndex; transferTable[requestedVertex] = j; targetIndex++; } targetIndices[i] = j; } } var newVertexCount = targetIndex; var targetVertices = new Vector3[newVertexCount]; var targetCoords = new Vector2[newVertexCount]; var targetNormals = new Vector3[newVertexCount]; var boneWeights = new BoneWeight[newVertexCount]; for (int i = 0; i < vertexCount; i++) { int j = transferTable[i]; if (j != Unassigned) { targetVertices[j] = newVertices.array[i]; targetCoords[j] = newCoords.array[i]; targetNormals[j] = newNormals.array[i]; boneWeights[j] = newWeights.array[i]; } } Vector4[] targetTangents; if (source.tangents.Length > 0) { //This code assumes that the new geometry is a proper superset of the old geometry. //But there is a catch; while new geometry is provided, new tangents are not. //So we source some tangents from the source data, but over that limit, we have to //generate them. targetTangents = new Vector4[newVertexCount]; var newGeometryStartsFrom = source.vertices.Length; for (int i = 0; i < newGeometryStartsFrom; i++) { int j = transferTable[i]; if (j != Unassigned) { targetTangents[j] = source.tangents[i]; } } //Based on code here: //http://www.cs.upc.edu/~virtual/G/1.%20Teoria/06.%20Textures/Tangent%20Space%20Calculation.pdf Vector3[] tan1, tan2; using (vectorThreePool.Get(vertexCount, true, out tan1)) using (vectorThreePool.Get(vertexCount, true, out tan2)) { for (int i = 0; i < newIndicesBySubmesh.Length; i++) { var triangles = newIndicesBySubmesh[i]; for (int j = 0; j < triangles.length;) { var j1 = triangles.array[j++]; var j2 = triangles.array[j++]; var j3 = triangles.array[j++]; Debug.Assert(j1 != j2 && j1 != j3); var isRelevant = j1 >= newGeometryStartsFrom || j2 >= newGeometryStartsFrom || j3 >= newGeometryStartsFrom; if (isRelevant) { var v1 = newVertices.array[j1]; var v2 = newVertices.array[j2]; var v3 = newVertices.array[j3]; //We have to test for degeneracy. var e1 = v1 - v2; var e2 = v1 - v3; const float epsilon = 1.0f / 65536.0f; if (e1.sqrMagnitude > epsilon && e2.sqrMagnitude > epsilon) { var w1 = newCoords.array[j1]; var w2 = newCoords.array[j2]; var w3 = newCoords.array[j3]; var x1 = v2.x - v1.x; var x2 = v3.x - v1.x; var y1 = v2.y - v1.y; var y2 = v3.y - v1.y; var z1 = v2.z - v1.z; var z2 = v3.z - v1.z; var s1 = w2.x - w1.x; var s2 = w3.x - w1.x; var t1 = w2.y - w1.y; var t2 = w3.y - w1.y; var r = 1.0f / (s1 * t2 - s2 * t1); var sX = (t2 * x1 - t1 * x2) * r; var sY = (t2 * y1 - t1 * y2) * r; var sZ = (t2 * z1 - t1 * z2) * r; var tX = (s1 * x2 - s2 * x1) * r; var tY = (s1 * y2 - s2 * y1) * r; var tZ = (s1 * z2 - s2 * z1) * r; var tan1j1 = tan1[j1]; var tan1j2 = tan1[j2]; var tan1j3 = tan1[j3]; var tan2j1 = tan2[j1]; var tan2j2 = tan2[j2]; var tan2j3 = tan2[j3]; tan1j1.x += sX; tan1j1.y += sY; tan1j1.z += sZ; tan1j2.x += sX; tan1j2.y += sY; tan1j2.z += sZ; tan1j3.x += sX; tan1j3.y += sY; tan1j3.z += sZ; tan2j1.x += tX; tan2j1.y += tY; tan2j1.z += tZ; tan2j2.x += tX; tan2j2.y += tY; tan2j2.z += tZ; tan2j3.x += tX; tan2j3.y += tY; tan2j3.z += tZ; tan1[j1] = tan1j1; tan1[j2] = tan1j2; tan1[j3] = tan1j3; tan2[j1] = tan2j1; tan2[j2] = tan2j2; tan2[j3] = tan2j3; } } } } for (int i = newGeometryStartsFrom; i < vertexCount; i++) { int j = transferTable[i]; if (j != Unassigned) { var n = newNormals.array[i]; var t = tan1[i]; // Gram-Schmidt orthogonalize Vector3.OrthoNormalize(ref n, ref t); targetTangents[j].x = t.x; targetTangents[j].y = t.y; targetTangents[j].z = t.z; // Calculate handedness targetTangents[j].w = (Vector3.Dot(Vector3.Cross(n, t), tan2[i]) < 0.0f) ? -1.0f : 1.0f; } } } } else { targetTangents = new Vector4[0]; } return(new MeshSnapshot(source.key, targetVertices, targetNormals, targetCoords, targetTangents, boneWeights, source.materials, source.boneMetadata, source.infillIndex, targetIndexArrays)); } }