PDA

View Full Version : pascal and learning 3d



laggyluk
19-11-2012, 10:19 PM
Hi guys. I'd like to make a 3D game in Lazarus. I don't know opengl yet but that's one of reasons for making it 3d. It's a voxel based game so I recon I don't need sophisticated 3d engine, just some painless access to opengl surface and I'll figure out how to build and draw those meshes.
Now, is lazarus any good for this? I'd really like to use pascal for this project but after some searching I've found that most resources are for old 2.1 opengl/ in russian/ won't compile
Should forget my pascal sympathy and just get back to c++ or is there some working solution that I've missed?

pitfiend
20-11-2012, 01:24 AM
Hi Laggyluk,
For learning opengl, I suggest you to take a look into excellent tutorials by NeHe http://nehe.gamedev.net (you can find source files there).
For an updated opengl header file, go to http://wiki.delphigl.com/index.php/dglOpenGL.pas/en

EDIT: NeHe has examples in many languajes, pascal is one of them, the dglOpenGL header is ready for delphi and freepascal.

Carver413
20-11-2012, 01:29 AM
here http://www.spacesimulator.net/index.php?p=home and here http://www.arcsynthesis.org/gltut/ you will find some more modern info. it is what I am using to figure things out.

laggyluk
20-11-2012, 08:40 AM
thanks :) I'll check it out

Ingemar
22-11-2012, 11:44 AM
OpenGL is in a strange situation now. You really should learn OpenGL 3.2+ today, not OpenGL 1/2. If you do use OpenGL 2, go for VAOs/VBOs and shaders instead of fixed pipeline.

laggyluk
22-11-2012, 07:55 PM
yeah, I'm after 3.2+ just had problem finding a basic example for pascal. And I didn't, even among those links but managed to craft it from old pascal opengl 2.1 and new 3.2 c++ tutorial.
btw in c++ stack (or vector? the one with .push() and pop() ;) is used for keeping and traversing matrices hierarchy in a convenient way. There's no vector class in delphi/pascal so what you guys are using instead?

User137
22-11-2012, 09:19 PM
TList is comparative to C++ vector-class? I don't use it though. Dynamic arrays are very handy, and keep the data linearily in it.

Carver413
22-11-2012, 10:03 PM
I would avoid creating classes to handle lower level stuff like vectors and matrix's use array's of record's as they are opengl friendly and well managed by fpc. never use multi dimensional array's as they will not be a single chunck of memory. for lower level stuff stick to procedural comands that dont require you to create a class ever time you want to use a command.



TFlt4 = packed record
case Integer of
0:(X,Y,Z,W:Single);
1:(S:Array [0..3] of Single);
2:(R,G,B,A:Single);
3:(X1,Y1,X2,Y2:Single);
end;

TArrFlt4 = array of TFlt4;

TFlt3 = packed record
case Integer of
0:(X,Y,Z:Single);
1:(S:Array[0..2] of Single);
2:(R,G,B:Single);
end;
TArrFlt3 = array of TFlt3;

Points:TArrFlt3;
SetLength(Points,10);
Points[0]:=VecFlt3(1,1,1);
Points[1]:=Points[0];
Points[1].X+=0.5;
Points[1].S[2]:=2;// same as Points[1].Y but is index able Points[1].S[i]:=10;

Colors:TArrFlt3;
SetLength(Colors,10);
Colors[i].R:=0.25;
Colors[i].B:=0.125;
Colors[i].G:=0;
Colors[2]:=VecFlt3(0.5,1.0,0);

Color:TFlt4;
Color.R:=0.75;
Color.B:=0.25;
Color.G:=0;
Color.A:=1.0;
Color:=VecFlt4(1,0.75,1,1);

Colors[2]:=Color;


as I have said these record type arrays are opengl friendy you can pass them like this @Points[0]

LP
22-11-2012, 11:34 PM
yeah, I'm after 3.2+ just had problem finding a basic example for pascal. And I didn't, even among those links but managed to craft it from old pascal opengl 2.1 and new 3.2 c++ tutorial.
btw in c++ stack (or vector? the one with .push() and pop() ;) is used for keeping and traversing matrices hierarchy in a convenient way. There's no vector class in delphi/pascal so what you guys are using instead?

In Pascal, you can use TList, TStack and TQueue. You can also use generics with these.

laggyluk
23-11-2012, 07:35 PM
TFlt4 = packed record
case Integer of
0:(X,Y,Z,W:Single);
1:(S:Array [0..3] of Single);
2:(R,G,B,A:Single);
3:(X1,Y1,X2,Y2:Single);
end;

as I have said these record type arrays are opengl friendy you can pass them like this @Points[0]
Should I use packed records for this purpose? the interweb says it has slower access performance than 'unpacked'

paul_nicholls
23-11-2012, 11:14 PM
Should I use packed records for this purpose? the interweb says it has slower access performance than 'unpacked'

Packed record are usually only required when you need to load/save data in a consistent way between different compilers or even languages, or when passing to programming API routines.
Besides that, I haven't noticed any slowdown when using packed records compared to normal records :)

Carver413
24-11-2012, 12:13 AM
a packed record prevents the compiler from inserting any space into your record if the compiler were to somehow change the size of your record then opengl would not understand these changes and would not work as expected.

laggyluk
28-11-2012, 07:32 PM
I figured out that for the world made of cubes it might be worth a try to feed points to the vertex shader instead of triangles and build cubes in the geometry shader. So I did and it seems to work. But only on the integrated graphics card :( I have 2 in my laptop and when I switch to nvidia all I see is darkness. Integrated driver says it's opengl 3.1 and nvidia's is 4.3. Can I somehow force nvidia to work in 'older' mode? all the tuts I've seen either use glew or glut (which i don't) to initialize the context so I'm confused how to do that

User137
29-11-2012, 03:50 AM
Can you show shader code? Graphics cards are very picky about that.

laggyluk
29-11-2012, 07:32 PM
hey, you were right :D i've run test with simplest shaders which resulted in black screen also so after some tinkering with syntax now everything works. too bad that shader linker didn't bother with reporting any problems

User137
30-11-2012, 03:30 AM
Should I use packed records for this purpose? the interweb says it has slower access performance than 'unpacked'
Example you quoted says packed, but it is actually byte-aligned to 32 and 64 bit, so it wouldn't make any difference.


TRGB1 = packed record
r, g, b: byte;
end;
This is not aligned. But if you make it an array and compare size:

TRGB2 = record
r, g, b: byte;
end;

arr1: array[0..255] of TRGB1;
arr2: array[0..255] of TRGB2;
The arr1 is 256-1280 bytes smaller than arr2. This is because if you take sizeof(TRGB1) you would get 3, but sizeof(TRGB2) would give you 4 or 8 depending on if you compile with 32 or 64 bit compiler, i guess... Now if you're dealing with OpenGL functions, you need to tell it exactly the structure of the data you are using. For that there is usually a "stride"-parameter, to describe the spacing. If you would send it color data, you should use TRGB1 style with size 3 bytes. All the data is in tight sequence without spaces in between. But actually you will notice that most records will be byte aligned anyway.

For example shader vertex-data, that will propably be the most time-critical array of them all, is byte-aligned no matter how you twist it:

TModelVertex = packed record
v, n: TVector; // 12+12 bytes
uv: TVector2f; // 8 bytes
end; // = 24+8 = 32 => 32/8 = 4 => aligned to 64 bits

LP
30-11-2012, 03:03 PM
Example you quoted says packed, but it is actually byte-aligned to 32 and 64 bit, so it wouldn't make any difference.


TRGB1 = packed record
r, g, b: byte;
end;
This is not aligned. But if you make it an array and compare size:

TRGB2 = record
r, g, b: byte;
end;

arr1: array[0..255] of TRGB1;
arr2: array[0..255] of TRGB2;
The arr1 is 256-1280 bytes smaller than arr2. This is because if you take sizeof(TRGB1) you would get 3, but sizeof(TRGB2) would give you 4 or 8 depending on if you compile with 32 or 64 bit compiler, i guess... Now if you're dealing with OpenGL functions, you need to tell it exactly the structure of the data you are using. For that there is usually a "stride"-parameter, to describe the spacing. If you would send it color data, you should use TRGB1 style with size 3 bytes. All the data is in tight sequence without spaces in between. But actually you will notice that most records will be byte aligned anyway.

Err, you got it wrong. For the sake of completeness I've just put your data types into FPC/Lazarus (1.0.2) and Delphi XE 3, and SizeOf(arr1) = SizeOf(arr2) = 768; SizeOf(TRGB1) = SizeOf(TRGB2) = 3.

Data alignment works by aligning starting offset of your data, not the size of your data. I also believe that for such cases, "packed" keyword is deprecated, at least in Delphi. What you probably meant was data padding, but I don't see how it can be useful for TRGB1/TRGB2 in terms of performance.

Edit: according to $Align option manual, I could reproduce data padding in the following example:



type
TSomeRecord = record
TinyValue: Byte;
BigValue: Word;
end;

TSomeArray = array[0..9] of TSomeRecord;

In above example, "packed" keyword does seems to have effect, specifically for "TinyValue".

User137
30-11-2012, 04:42 PM
I don't know exactly how it goes, but i tested a bit too after your comment. TRGB2 is indeed 3 bytes for some reason.

But these are both 8 bytes, and 5 if packed. Same with Lazarus and Delphi.

TRecord1 = record
a: single;
b: byte;
end;

TRecord2 = record
b: byte;
a: single;
end;
So it doesn't matter if the value is last one. Having only 1 variable type in record seems to be special case, that is not adding any padding.

LP
30-11-2012, 05:38 PM
By the way, I'm mistaken regarding packed being deprecated, after checking manuals again, I did not find any references to it. I remember reading about it somewhere since release of XE 2, but perhaps got it confused with something else. Sorry.

SilverWarior
01-12-2012, 12:48 AM
Packed records or packed arrays are used when you wnat to decrease the memory needed to store theese. Packed comes into effect if the records or arrays contains variables which by default aren't alined to 2, 4, 8 bit alingment. Default alingmnets for variables differ by variable type and is defined so that it offers optimized performance for working with theese variables.
But with the usage of Packed command you can override theese default alignments and pack data closely together thus lowering the memory consumption. Doing so also decreases the peformance of working with theese variables a bit since they are no longer optimally aligned. Unles you are using large record or large array difference in performance probably won't even be noticable.

Using of Packed records is sometimes necessary if you are sending some data to another application which is developed with different compiler since not all compilers use same data aligment for reaching optimal performance. In some cases using non packed records can cause for data in other program to be read incorectly.
So always make sure to check program documentation as of which type of records it can recieve. Same goes for varios API functions.

laggyluk
01-12-2012, 06:06 PM
This memory optimization gave me a slight headache today. Was trying to use dynamic array of singles as source for vbo data and result was trash. Later it turned out that there's no such problems with static array. Can I force dynamic array to be bit aligned(or whatever its the problem)? using 'packed array' didn't help and Id' rather have a buffer that can grow if needed.

Carver413
01-12-2012, 06:49 PM
I have been using dynamic arrays with no problem, never had to use pack arrays, just arrays of pack records. what are you tring to pass ?

laggyluk
01-12-2012, 11:13 PM
turned out that I was dereferencing a pointer in a wrong way
with static array it was just:
glBufferData(GL_ARRAY_BUFFER,x,buffer,GL_STATIC_DR AW);
and with dynamic should be:
glBufferData(GL_ARRAY_BUFFER,x,@buffer^[0],GL_STATIC_DRAW);

where 'buffer' is a pointer for that array type.

ps: it liiveees!!
http://farm9.staticflickr.com/8488/8235458267_14211cde9c_c.jpg

Carver413
02-12-2012, 12:15 AM
there is no way the compiler would change the layout for dynamic vs static that would break the compatibilty of the two. my guess is that you are miscalculating the size some where and the data isn't making it to the final destination. Edit:guess Iwas too slow, so you got it, looks good.

Ñuño Martínez
03-12-2012, 11:44 AM
Not sure if it's solved but I want to make a comment about record alignment. In the engine I wrote and used in the 2nd. PGD Challenge I didn't use records to store data that is used with OpenGL; I use vectors (ARRAY) instead. For example, to store xyz points I used something like:


CONST
COOR_X = 0;
COOR_Y = 1;
COOR_Z = 2;
TYPE
Vector3D = ARRAY [0..2] OF glFloat;

To access to each axis I use the CONST (i.e. Point[COOR_X], etc). This way I can use the "v" functions without alignment problems.

laggyluk
03-12-2012, 01:06 PM
yeah it works alright now
http://farm9.staticflickr.com/8347/8239862242_52c311ecc3_z.jpg

laggyluk
04-12-2012, 01:32 PM
I have a bunch of classes now, should I worry about writing destructors for them or memory will be freed autimatically when app terminates?

Carver413
04-12-2012, 01:49 PM
when it comes to classes whatever you create you must destroy. you should get heap trace up and running if you don't already. check the output file often to check mem leaks as you go along. also make sure to free up any opengl stuff in your destructors.

laggyluk
04-12-2012, 04:31 PM
ok, thanks. list of leaks is so long that it doesn't fit on the screen ;)

Carver413
05-12-2012, 02:53 PM
I built my frame work using a custom linked list design. all things are connected so I worry little about mem leaks. I worked on it for over a year before I started checking for leaks and I wasn't really surprised that I had none. so much more is possible with such a design.

SilverWarior
05-12-2012, 03:00 PM
I built my frame work using a custom linked list design. all things are connected so I worry little about mem leaks. I worked on it for over a year before I started checking for leaks and I wasn't really surprised that I had none. so much more is possible with such a design.

Are you saying that you add all ingame classes into a single TList with which you free all of them in the end? It would be ince if you can give us more information on this approach or even show us some code examples of this.

Carver413
05-12-2012, 05:12 PM
this will probable explain it much better then I could
mine is a single linked looped list and I am using an array of children which makes it possible for each and every class to maintain any number of lists without a container class to manage them. I will try to extract it from my code if you really want it. I plan to release my framework when it is finished but I can't really say how long that will be.
http://en.wikipedia.org/wiki/Linked_list


(http://en.wikipedia.org/wiki/Linked_list)

laggyluk
09-01-2013, 04:17 PM
another headache.. object picking!
I've read about different methods and looks like ray casting is a way to go in my case. But I'm having problems with gluUnProject , not sure what to feed to it.
Samples I've seen are useing something like that to obtain values for it:
glGetIntegerv(GL_VIEWPORT,@viewport);
glGetDoublev(GL_PROJECTION_MATRIX,@projectionMatri x);
glGetDoublev(GL_MODELVIEW_MATRIX,@modelMatrix);

those get me identity matrices for projection and model. I guess it's because i'm doing all 'projection' computations in shaders only. I think I know how to fill projection and viewport matrices but every model has it's own model matrix. Should I just use any's model matrix for gluUnProject ? (tried and it didn't work ;p )

Sascha Willems
09-01-2013, 04:48 PM
Do you really need ray casting? If you only want to select objects at e.g. a given cursor position, just go for color selection. Especially with shaders that's pretty simple and fast. I'm using this technique for selection too (see here (http://www.saschawillems.de/?p=763)). It's easy to implement and extremly fast.

It works like this :
- Render your scene to the backbuffer (so it's not visible), but only with colors and basic geometry. E.g. one building red, another yellow
- Limit the viewport to the cursor position (for performance)
- Read the pixel under the cursor (glReadPixels)
- Compare read colors with your color-to-object-table

It's very fast and can be implemented in just a few minuts. Especially when using only shaders you just need a new shader the outputs selection colors to the fragment buffer.

User137
09-01-2013, 05:00 PM
If you want to use ray casting, you can check nxPascal demos. Model and Picking demos both use that math. Doing it mathematically has the advantage of knowing exactly which model face the ray hits, and what is the normal vector at that point.

There is also GL_SELECT based function on the works, but it's not working in my tests yet. I used that style many years ago, so at least there's some code left.

laggyluk
09-01-2013, 05:23 PM
@Sascha: I'd use color picking if i could but I'm not texturing voxels and I don't plan to. To do color picking in this scenario I guess I'd have to render geometry with unique colours and then change them to proper values from some reference texture?

@User137: yeah, I'm using your engine as a reference from time to time :) but picking there is done in a way I've described in my previous post. Where do I get model matrix from if glGetDoublev(GL_MODELVIEW_MATRIX) doesn't seem to do it's job?
Internet says GL_SELECT is obsolete and shouldn't be used anymore

edit: another problem with color picking is that I have tons of cubes on the scene so lookup would be costly unless I encode cube coords into color..

Sascha Willems
09-01-2013, 08:50 PM
The old OpenGL-Selection (GL_SELECT) shouldn't be used anymore and as far as I know it isn't available in newer OpenGL-versions anyway. And even if it would, hardware vendors decided to drop hardware support for it some time ago, so rendering in selection mode is done in software and therefore often extremly slow.

As for color selection via shaders : No, you don't need textures at all. Just assing a color to each voxel when creating it (you've got 24 Bits to encode your color, that should be sufficent), render with that color, pick it from the backbuffer and compare. If you render a lot of cubes you should have some kind of visibility check (e.g. an octree), so you can use that to speed up this process.

laggyluk
09-01-2013, 08:59 PM
what I ment is that voxel/cubes won't be covered with textures, vertex color is used to represent diferrent block types. With color picking I'd use vertex color to identify the cube and lose info about how this block should look like. Maybe if I could have 2 atributes passed to shader, one with 'block color' and another with 'id color' and then render to two different textures? one for picking and other for showing on screen.

edit: i did it :D geometry picking that is. used some unprojecting functions from glscene

phibermon
30-01-2013, 07:14 PM
Picking should absolutely not be used for a voxel engine (Please forgive me Sascha, I've got an awesome amount of respect for you) you're hitting the limits of the hardware (or at least eventually will) the last thing you want is to render an additional pass of the scene for only a single task. Regardless if you remove textures or not, that's a hell of a lot of geometry to render twice.

You could alleviate much of the performance hit by using MRTs (Multiple render targets) and actually render the picking data to a back-buffer, whilst doing the normal rasterization on your main buffer (or a further back-buffer if you're aiming for a deferred renderer). If you were looking to do minecraft style rendering, IE Ambient Occlusion, there may be additional data you can store in this back-buffer that would make this method more attractive.

However you're just not going to beat raycasting when you've got a lot geometry, especially as you're storing your data in an oct-tree, there are oct-tree optimized raycast algorithms that you really want to use and not just for ray-casting the view direction to select stuff etc. if you want to do oct-tree style ambient occlusion you'll need rays, if you want to do any form of path finding in a voxel terrain, you'll need rays.

The math isn't too complex, Jink (my soon to be released game engine) has a full range of ray-casting functions and algorithms, optimised for oct-trees, kd-trees etc

Ray-casting, is a requirement for rendering 3D graphics onto a 2D display (I didn't say Ray-tracing before anybody jumps on me). it's all happening in the API even if you don't use it yourself, projecting the 3D vertices into screen-space coordinates.

You're just casting in the opposite direction to do picking, you cast a line from the position the mouse intersects the camera clipping plane, outwards, the combination of view and projection matricies associated with the camera determining the two intersecting planes the ray is defined by (or a vector and position, whatever is most useful for your spatial partitioning scheme)

Once that is done you have a vector in 3D space, for raycasting, think of it as an infinite line.

Then you're either finding the closest 3D object to that line, or doing line/BBox followed by line/triangle intersection tests to determine the 'hit' object.

Obviously oct-tree optimizations come into play at this stage, testing the intersection of this line against the bounding boxes of your tree nodes, you then perform this test, traversing down towards the leaves like you would when you're testing your camera frustum against it, only line/bbox is a lot faster than Frustum/BBox intersection testing.

You further optimize the traversal because you should have stored your visible nodes during the frustum test, so you only need to test against that set of nodes.

phibermon
30-01-2013, 07:34 PM
Oh and anybody who doesn't think that record alignment is important for openGL, you could not be more wrong. in situations like loading single/dual channel image data or using uniform buffers, you'll very quickly discover that you'll need to setup either your record alignment or OpenGLs packing/unpacking options. You might of not come across these issues but that'll be because the default alignment on your platform matches that in your hardware/OpenGL driver implementation (see std140 block layout for and example) but deal with the more exotic GL features on multiple architectures and it matters a lot.

You can't just use things like NumX * sizeof(X) and expect the layout on the target hardware to be the same.

Oh and I've come across plenty of formats that store arrays of structures that must be tightly packed (aligned to 1 byte boundary). If Pascal pads a field out with a few bytes for optimized memory access and you just stream the bytes from a file across the array, you'll have written your last byte from the file before you reach the last byte in the array.

Unfortuantly because of Intel CPU's and things like standards, most things are just 4-byte aligned, so many programmers never learn about it and will one day spend weeks trying to find the bug.

The point is that records are optimized by the compiler so that memory operations operate as fast as possible.

Assuming 32bit floats (single) it's actually faster on Intel hardware to load TVec3 data (8+8+8 bytes) that's aligned to TVec4 (8+8+8+8 bytes) into openGL with a stride parameter, than it is to load tightly packed TVec3 (assuming you're got hardware that comes with decent drivers). Because the Vec3 data will only be aligned to vec4 boundries on the hardware anyway, so any space you save by tightly packing your data you loose in A) system read performance and B) GPU unpacking operation.

you will only see the performance difference between a packed and non packed record/array, if the combined size of the record/array elements don't already fall on the boundry. if they do not, then packing this data will slow memory operations.

It is in fact, on the latest cards pretty pointless to use anything but a vec4. it takes no longer to copy the data (the bus is essentially transfering your vec3 to the GPU in a vec4 'box') and it's all operating on 32/64byte vectors in silicon.

laggyluk
30-01-2013, 09:01 PM
my voxels are not voxels actually, just a 3d array of cubes. For picking I used Bresenham's line algorithm, works pretty fast for that purpose

laggyluk
17-02-2013, 02:03 PM
i have some weird mystic bug this time. when in deffered rendering mode with ssao enabled some meshes dissapear when frustrum culled but won't ever show up again when camera turns back to them. this doesn't happen in forward rendering or in deferred with ssao disabled. when not frustrum culling with ssao enabled it also works ok. looks like not rendering them makes them disappear forever. opengl desn't report errors. I've been trying to track it down for 2 days with no luck :( any hints?