Posted 29 February 2004 - 07:01 AM
The resetState method is a little more complex than you think, given the constraints:
* guaranteed determinism required (everything in default state unless I specifically mention it)
* minimize number of state toggles
* one I didn''t mention: vertex programs and fragment programs must be compiled in advance, I''m assuming that compiling a VP isn''t that fast.
For example, let''s say that "resetState" says this about lighting:
And let''s say I do this:
At this point, the code *appears* to have enabled one light. But, it contains a bug: at this point, several lights could be enabled, because resetState never disabled light1-light31.
So I could change resetState to look like this:
for (int i=0; i<31; i++) glDisable(GL_LIGHT0+i);
But now I''m doing 32 unnecessary state-setting operations in a very common case, namely, when lighting is off. I could minimize the state-setting as follows. First, keep one set of global variables indicating OpenGL''s current state. Second, keep a parallel set of global variables indicating what I desire it to be. Write an "applyState" function that compares the desired state to the actual state, and surgically fixes the actual state. Given this function, rendering would look like this:
desiredState.Lighting = true;
desiredState.Light = true;
We probably want to move some of this code to initialization time. Vertex programs can''t be compiled at render time, this has to be done at initialization time. Second, these state-structs are gigantic, so it''s probably faster to set them up at initialization time. Let me alter the code:
// "resetState" is faster if called at initialization time:
// "compileVertexProgram" cannot be done at rendering time:
renderingState1.Lighting = true;
renderingState1.Light = true;
renderingState1.FragmentProgram = compileFragmentProgram("....");
at render time:
// Don''t know camera position at initialization time:
// Don''t know the texture at initialization time:
renderingState1.ModelView = currentCameraPosition;
renderingState1.Texture = currentTexture;
Unfortunately, now I''m back to where I started: I have a global state, which I''m modifying incrementally. I''ve lost my guarantee of determinism. I can fix this too, by copying the rendering state into a local variable and then modifying it locally:
renderingState1a.ModelView = currentCameraPosition;
renderingState1a.Texture = currentTexture;
Ideally, I should make the global rendering states "const" so that I can''t accidentally alter them, which would be a likely occurrence, given that I''m typing "renderingState1a.x=y" over and over. A one-character brain-fart would alter the global variable.
This solution is clearly sensible - a viable option. Which solution is better?
Well, given C++, obviously, this solution is better. It''s easy to program this solution in C++, hard to program the little-language solution. In fact, I''d say, I spent way too much time programming the little-language solution, if I had it all to do over again, I''d do it this way.
Which is better if you''re using Lisp? In Lisp, both solutions are equally easy to program, so the decision wouldn''t be made on the basis of programmer time. Instead, you would choose based on which notation you think is more elegant and expressive. I suppose that''s in the eye of the beholder.
To me, the point of it is that Lisp allows me to use little-language solutions. If you''re a Lisp programmer, you soon find yourself using little languages quite a lot. Even people in the C++ world use little languages occasionally - witness Corba, SQL, etc. But they don''t use them often, because of the high barrier to entry. Are they really an "important" tool? If you ask a Lisp programmer, he would say definitely. Can you do without? Of course.