void ConsumeJobYield(JobState jobState) { if (jobState.HasException) { Debug.LogException(jobState.Exception); } else { var jobSpecification = jobState.Specification; var jobYield = jobState.Yield; bonePresenceAlfaBuffer.Clear(); bonePresenceBravoBuffer.Clear(); foreach (var kvp in jobSpecification.NodeMetadata) { bonePresenceAlfaBuffer[kvp.Key] = kvp.Value.IsConsideredSevered; bonePresenceBravoBuffer[kvp.Key] = !kvp.Value.IsConsideredSevered; } var originalSubjectTransform = jobSpecification.Subject.transform; bool useAlternateForFront, useAlternateForBack; if (jobSpecification.Hackable.alternatePrefab == null) { useAlternateForFront = false; useAlternateForBack = false; } else { useAlternateForFront = jobSpecification.Hackable.cloneAlternate(bonePresenceAlfaBuffer); useAlternateForBack = jobSpecification.Hackable.cloneAlternate(bonePresenceBravoBuffer); } GameObject alfaObject, bravoObject; var backIsNew = useAlternateForBack; if (backIsNew) { var backSource = useAlternateForBack ? jobSpecification.Hackable.alternatePrefab : jobSpecification.Subject; bravoObject = (GameObject)Instantiate(backSource); bravoObject.name = string.Format("{0} (Bravo)", jobSpecification.Subject); } else { bravoObject = jobSpecification.Subject; } var alfaSource = useAlternateForFront ? jobSpecification.Hackable.alternatePrefab : jobSpecification.Subject; alfaObject = (GameObject)Instantiate(alfaSource); HandleHierarchy(alfaObject.transform, bonePresenceAlfaBuffer, jobSpecification.NodeMetadata); HandleHierarchy(bravoObject.transform, bonePresenceBravoBuffer, jobSpecification.NodeMetadata); var parent = originalSubjectTransform.parent; var position = originalSubjectTransform.localPosition; var scale = originalSubjectTransform.localScale; var rotation = originalSubjectTransform.localRotation; alfaObject.transform.parent = parent; alfaObject.transform.localPosition = position; alfaObject.transform.localScale = scale; alfaObject.transform.localRotation = rotation; alfaObject.layer = jobSpecification.Subject.layer; alfaObject.name = string.Format("{0} (Alfa)", jobSpecification.Subject); if (backIsNew) { bravoObject.transform.parent = parent; bravoObject.transform.localPosition = position; bravoObject.transform.localScale = scale; bravoObject.transform.localRotation = rotation; bravoObject.layer = jobSpecification.Subject.layer; Destroy(jobSpecification.Subject); } ApplySnapshotsToRoot(alfaObject, jobYield.Alfa); ApplySnapshotsToRoot(bravoObject, jobYield.Bravo); var results = new GameObject[] { alfaObject, bravoObject }; jobSpecification.Hackable.handleSlice(results); } }
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); } }