Jump to content

tube mesh


jerome
 Share

Recommended Posts

Hi,

 

Does anyone want to implement a tube mesh ?

 

A tube is more complex than a cylinder. Given a path and a radius, a tube will be a kind of curved cylinder along the path.

This shape is really useful as the path could be computed from any math function.

 

path : array of vector3

radius : number, the radius of the tube

segments : number of visual segments to draw the tube

 

maybe other paramaters

 

createTube(path, radius, segments, scene) returns a mesh

 

I would like to code it (js for now) but it seems not that trivial ... If anyone has ideas about the way to start it, he/she is welcome to me

 

[EDIT]

really not trivial : http://www.cs.indiana.edu/pub/techreports/TR425.pdf

Link to comment
Share on other sites

J, we got a big fat math library to simplify all of those ugly transforms.  Just sprinkle your code with Babylon-Brand Faery Dust, and your code will always do what you MEANT it to do.  :)

 

When you say ribbons, is that almost the same as saying "rings"?

 

Anyone who has spent any time playing with cylinders knows that Babylon cylinders are drawn in such a way that... well... they're made to be efficient, not easily play-around-able.  See this example.  See the way its drawn?  Sure we do.  It's drawn linearly.  It is quite a pain to locate one "ring" of this cylinder to apply a different diameter to it. 

 

Although I did hack some math waves into it... in this demo, I was not able to get it to animate the wave continuously thru the height subdivs.  And nobody answered my beggings in The Wingnut Chronicles... for someone else to animate it FOR me.  :)

 

But ok, let's look at this from my simpleton ways.  Let's say we invented BABYLON.CyliTubeManager?  Know where I'm going?  Good, because I sure don't.

 

It starts with a base class called ring.  A ring can be a cap, either fanned or center-point pie style... so it works as a circular plane all by itself... something Dad72 and others have mentioned needing at times, and I too, think it would be handy.

 

But a ring need not be a cap.  It can be just a ring.  And when a pile of these rings are placed into the .rings array of a BABYLON.CyliTubeManager... it makes a tube.  The manager returns a single mesh... plotted in such a way that we can easily screw-with any ring, capped or not.  There is no limit to the number of rings that a CyliTubeManager can manage... but it ALWAYS returns a single mesh, optimized as wanted.  AND, all rings must have the same number of lateral segments (same number of points as each other).

 

Now... about paths and bending the tube.  One thing I noticed in 3D Max... was the upper and lower limits of a bend modifier.  Let's say your tube needs to make a 90 degree turn in the middle.  We want the tube to go straight for 33%, then use the middle 33% to make the 90 degree turn, and then use the final 33% to go straight (along the new angle).  One thing I've noticed... is that it smooths the bend more... IF lots of rings are used in the bend area.  More interpolator steps always means smoother transverse, right?

 

So, what would be sweet... is if the one or many 'bend' (path?) objects that get applied to our new CyliTubeManager... have lots of power to allow the user to concentrate rings in the bend area... and minimize the rings for straight runs (an optimization).  This takes some... hmm... fuzzy logic?  *shrug*  How many rings per bend, and how many bends in a single tube... all tough factors.

 

What have we concluded?  Not a thing, but I sure have rambled-on for a good long while.  This "long version" of making a tube/cylinder... SHOULD allow me to animate my wave up/down thru the mesh much easier... because I might be able to dynamically apply diameters to rings... live.

 

Wanna know where this all started?  You guessed it... the cartoon cannon.  Nearly every cannon in every TV cartoon... "puckers-up" before it fires.  I think this may be called "anticipation" in the animation industry.  My idea of "Artillery Duel" in BJS... really needs to have its cannon pucker before it fires... for good smiles.  The cannon also has to stretch-out long and thin AFTER it fires the cannonball.  In Max, this might be done to a cylinder via spherify, and then negative spherify.  The cartoon cannon firing... a classic, and a necessity for our fun games.

 

Exciting, huh?  Have I been thinking of tubes (and rings), lately?  You bet.  Jer... you must be psychic!  :)

Link to comment
Share on other sites

Waaaooowww

 

Very nice waves !!!

This type of mesh exists in threejs as standard object and is called a LatheGeometry : a array of points computed from a curve function, a start angle, a end angle.

The curve is rotated around an axis between these two angles to form the shape.

Don't you want to request to push your code in BABYLON.Mesh as a static method ?

 

I talked about these ribbons : http://www.babylonjs-playground.com/#2E9DTS#9 because I think it will be easier than cylinders to connect them together along a curve,

because ribbons are themselves just defined by two curves, which could here two circles... but not mandatory on parallel planes as cylinders are.

 

I'm on my way to code these tubes.

 

So far, given a curve function, I get :

the curve points (white lines),

the tangent vectors on each point (red), to define an orthogonal plane,

a distant (radius away) vector (green) to each point and in each plane : the one I will try  to rotate along a circle in the so defined plane.

Thus I will have for each curve point a circle path orthogonal to the curve. Each circle could then become a path for my successive ribbons.

[EDIT] ooops

forgot the link : http://logiciels.iut-rodez.fr/proto/weathermap/test2/tube.html

[\EDIT]

 

Just a question :

does the Matrix.RotationAxis(axis, angle) need the given axis pass thru the origin (0,0,0) ?

 

I'm trying, given a vector3, to rotate it around an vector (*) defined axis some angle

 

 

(*) french vecteur, not point

Link to comment
Share on other sites

I intend to expand my ribbon object to make it even more useful :

createRibbon("name", [path1, path2, ..., pathN], optionalOffset, scene)

well,

a path is array of succesive vector3 along a curve of your choice

 

- if two paths are given, it will do the same as it currently does : create a surface between these two paths,

 

- if more than two are given, it will create a surface between each curve, re-using the vertices common to two surfaces, as the same curve is then used by two consecutive ribbons (not clear ? I'll code an example soon in the playground),

 

- if only one path is given, the ribbon will be constructed by linking vertex n to vertex n+offset

 

example: imagine a helix curve, it is a lone curve,

but you can link dots (to draw triangles, and then faces) to other dots further on the same curve, say, after one complete rotation (+2*PI)

so a lone curve can be used to design a shape too.

 

Imagine you have a very long rope and you wrap it around an object from the bottom to the top :

it would some helix-like curve,

then we could draw triangles between each rope level.

 

That's what my versatile ribbon intends to do !

 

(not sure I'm very clear though)

Link to comment
Share on other sites

J, I think your idea will give you angled lateral lines, and thus it cannot be cleanly capped, nor can the number of height subdivs be clearly set.  And I think textures will map onto it strangely angled, and possibly with the texture pattern full of many triangular stretched "jaggies" (disrupting the texture's pattern if it has one).

 

Of course, you are free to coil up a ribbon in such a way, but I think you will find it to have limited usefulness (possibly quite far from "versatile").  But this is just my opinion.  I'll reserve my final opinions until after I see you build one, and see its tube ends, and see a texture mapped onto it.

 

A closed tube is still the objective, right?  Not a coil spring (an open-air tube), correct?  Just possibly, you are talking apples and I am talking oranges.  :)  It's ok, both are delicious!

 

I wish someone mathematically smarter than I... would join-in here.  I don't think that I am doing real well at keeping-up with your visions, Jerome.

 

As far as folks using things I make... as part of the framework... I leave that to the experts.  If a framework maintainer/developer sees something I did that they like, they are free to use it (after coding it more wisely).  I am really not much of an innovator, though.  I'm quite good at making contorted hacks that bog cpu's to a crawl, though.  :)

Link to comment
Share on other sites

I don't know what it will give, that's why I'm trying.

 

The classical way to do a tube is to stack many small cylinders and make some hard and heavy math (Frenet-Serret formulas) to compute some smooth corners between two cylinders with different axis. This is the way many libs do (so does threejs, too)

 

As this way is far beyond my math skills, I'm trying another approach with my ribbons.

Viewing the video tutorial, I heard DK saying "in 3D, we are always faking to render things with no or less computation". This sentence gave me the idea of sticking ribbons around the shape instead of constructing it with cylinders. Mummy approach, so :D  ( BABYLON.Mesh.Mummify("returnOfTheMummy", [vectors3] ? )

 

I hope the result will fake enough about the smooth corner problem to avoid the Frenet-Serret way.

Wait and see my first tests soon ;)

Link to comment
Share on other sites

 

Of course, you are free to coil up a ribbon in such a way, but I think you will find it to have limited usefulness (possibly quite far from "versatile").  But this is just my opinion.  I'll reserve my final opinions until after I see you build one, and see its tube ends, and see a texture mapped onto it.

 

A closed tube is still the objective, right?  Not a coil spring (an open-air tube), correct?  Just possibly, you are talking apples and I am talking oranges.  :)  It's ok, both are delicious!

 

 

waaarrrffff, so funny !!

 

The closed tube is not my main objective. It's quite easy to draw a circular cap (*) on top of each end, so this could an optional parameter.

The very difficulty of tube design is in the maths needed to represent those smooth angles. Something called the parallel transport design .

That's why I'm trying another simplier approach, hopping it will do the job enough to get acceptable tubes.. with far less complexity and computation.

 

I meet your friendly challenge to show this kind of ribbon can be more versatile than you could expect   ;) . I currently work at implementing it as I described in this post so we could construct a full mesh from a curve array at once.

 

(*) BJS lacks some plane disc, doesn't it ? I'll do this.

Link to comment
Share on other sites

As long as you are having fun.  :)  I'm sure you realize that all of the standard BJS shapes get most of their power from babylon.abstractMesh.js, yes?  If you want your meshes to fit the criteria of a BJS basic shape, yours must do the same.  That way, all of the framework's basic mesh have the same standard methods and properties.  These interfaces to the basic shapes are what users expect to be available on every basic shape-mesh.

 

There's no need to meet my challenges.  I'm a nobody.  The two primary challenges of any mesh tool would be to #1 meet the criteria of being a standard BJS built-in shape, and #2 keep it easy/simple to use.  But you know all this already, right?  Your new meshes will all have the properties and methods found on https://github.com/BabylonJS/Babylon.js/blob/master/Babylon/Mesh/babylon.abstractMesh.js, I'm sure of it.  ;)

 

I hope I'm not being a buzzkill.  Go get 'em, Jerome!  Take no prisoners!  I might not understand everything you say, but it sure is entertaining and thought-provoking to watch you think, play, and talk.  :)

Link to comment
Share on other sites

arf... I realize my english is not that good ...

 

I try to be simplier :

 

First, I don't want to provoke or hurt anybody. If I accidentally do, it's ever not intented and probably due to my bad english humor/jokes (as they aren't the same way/mood in french). Sorry for that, sincerely. :( I'm probably too talkative and this increases the risk of misunderstanding !

 

Then, when I talk about challenges, it's alway friendly and just for fun. Some spitit/brain/thought/hack challenge if you like. Maybe again one of my many faults, but when I hear someone in my work context says "no, we can't, it's not possible", I just can't stop answering "why  do not give a try ?". I just like these little fun everyday challenges. I agree it's often useless or I can rarely succeed, but sometimes it brings a solution or at least a new point of view on an a priori impossible problem.

 

At least, I didn't know about the abstractMesh, so thank you to teach me :D

I don't feel competent enough until now to code a real BJS new mesh (as I even hadn't time to learn some TS). I'm just working on the concept or the idea and then its implementation : easy function signature, logic and computation.

I believe, but maybe I'm wrong, this elementary mesh is really convenient and powerful. As it doesn't exist in threejs either, I feel very motivated to make something new and useful (hope so). Maybe I misbelieve and just waste my time ... arf, enthusiastic people messing around. :lol:

 

My objective is just to have a final functionnal function to demonstrate in the playground.

Then I hope that someone interested in, better than me and more familiar with TS and BJS integration (you ?) could get it and make it a right basic BJS shape (interfaced with abstractMesh, etc) to be PR into the framework.

 

Elseway this will wait for me to learn TS, abstractMesh, PR, etc. Not that important, anyway :)

Link to comment
Share on other sites

Hi jahow!  I agree! 

 

Jerome, it's not that difficult to make your mesh BJS Basic Shape-ready.  It happens automatically if your mesh-building is done using some tools that are provided by the framework.

 

If you look at the BABYLON.VertexData object (api / code), this class generates all the shape information for our basic shapes.  Shape information like positions, normals, indices, uvs, colors, etc.

 

Take a look at my goofy wave-oscillated cylinder demo, again, if you will.  Look at the plotCylinder function at line 34.  Keep it open in a window. 

 

In another window, look at https://github.com/BabylonJS/Babylon.js/blob/master/Babylon/Mesh/babylon.mesh.vertexData.js#L396 .  Same function, yes?  I just borrowed the CreateCylinder function from the VertexData object... and put it into my code... and then renamed it PlotCylinder.  I renamed it for a reason. 

 

I needed to borrow one more CreateCylinder function from another object... the Mesh-class object (not abstractMesh, just Mesh)... which is the function called by users to actually create the cylinder.  Look here...  https://github.com/BabylonJS/Babylon.js/blob/master/Babylon/Mesh/babylon.mesh.js#L1079 and you'll see ANOTHER CreateCylinder function.  I stole that function, too, and put it into my code.... but I kept its name being CreateCylinder.  You can think of this smaller CreateCylinder function... as a layer or wrapper atop the other, bigger (vertexData) CreateCylinder function, but that might not be the correct terminology.

 

In that 2nd smaller CreateCylinder (the one on/from Mesh)... look at lines 1089-1092 (or lines 25-28 in my demo):

var cylinder = new Mesh(name, scene);var vertexData = BABYLON.VertexData.CreateCylinder(height, diameterTop, diameterBottom, tessellation, subdivisions);vertexData.applyToMesh(cylinder, updatable);

Those are the magic lines.  1089 makes a new blank abstractMesh.  There's your basic mesh, without any shape.  The next line creates a vertexData object by running the CreateCylinder function on the VertexData class.  In my demo, I run PlotCylinder... the same function but renamed.  It is a constructor and it returns a "stocked" VertexData object... ready to be applied to a blank mesh.

 

The last line... applies the (cylinder shape data) vertexData object... to the blank abstract mesh.

 

So, if you follow these general procedures, you create mesh that are BJS-fortified (properly ready to become BJS basic shapes).  You could do the same thing with your ribbon mesh.  Use my demo as a template.  Rename my PlotCylinder... to PlotRibbon and recode-it.  Rename my CreateCylinder... to CreateRibbon and recode it.  Then just adjust my line 26 to read...

 

var vertexData = PlotRibbon(arg, arg, arg.... etc);

 

Then do all your plotting work... inside the PlotRibbon function (supported/wrapped by your CreateRibbon function).  Later when you are happy with the functionality, your PlotRibbon would be placed on the framework's vertexData class (possibly renamed to CreateRibbon)... and your CreateRibbon function (the smaller, wrapper function) gets placed on the Mesh object, thus exposing it to the users.  Easy, right?  ;)

 

VertexData class contains the code to make the ACTUAL shapes, and the Mesh class contains the smaller wrapper methods used by users to call the shape-making functions on the vertexData class.  It's a two-piece operation.  Study it for a while... it's actually easier than it looks/sounds.  Try to make your mesh... using the general template in my demo.

 

Lastly, I don't see anything you do as being offensive to anyone.  Your enthusiasm is actually quite enjoyable, and you have a good personality, and a big chunk of active brains to top it off.  You're doing great work, and it was time to tell you about the conventions of making BJS-ready mesh.  Don't let those conventions scare you away from continuing... because the conventions are not very difficult to work within the boundaries-of.  And if you follow these conventions, you're going to become (even more of...) a superstar around here.  :)

 

It might be wise to notice the 'updatable' flag parameter at the end of my line 15 and at the end of every other BJS basic shape constructor.  I don't know much about it, but it has to do with changing vertexData shape data (positions, normals, indices, uvs, etc) after that data has already been applied to a blank abstract mesh.  It might be involved with the life of a VertexBuffer after its been applied to a mesh.  If a mesh is constructed non-updatable, the VertexData and VertexBuffer might be flagged for garbage collection, as they will no longer be needed.   Don't quote me on that... I am essentially guessing.  :)

 

There's others nearby that know MUCH more about this subject than I do.  The best I can do is tell you what little I know and hope it gives you some insight into some things that are new to you.  I also hope that these smarter people will join us in this conversation, if only to make sure I haven't told you incorrect things.  Now, for sure, you know the "trail" used to make the current library of basic mesh shapes.

 

There's also lots of talk about "geometry" and "polygons" laying around.  I don't know ANYTHING about those two things.  MAYBE those apply to what you do, more than my mesh-making knowledge... but we'll need to find someone to explain those things to us (to me), or go on a massive internet-reading mission to learn how those things differ from "mesh".  I sometimes see framework functions that have "GeometryOrMesh in their function names, so, hmm.  Not sure what the story is, there.  :/

 

Be well!

Link to comment
Share on other sites

thank you Wingut for this so detailed tutorial !!!  :)

 

I don't really need other bjs basic method except the VertexData object and some math libs (rotationAxis, etc).

I don't re-invent the wheel  :P

My work is more about the logical way to avoid the Frenet stuff and some dedicated maths.

Link to comment
Share on other sites

My pleasure.  You understand it perfectly, and long before my speech.  :)  But, you know, when a guy talks in the forum, he talks to many different people at many different levels of expertise.  I over-explain quite severely, often.  You're using the framework mesh tools perfectly.   I don't see one mention of Engine._gl in any of your code.  Tiz good.  :D

 

Now if you start mucking around in THIS area:

 

https://github.com/BabylonJS/Babylon.js/blob/master/Babylon/babylon.engine.js#L851

 

...that's when we might think about placing some medical equipment near you, in case you "go off the deep end".  hehe

 

But seriously, you might want to hijack some code out of that area of 'Engine'.  You seem plenty smart enough to play around under the floorboards of the framework... closer to the _gl layer.  Just teach us everything you learn or already know.  We (at least SOME of us) are enjoying riding-along on your adventures.

Link to comment
Share on other sites

ok let's go for my first attempt.

 

Remember :

  1. I want a tube mesh along a given curve
  2. I want a simple call signature function (so it's easy to use)
  3. I don't want angular junctions on my tube, only smoothly rounded corners
  4. I try to avoid to implement all the Frenet stuff which is to difficult for me

 

The integration in abstractMesh will be done later ...

 

So here is my curve (white) : http://www.babylonjs-playground.com/#21SM6Y

var curvePoints = function(l, t) {  var path = [];  var step = l / t;  for (var i = -20; i <= l; i += step ) {    path.push(new BABYLON.Vector3(i, Math.sin(i) * i / 2, Math.cos(i) * i / 2 ));  }  return path;};

First, I get  the tangent vectors (red) at every point of this curve : http://www.babylonjs-playground.com/#21SM6Y#1

 

Then knowing each point and each tangent vector, I determine an orthogonal vector (green) in the plane normal to the tangent vector (let's call it the radial plane) on this point (here, I use some algorithmic human trick instead of resolving equations like intersection between a sphere and a plane). I scale this vector at radius size.

Following, people ?

 

Take a look : http://www.babylonjs-playground.com/#21SM6Y#2

 

I can then rotate this orthogonal vector along a circle (blue) in the radial plane around the tangent vector (still following ?).

I do this for each point : http://www.babylonjs-playground.com/#21SM6Y#3

 

hoho, starting to look like a tube !

 

At least, I ribbonise all that stuff : each circle will be a path in the paths array parameter needed by createRibbon()

tandadaammm : http://www.babylonjs-playground.com/#21SM6Y#6

 

really looks like a tube now :)

 

some plain material, et voilà : http://www.babylonjs-playground.com/#21SM6Y#4

not so bad, isn't it ?

createTube(curve, radius, tesselation, scene);

No Frenet stuff, no sharp angular corners, simple call signature !

 

But I have my tube twisted in the middle of the curve :( (because of no parallel transport, remember the orthogonal vector positions making some kind of helix).

This feature would be cool if I wanted a plane shape (a star, a square, etc) to rotate gently following the curve. So I keep it for further use.

 

The same with no backFaceCulling : http://www.babylonjs-playground.com/#21SM6Y#5

We can see some ribbon junctions inside, so maybe some optimization in the ribbon normals should be done too.

 

And I'll try soon another similar approach to simulate the parallel transport ;)

Link to comment
Share on other sites

Here we go, ladies and gentleman !!!

 

We are honored to introduce you at least a real tube : http://www.babylonjs-playground.com/#1W1TVT#1

 

I won't describe here the way it is designed because my english is quite approximative and you will get bored soon.

 

Of course, there are still some many tiny defaults if you make the material transparent for example, but it's due to incomplete normals declaration in the mesh ribbon (to be fixed soon).

 

So please, test it along any of your curves so I'll be sure it can resist any kind of twisting :) before making it a real typescript BJS object (help needed here).

call signature :

createTube(curve, radius, tesselation, scene);

curve : array of vector3

radius : tube radius

tesselation : number of radial segments

scene : current scene

 

 

in the playground code :

 

line 65 : function creating a curve. You can use your own of course. We just need an array of successive Vector3 (at least two) to define a curve/path.

 

but you can forget all of that and go straight to

 

line 223 : scene declaration

line 243 : path population

line 251 : tube creation

 

 

 

<auto-congratulation>

 

I just want make you know that I met a math prof here at work (university) to whom I exposed on a piece of paper my design without using Frenet formulas usually needed to achieve this.

He just found it very original and smart, then he validated my approach, subject to he can see the final visual result because he couldn't figure if/how it was appliable in the reality.

This made me proud as I remember me suffering in my distant hard math courses :D

 

[EDIT] Maybe many other people use the same design as me already,  and I finally just re-invented the wheel, but I just couldn't find other ways than Frenet after many many web searches [/EDIT]

 

</auto-congratulation>

Link to comment
Share on other sites

yep

My ribbon needs some fixes about indices.

And texture UVs settings too (I need to learn about them before).

 

This tube code is just the first functional draft, it probably needs some corrections here and there.

I coded it fast because I was motivated by my math design (quite simple therefore).

 

And I didn't really take care about the playground scene, just copied/cut a former one with lights in place :mellow:

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...