Snapping Branch
@andybak snapping discussion continued...
66 Replies
The idea behind this looks right to me.
Is there anyone here that has a good understanding of the transformation stack? My first guess is that App.Scene.Pos is not the thing we want.
What is the transformation that changes when you scale/rotate the whole scene?
Seems to be SceneScript->Main Canvas->stuff and when I change the scale rotation in the app it applies the change to the SceneParent (SceneScript)
(link to start of discussion in entirely the wrong channel: https://discord.com/channels/783806589991780412/884503906703528038/889986531945742346 )
App.Pose appears is defined as Coords.AsGlobal[transform] on SceneScript
the root of unity's scene graph we call "global space"so that sounds wrong. we don't want to work in the unity scene space - we want the canvas space
"canvas space" = the user's artwork. Typically there's just a single Canvas whose pose is the same as the Scene pose (it's a direct child of the scene, with identity localTransform). But when you have a selection or groups, those are additional Canvas instancesHowever - this sounds like canvas space == global space == scene pose
But when you have a selection or groups, those are additional Canvas instancesMy initial thought is that we can ignore this as we're working ON a selection and not IN a selection but I might be wrong. If I'm right then this should be a no-op. I'm transforming from global space to canvas space. I'm curious to separately test canvas translation, rotation and scale to see which ones mess with the grid snapping. That might be a clue. Bit tricky to do manually. I guess I need to write some tests.
Moat implemented grid snapping for drawing: https://github.com/moatdd/open-brush/commit/e8884ab629853019b5f2a3bbd978f16bb55b5c69#diff-e9f29b3b0bf0aeb63aa1b06e01596135252707616aa98b874daf5123b9b26f8dR31
O frabjous day! I nicked Moat's snapping code and it worked
and then just:
@moat9195 - thank you
oh nice!
App.ActiveCanvas ftw
I do wonder why he chose to use worldToLocalMatrix rather than just inverse. They should be equivalent.
Perhaps that reads better...
oh lovely
I forget why I didn't just use transform.InverseTransformPoint() and transform.TransformPoint()
it should probably work just fine
bonks head teehee
๐ Good stuff. How does the whole ActiveCanvas thing work?
afaik, there's really only ONE canvas in OB
but I think if you wanted to, you could instantiate multiple canvases, like having separate layers
and a canvas is a 3D VR canvas, not a 2D thingy
so the canvas stores all your brush strokes
IIRC, I tried App.MainCanvas and this didn't work
and when you perform view manipulations (by pinch-zooming and shoving it around) you are manipulating the canvas, which is a gameobject that acts as the parent for all the brush strokes
the user does not get scaled, nor do they move around during a view operation
yeah, I dunno about MainCanvas, but ActiveCanvas worked
I see view manipulations changing the SceneParent transform. Is that related to the canvas? I assumed the term canvas would correspond to the "Main Canvas" object.
Now I need to figure out how to unify the UI for the 3 types of snapping: Snapping while drawing, snapping selections (and other widgets) - and snapping procedural geometry creation (currently just my polyhedra but I have plans for this functionality)
Moat used the canvas zoom level to determine the snapping grid. I kinda like the economy of this approach but I'd have to use it in anger to get a sense of whether it wasn't one overload too far. I used a "snap settings" panel which is clunky but explicit. I don't love it but it works in a "nobody ever got fired for choosing IBM" way (if that strained metaphor makes sense to anyone other than myself)
Three related issues:
1. How to turn grid/angle snapping on and off
2. How to set the snap angle and grid size
3. How to visualize the grid
for #3, I was thinking there would be a volumetric sphere (radius can vary) around the brush hand where the lines would fade out toward the edge. So maybe a penumbra, umbra sort of thing.
Check out Moat's approach. It's not that far off this (but I'd like to make it less psychedelic)
Hmmm, I was thinking of having grid lines drawn on the empty space, not on the object. Perhaps it's useful to both or only 1?
Moat's visualization looks like it's a custom shader that branches from the normal code to his based on whether it's in the sphere. Or maybe an additional shader pass that bails out if it's not in the sphere.
I could check out the code but it's so much easier to speculate ๐
it's a custom unlit blend shader applied to a sphere
it reads the depth buffer and uses the camera's position to figure out the world position of each fragment
and then tints each fragment based its position
I applied it just to the sphere because I wanted people to be able to preview their scene with the effect off just by moving the sphere out of the way, and i felt that the grid was only necessary to see within close proximity to the brush
I think I fed the camera's world position into the shader's global parameters
there may be nicer ways to ascertain a fragment's world position than what I used
I primarily made the tool for creating architectural hulls or for furniture, etc.
and I also had to make it apply its effect adaptively depending on the user's zoom level
because I didn't wanna show a zillion teeny teeny lines when you zoom out, or really, to show grid lines that were either too close together or too far apart to practically and physically interact with in VR
I'm happy to answer any questions
AmbientGrid is used in one of the environments and seems not far off that. I'll see if I can hack that in to the main scene
That's pretty cool, thanks for sharing. I don't know a better to recover world position per pixel without interfering with the way everything is rendered. Totally onboard with the sphere reasoning. What do you think about having the grid lines also be in empty space (or only be)?
Trying to get something similar to the Gravity Sketch Grid working.
Not quite there but is this a good approach?
I certainly like it's simplicity and it may even be a good option for Quest.
Also makes me wonder if actually rendering the lines is too intrusive...๐ค
Maybe also scale them up or down based on proximity to the brush pointer.
I think it would be less jarring if the cracker-jacks faded out and in rather than jumping.
Yeah - I was trying for that... Gravity Sketch for comparison: https://www.youtube.com/watch?v=CokKy9knc04&t=30s
Gravity Sketch
YouTube
Grid - Tools Tutorial Gravity Sketch VR
Tools Tutorial Gravity Sketch VR
Gravity Sketch VR 1.5
Basic tutorial series
How to use the grid for precise spacing when modeling
For more information, please check our live user guide:
https://docs.google.com/presentation/d/1M5Kt6qRMP_o-WOYwJkXifdAiedBv-E1B7zHw2T91GBA/edit?ts=597f09ef#slide=id.g2d1b26405f_0_0
Please leave feedback in the c...
let's see. to do the Gravity Sketch thing, you could use a 3D model consisting of many of those little gnomon/crosses arranged in a cubic lattice within a spherical volume...
and then assign to them a shader that fades their fragment opacity based on their distance to the cursor
this way it only takes a single game object, a single mesh renderer, and a fairly simple unlit blend shader
it would need a monobehaviour to feed the brush position to the shader and to move the 3D cross-grid around
using an actual lattice without gaps would probably take fewer triangles and allow for continuous grid lines, but would mean more of the fragment code would run over a greater area of the screen
I don't know the significance of CPU/GPU impact between the two methods
Did you see the video I posted above? I'm not far off (I borrowed a nice shader that did all the hard work in the vertex stage and used DrawProceduralNow to trigger it from Unity)
I just need to get the interpolation working, Mine jumps and I'm trying to fix that now.
yeah, I saw yours -- I'm not sure if drawing it procedurally is faster or slower. It looks close though.
I could swap to a static mesh but it's more flexible this way so I'm going to stick with it.
that's true
I doubt the triangle count is a bottleneck
if it's not a static mesh then you can control line thickness
aye
be that as it may, you could procedurally generate a static mesh muahahaha
meaning that you can define a mesh at runtime to be passed to a meshfilter/meshrenderer that is only changed when changing the graph settings
but your method allows for a grid that responds in real-time
too tired to fix this now. I'll try again in that 60m window of opportunity at the start of the day where my brain works and nobody is distracting me
the after-coffee caffeine buzz window
I know that window well
tea, dear chap. some of us are british
noted
@moat9195 @michael_guerrero See my last commit. It's getting there and I've integrated it into the scene. It was working pretty perfectly in a simple minimal Unity project but I can't quite get the transformation to match the canvas working. The scale and rotation are fine but a) the offset from the origin changes and b) the quantization is messed up when you scale the canvas.
Plus I still need to wire it up so it reflects changes to the snap grid resolution - but I'm stuck on a and b right now
Grateful for any help. Line 160 in SnapGrid3d.shader got me quite far:
And _CanvasMatrix is set in SnapGrid3D.cs:
(I'm can get away with using "transform" here because the gameobject concerned is a child of the canvas)
Minimal Unity project Example:
you could use transform.parent to get the parent of a child object
there's also transform.root
@andybak
but lemme fire up that minimal example
poke me when you get online and we'll get this one
Might be a while. got a pre-wedding, wedding and hangover to navigate first.
no prob
@moat9195 I tidied up the minimal example so that it's conceptually the same problem as I have with Open Brush: https://github.com/andybak/Grid3D
GitHub
GitHub - andybak/Grid3D: simple 3d grid renderer for Unity
simple 3d grid renderer for Unity. Contribute to andybak/Grid3D development by creating an account on GitHub.
The hierarchy is the same. So - here's what should be happening:
the canvas object should control scale and rotation - plus it determines the zero point for snapping.
The Pointmanager should be the center of the mesh. Rotating or scaling it shouldn't change anything but translations should affect the mesh (except the translation needs to be quantized based on the zero point of the Canvas)
I got close but then my eyeballs melted which made it hard to continue working. Take a look at lines 148-160: https://github.com/andybak/Grid3D/blob/master/Assets/Shaders/SnapGrid3D.shader#L148
I do wonder whether there's a simpler solution that involves parenting the PointerManager to the canvas and doing all the calculations in canvas space. But this feels like it's nearly there so I'm a bit reluctant to scrap it for a different approach.
Incidentally - HLSL doesn't have an "matrix.inverse" function but I think transpose() is fine in this situation
transpose is equal to the inverse for "orthogonal" matrices. https://en.wikipedia.org/wiki/Orthogonal_matrix
An example of a matrix which is orthogonal is a pure rotation matrix (no scale or translation).
Orthogonal matrix
In linear algebra, an orthogonal matrix, or orthonormal matrix, is a real square matrix whose columns and rows are orthonormal vectors.
One way to express this is
Q
T
Q = Q
Q
...
Q
T
Q = Q
Q
...
Ah. So it won't work with uniform scale and translation? I guess I can just pass in the inverse from the C# side of things then
that might explain some of the issues i was seeing.
but not all of them
Using a sophisticated programming technique called "just change stuff to see what it does" I've got a bit further. The quantization is still wonky but it respects the rotation/scale of the canvas and the position of the Pointer in what appears to be the right kinda way
I might have cracked it. Watch this space...
aaaa I missed your post
ok
Soooooo close! One last bugette...
The origin of the grid isn't stable when you move the canvas. But the scale and rotation work perfectly.
And the visualization looks fairly good. I could live with it
trying to work out what the fudge factor is that will fix the origin.
YYYYEEEESSSSSSS!!!!!!!
I haven't actually turned snapping on but you can see the grid is consistent whatever you do with the canvas and matches my roughly drawn cube
(the grid viz is much easier to understand in vr. i might scale it down to closer to 3x3 instead of 5x5)
looks great
somewhere in my grid code is the logarithmic dynamic grid spacing algorithm
that helps to make it so that the grid is usable at all resolutions
Yeah I saw that
it subdivides by a factor of 2, dynamically
but that can wait
the grid's looking very very useful
I'm leaning towards an explicit grid resolution - not sure people will like the grid being tied to zoom
mm. fair.
just been trying out grid snapping with my polyhedra. piece of cake to make a grid of shapes.
only problem is - i now want hex and iso grid snapping ๐ฆ
I like dynamic grids so that when I zoom out the grid doesn't become super fine
and then the other uniform plane tilings... and of course the 3d space groups...
but I think that the nice thing about fixed grids is it allows you to maintain a sense of scale
i wonder if there's a middle ground. you set a small, medium or large grid and that is dynamic with scale
because there's a zoom level where a 0.1 unit grid is useless - and same for large grids
anyway - i just pushed a build that has all my stuff plus your branch all combined: https://github.com/IxxyXR/open-brush/actions/runs/1283796932#artifacts
GitHub
Merge remote-tracking branch 'origin/features/snapping' into integr...
Open Source Fork of Tilt Brush. Contribute to IxxyXR/open-brush development by creating an account on GitHub.
i just need to get your drawing snapping integrated
and fix the crappy ui (currently the grid widgets are invisible for some bizarre reason)
the grid visualization is pretty neat even without the snapping. you can get a nice "close but still a bit sketchy" look
Grid snap working for painting now.
Another example