private int AddParticles(int amount) { // get constraint batches: ObiDistanceConstraintBatch distanceBatch = rope.DistanceConstraints.GetFirstBatch(); ObiBendConstraintBatch bendingBatch = rope.BendingConstraints.GetFirstBatch(); amount = Mathf.Min(amount, rope.PooledParticles); // if no particles can be added, just return. if (amount == 0) { return(0); } // find current constraint and hot constraint: int constraint = rope.GetConstraintIndexAtNormalizedCoordinate(normalizedCoord); rope.DistanceConstraints.RemoveFromSolver(null); rope.BendingConstraints.RemoveFromSolver(null); // find indices of first N inactive particles. we'll need them to create new rope. // the first and last particle indices in this array will be the ones in the current constraint. int[] newParticleIndices = new int[amount + 2]; for (int i = 0, j = 0; i < amount && j < rope.TotalParticles; ++j) { if (!rope.active[j]) { newParticleIndices[i + 1] = j; rope.active[j] = true; rope.invMasses[j] = 1.0f / ObiRope.DEFAULT_PARTICLE_MASS; ++i; } } // TODO: closed curves have a different amount of bend constraints! Vector4[] zeroVelocity = new Vector4[] { Vector4.zero }; Vector4[] refPosition1 = new Vector4[1]; Vector4[] refPosition2 = new Vector4[1]; if (direction) { // fill first and last indices of the new particles array with the ones in the current constraint: newParticleIndices[0] = distanceBatch.springIndices[constraint * 2]; newParticleIndices[newParticleIndices.Length - 1] = distanceBatch.springIndices[constraint * 2 + 1]; // update normalized coord: normalizedCoord = constraint / (float)(distanceBatch.ConstraintCount + amount); Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition1, 1, rope.particleIndices[newParticleIndices[0]]); Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition2, 1, rope.particleIndices[newParticleIndices[newParticleIndices.Length - 1]]); // update constraints: distanceBatch.SetParticleIndex(constraint, newParticleIndices[newParticleIndices.Length - 2], ObiDistanceConstraintBatch.DistanceIndexType.First, rope.Closed); bendingBatch.SetParticleIndex(constraint, newParticleIndices[newParticleIndices.Length - 2], ObiBendConstraintBatch.BendIndexType.First, rope.Closed); bendingBatch.SetParticleIndex(constraint - 1, newParticleIndices[1], ObiBendConstraintBatch.BendIndexType.Second, rope.Closed); // add constraints and particles: for (int i = 1; i < newParticleIndices.Length - 1; ++i) { Vector4[] pos = new Vector4[] { refPosition1[0] + (refPosition2[0] - refPosition1[0]) * i / (float)(newParticleIndices.Length - 1) * 0.5f }; Oni.SetParticlePositions(rope.Solver.OniSolver, pos, 1, rope.particleIndices[newParticleIndices[i]]); Oni.SetParticleVelocities(rope.Solver.OniSolver, zeroVelocity, 1, rope.particleIndices[newParticleIndices[i]]); int newConstraintIndex = constraint + i - 1; distanceBatch.InsertConstraint(newConstraintIndex, newParticleIndices[i - 1], newParticleIndices[i], rope.InterparticleDistance, 0, 0); bendingBatch.InsertConstraint(newConstraintIndex, newParticleIndices[i - 1], newParticleIndices[i + 1], newParticleIndices[i], 0, 0, 0); } } else { // fill first and last indices of the new particles array with the ones in the current constraint: newParticleIndices[0] = distanceBatch.springIndices[constraint * 2 + 1]; newParticleIndices[newParticleIndices.Length - 1] = distanceBatch.springIndices[constraint * 2]; // update normalized coord: normalizedCoord = (constraint + amount) / (float)(distanceBatch.ConstraintCount + amount); Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition1, 1, rope.particleIndices[newParticleIndices[0]]); Oni.GetParticlePositions(rope.Solver.OniSolver, refPosition2, 1, rope.particleIndices[newParticleIndices[newParticleIndices.Length - 1]]); // update constraints: distanceBatch.SetParticleIndex(constraint, newParticleIndices[newParticleIndices.Length - 2], ObiDistanceConstraintBatch.DistanceIndexType.Second, rope.Closed); bendingBatch.SetParticleIndex(constraint, newParticleIndices[1], ObiBendConstraintBatch.BendIndexType.First, rope.Closed); bendingBatch.SetParticleIndex(constraint - 1, newParticleIndices[newParticleIndices.Length - 2], ObiBendConstraintBatch.BendIndexType.Second, rope.Closed); // add constraints and particles: for (int i = 1; i < newParticleIndices.Length - 1; ++i) { Vector4[] pos = new Vector4[] { refPosition1[0] + (refPosition2[0] - refPosition1[0]) * i / (float)(newParticleIndices.Length - 1) * 0.5f }; Oni.SetParticlePositions(rope.Solver.OniSolver, pos, 1, rope.particleIndices[newParticleIndices[i]]); Oni.SetParticleVelocities(rope.Solver.OniSolver, zeroVelocity, 1, rope.particleIndices[newParticleIndices[i]]); distanceBatch.InsertConstraint(constraint + 1, newParticleIndices[i], newParticleIndices[i - 1], rope.InterparticleDistance, 0, 0); bendingBatch.InsertConstraint(constraint, newParticleIndices[i + 1], newParticleIndices[i - 1], newParticleIndices[i], 0, 0, 0); } } rope.DistanceConstraints.AddToSolver(null); rope.BendingConstraints.AddToSolver(null); rope.PushDataToSolver(ParticleData.ACTIVE_STATUS); rope.UsedParticles += amount; rope.RegenerateRestPositions(); return(amount); }
public override void OnInspectorGUI() { serializedObject.Update(); GUI.enabled = rope.Initialized; EditorGUI.BeginChangeCheck(); editMode = GUILayout.Toggle(editMode, new GUIContent("Edit particles", Resources.Load <Texture2D>("EditParticles")), "LargeButton"); if (EditorGUI.EndChangeCheck()) { SceneView.RepaintAll(); } GUI.enabled = true; EditorGUILayout.LabelField("Status: " + (rope.Initialized ? "Initialized":"Not initialized")); GUI.enabled = (rope.ropePath != null && rope.Section != null); if (GUILayout.Button("Initialize")) { if (!rope.Initialized) { CoroutineJob job = new CoroutineJob(); routine = EditorCoroutine.StartCoroutine(job.Start(rope.GeneratePhysicRepresentationForMesh())); EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } else { if (EditorUtility.DisplayDialog("Actor initialization", "Are you sure you want to re-initialize this actor?", "Ok", "Cancel")) { CoroutineJob job = new CoroutineJob(); routine = EditorCoroutine.StartCoroutine(job.Start(rope.GeneratePhysicRepresentationForMesh())); EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } } } GUI.enabled = true; GUI.enabled = rope.Initialized; if (GUILayout.Button("Set Rest State")) { Undo.RecordObject(rope, "Set rest state"); rope.PullDataFromSolver(ParticleData.POSITIONS | ParticleData.VELOCITIES); } if (GUILayout.Button("Set Particles to Layer")) { if (EditorUtility.DisplayDialog("Particle phases", "This will reset all per-particle layer values to the GameObject layer. Are you sure?", "Ok", "Cancel")) { Undo.RecordObject(rope, "Set particles to layer"); for (int i = 0; i < rope.phases.Length; i++) { rope.phases[i] = Oni.MakePhase(rope.gameObject.layer, rope.SelfCollisions?Oni.ParticlePhase.SelfCollide:0); } rope.PushDataToSolver(ParticleData.PHASES); } } GUI.enabled = true; if (rope.ropePath == null) { EditorGUILayout.HelpBox("Rope path spline is missing.", MessageType.Info); } if (rope.Section == null) { EditorGUILayout.HelpBox("Rope section is missing.", MessageType.Info); } EditorGUI.BeginChangeCheck(); ObiSolver solver = EditorGUILayout.ObjectField("Solver", rope.Solver, typeof(ObiSolver), true) as ObiSolver; if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(rope, "Set solver"); rope.Solver = solver; } bool newSelfCollisions = EditorGUILayout.Toggle(new GUIContent("Self collisions", "Enabling this allows particles generated by this actor to interact with each other."), rope.SelfCollisions); if (rope.SelfCollisions != newSelfCollisions) { Undo.RecordObject(rope, "Set self collisions"); rope.SelfCollisions = newSelfCollisions; } Editor.DrawPropertiesExcluding(serializedObject, "m_Script", "chainLinks"); bool newThicknessFromParticles = EditorGUILayout.Toggle(new GUIContent("Thickness from particles", "Enabling this will allow particle radius to influence rope thickness. Use it for variable-thickness ropes."), rope.ThicknessFromParticles); if (rope.ThicknessFromParticles != newThicknessFromParticles) { Undo.RecordObject(rope, "Set thickness from particles"); rope.ThicknessFromParticles = newThicknessFromParticles; } float newTwist = EditorGUILayout.FloatField(new GUIContent("Section twist", "Amount of twist applied to each section, in degrees."), rope.SectionTwist); if (rope.SectionTwist != newTwist) { Undo.RecordObject(rope, "Set section twist"); rope.SectionTwist = newTwist; } EditorGUILayout.Space(); EditorGUILayout.LabelField("Rendering", EditorStyles.boldLabel); ObiRope.RenderingMode newRenderMode = (ObiRope.RenderingMode)EditorGUILayout.EnumPopup(rope.RenderMode); if (rope.RenderMode != newRenderMode) { Undo.RecordObject(rope, "Set rope render mode"); rope.RenderMode = newRenderMode; } float newUVAnchor = EditorGUILayout.Slider(new GUIContent("UV anchor", "Normalized point along the rope where the V texture coordinate starts. Useful when changing rope length."), rope.UVAnchor, 0, 1); if (rope.UVAnchor != newUVAnchor) { Undo.RecordObject(rope, "Set rope uv anchor"); rope.UVAnchor = newUVAnchor; } // Render-mode specific stuff: if (rope.RenderMode != ObiRope.RenderingMode.Chain) { ObiRopeSection newSection = EditorGUILayout.ObjectField(new GUIContent("Section", "Section asset to be extruded along the rope path.") , rope.Section, typeof(ObiRopeSection), false) as ObiRopeSection; if (rope.Section != newSection) { Undo.RecordObject(rope, "Set rope section"); rope.Section = newSection; } float newThickness = EditorGUILayout.FloatField(new GUIContent("Section thickness scale", "Scales mesh thickness."), rope.SectionThicknessScale); if (rope.SectionThicknessScale != newThickness) { Undo.RecordObject(rope, "Set rope section thickness"); rope.SectionThicknessScale = newThickness; } uint newSmoothness = (uint)EditorGUILayout.IntSlider(new GUIContent("Smoothness", "Level of smoothing applied to the rope path."), Convert.ToInt32(rope.Smoothing), 0, 3); if (rope.Smoothing != newSmoothness) { Undo.RecordObject(rope, "Set smoothness"); rope.Smoothing = newSmoothness; } Vector2 newUVScale = EditorGUILayout.Vector2Field(new GUIContent("UV scale", "Scaling of the uv coordinates generated for the rope. The u coordinate wraps around the whole rope section, and the v spans the full length of the rope."), rope.UVScale); if (rope.UVScale != newUVScale) { Undo.RecordObject(rope, "Set rope uv scale"); rope.UVScale = newUVScale; } bool newNormalizeV = EditorGUILayout.Toggle(new GUIContent("Normalize V", "Scaling of the uv coordinates generated for the rope. The u coordinate wraps around the whole rope section, and the v spans the full length of the rope."), rope.NormalizeV); if (rope.NormalizeV != newNormalizeV) { Undo.RecordObject(rope, "Set normalize v"); rope.NormalizeV = newNormalizeV; } } else { Vector3 newLinkScale = EditorGUILayout.Vector3Field(new GUIContent("Link scale", "Scale applied to each chain link."), rope.LinkScale); if (rope.LinkScale != newLinkScale) { Undo.RecordObject(rope, "Set chain link scale"); rope.LinkScale = newLinkScale; } bool newRandomizeLinks = EditorGUILayout.Toggle(new GUIContent("Randomize links", "Toggling this on this causes each chain link to be selected at random from the set of provided links."), rope.RandomizeLinks); if (rope.RandomizeLinks != newRandomizeLinks) { Undo.RecordObject(rope, "Set randomize links"); rope.RandomizeLinks = newRandomizeLinks; } EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(chainLinks, true); if (EditorGUI.EndChangeCheck()) { // update the chain representation in response to a change in available link templates: serializedObject.ApplyModifiedProperties(); rope.GenerateProceduralChainLinks(); } } // Progress bar: EditorCoroutine.ShowCoroutineProgressBar("Generating physical representation...", routine); // Apply changes to the serializedProperty if (GUI.changed) { serializedObject.ApplyModifiedProperties(); } }