The Big List of Lux TODOs
Now that Lux supports attribute_buffer_views, investigate whether it’s feasible and efficient to skip bind() calls for many attribute_buffer_views over the same Lux.buffer.
There currently is an API mismatch between attribute buffers and element buffers. I originally tried to make attribute and element buffers look as much like each other as possible, but I thiknk that turned out to be a bad decision. In Lux.model(), the elements option returns an element buffer, which behaves entirely differently from the attribute buffer. In particular, attribute buffers get wrapped in Shade(). This means that to get to the buffer, it takes an extra “get()”. I think the solution is to accept that element buffers and attribute buffers have similar APIs but are entirely different things at heart..
The painful part here is that we will possibly have to redesign constant_value() to depend on bound variables, which will interact with the memoization infrastructure unless I am careful.
Right now the algorithm in place is correct, but conservative. Ideally, I will want to propagate BDDs up the expression tree and check them
Without relying on the AST, that is.
float(float(x)) => float(x),
float(x) => x, if x is known to be float
This would be an incredibly cool feature. Since I hold the AST, I think I could use automatic differentiation to detect expressions which are affine in the attribute variables. These are safe to hoist to a vertex shader, since fragments are linearly interpolated (what about perspective correction?)
The idea would be to make Shade.Types.float_t.is_vec() true, and then special-case a swizzle from a float to simple return a Shade.vec() with the right dimension. This would make the language slightly incompatible with GLSL ES, but it might be harmless. And having is_vec() true for float, vec2, vec3 and vec4 would be very convenient, since is_vec() could become a proxy for “can use .add, .sub and .mul”.
Although I would like there to be no need for user-defined GLSL, there is bound to be some situations in which Shade is not expressive enough to support a valid use case. I could support arbitrary functions which do not access any variables outside their scope. The main complication is support for array and struct types. More generally, how do I pass arbitrary shade values as parameters, and how do I interpret the results back?
Right now there exist Shade functions, which are javascript functions to build shade expressions. With user-defined GLSL, we will need an actual function type that denotes values to which you can apply new values and get the result.
functions like cosh which just build other expressions should have stricter type checking.
It should be possible to implement some form of static checking for Shade.make when passing functions.
Batches can be drawn in different modes, currently:
- “regular” drawing
- additive blending
- alpha-blended
But scenes can also be rendered in different modes, currently
- regular drawing mode
- picking mode
- depth unproject mode
These two possibilities interact, and the result is a double dispatch, currently implemented badly on Lux.DrawingMode.*
This should be fixed.
Namely, the Lux.DrawingMode.<foo>.set_<bar>_caps functions dispatch on both foo and bar.
One better solution is to have a dictionary and dispatch on (<foo>, <bar>) pairs.
This would pave the way for extensible batch drawing modes. But extending rendering modes is not as easy because, for example, the picking procedure requires extra state to be kept around. This should be designed carefully (in other words, I’m afraid of trying it out right now)
Part of it exists throughout Lux, but it’s scattered and untested.
The way to do this is to add underscore_equivalents of the GLSL ugly camelCase functions. This way, people familiar with GLSL can use them, while people coming directly to Lux will use underscore
One possibility is to create a WebGLObject prototype that knows how to turn itself into a Shade expression
It is very convenient to use runtime type checking to get polymorphism, but it seems like it tends to proliferate along the code. I should try to consolidate all these calls in a single API of some sort.
I’ve been seeing a lot of infinite loops because of co-recursion in constant_value. What do I do about it?
Update: the issue here is that many expressions lack a true definition of element(), and so when element(i) returns element.at(i), at(i).element() runs the risk of diverging.
This should go together with writing the semantics for Shade expressions.
functions like cosh which just build other expressions should have stricter type checking.
Lux.Marks.* need to be on the same coordinate system, need to consistently all take functions or expressions.
Now that I figured out a nice way to make aligned rects work, I should extend this to lines, etc. The main problem is interaction with attribute_buffer, but that’s inevitable without geometry shaders. I’ll need documentation.
According to the spec, they must be square. But I can create them in WebGL without any trouble. I wonder if things will break.