Archived

This topic is now archived and is closed to further replies.

Thunder_Hawk

strlen Incorrect?

Recommended Posts

As some of you may know I''ve been toying around with a Print function for Open GL. Everytime I fix something another bug seems to appear for no logical reason. However, this one takes the cake. Excuse the variable names please, I''ve been working on this function *all* day. (Quite frustrating)
  
bool glPrint (float x, float y, char* goddamnit)
{
	if (!goddamnit) return false;
	unsigned int length;
	length = strlen(goddamnit)*4;
	float *buffer;
	buffer = new float[length*20];		//can this be done?

	if (!buffer) return FALSE;
	double actualx, actualy, subx, suby;		//actual is for the vertex positions and sub is for texcoords

	actualx = x;
	actualy = y;
	unsigned int loop;
	for (loop = 0; loop < length; loop++, goddamnit++) {
		subx = ((*goddamnit-32)%16)*0.0625;
		suby = int((*goddamnit-32)/16)/8.0f;
//		glTexCoord2f (	s		,		t	);	glVertex3f (	x		,		y		,		z);

		buffer[loop*20+0] = subx; 		buffer[loop*20+1] = 1.0f-suby-0.120f;	buffer[loop*20+2] = actualx;		buffer[loop*20+3] = actualy; 		buffer[loop*20+4] = 0.0f;	//BL

		buffer[loop*20+5] = subx+0.0625f; 	buffer[loop*20+6] = 1.0f-suby-0.120f; 	buffer[loop*20+7] = actualx+0.5f;	buffer[loop*20+8] = actualy;		buffer[loop*20+9] = 0.0f;	//BR

		buffer[loop*20+10] = subx+0.0625f;	buffer[loop*20+11] = 1.0f-suby;		buffer[loop*20+12] = actualx+0.5f;	buffer[loop*20+13] = actualy+0.5;	buffer[loop*20+14] = 0.0f;	//TR

		buffer[loop*20+15] = subx;		buffer[loop*20+16] = 1.0f-suby;		buffer[loop*20+17] = actualx;		buffer[loop*20+18] = actualy+0.5;	buffer[loop*20+19] = 0.0f;	//TL

		actualx += 0.5f;
	}
	glPushMatrix();
//	glLoadIdentity();

//	glPushAttrib (GL_COLOR_BUFFER_BIT);

//	glColor3ub (r, g, b);

	glBindTexture (GL_TEXTURE_2D, texture[2].texID);
	glColor3f (0.0f, 1.0f, 0.0f);
	glInterleavedArrays (GL_T2F_V3F, 0, buffer);
	glDrawArrays (GL_QUADS, 0, length);
//	glPopAttrib();

	glPopMatrix();
	if (buffer){
		delete [] buffer;
	} else {
		return FALSE;
	}
	buffer = 0;
	return TRUE;
}
  
At long last it seemed to be working correctly, but then I couldn''t figure out what kind of pattern I was seeing. The number of characters on the screen seemed to be truncated in a nonsensical fasion. Upon further inspection I basically used different methods to do the same thing. Out of blind frustration, I decided to add 30 or so more characters to the end of my test string. At that point I noticed more of my string appeared on the screen. A quick count revealed that approximately one-fourth of the characters where being displayed, thus I tryed the logical fix. Which leads me to this: WHY ON EARTH DO I HAVE TO MULTIPLY THE RESULT OF strlen() BY 4???????? If someone can answer that I will be impressed. I have never seen a problem quite so strange as this. As Spock might say, "That is illogical." Well, it''s about time to hit the sack, I hope to see this answered sometime in the future! _____________________________ And the Phoenix shall rise from the ashes... --Thunder_Hawk -- ¦þ ______________________________

Share this post


Link to post
Share on other sites
slap me if i''m wrong, but when you call glDrawArrays, you''re passing length instead of length * 4. the reason you need this is because that parameter specifies the number of indices, not the number of quads

Share this post


Link to post
Share on other sites
OK, I not awake yet but...

If buffer = 20*length and the max subscript you use is buffer[loop*20+19] where loop max is length.

Then unless you extend buffer (you''ve done so by *4) you would get an overrun on the buffer assignment.

If it is something more complex my brain is still snoring happily to itself and I will have to have a look again when I can understand any code longer a couple of lines.

Regards

BaelWrath

If it is not nailed down it''s mine and if I can prise it loose,
it''s not nailed down!

Share this post


Link to post
Share on other sites
quote:
Original post by Fragmo
slap me if i''m wrong, but when you call glDrawArrays, you''re passing length instead of length * 4. the reason you need this is because that parameter specifies the number of indices, not the number of quads


No slapping required. I''m assuming that if it still works correctly when I move the *4 to glDrawArrays, then you are right. Thank you, that will be the last random feature in my code today. Subsequently, knowing this, the function will run more efficiently.

I will sleep more easily tonight knowing I won''t have to fix this in the morning. Thanks and goodnight.



_____________________________

And the Phoenix shall rise from the ashes...

--Thunder_Hawk -- ¦þ
______________________________

Share this post


Link to post
Share on other sites
*edit* While thinking this up, two others came up with the same solution. At least I was right, though *edit*

Are you sure this code works as it stands? It seems to me you've got some bounds issues to be concerned with...length is four times larger then then length of your char array, and yet you advance both loop and goddamnit once per iteration. So if your string is 5 characters long, you'll iterate 20 times, and end up 15 characters past the end of your array. Unless I'm missing something, you need to trace through that loop a few times and see what is actually happening with that goddamnit variable.

Also, just a quick guess based on a quick scanning of your code. I know nothing about OpenGL (or any 3d graphics API for that matter), but I assume that 'glDrawArrays (GL_QUADS, 0, length);' tells opengl to draw 'length' thingies from the buffer array. Each letter seems to involve 4 thingies, so it makes sense to me that if you only tell glDrawArrays the number of letters you have, you'll only draw length/4 letters. What's this tell me? That you should create buffer as lenght*4 floats long, but don't actually increase length. Then pass that glDrawArrays function length*4. The more I think about this, the more sense it makes, but my complete lack of knowledge of what those thingies are is a rather severe stumbling block, so I'm probably completely wrong.

CM

[edited by - Conner McCloud on July 18, 2002 3:04:57 AM]

Share this post


Link to post
Share on other sites
The code is now corrected...by the way this is the kind of thing I was worried about...by moving the * 4 to the glDrawArrays() function call. The code I posted before should work perfectly as it was (if my imagination is working properly at this hour) with the side effect that it wasted (read: not leaked)60*length*4bytefloats per call. Ah well, that''s enough typing for one day...jeesh

_____________________________

And the Phoenix shall rise from the ashes...

--Thunder_Hawk -- ¦þ
______________________________

Share this post


Link to post
Share on other sites
Actually I'm not sure...it did work at the time but it should have gotten garbage data by going past the end of the goddamnit variables length. On a side note I'm adding support for \n and it appears that strlen() doesn't count the character. If I add a second length variable and decrement it once for each \n and use the correct variable in each spot I end up with the last characters truncated by the number of newline characters.


[EDIT] Sorry about this newline is counted I just had to add an addition loop variable (I had half of it set up...LOL) the way I had it the array sections where there were newlines were empty (you can see the problem there) [/EDIT]

[EDIT2] I figured I'd post the final code if anyone's interested (actually I may add a style parameter for alignment tomarrow)

  
bool glPrint (float x, float y, GLubyte r, GLubyte g, GLubyte b, char* goddamnit)
{
if (!goddamnit) return false;
unsigned int length, actuallength;
unsigned int loop, loop2;
length = strlen(goddamnit);
actuallength = length;
for (loop = 0; loop < length; loop++) {
if (*(goddamnit+loop) == '\n') actuallength--;
}
float *buffer;
buffer = new float[actuallength*20];
if (!buffer) return FALSE;
double actualx, actualy, subx, suby; //actual is for the vertex positions and sub is for texcoords

actualx = x;
actualy = y;
for (loop = 0, loop2 = 0; loop < length; loop++, goddamnit++) {
if (*goddamnit != '\n') {
subx = ((*goddamnit-32)%16)*0.0625;
suby = int((*goddamnit-32)/16)/8.0f;
// glTexCoord2f ( s , t ); glVertex3f ( x , y , z);

buffer[loop2*20+0] = subx; buffer[loop2*20+1] = 1.0f-suby-0.120f; buffer[loop2*20+2] = actualx; buffer[loop2*20+3] = actualy; buffer[loop2*20+4] = 0.0f; //BL

buffer[loop2*20+5] = subx+0.0625f; buffer[loop2*20+6] = 1.0f-suby-0.120f; buffer[loop2*20+7] = actualx+0.5f; buffer[loop2*20+8] = actualy; buffer[loop2*20+9] = 0.0f; //BR

buffer[loop2*20+10] = subx+0.0625f; buffer[loop2*20+11] = 1.0f-suby; buffer[loop2*20+12] = actualx+0.5f; buffer[loop2*20+13] = actualy+0.5; buffer[loop2*20+14] = 0.0f; //TR

buffer[loop2*20+15] = subx; buffer[loop2*20+16] = 1.0f-suby; buffer[loop2*20+17] = actualx; buffer[loop2*20+18] = actualy+0.5; buffer[loop2*20+19] = 0.0f; //TL

actualx += 0.5f;
loop2++;
} else {
actualy -= 0.5f;
actualx = x;
}
}
glPushMatrix();
// glLoadIdentity();

glPushAttrib (GL_COLOR_BUFFER_BIT);
glColor3ub (r, g, b);
glBindTexture (GL_TEXTURE_2D, texture[2].texID);
glInterleavedArrays (GL_T2F_V3F, 0, buffer);
glDrawArrays (GL_QUADS, 0, actuallength*4);
glPopAttrib();
glPopMatrix();
if (buffer){
delete [] buffer;
} else {
return FALSE;
}
buffer = 0;
return TRUE;
}

bool glPrintf (float x, float y, GLubyte r, GLubyte g, GLubyte b, char* goddamnit, ...)
{
va_list arglist;
char buff[256];
va_start (arglist, goddamnit);
vsprintf (buff, goddamnit, arglist);
va_end (arglist);
return glPrint (x, y, r, g, b, buff);
}


_____________________________

And the Phoenix shall rise from the ashes...

--Thunder_Hawk -- ¦þ
______________________________


[edited by - Thunder_Hawk on July 18, 2002 3:51:26 AM]

[edited by - Thunder_Hawk on July 18, 2002 4:10:59 AM]

Share this post


Link to post
Share on other sites
Not sure if anyone is actually interested in this, but a while ago I was fiddling around with text-rendering in OpenGL ( and DirectX, as I kinda play around with both ), and despite finding several solutions out there, I wasn''t too happy with them, mostly because they were used so differently from the "standard" way of getting text printed. So I created a couple of classes to make printing text in OpenGL ( and DirectX, although I never implement a DirectX version ), in the same manner as most people would be used to from "console" programming. That is, it would support statements like:
nFont* font = new nglFont ( "font.bmp" );
fprintf ( font, "Hello %s\n", player->name );
*font << "Hello " << player->name << endl;
setpos ( *font, 100, 10 );
fprintf ( font, "Hepp\n" );
*font << setpos ( 100, 10 ) << "Hepp" << endl;

and so on.

Just on the offchance that this might be useful to someone, here''s the code:

  
// ----- font.h -----

#ifndef N_FONT_H
#define N_FONT_H

#include <iostream>
#include <string>
#include <sstream>

class nFont;

typedef nFont&(*pfnFontFunc)(nFont*);

nFont& endl (nFont*);
nFont& flush (nFont*);

class nFont {
public:
nFont& operator<< (const unsigned char& c);
nFont& operator<< (const char& c);
nFont& operator<< (const unsigned short& s);
nFont& operator<< (const short& s);
nFont& operator<< (const unsigned int& i);
nFont& operator<< (const int& i);
nFont& operator<< (const unsigned long& l);
nFont& operator<< (const long& l);
nFont& operator<< (const float& f);
nFont& operator<< (const double& d);
nFont& operator<< (const std::string& s);
nFont& operator<< (pfnFontFunc pfn);

nFont& flush();


friend nFont& endl(nFont*);
friend nFont& flush(nFont*);

protected:
std::ostringstream s;
virtual void printString (const char* str);
};


void fprintf (nFont&, const char*, ...);
void fprintf (nFont*, const char*, ...);


#endif


// ----- font.cpp -----

#include "font.h"
#include <stdarg.h>

using namespace std;

nFont& nFont::operator << (const unsigned char& c) {
s << c;
return *this;
}
nFont& nFont::operator << (const char& c) {
s << c;
return *this;
}
nFont& nFont::operator << (const unsigned short& us) {
s << us;
return *this;
}
nFont& nFont::operator << (const short& ss) {
s << ss;
return *this;
}
nFont& nFont::operator << (const unsigned int& i) {
s << i;
return *this;
}
nFont& nFont::operator << (const int& i) {
s << i;
return *this;
}
nFont& nFont::operator << (const unsigned long& l) {
s << l;
return *this;
}
nFont& nFont::operator << (const long& l) {
s << l;
return *this;
}
nFont& nFont::operator << (const float& f) {
s << f;
return *this;
}
nFont& nFont::operator << (const double& d) {
s << d;
return *this;
}
nFont& nFont::operator << (const string& str) {
s << str;
return *this;
}

nFont& nFont::operator << (pfnFontFunc func) {
(*func)(this);
return *this;
}

nFont& flush (nFont* font) {
return font->flush();
}

nFont& endl (nFont* font) {
font->s << endl;
return font->flush();
}

void nFont::printString (const char* str) {
cout << s;
cout.flush();
}

nFont& nFont::flush() {
s.flush();
this->printString (s.str().c_str());
string s2;
s.str(s2);
cout.flush();
return *this;
}


void fprintf (nFont& font, const char* fmt, ...) {
char text[1024];
va_list ap;
if ( fmt == NULL )
return;

va_start ( ap, fmt );
vsprintf ( text, fmt, ap );
va_end ( ap );
font << text;
if (text[strlen(text)-1] == ''\n'')
font.flush();
}

void fprintf (nFont* font, const char* fmt, ...) {
char text[1024];
va_list ap;
if ( fmt == NULL )
return;

va_start ( ap, fmt );
vsprintf ( text, fmt, ap );
va_end ( ap );
(*font) << text;
if (text[strlen(text)-1] == ''\n'')
font->flush();
}



// ----- glfont.h -----

#ifndef N_GL_FONT_H
#define N_GL_FONT_H

#include "font.h"
#include "textureloader.h"


class nglFont : public nFont {
public:
nglFont ( const char* strFontTexture );
~nglFont();

void X (double _x) { x = _x; }
void Y (double _y) { y = _y; }


friend nglFont& setx ( nglFont&, double _x );
friend nglFont& sety ( nglFont&, double _y );
friend nglFont& setpos ( nglFont&, double _x, double _y );
private:
void printString (const char* str);
unsigned int base;
unsigned int texid;
double x, y;
};


class double_nglfont_obj {
public:
double_nglfont_obj ( nglFont& (*f) ( nglFont&, double), double d ) : func(f), val(d) { }
nglFont& operator() ( nglFont& f) const {
return (*func)(f, val);
}
private:
nglFont& (*func) ( nglFont&, double );
double val;
};

class doubledouble_nglfont_obj {
public:
doubledouble_nglfont_obj ( nglFont& (*f) ( nglFont&, double, double ), double x, double y ) : func(f), val1(x), val2(y) { }
nglFont& operator() ( nglFont& f ) const {
return (*func)(f, val1, val2);
}
private:
nglFont& (*func) ( nglFont&, double, double );
double val1, val2;
};

nglFont& operator<< ( nglFont& font, const double_nglfont_obj& im );
nglFont& operator<< ( nglFont& font, const doubledouble_nglfont_obj& im );

double_nglfont_obj x ( double _x );
double_nglfont_obj y ( double _y );
doubledouble_nglfont_obj pos ( double, double );

nglFont& setx (nglFont& font, double _x );
nglFont& sety (nglFont& font, double _y );
nglFont& setpos ( nglFont& font, double, double );


#endif


// ----- glfont.h -----

#include "glfont.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#ifndef NDEBUG
#define new new(_NORMAL_BLOCK,__FILE__,__LINE__)
#endif
#include <GL/GL.h>

#include "glngine.h"


nglFont::nglFont ( const char* strFontTexture ) {
base = 0;
x = y = 0;
texid = LoadTexture ( strFontTexture );
if ( texid == 0 )
return;

float cx, cy;
float step = 0.0625f;
base = glGenLists ( 256 );
glBindTexture ( GL_TEXTURE_2D, texid );

for ( int i = 0; i < 256; i++ ) {
cx = float((i%16)/16.0f);
cy = float((i/16)/16.0f);

glNewList ( base + i, GL_COMPILE );
glBegin ( GL_QUADS );
glTexCoord2f ( cx, 1 - cy - step );
glVertex2i ( 0, 0 );

glTexCoord2f ( cx + step, 1 - cy - step );
glVertex2i ( 16, 0 );

glTexCoord2f ( cx + step, 1 - cy );
glVertex2i ( 16, 16 );

glTexCoord2f ( cx, 1 - cy );
glVertex2i ( 0, 16 );
glEnd();
glTranslated ( 10, 0, 0 );
glEndList();
}
}

nglFont::~nglFont() {
if ( texid != 0 ) {
glDeleteLists ( 256, base );
glDeleteTextures ( 1, &texid );
}
}

void nglFont::printString ( const char* str ) {
if ( texid == 0 ) {
return;
}

glEnable ( GL_TEXTURE_2D );
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE );
glActiveTextureARB ( GL_TEXTURE0_ARB );
glBindTexture ( GL_TEXTURE_2D, texid );
glDisable ( GL_DEPTH_TEST );

glMatrixMode ( GL_PROJECTION );
glPushMatrix();

glLoadIdentity();
glOrtho ( 0, 640, 0, 480, -1, 1 );

glMatrixMode ( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();

glTranslated ( x, y, 0 );

glListBase ( base - 32 );

glCallLists ( strlen(str), GL_BYTE, str );

glMatrixMode ( GL_PROJECTION );
glPopMatrix();

glMatrixMode ( GL_MODELVIEW );
glPopMatrix();
}


nglFont& operator<< ( nglFont& font, const double_nglfont_obj& im ) {
return im ( font );
}

nglFont& operator<< ( nglFont& font, const doubledouble_nglfont_obj& im ) {
return im ( font );
}

double_nglfont_obj x ( double _x ) {
return double_nglfont_obj ( setx, _x );
}
double_nglfont_obj y ( double _y ) {
return double_nglfont_obj ( sety, _y );
}

nglFont& setx (nglFont& font, double _x ) {
font.x = _x;
return font;
}
nglFont& sety (nglFont& font, double _y ) {
font.y = _y;
return font;
}

doubledouble_nglfont_obj pos ( double _x, double _y ) {
return doubledouble_nglfont_obj ( setpos, _x, _y );
}

nglFont& setpos ( nglFont& font, double _x, double _y ) {
font.x = _x;
font.y = _y;
return font;
}


Ok, there''s a couple of assumptions here...
First of, it depends on having a function
unsigned int LoadTexture ( const char* strTexturename )
which loads and binds a texture and returns it''s ID.
The texture''s containing the fonts should be 256*256 pixels, and contain 16 rows of 16 characters each.
(Ok, it''s a couple of weeks since I wrote this, so I don''t remember all the details 100%, and it''s very likely that the 256*256 pixels is not really necessary. ).


There''s a couple of reasons things look like they do. I wanted a base-class which could be implemented (as easy as possible) with different API''s (I only did an opengl implementation myself), and I wanted it to support print-statements that was recognizable to C-programmers ( printf ), and to C++-programmers (the << operators ).
I was unable to find a decent way to inherit from the basic io-classes of C++, but it works acceptable anyway.

Oh, by the way, the glActiveTextureARB function call is there because I used multitexturing in an (unreleased) demo where I first used this class. You should be able to change it to single-texturing easily.

If you have any questions or comments (yay! I like those) post them here or drop me a mail.

-Neophyte

-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GED d- s:+ a- C++$ UL++ P++ L++ E W+ N+ o K? w !O M--(+) V-- PS+
PE Y+ PGP t-- 5++ X+ R(+) rv+(++) b+++ DI+ D(+) G e+>++ h r--> y+
----- END GEEK CODE BLOCK-----
geekcode.com

Share this post


Link to post
Share on other sites
That''s pretty cool, but way more than I wanted to get done . I may try that code out sometime. In the mean time, I wrote this function because I was unhappy with the fact that NeHe''s code can''t support escape sequences (I think) and I had a hunch that this method might not drain as much on the fps (untested). With this function I could easily load in a file and display it quick and dirty. Anyway I want it for my version of Pong. I actually don''t remember what kind of rules the original had, but this is what I have so far. It can be downloaded on my site. It''s not finished yet. By the way, does anyone know a better place to host a site for free?

_____________________________

And the Phoenix shall rise from the ashes...

--Thunder_Hawk -- ¦þ
______________________________

Share this post


Link to post
Share on other sites