|
View:
New views
1 Messages
—
Rating Filter:
Alert me
|
|
|
Looking for feedback and ideas.Hi,
I'm currently playing with Io as a potential basis for a procedural animation framework, along the lines of Steve May's AL <http://accad.osu.edu/%7Esmay/AL/> . I started looking at Io because of it's inherent introspective nature, which is quite important in the concept. I've hit quite a few brick walls along the way, mostly due to my lack of knowledge of Io and template based programming, and have had some valuable feedback on #io. I'd like to get more feedback, specifically to identify if I'm even heading in the 'right' direction for Io, it could be that in trying to mimic some of the cool functionality of AL, I'm missing something in Io that would be better suited to the task. If it's ok, I'll give a quick overview of the idea, and then show my test code as it stands, and welcome any and all feedback. Firstly, I have a RenderMan binding setup (http://github.com/pgregory/ioman) that works fine, it initially just provides a simple Object with mapping to the standard RenderMan API calls, and then adds some sugar for the state management API calls (RiTransformBegin/End, RiAttributeBegin/End etc.) that mean I don't need to manage the state open/close calls, it's done in a method, and it carries the target context through, making it more readable and manageable. As an example, the following ioman calls in 'normal' mode... rm := RenderMan clone rm transformBegin rm sphere(1, -1, 1, 360) rm transformEnd equate to the following in the 'simple' mode... rm := RenderMan clone rm transform( sphere(1, -1, 1, 360) ) And onto the framework. The concept is to have 'model's and 'avar's, a model is an encapsulation of the code necessary to draw a scene element using the RenderMan API. The important part of the model concept is that it stores the code necessary to recreate the representation, so it can be re-executed at a later time. The reason for this is related to the idea of avars. An avar is an 'animation variable', or a value that changes during the animation based on various criteria, most commonly time. By using the value of avars in the 'body' of a model, we can change the value of avars, using various mechanisms, and have the models that depend on them automatically recalculate if necessary. Other parts of the scene, that are static, or whose avars aren't updating at that point simply recall a cached version of their RenderMan representation, making updates quicker. Ideally, the code in the 'model' body should be as clear and simple as possible, and can include nested calls to other models. The top level model is a world, which is no different to any other model, except that there should only be one world, so that display mechanisms can easily identify and use it. Onto the code, here is my current implementation, along with a test use example... Range #---------------------------------------------------------- # API #---------------------------------------------------------- model := Object clone do ( name ::= "" body := method( self theBody := call message argAt(0) m := self theBody x := nil self expressions := list while(m != nil, msg := m name asMessage msg setArguments(m arguments) self expressions append(msg) m = m next while(m != nil and m isEndOfLine == false, x = m name asMessage x setArguments(m arguments) msg setNext(x) msg = msg next m = m next ) if( m != nil, m = m next ) ) ) render := method(rm, self rm := rm self expressions foreach(i, exp, result := self doMessage(exp) if( result hasSlot("render"), result render(rm) ) ) ) ) world := model clone do ( name ::= "theWorld" ) #---------------------------------------------------------- # Use example #---------------------------------------------------------- blade := model clone do ( name ::= "blade" length ::= 2 width ::= 0.3 thickness ::= 0.1 body( left := - width / 2 right := width / 2 top := - thickness / 2 bottom := thickness / 2 rm transform( translate(0, 0, -length/2) transform( rotate(90, 1, 0, 0) rotate(180, 0, 0, 1) cylinder(width/2, top, bottom, 180) disk(top, width/2, 180) disk(bottom, width/2, 180) ) patchMesh("bilinear", 2, "nonperiodic", 4, "periodic", Map with( "P", list(RtPoint {left, top, 0}, RtPoint {left, top, length}, RtPoint {right, top, 0}, RtPoint {right, top, length}, RtPoint {right, bottom, 0}, RtPoint {right, bottom, length}, RtPoint {left, bottom, 0}, RtPoint {left, bottom, length})) ) transform( translate(0,0,length) rotate(90, 1, 0, 0) cylinder(width/2, top, bottom, 180) disk(top, width/2, 180) disk(bottom, width/2, 180) ) ) ) ) scissor := model clone do ( name ::= "scissor" segments ::= 5 extension ::= 0.5 body( minangle := (0.3 / 2) * (180/Number constants pi) maxangle := 90 - minangle 0 to(segments) foreach(n, angle := 90 - (((90 * extension) max(minangle)) min(maxangle)) offset := blade length * (((90 - angle) * (Number constants pi / 180)) sin) rm transform( rotate(-angle, 0, 1, 0) blade render(rm) rotate(2 * angle, 0, 1, 0) translate(0, blade thickness, 0) blade render(rm) ) rm translate(0, 0, offset) ) ) ) theWorld := world clone do ( body( rm world( scissor render(rm) ) ) ) rm := RenderMan clone rm frame(1, format(320, 240, 1) display("test.tif", "framebuffer", "rgba", Map with("string compression", "lzw")) projection("perspective", Map with("fov", 50)) translate(0,0,4) rotate(-50, 1,1,0) theWorld render(rm) ) As you can see, my base 'model' object has a 'body' method, that takes a single arbitrary message tree, and processes it to store it as a list of distinct messages on the model. They are then executes in the context of the model (so that it can access local data defined on the model), and with the render context (RenderMan clone) available via a local variable 'rm'. Currently this works quite well, and produces an image (here <http://pgregory.users.sourceforge.net/test.png> ) but there are some things that don't 'feel' quite right to me... 1. The requirement to call render on nested models, i.e. blade render(rm), ideally it might be nice to just put 'blade' there and have the system work out it's a 'model' and render it. I tried this, and it sort of works, you can see the code in model:render, which checks if a result has a 'render' slot and calls it. It works in some cases, specifically the 'scissor render(rm)' can be reduced to 'scissor', but the 'blade' ones cannot, as they are in a parent message, the foreach, so they are processed as a single message tree, not individual ones, and don't ever see the 'hasSlot("render")' test. 2. The requirement to do rm .... in most places in the body, it would be nice to be able to just have that resolved automatically, but I couldn't find a reliable way of doing that without losing in other areas, such as locals access etc. Well, sorry for the long post, if you're still here and reading, thanks for taking the time to read through. As I said at the start, I'm open to any comments and suggestions you might have, especially if it's highlighting an Io language feature that particularly suits my project that I'm not aware of. Thanks Paul Gregory |
| Free embeddable forum powered by Nabble | Forum Help |