OpenGL saving objects for later drawing - c

I'm making a program using openGL with transparent objects in it, so obviously I have to paint those last. Unfortunately I was unaware of this requirement when beginning this project and now it would be a real pain to reorder it painting those at last.
I'm drawing objects by calling my drawing functions after translating and rotating the scene. There can be multiple translations and rotations before an actual drawing (e.g. first I draw the ground, then translate, then call the drawing of the house, which repeatedly translates and rotates, then calls the drawing of the walls and so on).
So my idea was saving the current modelview matrices in a list instead of painting the transparent objects when I normally would, then when I'm done with the opaque stuff, I iterate through my list and load each matrix and paint each object (a window, to be precise).
I do this for saving a matrix:
GLdouble * modelMatrix = (GLdouble *)malloc(16 * sizeof(GLdouble));
glGetDoublev(GL_MODELVIEW, modelMatrix);
addWindow(modelMatrix); // save it for later painting
And this is the "transparent stuff management" part:
/***************************************************************************
*** TRANSPARENT STUFF MANAGEMENT ******************************************
**************************************************************************/
typedef struct wndLst {
GLdouble * modelMatrix;
struct wndLst * next;
} windowList;
windowList * windows = NULL;
windowList * pWindow;
void addWindow(GLdouble * windowModelMatrix) {
pWindow = (windowList *)malloc(sizeof(windowList));
pWindow->modelMatrix = windowModelMatrix;
pWindow->next = windows;
windows = pWindow;
}
void clearWindows() {
while(windows != NULL) {
pWindow = windows->next;
free(windows->modelMatrix);
free(windows);
windows = pWindow;
}
}
void paintWindows() {
glPushMatrix(); // I've tried putting this and the pop inside the loop, but it didn't help either
pWindow = windows;
while(pWindow != NULL) {
glLoadMatrixd(pWindow->modelMatrix);
Size s;
s.height = 69;
s.width = 49;
s.length = 0.1;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
glColor4f(COLOR_GLASS, windowAlpha);
drawCuboid(s);
glDepthMask(GL_TRUE);
glDisable(GL_BLEND);
pWindow = pWindow->next;
}
glPopMatrix();
}
/* INTERFACE
* paint all the components, that are not yet painted,
* then clean up.
*/
void flushComponents() {
paintWindows();
clearWindows();
}
/**************************************************************************/
I call flushComponents(); at the very end of my drawings.
The problem is, that the windows don't get in their place, instead I get weird-shaped blue objects randomly appearing and disappearing in my scene.
Am I doing something wrong? Or such matrix manipulations cannot even be used like this? Then what other method could I use for doing this?
Here is the full code if you need it: farm.zip Matrix-saving is at components.c line 1548, management is at line 142. It might not work on Windows without some minor hacking with the includes, which should probably be done in global.h.
Edit: I can only use C code and the glut library to write this program.
Edit 2: The problem is glGetDoublev not returning anything for some reason, it leaves the modelMatrix array intact. Though I still have no idea what causes this, I could make a workaround using bernie's idea.

OpenGL is not a math library. You should not use it for doing matrix calculations. In fact that part has been completely removed from OpenGL-3. Instead you should rely on a specialized matrix math library. That allows you to calculate the matrices for each object with ease, without jumping through the hoops of OpenGL's glGet… API (which was never meat for this kind of abuse). For a good replacement look at GLM: http://glm.g-truc.net/

Try adding glMatrixMode(GL_MODELVIEW) before your paintWindows() method. Perhaps you are not modifying the correct matrix.
The basic idea of your method is fine and is very similar to what I used for transparent objects. I would however advise for performance reasons against reading back the matrices from OpenGL. Instead, you can keep a CPU version of the current modelview matrix and just copy that to your window array.
As for your comment about push and pop matrix, you can safely put it outside the loop like you did.
edit
Strictly speaking, your method of rendering transparent objects misses one step: before rendering the list of windows, you should sort them back to front. This allows for overlapping windows to have the correct final color. In general, for 2 windows and a blending function:
blend( blend(scene_color, window0_color, window0_alpha), window1_color, window1_alpha )
!=
blend( blend(scene_color, window1_color, window1_alpha), window0_color, window0_alpha )
However, if all windows consist of the exact same uniform color (e.g. plain texture or no texture) and alpha value, the above equation is true (window0_color==window1_color and window1_alpha==window0_alpha) so you don't need to sort your windows. Also, if it's not possible to have overlapping windows, don't worry about sorting.
edit #2
Now you found something interesting with the erroneous matrix readback. Try it out with the following instead (you certainly don't need double precision):
GLfloat* modelMatrix = (GLfloat*)malloc(16 * sizeof(GLfloat)); // glass
glGetFloatv(GL_MODELVIEW, modelMatrix);
addWindow(modelMatrix); // save it for later painting
If that still doesn't work, you could directly store references to your houses in your transparent object list. During the transparent rendering pass, re-render each house, but only issue actual OpenGL draw calls for the transparent parts. In your code, putWallStdWith would take another boolean argument specifying whether to render the transparent or the opaque geometry. This way, your succession of OpenGL matrix manipulation calls would be redone for the transparent parts instead of read using glGetxxx(GL_MODEL_VIEW).
The correct way to do it however is to do matrix computation on the CPU and simply load complete matrices in OpenGL. That allows you to reuse matrices, control the operation precision, see the actual matrices easily, etc.

Related

Wasm with emscripten and SDL - How to clear only part of the renderer

I've created a Wasm animation using emscripten and SDL. I am now trying to create a button where the user can start and stop/pause the animation. The problem I have is that the whole canvas is cleared on every iteration. As I see it, there are 3 options here:
Redraw the button on every frame.
Only clear the part of the canvas that the animation uses on each iteration.
Use a HTML form for the input buttons.
(1) seems inefficient. (3) would require me to start interoperating between JS and Wasm. So far I've been able to build the whole thing without writing a single line of JS, and would prefer to keep it that way. So probably (2) is best.
I know that with JS it is possible to specify the dimensions of a clearRect call. I can't find this functionality in the SDL API however.
Below is the mainLoop function (which gets called about 60 times a second) (This is written in C if it's unclear. Not C++). My question is, how can I adapt the "SDL_RenderClear" so that it only clears one half of the canvas? (The half with the animation. I.e. don't clear the half where I want to make buttons.
main.c
#include <stdio.h>
#include <SDL2/SDL.h>
#include <emscripten.h>
#include <stdlib.h>
#include <stdbool.h>
<....>
/**
* The loop handler, will be called repeatedly
*/
void mainLoop(void *arg) {
struct Context *ctx = arg;
printf("iteration: %d\n", ctx->iteration);
SDL_SetRenderDrawColor(ctx->renderer, 255, 0, 100, 255);
SDL_RenderClear(ctx->renderer);
bool running = true;
if (running) {
for (int j=1; j<8; j++) {
for (int i=1; i<130; i++) {
// drawRect is another app function that calls the SDL to draw a rectangles.
drawRectangle(ctx, i, j);
}
}
}
SDL_RenderPresent(ctx->renderer);
ctx->iteration++;
}
<....>
You can just paint over parts you don't want with SDL_RenderFillRect. However, unless you do a lot of trickery, it isn't very helpful.
Formal reason - https://wiki.libsdl.org/SDL_RenderPresent says "The backbuffer should be considered invalidated after each present; do not assume that previous contents will exist between frames."
There are many cases when your buffer will be (partially) invalidated, e.g. moving window out of screen bounds. I don't know about webasm, you probably need to check webgl documentation if anything about buffer contents is being promised.
Even if you can rely on contents to be kept, but e.g. you have double buffered setup. You've rendered to first buffer, performed buffer swap - now you have second buffer, which contents isn't initialised yet. After second swap it could be more-or-less similar, but any change will be lagging behind 1 frame.
Some 3d software (e.g. blender) did what you described but even there it wasn't 100% stable and usually there was an option to disable that behabiour. Performing full redraw is much cheaper than you think, and if it becomes too costly - you can always render part of your screen into render texture and use it as single static image, updating it when necessary.

C create a txt.ppm file from a safed textfile

PPM1
Textfile
I tried create a C code, that can create a ppm, like on the picture 1 from a textfile like on picture 3. When somemone can help, it where great. I am a new Programmer, i tried do do that Code for 6h. I tried to scan in the data from the textfile and put it in a array and try to make withe that a ppm, but my code is unusable:/.
The path forward is to split the task into smaller sub-tasks, solve and test each one of them separately, and only after they all work, combine them into a single program.
Because the OP has not posted any code, I will not post any directly useful code either. If OP is truly blocked due to not getting any forward progress even after trying, this should actually be of practical use. If OP is just looking for someone to do their homework, this should annoy them immensely. Both work for me. :)
First sub-task is to read the input in an array. There are several examples on the web, and related questions here. You'll want to put this in a separate function, so merging into the complete project later on is easier. Since you are a beginner programmer, you could go for a function like
int read_numbers(double data[], int max);
so that the caller declares the maximum number of data points, and the function returns the number of data points read; or negative if an error occurs. Your main() for testing that function should be trivial, say
#define MAX_NUMBERS 500
int main(void)
{
double x[MAX_NUMBERS];
int i, n;
n = read_numbers(x, MAX_NUMBERS, stdin);
if (n <= 0) {
fprintf(stderr, "Error reading numbers from standard input.\n");
return EXIT_FAILURE;
}
printf("Read %d numbers:\n", n);
for (i = 0; i < n; i++)
printf("%.6f\n", x[i]);
return EXIT_SUCCESS;
}
The second sub-task is to generate a PPM image. PPM is actually a group of closely related image formats, also called Netpbm formats. The example image is a bitmap image -- black and white only; no colors, no shades of gray --, so the PBM format (or variant of PPM) is suitable for this.
I suspect it is easiest to attack this sub-task by using a two-dimensional array, sized for the largest image you can generate (i.e. unsigned char bitmap[HEIGHT_MAX][WIDTH_MAX];), but note that you can also just use a part of it. (You could also generate the image on the fly, without any array, but that is much more error prone, and not as universally applicable as using an array to store the image is.)
You'll probably need to decide the width of the bitmap based on the maximum data value, and the height of the bitmap based on the number of data points.
For testing, just fill the array with some simple patterns, or maybe just a diagonal line from top left corner to bottom right corner.
Then, consider writing a function that sets a rectangular area to a given value (0 or 1). Based on the image, you'll also need a function that draws vertical dotted lines, changing (exclusive-OR'ing) the state of each bit. For example,
#define WIDTH_MAX 1024
#define HEIGHT_MAX 768
unsigned char bitmap[HEIGHT_MAX][WIDTH_MAX];
int width = 0;
int height = 0;
void write_pbm(FILE *out); /* Or perhaps (const char *filename)? */
void fill_rect(int x, int y, int w, int h, unsigned char v);
void vline_xor(int x, int y, int h);
At this point, you should have realized that the write_pbm() function, the one that saves the PBM image, should be written and tested first. Then, you can use the fill_rect() function to not just draw filled rectangles, but also to initialize the image -- the portion of the array you are going to use -- to a background color (0 or 1). All of the three functions above you can, and should, do in separate sub-steps, so that at any point you can rely on that the code you've written earlier is correct and tested. That way, you only need to look at bugs in the code you have written since the last successful testing! It might sound like a slow way to progress, but it actually turns out to be the fastest way to get code working. You very quickly start to love the confidence the testing gives you, and the fact that you only need to focus and worry about one thing at a time.
The third sub-task is to find out a way to draw the rectangles and vertical dotted lines, for various inputs.
(I cheated a bit, above, and included the fill_rect() and vline_xor() functions in the previous sub-task, because I could tell those are needed to draw the example picture.)
The vertical dotted lines are easiest to draw afterwards, using a function that draws a vertical line, leaving every other pixel untouched, but exclusive-ors every other pixel. (Hint: for (y = y0; y < y0 + height; y += 2) bitmap[y][x] ^= 1;)
That leaves the filled rectangles. Their height is obviously constant, and they have a bit of vertical space in between, and they start at the left edge; so, the only thing, is to calculate how wide each rectangle needs to be. (And, how wide the entire bitmap should be, and how tall, as previously mentioned; the largest data value, and the number of data values, dictates those.)
Rather than writing one C source file, and adding to it every step, I recommend you write a separate program for each of the sub-steps. That is, after every time you get a sub-part working, you save that as a separate file, and keep it for reference -- a back-up, if you will. If you lose your way completely, or decide on another path for solving some problem, you only need to revert back to your last reference version, rather than start from scratch.
That kind of work-flow is known to be reliable, efficient, and scalable: it does not matter how large the project is, the above approach works. Of course, there are tools to help us do this in an easy, structured manner (with comments on each commit -- completed unit of code, if you will); and git is a popular, free, but very powerful one. Just do a web search for git for beginners if you are interested in it.
If you bothered to read all through the above wall of text, and grasped the work flow it describes, you won't have much trouble learning how to use tools like git to help you with the workflow. You'll also love how much typing tools like make (and the Makefiles containing the make recipes) help, and how easy it is to make and maintain projects that not only work, but also look professional. Yet, don't try to grasp all of it at once: take it one small step at a time, and verify you have a solid footing, before going onwards. That way, when you stumble, you won't hurt yourself; just learn.
Have fun!

glPolygonOffset() not work for object outline

I'm recently playing with glPolygonOffset( factor, units ) and find something interesting.
I used GL_POLYGON_OFFSET_FILL, and set factor and units to negative values so the filled object is pulled out. This pulled object is supposed to cover the wireframe which is drawn right after it.
This works correctly for pixels inside of the object. However for those on object outline, it seems the filled object is not pulled and there is still lines there.
Before pulling the filled object:
  
After pulling the filled object:
  
glEnable(GL_POLYGON_OFFSET_FILL);
float line_offset_slope = -1.f;
float line_offset_unit = 0.f;
// I also tried slope = 0.f and unit = -1.f, no changes
glPolygonOffset( line_offset_slope, line_offset_unit );
DrawGeo();
glDisable( GL_POLYGON_OFFSET_FILL );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
DrawGeo();
I read THIS POST about the meaning and usage of glPolygonOffset(). But I still don't understand why the pulling doesn't happen to those pixels on border.
To do this properly, you definitely do not want a unit of 0.0f. You absolutely want the pass that is supposed to be drawn overtop the wireframe to have a depth value that is at least 1 unit closer than the wireframe no matter the slope of the primitive being drawn. There is a far simpler approach that I will discuss below though.
One other thing to note is that line primitives have different coverage rules during rasterization than polygons. Lines use a diamond pattern for coverage testing and triangles use a square. You will sometimes see software apply a sub-pixel offset like (0.375, 0.375) to everything drawn, this is done as a hack to make the coverage tests for triangle edges and lines consistent. However, the depth value generated by line primitives is also different from planar polygons, so lines and triangles do not often jive for multi-pass rendering.
glPolygonMode (...) does not change the actual primitive type (it only changes how polygons are filled), so that will not be an issue if this is your actual code. However, if you try doing this with GL_LINES in one pass and GL_TRIANGLES in another you might get different results if you do not consider pixel coverage.
As for doing this simpler, you should be able to use a depth test of GL_LEQUAL (the default is GL_LESS) and avoid a depth offset altogether assuming you draw the same sphere on both passes. You will want to swap the order you draw your wireframe and filled sphere, however -- the thing that should be on top needs to be drawn last.

Esri Silverlight control Pan/Zoom from code

I have trouble getting Map behave properly when calling ZoomToResolution and PanTo
I need to be able to Zoom into specific coordinate and center map.
The only way I got it working is by removing animations:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
Otherwise if I make call like this:
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
It does one or another (i.e. pan or zoom, but not both). If (after animation) I call this code second time (map already zoomed or panned to needed position/level) - it does second part.
Tried this:
control.MapControl.ZoomToResolution(ZoomLevel, MapPoint());
Same issue, internally it calls above commands
So, my only workaround right now is to set Zoom/Pan duration to 0. And it makes for bad UX when using mouse.
I also tried something like this:
this.MapControl.ZoomDuration = new TimeSpan(0);
this.MapControl.PanDuration = new TimeSpan(0);
control.MapControl.ZoomToResolution(ZoomLevel);
control.MapControl.PanTo(MapPoint());
this.MapControl.ZoomDuration = new TimeSpan(750);
this.MapControl.PanDuration = new TimeSpan(750);
Which seems to be working, but then mouse interaction becomes "crazy". Mouse scroll will make map jump and zoom to random places.
Is there known solution?
The problem is the second operation replaces the previous one. You would have to wait for one to complete before starting the next one. But that probably doesn't give the effect you want.
Instead zoom to an extent, and you'll get the desired behavior. If you don't have the extent but only center and resolution, you can create one using the following:
var zoomToExtent = new Envelope(point.X - resolution * MapControl.ActualWidth/2, point.Y, point.X + resolution * MapControl.ActualWidth/2, point.Y);
Btw it's a little confusing in your code that you call your resolution "ZoomLevel". I assume this is a map resolution, and not a level number right? The esri map control doesn't deal with service-specific levels, but is agnostic to the data's levels and uses a more generic "units per pixels" resolution value.

WPF: Collision Detection with Rotated Squares

With reference to this programming game I am currently building.
Thanks to the answers from this post, I am now able to find the x-y coordinates of all the points of the rectangles (even when rotated), and Collision-Detection with Walls is almost working perfectly now.
Now I need to implement collision detection with the bots themselves (cause obviously, there will be more than one bot in the Arena).
Square-Square Collision Detection (Non-rotated) is not valid in this case because the bots will be turned at an angle (just like I described here).
So what is the best way to implement this form of Rotated Rectangles Collision Detection in WPF?
I guess there must be some math involved, but usually it turns out that there are functions in WPF that "calculate" these maths for you (just like in this case)
Solution
By using the method I posted as a solution to this previous question and a WPF method called IntersectsWith (from Rect), I was able to solve this issue of rotated rectangles collision detection like so:
public Rect GetBounds(FrameworkElement of, FrameworkElement from)
{
// Might throw an exception if of and from are not in the same visual tree
GeneralTransform transform = of.TransformToVisual(from);
return transform.TransformBounds(new Rect(0, 0, of.ActualWidth, of.ActualHeight));
}
Vehicle IsBotCollided(IEnumerable<Vehicle> blist)
{
//currentBounds is of type Rect, which contains the 4 points of the rectangle (even when rotated)
var currentBounds = GetBounds(BotBody, BattleArena);
//Then I check if the current bounds intersect with each of the other Bots` bounds that are also in the Arena
foreach (Vehicle vehicle in blist)
{
if(GetBounds(vehicle.BotBody, BattleArena).IntersectsWith(currentBounds))
{
return vehicle;
}
}
return null;
}
I would check each line for collision (so you'd have max. 4*4 line collision checks, if two lines collide, the bots do, too, and you can stop), although I'm sure there are better/faster ways to do this. If the rectangles can have different sizes, you should also check if the smaller is inside the other.
The performance could be slightly increased if you first check the rotated x/y-min/max-value of the rectangles (or you can even calculate two circles around the bots and check these, which is even faster) so you don't have to check the lines if they are far away from each other.

Resources