methusalah / splinemesh Goto Github PK
View Code? Open in Web Editor NEWA Unity plugin to create curved content in real-time with bézier curves
Home Page: https://twitter.com/dumas181
License: MIT License
A Unity plugin to create curved content in real-time with bézier curves
Home Page: https://twitter.com/dumas181
License: MIT License
Forked, cloned, opened in unity, now fork is polluted with tracked Library files, that for some reason get into git despite being blocked by gitignore, do you happen to know what causes it?
Hi
I create empty GameObject on Inspector, And then add Spline.
I drag it to Project window to create a prefab.
Select the GameObject in Inspector, then Set Selected Node Position to (5,5,0) and save Scene. If you open other Scene and Back to this Scene, you can see the Position is not (5,5,0) but the value of Prefab(5,0,0). If you modify the position use Script ,the effict like this.
If you Uppack Complete the GameObject and modify the Position, it can Serialize. After you open oterh Scene and Back to this Scene, you can see the position is (5,5,0). Or if you modify in Scene window use the Axis, it can Serialize too.
Thanks.
The extrusion needs to be enclosed by base faces. The editor should provide a way to control the UV offset, rotation and scale on these faces.
Currently, MeshBender apply a mesh on a curve, regardless of the scale of the mesh. To create a railroad, for example, you have to insure each curve has exactly the same length, which is impossible to do and absurd.
MeshBender would allow to bend a mesh on a given spline interval instead of a curve. It would also allow to specify the number of time the mesh has to be placed, or the scale along spline.
The ExtrusionSegment behaviour does set isDirty to true, but doesn't set it to false after computing the vertices. This leads to it computing the segment on every frame, resulting in high CPU usage, and low framerates.
It is often impossible to enter nodes coordinates in the inspector to have precisely placed spline nodes.
I'm currently working on a project where a player can drag a wire between a voltage source and a potential sink for it, using Spline Mesh. It works great so far, except that in initial trials, the SplineExtruder I was using also added a mesh collider to the generated mesh, which I had to comment out in the code to turn off.
I'm hesitant to submit a fix until I fully understand how to use Spline Mesh, but this seems like it would be a great thing to make optional! Maybe with a boolean check box? I really don't need to worry about collisions with it.
Whenever I go to GameObject>3D Object>Spline no Spline object is created and when i imported the package it used to create Spline but in sometime it becomes non editable. I re-imported but it doesn't work.
so that the texture has the same scale all along the spline, not accordingly to each curve length.
Up vector handle is always 1 unit far from the node. This is unusable if the user work at a large or tiny scale.
The handle should be scaled according to the camera distance, and outside the translation gizmo so it can always be handled
Up vector disallow the usage of roll and final result may be awkward, especially when both are set to non zero.
Roll should be applied according to the up vector.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor.Experimental.SceneManagement;
#endif
namespace SplineMesh {
///
private GameObject generated;
private Spline spline = null;
private bool toUpdate = false;
public bool initOnEnable = false;
[Tooltip("Mesh to bend along the spline.")]
public Mesh mesh;
[Tooltip("Material to apply on the bent mesh.")]
public Material material;
[Tooltip("Physic material to apply on the bent mesh.")]
public PhysicMaterial physicMaterial;
[Tooltip("Translation to apply on the mesh before bending it.")]
public Vector3 translation;
[Tooltip("Rotation to apply on the mesh before bending it.")]
public Vector3 rotation;
[Tooltip("Scale to apply on the mesh before bending it.")]
public Vector3 scale = Vector3.one;
[Tooltip("If true, a mesh collider will be generated.")]
public bool generateCollider = true;
[Tooltip("If true, the mesh will be bent on play mode. If false, the bent mesh will be kept from the editor mode, allowing lighting baking.")]
public bool updateInPlayMode;
[Tooltip("If true, a mesh will be placed on each curve of the spline. If false, a single mesh will be placed for the whole spline.")]
public bool curveSpace = false;
[Tooltip("The mode to use to fill the choosen interval with the bent mesh.")]
public MeshBender.FillingMode mode = MeshBender.FillingMode.StretchToInterval;
private void OnEnable() {
if (initOnEnable) {
Init();
}
}
public void Init() {
if (_instanceId.Length < 1) {
if (transform.childCount > 0 && transform.GetChild(0).name.Contains(GetType().Name)) {
string name = transform.GetChild(0).name;
_instanceId = name.Substring(0, name.IndexOf("_"));
} else {
_instanceId = UnityEngine.Random.Range(0, 999999999).ToString();
}
}
// tip : if you name all generated content in the same way, you can easily find all of it
// at once in the scene view, with a single search.
string generatedName = _instanceId + "_genBy_" + GetType().Name;
var generatedTranform = transform.Find(generatedName);
generated = generatedTranform != null ? generatedTranform.gameObject : UOUtility.Create(generatedName, gameObject);
spline = GetComponentInParent<Spline>();
spline.NodeListChanged += (s, e) => toUpdate = true;
toUpdate = true;
}
private void OnValidate() {
if (spline == null) return;
toUpdate = true;
}
private void Update() {
// we can prevent the generated content to be updated during playmode to preserve baked data saved in the scene
if (!updateInPlayMode && Application.isPlaying) return;
if (toUpdate) {
toUpdate = false;
CreateMeshes();
}
}
public void CreateMeshes() {
#if UNITY_EDITOR
// we don't update if we are in prefab mode
if (PrefabStageUtility.GetCurrentPrefabStage() != null) return;
#endif
var used = new List();
if (curveSpace) {
int i = 0;
foreach (var curve in spline.curves) {
var go = FindOrCreate(_instanceId + "segment " + i++ + " mesh");
go.GetComponent<MeshBender>().SetInterval(curve);
go.GetComponent<MeshCollider>().enabled = generateCollider;
used.Add(go);
}
} else {
var go = FindOrCreate(_instanceId + "segment 1 mesh");
go.GetComponent<MeshBender>().SetInterval(spline, 0);
go.GetComponent<MeshCollider>().enabled = generateCollider;
used.Add(go);
}
// we destroy the unused objects. This is classic pooling to recycle game objects.
foreach (var go in generated.transform
.Cast<Transform>()
.Select(child => child.gameObject).Except(used)) {
UOUtility.Destroy(go);
}
}
private GameObject FindOrCreate(string name) {
var childTransform = generated.transform.Find(name);
GameObject res;
if (childTransform == null) {
res = UOUtility.Create(name,
generated,
typeof(MeshFilter),
typeof(MeshRenderer),
typeof(MeshBender),
typeof(MeshCollider));
res.isStatic = !updateInPlayMode;
} else {
res = childTransform.gameObject;
}
res.GetComponent<MeshRenderer>().material = material;
res.GetComponent<MeshCollider>().material = physicMaterial;
MeshBender mb = res.GetComponent<MeshBender>();
mb.Source = SourceMesh.Build(mesh)
.Translate(translation)
.Rotate(Quaternion.Euler(rotation))
.Scale(scale);
mb.Mode = mode;
return res;
}
}
}
When I hear "direction" when used to describe a vector, I anticipate an absolute vector pointing in the direction provided by its elements. So with SplineNode.Direction
, I failed to realize that it requires a world coordinate.
Rather than changing this entirely and breaking previous code, adding an additional property for the same field, LocalDirection
, which subtracts the position of the SplineNode and gives the direction in local space, could be handy. We would still only have one direction
field, but two ways of accessing and changing it based on the context.
While undo/redo is implemented in most editing scenario, the spline is not updated.
This feature must be activated for all node edition, loop/unloop and extrusion shape modification, and should allow content update.
As commented by philc_uk on the asset store.
For this there will need to be a Curve interface or base class and probably separated direction indicators on the nodes.
In the editor interface you should be able to select the node curve types:
the SplineEditor.cs
and SplineExtrusionEditor.cs
are editor related code that prevent compilation to standalone player.
I have added #if UNITY_EDITOR
and #endif
and it seems to works.
It may be good idea to add it to here also?
Hi,
I've encounter some problem these days when I adjust the scale X in repeat mode.
When using curve spcce, If I adjust the (scale X) that
(size of mesh) x (scale X ) x (repetitionCount) ≒ spline.Length
Some meshes would fail with following error message.
Error message like
"Mesh.uv is out of bounds. The supplied array needs to be the same size as the Mesh.vertices array."
"Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: X, VertexCount: Y"
It seems that the number of vertices does not match to (source.Mesh.vertices.Count() x repetitionCount)
After some test, I found that the problem is caused by floating point precision problem
The value "distOnSpline" of "distance" will be slight greater than slpine.Length (about 10^06).
And it will cause different problem in non-curve space and curve space setting.
In Non-curve space:
} else {
float distOnSpline = intervalStart + distance;
//if (true) { //spline.isLoop) {
while (distOnSpline > spline.Length) {
distOnSpline -= spline.Length;
}
//} else if (distOnSpline > spline.Length) {
// continue;
//}
sample = spline.GetSampleAtDistance(distOnSpline);
}
This will make the end of the mesh incorrectly links to the begin of the mesh.
In Curve space:
if (!useSpline) {
if (distance > curve.Length)
continue;
sample = curve.GetSampleAtDistance(distance);
}
And it will make these vertices skips when adding them into 'bentVertices' after iterations.
bentVertices.Add(sample.GetBent(vert));
I think that's how the vertices number do not meet uv and triangle numbers.
Here's my workaround now, but I think there's some better way.
if (!useSpline)
{
if (distance > curve.Length)
{
distance = curve.Length;
}
sample = curve.GetSampleAtDistance(distance);
} else {
float distOnSpline = intervalStart + distance;
if (distOnSpline > spline.Length + 0.0001f)
{
while (distOnSpline > spline.Length)
{
distOnSpline -= spline.Length;
}
}
else if (distOnSpline > spline.Length)
distOnSpline = spline.Length;
sample = spline.GetSampleAtDistance(distOnSpline);
}
sampleCache[distance] = sample;
I just brutally add some offset (0.0001f) to avoid floating point precision.
It works for me but there should be some better solutions.
Thanks.
Node buttons are sometimes visible even if the node is out of the field of view
Excuse me, can you draw a standard arc from an angle?
This error happens when loading the Showcase in the editor. There are about 20 of them , like this:
SendMessage cannot be called during Awake, CheckConsistency, or OnValidate
UnityEngine.GameObject:.ctor(String, Type[])
SplineMesh.UOUtility:Create(String, GameObject, Type[]) (at Assets/SplineMesh/Scripts/Utils/UOUtility.cs:9)
SplineMesh.ExampleGrowingRoot:Init() (at Assets/SplineMesh/Scripts/Example/ExampleGrowingRoot.cs:81)
SplineMesh.ExampleGrowingRoot:OnValidate() (at Assets/SplineMesh/Scripts/Example/ExampleGrowingRoot.cs:49)
UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr, Boolean&)
You can find info about this problem here: https://forum.unity.com/threads/sendmessage-cannot-be-called-during-awake-checkconsistency-or-onvalidate-can-we-suppress.537265/ and here: https://forum.unity.com/threads/sendmessage-cannot-be-called-during-awake-checkconsistency-or-onvalidate.428580/.
Basically this problem happens if you call the Unity API to set stuff before the system is stable. There is a partial description here: https://docs.unity3d.com/2019.1/Documentation/ScriptReference/GameObject-tag.html, but it applies to lots of other stuff.
It's new, since some time in 2019, there is a bug report, Unity have responded and it's probably not going away any time soon. The ideal fix is: don't set stuff in Awake() or OnValidate(). I'm running 2020.1.1 and I don't have any old versions available to check.
Hi
In the SplineMeshTilling, I see the follow script:
foreach (var go in generated.transform
.Cast()
.Select(child => child.gameObject).Except(used)) {
UOUtility.Destroy(go);
}
But some times, one or more child did not destroy. After that, I store the "Select" result to an Array and use "for" instead of "foreach". The problem is solved.
Thanks.
Is there any plan to improve the Spline Gizmo display and handle in the scene view.
Currently there is some small annoying thing when working with the spline in scene view
this is just an idea, would it be possible to adapt the technique used by Sebastien Lague to deal with the scene view handling and display? https://github.com/SebLague/Path-Creator
having the best of two world would be awesome
I need to create a "Basic Pipe" slpine where each node of the spline is dynamic and constantly updating position and rotation information. The current example doesn't allow you to modify those parameters? Any suggestions on how to make these changes?
In the name of sweet baby Jesus will you PLEASE use a namespace for your code! This is especially important if you have such generic class names like "Tool", "Vertex", and "Spline". I value my sanity. Thanks!
unity uses Z as the forward axis but spline mesh works using the x axis as the forward axis.
this is the case in the examples but also in core code like MeshBender.
As mentioned in my other issue report, I'm working on a game with a player running wire between devices, using SplineMesh. Right now I have it set to change its end node's Position
to the transform.position
of the player. This almost works, but on inspection during play, I see that only one of the control points for that final node is moving, the other is stuck in place. This causes radical curvature changes as I move the player around.
There's a very good chance that I'm missing something here, and there might be a better way to do this. Is this intentional behavior? Or is there a better way to move the control point at runtime?
MeshBender is slow and causes significant frame loss when adjusting nodes. I ran a deep profile when bending a mesh of 1720 vertices and discovered that SourceMesh.BuildData()
was taking ~300ms to run and updating mesh normals in a nested loop. I hoisted Mesh.normals
out of the loop saw significant performance improvement without a loss of accuracy.
Rename scripts from the not obsolete form of exemple to example.
Alternatively the example scripts could be renamed to general components which could directly be used. ExempleSower
would then be renamed to SplineSower
in line with the SplineExtrusion
script (example). This way it wouldn't be weird to use "example" components on your objects.
when trying to input in or drag the individual node fields, the value seems to change for about 1 frame (visible only in the spline/extrusion shape, not in the actual field), before reverting back to default immediately. editing direction/position/up vector using the handles works as expected.
i dug around in the code a bit but could not find the cause. i put a debug line in one of the property setters and it looks like it's being called correctly when editing the value, but not when it reverts back to default, which might point to a problem with serialization?
using unity 2018.3.14f1 on windows 10 (x64), tried with both the asset store version and the code in this repository.
thank you!
As commented by philc_uk on the asset store. This could be implemented in the sower or as a separate script.
The code from the comment:
..
public bool SnapToSurface = true;
..
Line 110:
go.transform.position += binormal;
if (SnapToSurface)
{
RaycastHit hit = new RaycastHit();
Physics.Raycast (new Ray (go.transform.position, Vector3.down), out hit);
if (hit.collider != null)
{
go.transform.position = hit.point;
}
}
Hey,
first of all thanks for the asset.
Bu my problem is, that im trying to change scale X of spline mesh tiling via script. Its changing in console, but I can't see the changes in play mode.
Is there something im doing wrong? Help will be much appreciated :)
splineMesh.scale.x += splineMesh.scale.x * Time.deltaTime;
Here you can see, that the scale of x variable has been changed to 2 (was 0.1). But no changes in editor or play mode :/ When X is 2, the spline should have "grown", where number 2 is in picture, but it has stayed in place.
EDIT:
Also when I animate variable X, the value changes in animation, but the spline isn't updating.
Hi, thank you so much for the great tool and the open source initiative! Great tool.
Sadly, I wasn't able to make it work on Unity 2022.1.5f1 (haven't tested on other versions at the moment)
I get the following error
Assets\SplineMesh\Scripts\Bezier\SplineNode.cs(105,35): error CS0066: 'SplineNode.Changed': event must be of a delegate type
I tried the following patch but to no avail (I'm not very familiar with events and delegates in c#)
https://docs.microsoft.com/en-us/dotnet/csharp/misc/cs0066
public delegate EventHandler();
public event EventHandler Changed;
A screenshot of the error if this could help :)
https://pasteboard.co/mN3L28uBbZRz.png
Many thanks!
Hi
I am using SplineMeshTiling.
There are two nodes on Spline. The fields of Element 0 are: Position(0,0,0), Direction(0,0,0). The flelds of Element 1 are: Position(0,-10,0), Direction(0,-10,0).
And then i add Mesh and Material to SplineMeshTiling. You can see something wrong on the Element 0.
If you set Direction of Element 0 to (0,-1,0). The Mesh rendering is correct.
Thanks.
Hello, I am new to Unity and am wondering if this editing tool support in game editing ? Thank-you.
Hello,
I am unable to edit the Position and Direction values of nodes in the inspector
I can't seem to rotate any of the nodes in the Scene view. Also it's not possible to set the rotation / position values in the Inspector. Unity 2017.3.1
Thanks!
the generated object life cycle is not perfect. It relies on root instance ID which is a fragile pattern.
We should be able to provide better life cycle management by enforcing unique instance of a generator and by placing each generator on separate children of the spline root object
HI,
I have 4 splines with 10 points each which I'm changing in 400Hz.
for each update I'm changing (in Spline.cs):
public void UpdateNode(int i, Vector3 newPosition)
{
if (i < nodes.Count)
{
nodes[i].Position = newPosition;
}
}
the problem is that after each change rasie event:
/// <summary>
/// Node position
/// </summary>
public Vector3 Position {
get { return position; }
set {
if (position.Equals(value)) return;
position.x = value.x;
position.y = value.y;
position.z = value.z;
Changed?.Invoke(this, EventArgs.Empty);
}
}
which cause performance issuses.
is ther ea way to update alll spline points in a batch and only after that recreate/update the presentation/spline itself?
it will reduce redrawing by 10...
SplineMeshTiling misses deleting some segments.
Hi,
I 've using this amazing script for weeks and it helps me a lot.
Then found that some segment will not be deleted when switching curve space to non-curve space.
I think it come from the following scripts.
SplineMeshTiling.cs
// we destroy the unused objects. This is classic pooling to recycle game objects.
foreach (var go in generated.transform
.Cast<Transform>()
.Select(child => child.gameObject).Except(used)) {
UOUtility.Destroy(go);
}
Casue the children changes after the during the 'foreach' iteration.
One segment needed to be destroyed will be skiped in every two iteration.
Using backward iteration from the original IEnumerable list.
Or simply add ".ToList()" ,
making it a saved list may solve this problem.
SplineMeshTiling.cs
// we destroy the unused objects. This is classic pooling to recycle game objects.
foreach (var go in generated.transform
.Cast<Transform>()
.Select(child => child.gameObject).Except(used).ToList()) {
UOUtility.Destroy(go);
}
Thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.