[racket] Crowdsourcing Pict3D's design

159 views
Skip to first unread message

Neil Toronto

unread,
Mar 8, 2015, 10:54:18 PM3/8/15
to Racket Users
Pict3D is finally ready for public consumption. You can install the
package either in DrRacket using "File -> Install Package..." or from
the command line using

raco pkg install pict3d

The GitHub page is here:

https://github.com/ntoronto/pict3d

All features are documented. The API is stable. I've verified that it
runs on Racket 6.1.1 and the current development version. There are
reports of it working on at least two Windows systems, two Mac OS X
systems, and two Linux systems. It might not eat your graphics card.

Current features:

* Works in untyped and Typed Racket

* Spheres, rectangles, triangles and quads with per-vertex attributes

* A system of groups and affine (arbitrary parallel-line-preserving)
transformations for sticking Pict3Ds together

* Ray-against-scene and line-against-scene collision detection

* Render targets: an interactive debugging view in DrRacket's REPL,
`pict3d->bitmap` and `pict3d-canvas%`

* big-bang3d (currently without networking)

------------------

Now we get to crowdsourcing.

I've reached the limits of what I can do without working on Pict3D
full-time. In particular, I don't know whether the API is awkward or
actually quite nice for the uses it'll be put to. I've drawn on some
personal game design and coding experience, but that only goes so far.

This is where you come in.

If you've ever had the slightest hankering to do some real 3D but
avoided it because of the pain that usually goes with it, try Pict3D.
(If it fails to work, please submit a bug report on the GitHub page.)
Got a visualization project? Try Pict3D. Want to make a game? Try
Pict3D's version of Big Bang. Want to just fool around in 3D space for a
bit? Try Pict3D, and report back on how it goes.

Anything is fair game for criticism, praise or suggestions: groups and
pinning, distinguishing direction and position vectors, presence or lack
of shapes you need, how to allow shader programming, file formats that
would be nice to import models from, information that would be nice to
have in the interactive debugging view, how to add texturing to the API,
and even the tone of the documentation.

I'm looking forward to your reports, all my little minions. Wait, did I
say that out loud?

Neil ⊥
____________________
Racket Users list:
http://lists.racket-lang.org/users

Alexander D. Knauth

unread,
Mar 9, 2015, 3:39:45 PM3/9/15
to Neil Toronto, racket users list
Is there a good way to draw a smooth curved cylinder?

J Arcane

unread,
Mar 9, 2015, 5:32:45 PM3/9/15
to Alexander D. Knauth, racket users list
I'm really curious about this. I've never touched 3D, partly because most 3D frameworks/libraries are hardcore C/C++.

A 3d big-bang gives me happy thoughts.

Neil Toronto

unread,
Mar 9, 2015, 5:59:20 PM3/9/15
to Alexander D. Knauth, racket users list
Sure! You could assemble it yourself out of triangles and quads. :)

OK, maybe that's not "good". It's a fun problem, though, so I gave it a
shot. I've attached my solution. The `parametric-cylinder` function
accepts a function that takes a "time" parameter `t` and returns four
values: the center of the cylinder at time `t`, the first derivative of
the function from `t` to center, the second derivative, and a radius. It
should work on any parametric function that doesn't have any collinear
segments.

You probably want something like the `helix` example.

An easier-to-use solution would probably derive the first and second
derivatives from samples of the center position.

Neil ⊥
parametric-cylinder.rkt

Konrad Hinsen

unread,
Mar 10, 2015, 9:39:05 AM3/10/15
to Racket Users
Neil Toronto writes:

> Pict3D is finally ready for public consumption. You can install the
> package either in DrRacket using "File -> Install Package..." or from
> the command line using
>
> raco pkg install pict3d

This looks quite impressive - thanks!

Where can I download some spare hours to play with this? ;-)

Konrad.

John Clements

unread,
Mar 10, 2015, 7:23:47 PM3/10/15
to Konrad Hinsen, Racket Users
Neil, I'm having a problem on OS X 10.9 where the mouse isn't behaving as I expect. I may have to make a movie of this, but I'm hoping not. The short version is this: I can't look around. More specifically:
- I run the example program (with the circle of radius 1/2).
- I click on the image.
- I can move the camera with the keys as expected.
- Moving the mouse results in quivering,  but nothing more.  After fiddling with it for about five minutes, I've decided that there might be a "center the mouse" call that's being used to keep the (hidden) mouse cursor in the middle of the window, and that on my mac, at least, this centering call is also returning the orientation of the camera to its original location. The result is that I feel like I'm struggling with a malevolent genie; I can make the camera turn just a bit, but no matter how hard I run, I'm pulled back to the same location.

Let me know if you want me to make a movie of this.

Otherwise: fantastic. Awesome. Fix it so I can show it to all my graphics colleagues.

John

Neil Toronto

unread,
Mar 10, 2015, 9:36:27 PM3/10/15
to us...@racket-lang.org
Robby was afraid that might happen on OS X. The weird thing is that I
don't have reports of it from any other OS X users.

It looks like we'll need alternative input methods for movement as well.
(Turns out that WASD aren't necessarily near each other on all
keyboards.) I'll add this to my input revamp list.

Neil ⊥

Jack Firth

unread,
Mar 10, 2015, 9:40:02 PM3/10/15
to Neil Toronto, racket users list
This happens on my OS X machine as well, in precisely the same way.

Alexander D. Knauth

unread,
Mar 10, 2015, 9:40:54 PM3/10/15
to Neil Toronto, racket users list
I get that too.

Neil Toronto

unread,
Mar 10, 2015, 9:41:12 PM3/10/15
to us...@racket-lang.org
On 03/10/2015 09:35 AM, Konrad Hinsen wrote:
> Neil Toronto writes:
>
> > Pict3D is finally ready for public consumption. You can install the
> > package either in DrRacket using "File -> Install Package..." or from
> > the command line using
> >
> > raco pkg install pict3d
>
> This looks quite impressive - thanks!
>
> Where can I download some spare hours to play with this? ;-)

From the same place I downloaded the spare hours to make it. :p

If you need justification, this will render molecular structures
thousands of times faster than Plot does. It won't output PDFs, but you
can set the background color to white or transparent and render up to
1024x1024 bitmaps and save them as JPEGs or PNGs.

Neil ⊥

Neil Toronto

unread,
Mar 10, 2015, 9:43:50 PM3/10/15
to Alexander D. Knauth, racket users list
Welp, that's it. Also, you got ninja'd by Jack.

I have some ideas. We'll see how they turn out.

What I really need is mouse grab, but Racket's GUI framework doesn't
provide a way to do it.

Neil ⊥

Robby Findler

unread,
Mar 10, 2015, 10:55:37 PM3/10/15
to Neil Toronto, racket users list
I think Mac OS X's official stance on mouse grab (which I mostly
support personally) is: get a new UI design.

How about: when the snip has the focus, shift and mouse down rotates
things and normal mouse down just moves the mouse? Then you'd get the
ability to slide along left and then go back and slide along some
more. Does something like that work?

Robby

John Clements

unread,
Mar 11, 2015, 1:39:52 AM3/11/15
to Robby Findler, racket users list
Maybe simpler: why not just treat the mouse like a joystick? That is, if you press the mouse down and move it a little to the right and hold it there, the camera continues to move smoothly until you release.

Neil Van Dyke

unread,
Mar 11, 2015, 1:52:25 AM3/11/15
to racket users list
John Clements wrote on 03/11/2015 01:38 AM:
> Maybe simpler: why not just treat the mouse like a joystick? That is,
> if you press the mouse down and move it a little to the right and hold
> it there, the camera continues to move smoothly until you release.

Firefox "autoscroll" is one popular example of this. Middle-mouse-down
on most parts of the page puts an indicator at pointer position on
screen; then, until mouse-up, the direction and speed of the scrolling
is relative to the direction and distance of pointer from this indicator.

https://support.mozilla.org/en-US/kb/advanced-panel-accessibility-browsing-network-upda#w_browsing

Neil V.

Konrad Hinsen

unread,
Mar 11, 2015, 3:30:50 AM3/11/15
to us...@racket-lang.org
On 11/03/15 02:38, Jack Firth wrote:

> This happens on my OS X machine as well, in precisely the same way.

Me too. I didn't notice before because I didn't know it should work. I
was just disappointed that I couldn't navigate with the mouse.

On 11/03/15 02:39, Neil Toronto wrote:

> If you need justification, this will render molecular structures
> thousands of times faster than Plot does. It won't output PDFs, but you
> can set the background color to white or transparent and render up to
> 1024x1024 bitmaps and save them as JPEGs or PNGs.

It's indeed my plan to use this for molecular visualization. But also
for other uses, in particular in teaching. This looks like the first
usable successor to VPython (http://vpython.org), which is a very nice
visualization system IF you manage to install it on your computer. I
haven't succeeded for several years, unfortunately. DrRacket+Pict3D are
trivial to install, that's a big plus.

Neil Toronto

unread,
Mar 12, 2015, 8:13:23 PM3/12/15
to us...@racket-lang.org
This is what I ended up doing, as well as disabling mouse look on OS X.
I used Firefox's autoscroll as inspiration, and I think I've made it
usable enough. Let me know what you think.

Neil

Sam Tobin-Hochstadt

unread,
Mar 12, 2015, 8:22:48 PM3/12/15
to Neil Toronto, us...@racket-lang.org

What do browsers do to implement mouse lock on Macs?

Alexander D. Knauth

unread,
Mar 14, 2015, 10:29:59 PM3/14/15
to Neil Toronto, racket users list

On Mar 8, 2015, at 10:53 PM, Neil Toronto <neil.t...@gmail.com> wrote:

> If you've ever had the slightest hankering to do some real 3D but avoided it because of the pain that usually goes with it, try Pict3D. (If it fails to work, please submit a bug report on the GitHub page.) Got a visualization project? Try Pict3D. Want to make a game? Try Pict3D's version of Big Bang. Want to just fool around in 3D space for a bit? Try Pict3D, and report back on how it goes.

I made a version of the game unpossible using pict3d:
https://github.com/AlexKnauth/my-unpossible
It’s a bit slow and jerky, but that’s probably entirely because I didn’t try too hard to make my end of it fast.
If you have any suggestions that would be great.


On Mar 9, 2015, at 5:58 PM, Neil Toronto <neil.t...@gmail.com> wrote:

> On 03/09/2015 03:37 PM, Alexander D. Knauth wrote:
>> Is there a good way to draw a smooth curved cylinder?

> Sure! You could assemble it yourself out of triangles and quads. :)


>
> OK, maybe that's not "good". It's a fun problem, though, so I gave it a shot. I've attached my solution. The `parametric-cylinder` function accepts a function that takes a "time" parameter `t` and returns four values: the center of the cylinder at time `t`, the first derivative of the function from `t` to center, the second derivative, and a radius. It should work on any parametric function that doesn't have any collinear segments.
>
> You probably want something like the `helix` example.
>
> An easier-to-use solution would probably derive the first and second derivatives from samples of the center position.
>
> Neil ⊥

Thanks!
And by the way, for my use case, it was good that I could provide the derivative myself.
I ended up using it like this:
(parametric-cylinder
#:samples 1
(match-lambda
[0.0 (values pos dir (dir- new-dir dir) 1/2)]
[1.0 (values new-pos new-dir (dir- new-dir dir) 1/2)])
0.0 1.0)

Neil Toronto

unread,
Mar 14, 2015, 11:53:43 PM3/14/15
to Alexander D. Knauth, racket users list
On 03/14/2015 10:27 PM, Alexander D. Knauth wrote:
>
> On Mar 8, 2015, at 10:53 PM, Neil Toronto <neil.t...@gmail.com> wrote:
>
>> If you've ever had the slightest hankering to do some real 3D but avoided it because of the pain that usually goes with it, try Pict3D. (If it fails to work, please submit a bug report on the GitHub page.) Got a visualization project? Try Pict3D. Want to make a game? Try Pict3D's version of Big Bang. Want to just fool around in 3D space for a bit? Try Pict3D, and report back on how it goes.
>
> I made a version of the game unpossible using pict3d:
> https://github.com/AlexKnauth/my-unpossible
> It’s a bit slow and jerky, but that’s probably entirely because I didn’t try too hard to make my end of it fast.
> If you have any suggestions that would be great.

Very cool!

Let's be fair to you: it's not obvious how to make it fast. I'd love to
make the API so that it *is* obvious, or so that the techniques to make
it fast are done automatically.

Here's an indication about what's going on. Try these in the REPL:

(time (void (get-tube+obstacles (make-world-stream) +x 40)))
(time (void (combine (get-tube+obstacles (make-world-stream) +x 40))))

(Using `void` just keeps the Pict3Ds from getting rendered.) For both, I
get 75-125ms, so the culprit isn't `combine` - it's `get-tube+obstacles`.

But the `get-tube+obstacles` code isn't doing anything in a particularly
slow way. It's just that making shapes is slow. As evidence, the game
goes at a good clip, though the tube looks terrible, if I add
"#:segments 8" to the call to `parametric-cylinder`.

(I could probably speed `parametric-cylinder` way up by writing it using
a lower-level interface to the 3D engine, but this would eventually come
up again.)

What you need is to 1) create and combine the necessary Pict3Ds as much
as possible before the game starts; and 2) reuse them as much as
possible during the game.

You can even make the reused Pict3Ds fairly large if you freeze them.
Any modern 3D card won't have any problem with this, for example:

(freeze (combine (get-tube+obstacles (make-world-stream) +x 1000)))

Freezing takes a while, though, and has this annoying property that it
takes the most time the first time the frozen Pict3D is rendered. (The
actual freezing has to be done with an active OpenGL context.) I'm not
sure what to do about the latter problem.

If you don't mind, I have a couple of questions.

1. What was the most annoying surprise?

2. What was the most pleasant one?

Again, very cool!

Neil ⊥

Alexander D. Knauth

unread,
Mar 15, 2015, 4:56:55 PM3/15/15
to Neil Toronto, racket users list

On Mar 14, 2015, at 11:51 PM, Neil Toronto <neil.t...@gmail.com> wrote:

> What you need is to 1) create and combine the necessary Pict3Ds as much as possible before the game starts; and 2) reuse them as much as possible during the game.
>
> You can even make the reused Pict3Ds fairly large if you freeze them. Any modern 3D card won't have any problem with this, for example:
>
> (freeze (combine (get-tube+obstacles (make-world-stream) +x 1000)))
>
> Freezing takes a while, though, and has this annoying property that it takes the most time the first time the frozen Pict3D is rendered. (The actual freezing has to be done with an active OpenGL context.) I'm not sure what to do about the latter problem.

If I made it a finite game, with a finite tube, I could probably generate the whole tube once and move the camera along it, but with an infinite lazy tube, I’m not sure how I can reuse it.

> If you don't mind, I have a couple of questions.
>
> 1. What was the most annoying surprise?

The most annoying surprise. Before you gave me your parametric-cylinder function, I tried approximating it with a bunch of normal cylinders.
I created them upright, in position, and then rotated them, but I hadn’t realized that when it rotated them it also rotated their positions around the origin, so they weren’t showing up. Then I created them upright at the origin, then rotated them, and then used move to put them in place. The current behavior does make sense, but a note in the docs might be helpful.

> 2. What was the most pleasant one?

The most pleasant surprise was your parametric-cylinder function, and that it allowed me to supply the derivative, which was exactly what I needed to make the ends of the cylinders line up.

Neil Toronto

unread,
Mar 15, 2015, 6:00:10 PM3/15/15
to Alexander D. Knauth, racket users list
On 03/15/2015 04:54 PM, Alexander D. Knauth wrote:
>
> On Mar 14, 2015, at 11:51 PM, Neil Toronto <neil.t...@gmail.com> wrote:
>
>> What you need is to 1) create and combine the necessary Pict3Ds as much as possible before the game starts; and 2) reuse them as much as possible during the game.
>>
>> You can even make the reused Pict3Ds fairly large if you freeze them. Any modern 3D card won't have any problem with this, for example:
>>
>> (freeze (combine (get-tube+obstacles (make-world-stream) +x 1000)))
>>
>> Freezing takes a while, though, and has this annoying property that it takes the most time the first time the frozen Pict3D is rendered. (The actual freezing has to be done with an active OpenGL context.) I'm not sure what to do about the latter problem.
>
> If I made it a finite game, with a finite tube, I could probably generate the whole tube once and move the camera along it, but with an infinite lazy tube, I’m not sure how I can reuse it.

I'd generate a few different kinds of segments and then transform them
into place. Don't use `rotate` for that, though - there's an easier way.

Transforming a shape is pretty fast. Here's an insane example, using
many more segments for a cylinder than you'd normally ever need:


#lang racket

(require pict3d)

(define v1 (pos 1 1 1))
(define v2 (pos 2 3 2))
(define cyl
(time
(with-color (rgba "lightblue")
;; WOO 65536 TRIANGLES YEAH
(move-z (cylinder origin (dir 1/4 1/4 1/2) #:segments 16384)
1/2))))

(define pict
(time
(combine (sphere v1 0.2)
(sphere v2 0.2)
(transform cyl (point-at v1 v2 #:normalize? #f)))))

(time (pict3d->bitmap pict))
(time (pict3d->bitmap pict))


On my computer, creating `cyl` takes 5 seconds. Transforming it takes no
measurable time. Rendering it the first time takes 1.5 seconds, and
rendering it the second time takes 11 milliseconds. (Cylinders are
frozen, so they'll always render faster the second time.)

To stretch the cylinder between two points, the program

1. Creates the cylinder so that its bottom is at the origin and its
top is at (pos 0 0 1).

2. Uses `point-at` without normalization to move the origin to `v1` and
move (pos 0 0 1) to `v2`.

Based on this and your other feedback, I think Pict3D needs

* A `rotate-around` function that rotates a shape around a given point
(with the default being the center of its bounding box).

* A note in the docs for `rotate` et al that explain they rotate around
the origin.

* A "How Do I" section that includes things like the above example.

* Notes in the docs about performance characteristics of different
functions (e.g. that `cylinder` and `cone` return frozen Pict3Ds, and
what that means you can expect from them).

I expect performance characteristics to change, though, so that last one
might be late coming.

Neil ⊥

Alexander D. Knauth

unread,
Mar 15, 2015, 6:31:57 PM3/15/15
to Neil Toronto, racket users list
Thanks!

Actually, now that I think about it something like this would be really helpful, and probably make more sense:
Maybe a version of point-at that would let you specify which pos’s should match up where?

(my-point-at v1 v2 v3 v4 #:normalize? #f) ; maps v1 to v3 and v2 to v4
(my-point-at (pos 0 0 0) (pos 1 0 0) v1 v2 #:normalize? #f) ; maps the origin to v1, and (pos 1 0 0) to v2.
(my-point-at (pos 0 0 0) (pos 0 0 1) v1 v2) ; equivalent to (point-at v1 v2)
(my-point-at v1 dv1 v2 dv2) ; equivalent to (my-point-at v1 (pos+ v1 dv1) v2 (pos+ v2 dv2))

Alexander D. Knauth

unread,
Mar 15, 2015, 7:40:48 PM3/15/15
to Neil Toronto, racket users list
Does the attached file look like a good implementation of my-point-at, or would it do things I wouldn’t expect for things not on the line between the two points, or ? I’m asking because I don’t think I completely understand point-at and what it does.

my-point-at.rkt

Neil Toronto

unread,
Mar 16, 2015, 7:14:10 AM3/16/15
to Alexander D. Knauth, racket users list
It should work fine for what you want to do with it. (It's exactly what
I thought of when I read your spec for it.) The only possible thing that
can go wrong with affine transformations is unwanted shear [1], and
`point-at` never produces a transformation that shears. Aside from the
z-axis stretch, it's all rigid.

If you want to control *rotation* around the v1-v2 and v3-v4 axes,
you'll need to add a couple of arguments corresponding to the #:up and
#:angle arguments of `point-at`. I don't know how you'd pass those along
to the two `point-at` calls or how they'd interact. That doesn't mean
something reasonable is impossible, though.

To understand `point-at` better, run the following program and play with
the #:up and #:angle arguments. Most of the code retesselates the
vertices of an octahedron to try to get a uniform distribution of
directions. The important stuff is at the end.


#lang racket

(require pict3d)

(define (retesselate dvs)
(match-define (list dv0 dv1 dv2) dvs)
(define dv01 (dir-normalize (dir-scale (dir+ dv0 dv1) 0.5)))
(define dv12 (dir-normalize (dir-scale (dir+ dv1 dv2) 0.5)))
(define dv20 (dir-normalize (dir-scale (dir+ dv2 dv0) 0.5)))
(list (list dv0 dv01 dv20)
(list dv1 dv12 dv01)
(list dv2 dv20 dv12)
(list dv01 dv12 dv20)))

(define octahedron-dirs
(list (list +x +y +z)
(list +y -x +z)
(list -x -y +z)
(list -y +x +z)
(list +y +x -z)
(list -x +y -z)
(list -y -x -z)
(list +x -y -z)))

(define dvs
(remove-duplicates
(append*
(for/fold ([dvss octahedron-dirs]) ([_ (in-range 3)])
(append* (map retesselate dvss))))))

(combine
(for/list ([dv (in-list dvs)])
(basis 'camera (point-at (pos+ origin dv 2) origin
#:up +z #:angle 0))))


With the default #:up +z, there's a singularity at +z and another at -z,
where `point-at` isn't smooth. (Generally, with #:angle 0, the red x
axes point to their counterclockwise neighbors. At a pole, the only
counterclockwise neighbor is the pole itself.) Using #:up +x, the
singularities are at +x and -x. There's nothing we can do about this:
we're looking straight down the barrel of the Hairy Ball Theorem [2].
The `point-at` function must have at least one cowlick.

All this complexity comes from trying to make a rigid transformation
that's in some way reasonable when specified using only 5 degrees of
freedom: a position and a normalized direction. The result is mostly
intuitive, and works well enough.

Neil ⊥

[1] http://en.wikipedia.org/wiki/Shear_mapping
[2] http://en.wikipedia.org/wiki/Hairy_ball_theorem

On 03/15/2015 07:38 PM, Alexander D. Knauth wrote:
> Does the attached file look like a good implementation of my-point-at, or would it do things I wouldn’t expect for things not on the line between the two points, or ? I’m asking because I don’t think I completely understand point-at and what it does.
>
>
>
>

Alexander D. Knauth

unread,
Mar 16, 2015, 3:22:52 PM3/16/15
to Neil Toronto, racket users list
Would it work so that you could all instances of:
(pin
(combine pict1 (basis 'b1 (point-at v1 v2))) ‘(b1)
(combine pict2 (basis ‘b2 (point-at v3 v4))) ‘(b2))
Could be replaced with:
(combine
pict1
(transform pict2 (my-point-at v1 v2 v3 v4)))
I tried doing that with my game, and nothing weird happened, but am I missing something?

And if it does work like that, then would it make sense for you to extend point-at so that when it takes 4 arguments it does this?
Or should it be a separate function?

Or, would it make sense to provide a function like this?
(define (from-to t1 t2) ; It would probably need a better name, but I think it could be very useful and intuitive
(affine-compose t2 (affine-inverse t1)))
So then my-point-at could be defined as
(define (my-point-at v1 v2 v3 v4)
(from-to (point-at v1 v2) (point-at v3 v4)))
And instances of
(pin
(combine pict1 (basis ‘b1 t1)) ‘(b1)
(combine pict2 (basis ‘b2 t2)) ‘(b2))
could be replaced with
(combine pict1 (transform pict2 (from-to t2 t1)))

Would that make sense?

Neil Toronto

unread,
Mar 16, 2015, 4:07:47 PM3/16/15
to Alexander D. Knauth, racket users list
Nothing weird happened in your game probably because it doesn't depend
on any specific rotation.

A `from-to` function is a nice idea. In general, it says "change the
basis from t1 to t2". I'll add it to my list.

Neil ⊥

Neil Toronto

unread,
Mar 16, 2015, 5:38:53 PM3/16/15
to Alexander D. Knauth, racket users list
I think I like this incarnation:

(: relocate (case-> (-> Affine Affine Affine)
(-> Pict3D Affine Affine Pict3D)))
;; Moves `pict` from coordinate system `t1` to coordinate system
;; `t2`, or returns a transform that does so
(define relocate
(case-lambda
[(t1 t2) (affine-compose t2 (affine-inverse t1))]
[(pict t1 t2) (transform pict (relocate t1 t2))]))


And this derived function, which is usually called a "change of basis"
(though that term is overloaded, overused and ambiguous):

(: local-transform (case-> (-> Affine Affine Affine)
(-> Pict3D Affine Affine Pict3D)))
;; Applies `t` within coordinate system `local-t`
(define local-transform
(case-lambda
[(t local-t) (affine-compose local-t (relocate local-t t))]
[(pict t local-t) (transform pict (local-transform t local-t))]))


The `local-transform` function makes it easy to write something like
`rotate/center`.

Neil ⊥

Berthold Bäuml

unread,
Mar 18, 2015, 9:25:34 AM3/18/15
to Neil Toronto, Racket Users
Hi Neil,

Pict3D is a really awesome project and fun to use!

I have a question about the architecture and performance of Pict3D for scenes with millions of triangle. I want to visualize a robot having about 50 movable joints and each link of the robot is made from some 10^5 triangles (which works with no problem with 3D engines like Coin or OpenScenegraph). Has Pict3D to transfer all the triangles each time I want to render the robot with different joint positions or are they cached on the GPU and only the changed transformation matrices for each link have to be transferred? You mentioned that using "freeze" optimizes the transport (in my case I would freeze each link), but have freezed parts still to be transferred each time anew?

I made a simple test program which draws some cylinders with many segments. Making the assumption that each segment is made of two triangles, even for about 4*10^5 triangles each frame already takes about 70ms to render on my computer consuming 100% of a cpu core. On the same computer I can render the full robot at 30Hz moving all its joints taking only 20% of the cpu.

Did I miss something in making my example more performant or are there future plans for making pict3d faster?

Best,
Berthold

simple example:

#lang racket
(require pict3d)
(require pict3d/universe)


(define p(combine
(for/list ([i (in-range 100)])
(freeze (set-emitted
(cylinder (pos i 0 0) (pos (+ i 1/2) 1/2 1/2) #:segments 2000)
(emitted "lightgreen" 2))))))

(define (on-frame s n t)
(displayln (- t s))
t)

(define (on-draw s n t)
p)

(big-bang3d 0 #:on-frame on-frame #:on-draw on-draw)

> On 09 Mar 2015, at 03:53, Neil Toronto <neil.t...@gmail.com> wrote:
>
> Pict3D is finally ready for public consumption. You can install the package either in DrRacket using "File -> Install Package..." or from the command line using
>
> raco pkg install pict3d
>

> The GitHub page is here:
>
> https://github.com/ntoronto/pict3d
>
> All features are documented. The API is stable. I've verified that it runs on Racket 6.1.1 and the current development version. There are reports of it working on at least two Windows systems, two Mac OS X systems, and two Linux systems. It might not eat your graphics card.
>
> Current features:
>
> * Works in untyped and Typed Racket
>
> * Spheres, rectangles, triangles and quads with per-vertex attributes
>
> * A system of groups and affine (arbitrary parallel-line-preserving)
> transformations for sticking Pict3Ds together
>
> * Ray-against-scene and line-against-scene collision detection
>
> * Render targets: an interactive debugging view in DrRacket's REPL,
> `pict3d->bitmap` and `pict3d-canvas%`
>
> * big-bang3d (currently without networking)
>
> ------------------
>
> Now we get to crowdsourcing.
>
> I've reached the limits of what I can do without working on Pict3D full-time. In particular, I don't know whether the API is awkward or actually quite nice for the uses it'll be put to. I've drawn on some personal game design and coding experience, but that only goes so far.
>
> This is where you come in.
>

> If you've ever had the slightest hankering to do some real 3D but avoided it because of the pain that usually goes with it, try Pict3D. (If it fails to work, please submit a bug report on the GitHub page.) Got a visualization project? Try Pict3D. Want to make a game? Try Pict3D's version of Big Bang. Want to just fool around in 3D space for a bit? Try Pict3D, and report back on how it goes.
>

> Anything is fair game for criticism, praise or suggestions: groups and pinning, distinguishing direction and position vectors, presence or lack of shapes you need, how to allow shader programming, file formats that would be nice to import models from, information that would be nice to have in the interactive debugging view, how to add texturing to the API, and even the tone of the documentation.
>
> I'm looking forward to your reports, all my little minions. Wait, did I say that out loud?
>

> Neil ⊥
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users

--
-----------------------------------------------------------------------
Berthold Bäuml -- Head of Autonomous Learning Robots Lab
DLR, Robotics and Mechatronics Center (RMC)
Münchner Str. 20, D-82234 Wessling
Phone +49 8153 282489
http://www.robotic.de/Berthold.Baeuml

Neil Toronto

unread,
Mar 19, 2015, 8:10:05 AM3/19/15
to Berthold Bäuml, Racket Users
Thanks, and I'm glad you're having a good time. :)

Currently, the vertex data for frozen shapes is transferred every frame.
The 3D engine is saturating your AGP bus.

I have plans to add either `freeze/send` or a `#:send?` argument to
`freeze`, which will send the vertex data once to reside on the GPU,
until the frozen Pict3D is garbage-collected.

Since you seem to know what you're talking about, I have a couple of
questions for you about this.

1. I've also considered having a data size threshold after which the
data will be sent once. I'm not sure how to set this threshold, nor
even whether it's a good idea. Any thoughts on automating the
decision on what gets sent to the GPU?

2. Attribute-updating functions such as `set-color` could be much more
expensive for GPU-resident Pict3Ds. Here are a few options for
dealing with it:

A. Just update the attribute and re-freeze. If the old Pict3D hangs
around on the GPU too, let the user deal with memory issues.

B. Update the attribute and re-freeze, and warn the user somehow.

C. Update the attribute and *don't* re-freeze.

What do you think would be least annoying overall?

I also have plans to add engine support for indexed triangle meshes to
reduce vertex data duplication. I haven't settled on the front-end API
for this, but I'll probably introduce a `smooth` combiner and a `mesh`
constructor.

I'll bump these up on my to-do list. Thanks for trying it out!

Neil ⊥

Raoul Duke

unread,
Mar 19, 2015, 2:11:56 PM3/19/15
to Racket Users
> Currently, the vertex data for frozen shapes is transferred every frame. The
> 3D engine is saturating your AGP bus.

the year 2003 called and wants its technology back. :-)

Tomi Pieviläinen

unread,
Mar 19, 2015, 2:19:58 PM3/19/15
to us...@racket-lang.org
> Freezing takes a while, though, and has this annoying property that
> it takes the most time the first time the frozen Pict3D is rendered.
> (The actual freezing has to be done with an active OpenGL context.)
> I'm not sure what to do about the latter problem.

Would it be enough to render to a file like object, that is simply
discarded?

--
Tomi Pieviläinen, +358 400 487 504
A: Because it disrupts the natural way of thinking.
Q: Why is top posting frowned upon?

Neil Toronto

unread,
Mar 19, 2015, 10:02:39 PM3/19/15
to us...@racket-lang.org
On 03/19/2015 02:09 PM, Raoul Duke wrote:
>> Currently, the vertex data for frozen shapes is transferred every frame. The
>> 3D engine is saturating your AGP bus.
>
> the year 2003 called and wants its technology back. :-)

The year 1861 sent you a telegraph: they want their insult back. :p

Anyway... there are things missing from Pict3D not because they're
difficult, but because I haven't decided on a design. This one's a prime
example. The 3D engine already uses vertex buffer objects (i.e. current
tech) to stream vertex data. It would be easy to give each frozen Pict3D
its own, and send to and evict from the GPU based on some caching
policy. But I haven't researched good policies yet.

Texture and normal maps are another prime example. From a UI
perspective, texture coordinates have got to be the worst way to
associate sub-geometry details with a shape. I don't know what Pict3D
will have instead. I'm half-inclined to make a mini-DSL that compiles to
vertex and fragment shaders and let you figure it out. :D

Neil ⊥

Neil Toronto

unread,
Mar 19, 2015, 10:44:18 PM3/19/15
to us...@racket-lang.org

It has been brought to my attention that AGP buses have gone extinct,
and you were probably talking about that.

That's what I get for not putting computers together anymore. What's
next, machines without 3.5-inch floppy drives? What'll I do with all
these Turbo C++ and Windows NT install disks?

Raoul Duke

unread,
Mar 20, 2015, 12:44:38 AM3/20/15
to Neil Toronto, Racket Users
the only reason i know is because i finally bit the bullet and got
something "modern" (as in: pci-x instead of agp), for Minecraft. :-)

Berthold Bäuml

unread,
Mar 20, 2015, 11:45:31 AM3/20/15
to Neil Toronto, Racket Users
Hi Neil,

>
> I have plans to add either `freeze/send` or a `#:send?` argument to `freeze`, which will send the vertex data once to reside on the GPU, until the frozen Pict3D is garbage-collected.

My first intuition was to keep the data on the GPU and the "current" pict3d synchronized. Each time a pict3d should be rendered all frozen sub-picts which are not already on the GPU are sent and all data which is not part of the current pict3d is deleted from the GPU (or can be kept there for caching).

> Since you seem to know what you're talking about, I have a couple of questions for you about this.

I am no expert at all but only a user with large models to work with ...

> 1. I've also considered having a data size threshold after which the
> data will be sent once. I'm not sure how to set this threshold, nor
> even whether it's a good idea. Any thoughts on automating the
> decision on what gets sent to the GPU?

You talk only about the frozen data? Then I would do it the way described above.

> 2. Attribute-updating functions such as `set-color` could be much more
> expensive for GPU-resident Pict3Ds. Here are a few options for
> dealing with it:

I am not sure but don't modern OpenGL versions have the possibility to change the color without having to retransfer the complete geometry data anew (kind of a color array and a index array)?

> A. Just update the attribute and re-freeze. If the old Pict3D hangs
> around on the GPU too, let the user deal with memory issues.
>
> B. Update the attribute and re-freeze, and warn the user somehow.
>
> C. Update the attribute and *don't* re-freeze.
>
> What do you think would be least annoying overall?

Option A in combination with he "synchronization" scheme above would be my favorite: completely transparent to the user. Or I am missing something?

> I also have plans to add engine support for indexed triangle meshes to reduce vertex data duplication. I haven't settled on the front-end API for this, but I'll probably introduce a `smooth` combiner and a `mesh` constructor.

A 'mesh' constructor looks obvious to me. But what do you mean by a 'smooth' combinator? Should meshes combine differently than other primitives?

Best,
Berthold

--

-----------------------------------------------------------------------
Berthold Bäuml -- Head of Autonomous Learning Robots Lab
DLR, Robotics and Mechatronics Center (RMC)
Münchner Str. 20, D-82234 Wessling
Phone +49 8153 282489
http://www.robotic.de/Berthold.Baeuml

Reply all
Reply to author
Forward
0 new messages