修改mesh

This commit is contained in:
2025-11-04 23:11:56 +08:00
parent 32f07eb14e
commit e8f6308580
93 changed files with 1587 additions and 491 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b31243ee8abab458088c6fccccfb5c58
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using System.Linq;
namespace NBF
{
[CustomEditor(typeof(SimpleRopeInteraction))]
public class SimpleRopeInteractionEditor : Editor
{
public override void OnInspectorGUI()
{
DrawDefaultInspector();
if (GUILayout.Button("Find ropes in current scene"))
{
((SimpleRopeInteraction)target).ropes = StageUtility.GetMainStageHandle().FindComponentsOfType<Rope>().ToList();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 690ba92ccc18546d09d046bf31fa4887
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,171 @@
using UnityEngine;
using Unity.Mathematics;
using System.Collections.Generic;
namespace NBF
{
public class SimpleRopeInteraction : MonoBehaviour
{
[Tooltip("The mesh to show on the picked particle position. May be empty.")]
public Mesh pickedMesh;
[Tooltip("The mesh to show on the target position. May be empty.")]
public Mesh targetMesh;
[Tooltip("The material to use for the picked mesh")]
public Material pickedMaterial;
[Tooltip("The material to use for the target mesh")]
public Material targetMaterial;
[Tooltip("The maximum distance a rope can be picked from")]
public float maxPickDistance = 2.0f;
[Tooltip("The max allowable impulse strength to use. If zero, no limit is applied.")]
public float maxImpulseStrength = 3.0f;
[Tooltip("The mass multiplier to apply to the pulled rope particle. Increasing the mass multiplier for a particle increases its influence on neighboring particles. As this script pulls a single particle at a time only, it is beneficial to set the mass multiplier above 1 to improve the stability of the overall rope simulation.")]
public float leverage = 10.0f;
[Tooltip("The keyboard key to use to split a picked rope. May be set to None to disable this feature.")]
public KeyCode splitPickedRopeOnKey = KeyCode.Space;
[Tooltip("The list of ropes that may be picked")]
public List<Rope> ropes;
protected bool ready;
protected Rope rope;
protected int particle;
protected float distance;
protected float3 pickedPosition;
protected float3 targetPosition;
public void SplitPickedRope()
{
if (rope == null)
{
return;
}
ropes.Remove(rope);
var newRopes = new Rope[2];
rope.SplitAt(particle, newRopes);
if (newRopes[0] != null) ropes.Add(newRopes[0]);
if (newRopes[1] != null) ropes.Add(newRopes[1]);
rope = null;
}
protected Rope GetClosestRope(Ray ray, out int closestParticleIndex, out float closestDistanceAlongRay)
{
closestParticleIndex = -1;
closestDistanceAlongRay = 0.0f;
var closestRopeIndex = -1;
var closestDistance = 0.0f;
for (int i = 0; i < ropes.Count; i++)
{
ropes[i].GetClosestParticle(ray, out int particleIndex, out float distance, out float distanceAlongRay);
if (distance > maxPickDistance)
{
continue;
}
if (closestRopeIndex != -1 && distance > closestDistance)
{
continue;
}
closestRopeIndex = i;
closestParticleIndex = particleIndex;
closestDistance = distance;
closestDistanceAlongRay = distanceAlongRay;
}
return closestRopeIndex != -1 ? ropes[closestRopeIndex] : null;
}
public void FixedUpdate()
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Input.GetMouseButton(0))
{
// Mouse down
if (ready && rope == null)
{
// Not pulling a rope, find the closest one to the mouse
var closestRope = GetClosestRope(ray, out int closestParticleIndex, out float closestDistanceAlongRay);
if (closestRope != null && closestParticleIndex != -1 && closestRope.GetMassMultiplierAt(closestParticleIndex) > 0.0f)
{
// Found a rope and particle on the rope, start pulling that particle!
rope = closestRope;
particle = closestParticleIndex;
distance = closestDistanceAlongRay;
ready = false;
}
}
}
else
{
// Mouse up
if (rope != null)
{
// Stop pulling the rope
rope.SetMassMultiplierAt(particle, 1.0f);
rope = null;
}
}
if (rope != null)
{
// We are pulling the rope
// Move the rope particle to the mouse position on the grab-plane
pickedPosition = rope.GetPositionAt(particle);
targetPosition = ray.GetPoint(distance);
if (maxImpulseStrength == 0.0f)
{
rope.SetMassMultiplierAt(particle, 0.0f);
}
else
{
rope.SetMassMultiplierAt(particle, leverage);
}
rope.SetPositionAt(particle, targetPosition, maxImpulseStrength);
// Split the rope on key if keybind is set
if (Input.GetKey(splitPickedRopeOnKey))
{
SplitPickedRope();
}
}
}
public void Update()
{
if (!Input.GetMouseButton(0))
{
ready = true;
}
if (rope != null)
{
if (pickedMesh != null && pickedMaterial != null)
{
Graphics.DrawMesh(pickedMesh, Matrix4x4.TRS(pickedPosition, Quaternion.identity, Vector3.one * 0.25f), pickedMaterial, 0);
}
if (targetMesh != null && targetMaterial != null)
{
Graphics.DrawMesh(targetMesh, Matrix4x4.TRS(targetPosition, Quaternion.identity, Vector3.one * 0.25f), targetMaterial, 0);
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d741c2b5cb0190448822896128d51f8a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace NBF.Example
{
[RequireComponent(typeof(SimpleRopeInteraction))]
public class SimpleRopeInteractionGUI : MonoBehaviour
{
protected float desiredMaxImpulseStrength;
protected Rect guiRect = new Rect(10, 10, 200, 0);
public void Start()
{
desiredMaxImpulseStrength = GetComponent<SimpleRopeInteraction>().maxImpulseStrength;
}
public void Window(int id)
{
var interact = GetComponent<SimpleRopeInteraction>();
bool isLimiting = interact.maxImpulseStrength > 0.0f;
bool shouldLimit = GUILayout.Toggle(isLimiting, "Limit max impulse strength");
if (isLimiting != shouldLimit)
{
Event.current.Use();
if (shouldLimit)
{
interact.maxImpulseStrength = desiredMaxImpulseStrength;
}
else
{
desiredMaxImpulseStrength = interact.maxImpulseStrength;
interact.maxImpulseStrength = 0.0f;
}
}
var limitString = shouldLimit ? interact.maxImpulseStrength.ToString("0.0") + " Ns" : "Infinite";
GUILayout.Label("Max impulse strength: " + limitString);
interact.maxImpulseStrength = GUILayout.HorizontalSlider(interact.maxImpulseStrength, 0.0f, 10.0f);
GUI.enabled = false;
GUILayout.Label("Instructions: Use the left mouse button to interact with the ropes in the scene. While holding on to a rope using the mouse, press <SPACE> on the keyboard to cut it at the held position.");
GUI.enabled = true;
}
public void OnGUI()
{
guiRect = GUILayout.Window(0, guiRect, Window, "Interaction settings");
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0383ecb7d718b4bd682ab0d0f68bce82
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: