BSP (Quake 3) PVS Issues

dstaudigel
Unregistered
 
Post: #1
I have been working on a BSP level loader as an excercise. I've been using the code from gametutorials.com as a template/source, and have gotten as far as PVSs (Potential Viewing Sets).

The problem is simple, they don't work.

I've been trouble shooting for a while, and my code is now almost exactly the same (variable names changed) as the tutorial code (and, I might add, the code of a buddy of mine who claims to have it working on the PC). The fact that he is on a PC makes me think it's a byte-order issue, which seems not to be the case.

The only pattern I've noticed is that the PVS set is screwed up. It is not an issue of finding the correct leaf for my camera (checked that), and it is probably not an issue of loading, because i've checked over that a lot too.

Thanks a bundle if you can help. If it'll help, here is the relevant code (whole code is to large and complex to post):

For loading PVS data:

Code:
fseek(fp,aLump[kVisData].offset,SEEK_SET);
visData.numOfClusters = frint(fp,fe);
visData.bytesPerCluster = frint(fp,fe);

visData.pBitsets = (BYTE *)malloc(sizeof(BYTE) * visData.numOfClusters
* visData.bytesPerCluster);

fread(visData.pBitsets,sizeof(BYTE),visData.numOfClusters *
visData.bytesPerCluster,fp);

Fort detecting camera's leaf/cluster (i know the input vector is
correct) and it seems to work fine

Code:
int DJBSPLevel::fCluster(float pos[3])
{
    int i = 0;
    float distance = 0.0f;
    while(i >= 0)
    {
        tBSPNode&  node = aNode[i];            // array of nodes
        tBSPPlane& plane = aPlane[node.plane];        // array of planes

        distance =    plane.vNormal[0] * pos[0] + plane.vNormal[1] *
pos[1] +  plane.vNormal[2] * pos[2] - plane.d;

        if(distance >= 0)
            i = node.front;
        else        
            i = node.back;
    }
    i = ~i;

    return aLeaf[i].cluster; // aLeaf is the array of leaves
}

Testing visibility:

Code:
bool DJBSPLevel::isVisible(int from,int to)
{
    if(!visData.pBitsets || from < 0 ) { return 1; }
    
    char visSet = visData.pBitsets[((from)*visData.bytesPerCluster) + (to / 8)];

    // Now that we have our vector (bitset), do some bit shifting to find if
    // the "test" cluster is visible from the "current" cluster, according to the bitset.
    int result = visSet & (1 << (to & 7));

    return ( result );
    
}

Displaying the whole thing:

Code:
int cluster = fCluster(tr);
printf("Cluster: %i\n",cluster);

for(int i = 0 ; i < nLeaf ; i++)
{
    drawLeaf(i,cluster);
}

And drawing a leaf:

Code:
void DJBSPLevel::drawLeaf(int index,int fromCluster)
{
    if(index >= nLeaf)
        printf("index >= nLeaf %i >= %i\n",index,nLeaf);

    tBSPLeaf * leaf = &aLeaf[index];

    if(!isVisible(fromCluster,aLeaf[index].cluster))
    {
        return;
    }

    int startFace = leaf->leafface;
    int endFace = startFace + leaf->numOfLeafFaces;

    for(int i = startFace ; i < endFace ; i++)
    {
        drawFace(i % nFace);
    }
}

And drawing a face is fairly simple, and i know it works fine because
it works great when I elimiate the PVS code.

Anybody got any hints?

Daniel Staudigel
Quote this message in a reply
gwihlidal
Unregistered
 
Post: #2
It's definitely a little endian problem. I forgot the functions to use (haven't had a Mac for development in over a year and a half now - I know it sucks, but getting a new one soon) but I know it is\was in Endian.h

Any binary files on the PC that were written must have every numeric variable converted to Big Endian.

That should get it working for you probably.

I'm going to port the Quake 3 tutorials on Game Tutorials to the Mac right away. In the process of writing some new ones for the series with Frank Puig (Collision Detection and Curved Surfaces =)

~Graham
Quote this message in a reply
dstaudigel
Unregistered
 
Post: #3
Thank you SO much, if you could do that, it would be a life saver, and a great learning opportunity to boot. The problem is, how do I convert chars to big endian? All my other big/little endian problems have been easy to solve.

Daniel Staudigel
Quote this message in a reply
gwihlidal
Unregistered
 
Post: #4
=) Np

I unfortunately haven't been able to do Mac programming in a while but I'm slowly relearning what I used to do =)

Lemme see what I can figure out, I should post back in a quick bit

~Graham
Quote this message in a reply
gwihlidal
Unregistered
 
Post: #5
Ok (I *might* be wrong but I don't think I am)

You shouldn't have to endian convert the byte array of the vis data (byte = unsigned char), because the byte order isn't reversed with characters

But, in the vis data structure you have two 32 bit integers which have the number of clusters and the size of each cluster. Those will have to be endian swapped

I think CoreFoundation has endian swapping functions in it now, but I think there is an older Carbon version in Endian.h

Here should be the CF function to use:

CFSwapInt32LittleToHost

Hope that helps

~Graham
Quote this message in a reply
dstaudigel
Unregistered
 
Post: #6
My code for loading the header:

Code:
fseek(fp,aLump[kVisData].offset,SEEK_SET);

    visData.numOfClusters = frint(fp,fe);
    visData.bytesPerCluster = frint(fp,fe);

    printf("numOfClusters: %i byesPerCluster: %i\n",visData.numOfClusters,visData.bytesPerCluster);
    visData.pBitsets = (BYTE *)malloc(sizeof(BYTE) * visData.numOfClusters * visData.bytesPerCluster);

    fread(visData.pBitsets,sizeof(BYTE),visData.numOfClusters * visData.bytesPerCluster,fp);

and the code for frint:

Code:
void eswap(unsigned char * p,unsigned long sElem,unsigned int nElem)
{
    unsigned char * res = (unsigned char *)malloc(sElem * nElem);
    unsigned char * q = res;
    
    if(sElem < 2)
        return;
    
    for(unsigned int i = 0 ; i < nElem ; i++)
    {
        for(unsigned int j = 1 ; j < sElem+1 ; j++)
        {
            *(q++) = *(p + (i * sElem + (sElem - j)));
        }
    }

    memmove(p,res,sElem * nElem);

    free(res);
}

void ea2h(void * p,unsigned long sElem,unsigned nElem,endian arb)
{
    if(arb == bigEndian)
        eb2h(p,sElem,nElem);
    if(arb == littleEndian)
        el2h(p,sElem,nElem);
}

static inline int frint(FILE * fp,endian file)
{
    int t;
    fread(&t,sizeof(t),1,fp);
    ea2h(&t,sizeof(t),1,file);

    return t;
}

Also, i know frint() works because it's worked everywhere else in the file.

I'm afraid the problem is more difficult than that

Daniel Staudigel
Quote this message in a reply
gwihlidal
Unregistered
 
Post: #7
Here's what I do when I have endian issues that aren't working, this way works best

Get your PC buddy to output all info about the BSP map, num vertices, faces, leaves, brushes, planes, vis clusters, vis cluster size, etc... Saving it to a text file will work

You do the same with the same map, and compare your results. If things are wrong in one spot, you can pinpoint exactly which value is causing the grief.

Maybe try using the Core Foundation functions to test things, just in case. That code you have looks about right but sometimes things can be overlooked, at least you can be sure the Apple one works =)

Instead of saving a text file, it may be easier to run in windowed mode with the debugger open and view the locals window to see the values of all variables currently allocated?

Hopefully this helps ya out =)

~Graham
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Quake II Evolved and XCode ajmas 1 4,686 Aug 21, 2005 09:10 PM
Last Post: OneSadCookie
  Fruitz of dojo Mac OS X Quake port... maaaaark 6 4,433 Mar 11, 2005 07:25 PM
Last Post: PowerMacX
  Simple Quake Variant? deekpyro 8 4,699 Mar 17, 2003 12:21 PM
Last Post: burden