X_SPRITE replacement - (faster) 3D sprite with rotation (billboards)

Previous topic - Next topic

kanonet

Introduction:
If you ever wanted to be able to rotate X_SPRITEs or you want them to be faster (so you can create plenty of them, e.g. for a particle engine), than here is a solution for you:
My TSprite can get scaled, rotated and positioned anywhere in the 3D-scene, it will always turn to the camera (like X_SPRITE does), even if you use X_CAMERAUP (for a 6DOF camera). Its all encapsulated into one Type, for easy transfer to your projects. I tested this lib on Windows, Linux and Android, its all working perfectly and I know no reason why it should not work on any other major platform.

You can find newest libQMATH here: http://www.kanonet.de/downloads/libqmath
You always find the latest version on my website: http://www.kanonet.de/downloads/libsprite


Comparison with X_SPRITE:
differences:
  • its midhandled (centered)
  • need to get textured manually
  • scale=1 has alwasy the same size (1 unit in 3D environment), no matter what size the texture got
advantages:
  • rotation is possible
  • can draw two different types of billboards:
    • draw()   -> spherical billboard, same like x_sprite, but with rotation
    • draw2() -> cylindrical billboard, use it for trees etc., can rotate too
  • faster at least +5-400% on my machine (without texture ordering)
  • gives you the ability for texture ordering (+700-3200% more speed on my machine!  :O)
  • you can set texture coordinates, so you can draw all sprites from one single texture atlas!
disadvantages:
  • you need to call more than one function
  • my libQMATH is needed in your project


How to use it:
At game start, define the type:
Code (glbasic) Select
LOCAL sprite AS TSprite
In your gameloop render all your GLB 3D stuff, than call sprite.Start(), draw your sprites and texture them, try to use as few X_SETTEXTURE as possible if some sprites use the same texture, just make only one X_SETTEXTURE and draw all sprites that use it (texture ordering). You can call SetTextureCoords() to use a sprite Atlas (and avoid calls for X_SETTEXTURE)! End your sprite drawing with sprite.Stop(). Just use one Start() and Stop() pair each loop and draw all sprites between them. Do not use any GLB OpenGL calls between Start() and Stop()!
Code (glbasic) Select
sprite.Start()
X_SETTEXTURE 0
sprite.Draw()
sprite.Draw()
sprite.Draw2()
sprite.SetTextureCoords()
sprite.Draw()
sprite.Draw2()
X_SETTEXTURE 1
sprite.Draw()
X_SETTEXTURE 2
sprite.Draw()
sprite.Draw2()
sprite.Stop()



Code:
Code (glbasic) Select
// ------------------------------------------ //
// Project: TSprite
// Start: Sunday, July 24, 2011
// Autor: Kanonet
// Last Update: August 22, 2015
// For a newer Version go to: http://www.kanonet.de/downloads/libsprite
// ------------------------------------------ //

TYPE TSprite
INLINE
namespace libSPRITE {
typedef unsigned int    GLenum;
typedef void            GLvoid;
typedef int             GLint;
typedef int             GLsizei;
typedef float           GLfloat;
#ifdef __WIN32__
#ifndef APIENTRY
#define APIENTRY __stdcall
#endif
#else
#ifndef APIENTRY
#define APIENTRY
#endif
#endif
const GLint GL_TRIANGLE_STRIP = 0x0005;
const GLint GL_FLOAT = 0x1406;
const GLint GL_VERTEX_ARRAY = 0x8074;
const GLint GL_TEXTURE_COORD_ARRAY = 0x8078;
extern "C" {
void APIENTRY glNormal3f( GLfloat nx , GLfloat ny , GLfloat nz );
void APIENTRY glColor4f( GLfloat red , GLfloat green , GLfloat blue , GLfloat alpha );
void APIENTRY glEnableClientState( GLenum cap ); /* 1.1 */
void APIENTRY glDisableClientState( GLenum cap ); /* 1.1 */
void APIENTRY glVertexPointer( GLint size , GLenum typ , GLsizei stride , const GLvoid *ptr );
void APIENTRY glTexCoordPointer( GLint size , GLenum typ , GLsizei stride , const GLvoid *ptr );
void APIENTRY glDrawArrays( GLenum  mode,  GLint  first,  GLsizei  count );
}
struct TSpriteContainer {
float texcoord[8];
float vertices[12];
float m0, m1, m2;
float m4, m5;
float m8, m9, m10;
float normalx, normaly, normalz;
int lastnum;
} static TSpr = { {0.0f,1.0f,1.0f,1.0f,0.0f,0.0f,1.0f,0.0f} };
}
ENDINLINE

// start drawing of sprites, call it one time  each loop, before you draw your sprites and call Stop() when you did draw all your sprites
// do not use any normal GLB OpenGL between Start() and Stop()!
FUNCTION Start%:
STATIC mat#[]
X_GETCAMERAMATRIX mat[]
INLINE
using libSPRITE::TSpr;
TSpr.lastnum = 0;
TSpr.normalx = mat(2);
TSpr.normaly = mat(6);
TSpr.normalz = mat(10);
TSpr.m0  =  -mat(0);
TSpr.m4  =  -mat(4);
TSpr.m8  =  -mat(8);
TSpr.m1  =  mat(1);
TSpr.m5  =  mat(5);
TSpr.m9  =  mat(9);
TSpr.m10 =  qInvSQR(TSpr.normalx*TSpr.normalx + TSpr.normalz*TSpr.normalz);
TSpr.m2  =  TSpr.normalx*TSpr.m10;
TSpr.m10 *= TSpr.normalz;
libSPRITE::glEnableClientState( libSPRITE::GL_VERTEX_ARRAY );
libSPRITE::glEnableClientState( libSPRITE::GL_TEXTURE_COORD_ARRAY );
libSPRITE::glTexCoordPointer( 2, libSPRITE::GL_FLOAT, 0, &TSpr.texcoord );
libSPRITE::glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
ENDINLINE
ENDFUNCTION

// draw a spherical sprite (same like X_SPRITE)
// needs to be after Start() [and before Stop()]
// sprite will be midhandled (unlike X_SPRITE)
FUNCTION Draw%: x AS float, y AS float, z AS float, scale#=1.0, rotation#=0.0
INLINE
using libSPRITE::TSpr;
scale *= 0.5;
float rc = qSIN( rotation + 90 ) * scale;
float rs = qSIN( rotation      ) * scale;

float rot = TSpr.m1 * rs + TSpr.m0 * rc;
float xp  = TSpr.m1 * rc - TSpr.m0 * rs;
float xn  = rot-xp;  xp += rot;
TSpr.vertices[0] = x + xp;  TSpr.vertices[3] = x - xn;  TSpr.vertices[6] = x + xn;  TSpr.vertices[9] = x - xp;

rot = TSpr.m5 * rs + TSpr.m4 * rc;
xp  = TSpr.m5 * rc - TSpr.m4 * rs;
xn  = rot-xp;  xp += rot;
TSpr.vertices[1] = y + xp;  TSpr.vertices[4] = y - xn;  TSpr.vertices[7] = y + xn;  TSpr.vertices[10]= y - xp;

rot = TSpr.m9 * rs + TSpr.m8 * rc;
xp  = TSpr.m9 * rc - TSpr.m8 * rs;
xn  = rot-xp;  xp += rot;
TSpr.vertices[2] = z + xp;  TSpr.vertices[5] = z - xn;  TSpr.vertices[8] = z + xn;  TSpr.vertices[11]= z - xp;

if (TSpr.lastnum != 1) {
libSPRITE::glNormal3f( TSpr.normalx, TSpr.normaly, TSpr.normalz );
TSpr.lastnum = 1;
};
libSPRITE::glVertexPointer( 3, libSPRITE::GL_FLOAT, 0, TSpr.vertices );
libSPRITE::glDrawArrays( libSPRITE::GL_TRIANGLE_STRIP, 0, 4 );
ENDINLINE
ENDFUNCTION

// draw a cylindrical sprite (same like X_SPRITE)
// needs to be after Start() [and before Stop()]
// sprite will be midhandled (unlike X_SPRITE)
FUNCTION Draw2%: x AS float, y AS float, z AS float, scale#=1.0, rotation#=0.0
INLINE
using libSPRITE::TSpr;
scale *= 0.5;
float rc = qSIN( rotation + 90 ) * scale;
float rs = qSIN( rotation      ) * scale;

float rot = TSpr.m10 * rc;
float xn  = TSpr.m10 * rs;
float xp  = rot-xn;  xn += rot;
TSpr.vertices[0] = x - xp;  TSpr.vertices[3] = x + xn;  TSpr.vertices[6] = x - xn;  TSpr.vertices[9] = x + xp;

TSpr.vertices[1] = y + rs + rc;  TSpr.vertices[4] = y - rs + rc;  TSpr.vertices[7] = y + rs - rc;  TSpr.vertices[10]= y - rs - rc;

rot = TSpr.m2 * rc;
xn  = TSpr.m2 * rs;
xp  = rot-xn;  xn += rot;
TSpr.vertices[2] = z + xp;  TSpr.vertices[5] = z - xn;  TSpr.vertices[8] = z + xn;  TSpr.vertices[11]= z - xp;

if (TSpr.lastnum != 2) {
libSPRITE::glNormal3f( TSpr.m2, 0.0f, TSpr.m10 );
TSpr.lastnum = 2;
};
libSPRITE::glVertexPointer( 3, libSPRITE::GL_FLOAT, 0, TSpr.vertices );
libSPRITE::glDrawArrays( libSPRITE::GL_TRIANGLE_STRIP, 0, 4 );
ENDINLINE
ENDFUNCTION

// use this if you want to change texture coords
// its way faster to change texture coords instead of changing texture with X_SETTEXTURE
// it will affect all following draw()/draw2() calls, in all following loops!
// default is (0,1, 1,1, 0,0, 1,0)
FUNCTION SetTextureCoords%: t0x AS float, t0y AS float, t1x AS float, t1y AS float, t2x AS float, t2y AS float, t3x AS float, t3y AS float
INLINE
using libSPRITE::TSpr;
TSpr.texcoord[0] = t0x; TSpr.texcoord[1] = t0y;
TSpr.texcoord[2] = t1x; TSpr.texcoord[3] = t1y;
TSpr.texcoord[4] = t2x; TSpr.texcoord[5] = t2y;
TSpr.texcoord[6] = t3x; TSpr.texcoord[7] = t3y;
ENDINLINE
ENDFUNCTION

// call it after you did draw all your sprites
// its not totally necessary to use this function, but strongly recommended
FUNCTION Stop%:
INLINE
libSPRITE::glDisableClientState( libSPRITE::GL_TEXTURE_COORD_ARRAY );
libSPRITE::glDisableClientState( libSPRITE::GL_VERTEX_ARRAY );
ENDINLINE
ENDFUNCTION

ENDTYPE



Test Program:
Code (glbasic) Select
// --------------------------------- //
// Project: TSprite
// Start: Sunday, July 24, 2011
// Autor: Kanonet
// Last Update: September 06, 2013

SETSCREEN 800,600,0
SYSTEMPOINTER TRUE
ALLOWESCAPE FALSE
AUTOPAUSE FALSE
LIMITFPS -1

// number of sprites to draw:
LOCAL count%=1000

LOCAL root%=SQR(count)/2, line%, row%, fps, timer, mx%, my%=15, mz=-4, selected%=1, rot%

// texture
CREATESCREEN 0, 0, 16,16
USESCREEN 0
DRAWRECT 0,0, 16, 16, RGB(0,0,255)
DRAWRECT 12,0, 4,8, RGB(255,127,0)
DRAWRECT 0,12, 4,4, RGB(255,0,63)
DRAWLINE 0,0, 16,16, 0xffffff
DRAWLINE 0,16, 16,0, 0xffffff
USESCREEN -1
CREATESCREEN 0,-1,0,0

// sprite
LOCAL sprite AS TSprite

// mainloop
REPEAT

line=-root; row=-root; INC rot

// mouse for camera control:
mx = mx + MOUSEAXIS(0)
my = my + MOUSEAXIS(1)
mz = mz + MOUSEAXIS(2)
IF KEY(27) // + more sprites
INC count
root=SQR(count)/2
ENDIF
IF KEY(53) AND count>1 // - less sprites
DEC count
root=SQR(count)/2
ENDIF
IF KEY(2) THEN selected=1
IF KEY(3) THEN selected=2
IF KEY(4) THEN selected=3

X_MAKE3D 0.1, 1000, 45
X_CAMERA my*COS(mx)*root*0.2,mz*2,my*SIN(mx)*root*0.2, 0,0,0

X_DRAWAXES 0,0,0
X_CULLMODE 1

LOCAL cx=my*COS(mx)*root*0.2, cy=mz*2, cz=my*SIN(mx)*root*0.2

IF selected=1
FOR i=1 TO count

X_SPRITE 0, 0, line, row, 0.06

INC row
IF row>root
row=-root
INC line
ENDIF
NEXT
ELSEIF selected=2
sprite.Start()
FOR i=1 TO count

X_SETTEXTURE 0, -1
sprite.Draw(0, line, row, 0.5)

INC row
IF row>root
row=-root
INC line
ENDIF
NEXT
sprite.Stop()
ELSE
sprite.Start()
X_SETTEXTURE 0, -1
FOR i=1 TO count

sprite.Draw(0, line, row, 0.5)

INC row
IF row>root
row=-root
INC line
ENDIF
NEXT
sprite.Stop()
ENDIF

X_MAKE2D
ALPHAMODE 0.9
DRAWRECT 0,0,800,60,RGB(70,70,70)
PRINT "CAMERA CONTROL: MOVE MOUSE / MOUSE WHEEL    |    FPS: "+INTEGER(1000/(timer-fps)), 0,0
PRINT "SPRITES CURRENTLY: "+count+"    |    PRESS (+) FOR MORE SPRITES AND (-) FOR LESS", 0,15
PRINT "SELECT: (1) X_SPRITE    |    (2) TSPRITE    |    (3) TSPRITE WITH TEXTURE ORDERING", 0, 30
PRINT "SELECTED: "+selected, 0, 45

LOCAL b1%, b2%
MOUSESTATE mx, my, b1, b2
PRINT "v",mx,my
DRAWRECT 80,80,20,20,RGB(255,127,127)
PRINT "0", 90,90
IF mx>80 AND mx<100 AND my>80 AND my<100 AND b1 THEN selected=1
DRAWRECT 110,80,20,20,RGB(255,127,127)
PRINT "1", 120,90
IF mx>110 AND mx<130 AND my>80 AND my<100 AND b1 THEN selected=2
DRAWRECT 140,80,20,20,RGB(255,127,127)
PRINT "2", 150,90
IF mx>140 AND mx<160 AND my>80 AND my<100 AND b1 THEN selected=3

SHOWSCREEN
  fps=timer
timer=GETTIMERALL()
UNTIL KEY(1)
END



Finally some test results (FPS), done on my T410s (see signature), which have two graphiccards (i can select one to use):
Intel HD Graphics - 1000 Sprites: 46 / 48 / 393
Intel HD Graphics - 2000 Sprites: 24 / 25 / 253
Intel HD Graphics - 3000 Sprites: 16 / 17 / 196
Intel HD Graphics - 4000 Sprites: 12 / 13 / 152
Intel HD Graphics - 5000 Sprites: 10 / 10 / 125
Intel HD Graphics - 7500 Sprites: 7 / 7 / 88
Intel HD Graphics - 10000 Sprites: 5 / 6 / 68
NVidia NVS 3100M - 1000 Sprites: 85 / 459 / 500 (cant do more)
NVidia NVS 3100M - 2000 Sprites: 50 / 256 / 500 (cant do more)
NVidia NVS 3100M - 3000 Sprites: 35 / 175 / 500 (cant do more)
NVidia NVS 3100M - 4000 Sprites: 27 / 134 / 500 (cant do more)
NVidia NVS 3100M - 5000 Sprites: 22 / 110 / 500 (cant do more)
NVidia NVS 3100M - 7500 Sprites: 15 / 74 / 480
NVidia NVS 3100M - 10000 Sprites: 11 / 56 / 370
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

Slydog

#1
Wow, pretty cool stuff!    :nw:
And using Vertex Arrays too!
Great to see how to work with the OpenGL directly, will be referencing this soon.

By looking at the code, it appears to only support full textured sprites, correct?
ie: You can't have your sprites in a spritesheet / texture map?

It is probably pretty easy I suppose to add this support.
You use
Code (glbasic) Select
DIMDATA self.tex[], 0,1, 1,1, 0,0, 1,0
to define your texture coordinates, and you specify to use the entire texture.

You would just need a way to predefine a sprite's texture locations.
And move the 'glTexCoordPointer( 2, GL_FLOAT, 0, self.tex[] )' command to the .Draw() routine.

Code (glbasic) Select
TYPE TSpriteTexture
  tex[]

  // Set uv location by absolute values (between 0 and 1)
  FUNCTION Set: x1, y1, x2, y2 // y goes from bottom to top
    DIMDATA self.tex[], x1,y1,  x2,y1,  x1,y2,  x2,y2
  ENDFUNCTION

  // Set uv location by pixel and texture size (between 0 and texture size)
  FUNCTION SetByPixel: x1, y1, x2, y2, texture_size_x, texture_size_y
    self.Set(x1/texture_size_x, y1/texture_size_y, x2/texture_size_x, y2/texture_size_y)
  ENDFUNCTION

ENDTYPE


And then pass this to a modified .Draw() function:
Code (glbasic) Select
FUNCTION Draw%: x#,y#,z#, scale#=1, rotation#=0, uv AS TSpriteTexture
  glTexCoordPointer( 2, GL_FLOAT, 0, uv.tex[] )
... Remaining function code as is
ENDFUNCTION


Sample code:
Code (glbasic) Select
GLOBAL st_full AS TSpriteTexture  // Use when sprite is the entire texture
GLOBAL st_sprite1 AS TSpriteTexture // Location of sprite 1 in sprite sheet
GLOBAL st_sprite2 AS TSpriteTexture // Location of sprite 2 in sprite sheet
st_full.Set(0,0, 1,1)
st_sprite1.SetByPixel(10,11,  20,21,   256,256)  // Sprite1 is located from pixels 10,11 to 20,21 of a 256x256 sprite sheet
st_sprite2.SetByPixel(40,41,  50,51,   256,256)  // Sprite2 is located from pixels 40,41 to 50,51 of a 256x256 sprite sheet
sprite.Start()
X_SETTEXTURE 0
sprite.Draw(..., st_sprite1)
sprite.Draw(..., st_sprite2) // Sprite1 and Sprite2 share the same sprite sheet
X_SETTEXTURE 1
sprite.Draw(..., st_full)
sprite.Stop()


And ya, please somebody use this to create a particle generator!!!!!!   :good:


[Edit]  The above changes would slow down this somewhat because of the extra 'glTexCoordPointer' call per SPRITE as opposed to per FRAME.  So maybe for a particle generator you woudn't want these changes as speed would be very important, but you'd be limited to using particle textures that are full sprites.  Maybe two versions!  :D

Cool!  Forum Post #666 !!!  :nw::rant:   he he
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

kanonet

Yeah Slydog your right, it only supports full textures (like X_SPRITE does too), but its easy to add it, like you mentioned. Just a correction at possible speed drop: you would not need to set new texture coordinates each sprite, you just need to push new texturecoordinates, when you define a new texture (just put glTexCoordPointer behind X_SETTEXTURE), or when you change animationframe in this sprite sheet (just call it manually in this case). So you would not have to call it too often and so you would not lose to much speed.

In fact glTexCoordPointer is way faster, than X_SETTEXTURE so it would be fastest, if you pack all sprite textures in one spritesheet and just change the texturecoordinates. Would be good for the particle engine too, of cause.

BTW did you try the test program? What are your results like? Cuz i just tested it on only one pc, so would be interesting to see if its useful on other machines too.
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

kanonet

LOL i just realized, that i forgot to include the test program, its a bit pointless, asking for other peoples results, if you dont give them the code.^^
So its in the first post now. :'(
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

mentalthink

HI Kanoket, thanks a  lot about this program.... very very Nice... no doubt I try in my mobile Devices... perhaps can be awesome including in my space Ships...

Thanks a lot...

Wampus

Is this faster than drawing sprites using the same sprite sheet (i.e. just one texture) with POLYVECTOR?

Kitty Hello

This is like POLYVECTOR, but for 3D scenes. Top work! Put sticky.

kanonet

Was someone able to test this on other graphic cards or mobile devices?

If you dont want to include the hole gl.gbas (cuz its slow to compile), than you can include the following, it contains all opengl calls that are necessary to get it working:
Code (glbasic) Select
// OpenGL calls, only use gl.gbas _OR_ the following:

INLINE
#ifdef CENTERLINE_CLPP
#define signed
#endif
#define OGL ::
typedef unsigned int    GLenum;
typedef void            GLvoid;
typedef int             GLint;
typedef int             GLsizei;
}
extern "C" {
void __stdcall glEnableClientState( GLenum cap ); /* 1.1 */
void __stdcall glDisableClientState( GLenum cap ); /* 1.1 */
void __stdcall glVertexPointer( GLint size , GLenum typ , GLsizei stride , const GLvoid *ptr );
void __stdcall glNormalPointer( GLenum typ , GLsizei stride , const GLvoid *ptr );
void __stdcall glTexCoordPointer( GLint size , GLenum typ , GLsizei stride , const GLvoid *ptr );
void __stdcall glDrawArrays( GLenum mode , GLint first , GLsizei count );
}
namespace __GLBASIC__ {
ENDINLINE

CONSTANT GL_TRIANGLE_STRIP                                  = 0x0005
CONSTANT GL_FLOAT                                           = 0x1406
CONSTANT GL_DOUBLE                                          = 0x140A
CONSTANT GL_VERTEX_ARRAY                                    = 0x8074
CONSTANT GL_NORMAL_ARRAY                                    = 0x8075
CONSTANT GL_TEXTURE_COORD_ARRAY                             = 0x8078

FUNCTION glEnableClientState: cap
INLINE
OGL glEnableClientState(cap);
ENDINLINE
ENDFUNCTION

FUNCTION glDisableClientState: cap
INLINE
OGL glDisableClientState(cap);
ENDINLINE
ENDFUNCTION

FUNCTION glVertexPointer: size, typ, stride, ptr[]
typ=GL_DOUBLE;
INLINE
OGL glVertexPointer(size, typ, stride, &ptr(0));
ENDINLINE
ENDFUNCTION

FUNCTION glNormalPointer: typ, stride, ptr[]
typ = GL_DOUBLE;
INLINE
OGL glNormalPointer(typ, stride, &ptr(0));
ENDINLINE
ENDFUNCTION

FUNCTION glTexCoordPointer: size, typ, stride, ptr[]
typ = GL_DOUBLE;
INLINE
OGL glTexCoordPointer(size, typ, stride, &ptr(0));
ENDINLINE
ENDFUNCTION

FUNCTION glDrawArrays: mode, first, count
INLINE
OGL glDrawArrays(mode, first, count);
ENDINLINE
ENDFUNCTION
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

Hemlos

QuoteNow i want someone to create a decent particle engine with this.

Its already built, all i need to do it splice it in.

1. Is this much faster than polyvector?
2. Why is there only one rotation? you need at least 2.
3. Is this a "point sprite"?

If no to #3, do you know how to implement them?
Spritez3d would go from thousands of particles to millions overnight.



Bing ChatGpt is pretty smart :O

kanonet

1. I think you (and maybe Wampus?) got something wrong. Polyvector is for 2D graphic, this code here replaces X_SPRITE, which means its for 3D graphic. So for your 2D SpriteZ stick with Polyvector, but this code could be really useful for a 3D particle engine, because it has everything that X_SPRITE has, plus some extras and way more speed. This is a 3D-Sprite/Billboard, it can be places anywhere in 3D scene and it will always face the camera, no matter where it is. It just shares with polyvectors, that its fast and that you can optimise it with proper texture ordering (like always in 3D, texture ordering is faster than calling X_SETTEXTURE for each object).

2. In 3D scene you have 3 axes, so you need 3 rotations. But this is a billboard, so it autoimatically faces the camera, the hole point in this code is, that it handles the rotation around 2 axes independently, so it always faces the camera, so you dont need to worry about doing this by yourself. So you just need to worry about the 3rd rotation, thats why it is just 1 rotation, like in a 2D scene. Btw this is one of the advantages of this code over X_SPRITE which does not allow you to change this rotation.

3. No this is not a Pointsprite, i render a quad to the scene for each sprite, but i do this way faster, than native GLB could ever do it. A pointsprite is a special GPU feature on more modern graphic cards, so you dont need to render a quad, but just tell the GPU the point where to place the Sprite (its faster). Pointsprites do not work with older graphic hardware and maybe not with mobile platforms, but i dont know in detail how much its supported nowadays. And i dont know how to implement it, was never interested in it, cuz i wanted a solution that run even on old hardware. If you want to get it working, than you will definitely need to use INLINE, and im definitely the wrong person to ask about this, since i have no C skills.
EDIT: i just noticed, that sometimes every billboard is called a point sprite, but in my opinion this is not correct. But if you just mean a billboard, not the special GPU feature for faster billboards, than this is a point sprite, cuz it draws a 2D sprite on a point in 3D world. :D

How do you render your particles in you 3D SpriteZ now? With X_SPRITE or do you draw your own quads/triangles with GLB commands? Anyways, this code here is way faster than both solutions and it works on all PCs with OpenGL 1.1 or higher. I did not try it on a mobile device, since i don have one of them (besides an old Win Mobile phone).

See here for more details: http://nehe.gamedev.net/article/billboarding_how_to/18011/
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

Hemlos

I meant, is this faster than the    X_SETTEXTURE / X_DRAWOBJ ?

The quads are still too slow though, no matter which way you cut the cake.
Oh, how it would rock, if it were taking advantage of the gpu. :bed:

the PE only make one quad, and reuse it:

Code (glbasic) Select

FUNCTION EmitterSys_X_CreateSquare: Color
//flat square object, facing +z direction
LOCAL GenObjNum=GENX_OBJ()
X_AUTONORMALS 1 // smooth edges
X_OBJSTART GenObjNum
X_OBJADDVERTEX 0.5,0.5,0, 1,0, Color
X_OBJADDVERTEX -0.5,0.5,0, 0,0, Color
X_OBJADDVERTEX -0.5,-0.5,0, 0,1, Color
X_OBJNEWGROUP
X_OBJADDVERTEX -0.5,-0.5,0, 0,1, Color
X_OBJADDVERTEX 0.5,-0.5,0, 1,1, Color
X_OBJADDVERTEX 0.5,0.5,0, 1,0, Color
X_OBJEND
RETURN GenObjNum
ENDFUNCTION


This is the draw commands:

Code (glbasic) Select

FUNCTION EmitterSys_X_Trans3d: x,y,z , Scale , rx,ry,rz //move,scale,rotate an object
// This function Handles 3d inputs for,
// Move, Scale, Rotate for 3d objects.

X_MOVEMENT x,y,z
X_SCALING Scale, Scale, Scale
IF rz<>0 THEN X_ROTATION rz,0,0,1
IF rx<>0 THEN X_ROTATION rx,1,0,0
IF ry<>0 THEN X_ROTATION ry,0,1,0

RETURN TRUE //pass completed, return=1 (BOOL=TRUE)
ENDFUNCTION


Code (glbasic) Select

FUNCTION EmitterSys_X_DrawObj: ID , Texture1 , Texture2 , ShaderID // ID, Tex id1 , Tex id2, Shader
// This function Handles 3d inputs for,
// Shaders, Textures, Drawing for 3d objects.
//
// Shaders:
// To use a shader, you must create a SUB.
// The shader SUB MUST be named the same name in GLM_X_LoadShader: shadername$

//beta
IF ShaderID>0
X_SETSHADER ShaderID
sid$=ShadersArray$[ShaderID-1]
ProcessShaderInputsSUB = 1//CALLBYNAME(sid$) //1 will override
IF ProcessShaderInputsSUB = FALSE
GLM_2d_Window("GLM_X_DRAWOBJ")
PRINT "Sub not found" ,100,300
GLM_2d_Window_Sleep(3000)
END
ENDIF
ENDIF

X_SETTEXTURE Texture1 , Texture2
X_DRAWOBJ ID, 0
X_SETSHADER 0
RETURN

ENDFUNCTION


Bing ChatGpt is pretty smart :O

kanonet

My code here is faster than your approach, cuz im using glDrawArrays without any matrix manipulation. For maximum speed pack all your particle textures together into one texture, move the texture coordinates and call just one single X_SETTEXTURE before you draw all your particles. Just use my test program and extend it with your drawing routines to see how much faster it runs.
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

Hemlos

hmm, im having trouble getting your sample running  :rant:
Bing ChatGpt is pretty smart :O

kanonet

If you give more details, i may be able to help you.
Lenovo Thinkpad T430u: Intel i5-3317U, 8GB DDR3, NVidia GeForce 620M, Micron RealSSD C400 @Win7 x64

Hemlos

Compile it, let me see it run.
I dont know how to use your code.
Bing ChatGpt is pretty smart :O