Actual CBuffer code

Actual CBuffer code

Post by Malcolm Phillip » Tue, 22 Feb 2000 04:00:00



Here is a posting of code for a CBuffer, which I had promised earlier.
Sorry, if more than 1 copy of this gets through, my news server is reporting
errors.
Malc.

//CBuffer.cpp
/*

This code is to show how to program a CBuffer. Description of a C-Buffer is
at http://www.flipcode.com/
Also contained is a simple, fast, fixed-chunk-size memory allocation scheme.
It has been taken form existing working code, and displayed in a slightly
more readable form.
I don't do range-checking on either array, so either add that, or make sure
they are large enough.
If you wish me to explain parts furthur, you may contact me using the
address above.
Feel free to copy it and modify it for your own use.
Original version written to compile under Symantec C++ v8.6 for Macintosh.
I would be interested to know if you make any major improvements.
Credit given in a final program would be appreciated, but not mandatory.
Just please don't try to pass this off as your own work.
*/

#include "CBuffer.h"

//This is the vertical size of the display
//The array should really be dynamically allocated instead. i.e. as a
parameter to initCBuffer.
#define Vsize 480

//Set these to point to your scanline routines, modify as necessary.
void (*Scanline)(char *currPixel, textureRec *t, int width,
 float dudx, float dvdx, float dzdx, float u, float v, float z);
void (*ScanlineOverDraw)(char *currPixel, textureRec *t, int width,
 float dudx, float dvdx, float dzdx, float u, float v, float z);

//This struct stores the covered areas
struct span {
 span *next;
 int start, finish;

Quote:};

//The C-Buffer Storage
//Please note (Vsize<<4) is only a crude approximation of the space
required,
//and will likely be insufficient for your needs.
span Cbuf[(long)(Vsize<<4)];
//Ptr to the list of deleted nodes
span *free;

//Call before anything else
void initCBuffer()
{
 //Initialise to array. The first 'Vsize' entries contain only
 //pointers to the spans for that row, not actual span data.
 for (int i=Vsize;i>=0;i--) Cbuf[i].next = NULL;
 free = &Cbuf[Vsize];

Quote:}

//Call this instead of your usual Scanline function.
//Sort surfaces front to back first.
void CBufferScanline(char *rowStart, textureRec *t, int x, int y,
 int width, float dudx, float dvdx, float dzdx, float u, float v, float z)
{
 //s & f are temporary start and finish points.
 int s = x, f = x+width;
 //'extend' is the span that will be lengthened.
 //'prev' stores the previous span so we can hook up another to it.
 span *extend = NULL, *prev = &Cbuf[y], *curr = prev->next;

 while ((curr != NULL) && (width > 0)) {

  //Simple case: new span is to the left of all others so far
  if (x+width < curr->start) break;
  span *after = curr->next;
  //Middle case1: new span is clipped by another span on the right
  if (x < curr->start) {
   width=curr->start-x;
   //Calculate the starting pixel address, this should really be in the
scanline function I guess.
   char *currPixel = rowStart + x;
   Scanline(currPixel, t, width, dudx, dvdx, dzdx, u, v, z);
   float trim = curr->finish-x;
   //Adjust the texture coords
   u+=dudx*trim; v+=dvdx*trim; z+=dzdx*trim;

   x = curr->finish;
   if (f < x) f = x;
   width = f - x;
   if (extend != NULL) {

    //Dispose node 'curr' by prepending it to 'free'.
    curr->next = free;
    free = curr;

   } else
    extend = curr;
  //Middle case2: new span is clipped by another span on the left
  } else if (x <= curr->finish) {
   s = curr->start;
   float trim = curr->finish-x;
   //Adjust the texture coords
   u+=dudx*trim; v+=dvdx*trim; z+=dzdx*trim;

   x = curr->finish;
   if (f < x) f = x;
   width = f - x;
   extend = curr;
  } else
  //Final case: which ever case I have left
   prev = curr;
  curr = after;

 }
 span *newspan;
 if (width > 0) {
  //Draw any remaining span
  char *currPixel = rowStart + x;
  Scanline(currPixel, t, width, dudx, dvdx, dzdx, u, v, z);
 }
 if (extend != NULL) {
  newspan = extend;
  newspan->next = curr;
 } else {

  //Memory allocation of node 'newspan'
  newspan = free;
  if (free->next != NULL)
   //Case 1: Free list has 2 or more items so set it to the next one
   free = free->next;
  else {
   //Case 2: Free list has only 1 item, so the next free item is at free++
   //and all of memory before 'free' is in use.
   free++;
   free->next = NULL;
  }

  prev->next = newspan;
  newspan->next = curr;
 }
 newspan->start = s;
 newspan->finish = f;

Quote:}

//My own invention below here, used only for spans that must be drawn in the
painter's algo way,
//e.g. semi-transparent surfaces. You may have no use for this.
typedef struct span2 {
 char *currPixel;
 textureRec *t;
 int width;
 float dudx, dvdx, dzdx, u, v, z;
Quote:};

//This is a stack to store any scanlines to be overdrawn later
//Please note (Vsize<<6) is only a crude approximation of the space
required,
//and will likely be insufficient for your needs.
span2 OverDrawSpans[(long)(Vsize<<6)];
long currSpan=0;

//This simply traverses the list, trims the scanline,
//and pushes it onto the stack to be drawn later.
void CBufferOverDraw(char *rowStart, textureRec *t, int x, int y,
 int width, float dudx, float dvdx, float dzdx, float u, float v, float z)
{
 int f = x+width;
 span *extend = NULL, *prev = &Cbuf[y], *curr = prev->next;

 while (curr != NULL) {

  if (x+width < curr->start) break;
  if (x < curr->start) {
   char *currPixel = rowStart + x;
   span2 newSpan2={currPixel, t, curr->start-x, dudx, dvdx, dzdx, u, v, z};
   OverDrawSpans[currSpan++]=newSpan2;
  }
  if (f <= curr->finish) return;
  if (x <= curr->finish) {
   float trim = curr->finish-x;
   //Adjust the texture coords
   u+=dudx*trim; v+=dvdx*trim; z+=dzdx*trim;

   x = curr->finish;
   width = f - x;
  }
  curr = curr->next;

 }
 if (width > 0) {
  char *currPixel = rowStart + x;
  span2 newSpan2={currPixel, t, width, dudx, dvdx, dzdx, u, v, z};
  OverDrawSpans[currSpan++]=newSpan2;
 }

Quote:}

void RenderOverDrawSpans()
{
 //Draw those remaining spans in reverse order.
 for (long i=currSpan-1; i>=0; i--) {
  span2 *sp=&OverDrawSpans[i];
  ScanlineOverDraw(sp->currPixel, sp->t, sp->width, sp->dudx, sp->dvdx,
sp->dzdx, sp->u, sp->v, sp->z);
 }
 currSpan=0;

Quote:}

//CBuffer.h
#ifndef CBuffer
#define CBuffer

//Just use your own struct or whatever in place of this.
typedef struct textureRec {
 char *buf;
 int width, height;

Quote:};

//Set these to point to your scanline routines, modify as necessary.
extern void (*Scanline)(char *currPixel, textureRec *t, int width,
 float dvdx, float dudx, float dzdx, float v, float u, float z);
extern void (*ScanlineOverDraw)(char *currPixel, textureRec *t, int width,
 float dvdx, float dudx, float dzdx, float v, float u, float z);

//Call before anything else
void initCBuffer();

//Call this instead of your  usual Scanline function.
//Sort surfaces front to back first.
void CBufferScanline(char *rowStart, textureRec*t, int x, int y, int width,
float dvdx, float dudx, float dzdx, float v, float u, float z);

//My own invention, use only for spans that must be drawn in the painter's
algo way,
//e.g. semi-transparent surfaces. You may have no use for this.
void CBufferOverDraw(char *rowStart, textureRec*t, int x, int y, int width,
float dvdx, float dudx, float dzdx, float v, float u, float z);

//Call this after all calls to CBufferOverDraw.
void RenderOverDrawSpans();

#endif