Skip to content

Instantly share code, notes, and snippets.

@graphitemaster
Created September 29, 2012 17:49
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save graphitemaster/3804702 to your computer and use it in GitHub Desktop.
Save graphitemaster/3804702 to your computer and use it in GitHub Desktop.
#include "gmrast.h"
#include <math.h> /* acos, fabs, sqrtf */
#include <string.h> /* memset, memcpy */
#include <stdlib.h> /* malloc, calloc, free */
/*
* TODO:
* Fix up the rasterizer to use GM_* functions below
* for memory managment.
*/
#if 0
#define GM_MALLOC malloc
#define GM_CALLOC calloc
#define GM_FREE free
#endif
/*
* These need to be changed when the rasterizer is modified to allow
* more of X or is optimized to be capable of more X.
*/
#define GM_RENDER_PIPELINE_SIZE 4
#define GM_TEXTURE_LAYER_SIZE 2
#define GM_MAX_MIPMAPLEVELS 32
#define GM_MAX_LIGHT_COUNT 1
#define GM_GET_TEXEL(C,O) \
do { \
(C)->r = PipelineTexture[PipelineTextureLayer]->image[PipelineTextureMipMapLevel][(O)+0]; \
(C)->g = PipelineTexture[PipelineTextureLayer]->image[PipelineTextureMipMapLevel][(O)+1]; \
(C)->b = PipelineTexture[PipelineTextureLayer]->image[PipelineTextureMipMapLevel][(O)+2]; \
(C)->a = PipelineTexture[PipelineTextureLayer]->image[PipelineTextureMipMapLevel][(O)+3]; \
} while (0)
/*
* TODO: optimize this pile of shit
* Batch compute?
* SIMD?
*/
#define GM_COMPUTE_TRIANGLE_FACE_SIZE(A,B,C) \
(0.5 * (A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y)))
#define ALIGN(X) __attribute__((__aligned__ ((X))))
/*
* Aligned for future SIMD optimization?
* Any sane compiler should auto-vectorize these.
* We could very well force vectorize these using
* __attribute__((__vector__(size))).
*
* But what about other compiler support?
* Screw it, intrinsics are the only sure way.
*/
typedef struct { GMfloat x,y; } GMVector2f;
typedef struct { GMfloat x,y,z; } GMVector3f;
typedef struct { GMfloat x,y,z,w; } GMVector4f;
typedef struct { GMint x,y; } GMVector2i;
typedef struct { GMint x,y,z; } GMVector3i;
typedef struct { GMint x,y,z,w; } GMVector4i;
typedef struct { GMfloat m[9]; } GMMatrix3f;
typedef struct { GMfloat m[16]; } GMMatrix4f;
/*
* None of the following structures need alignment unless
* for some odd reason.
*/
typedef struct { _GMcolor_t r,g,b,a; } GMColor4i;
typedef struct { GMfloat r,g,b,a; } GMColor4f;
typedef struct {
GMint u;
GMint v;
} GMMappingGen;
typedef struct {
GMint width;
GMint height;
/* ([width*height*4]*mipcount) */
_GMpixel_t **image;
GMint mipcount;
} GMTexture2D;
typedef struct {
GMfloat z,u,v;
GMColor4i c;
} GMPolygonSide;
typedef struct {
GMVector4f pos; /* coordinate */
GMVector3f nrm; /* normal */
GMColor4i clr; /* color */
/* texture mapping coordiantes */
GMVector2f map[GM_TEXTURE_LAYER_SIZE];
} GMVertex;
typedef struct {
GMenum type;
GMVector3f dir;
GMMatrix4f mat;
GMColor4i diffuse;
GMColor4i ambient;
} GMLight;
/*
* Pipeline blend is an indirect call. BTB's seem to optimize it
* this shouldn't be that expensive as long as the BlendFunc isn't
* changed so much.
*/
#define GM_BLENDPROC_PARAMS \
_GMcolor_t *srcR, _GMcolor_t *srcG, _GMcolor_t *srcB, _GMcolor_t *srcA, \
_GMpixel_t *dstR, _GMpixel_t *dstG, _GMpixel_t *dstB, _GMpixel_t *dstA
typedef void (*GMBlendProc)(GM_BLENDPROC_PARAMS);
/*
* Defined before declared for const initialization of the Pipeline
* globals.
*/
void GMBlendNormal(GM_BLENDPROC_PARAMS);
/*
* These are all the globals which build up the rasterizer pipeline
* these should all stay within here.
*/
GMVertex *PipelineVertex [GM_RENDER_PIPELINE_SIZE] = {0};
GMVertex *PipelineMemVertex[GM_RENDER_PIPELINE_SIZE] = {0};
GMTexture2D *PipelineTexture [GM_TEXTURE_LAYER_SIZE] = {0};
GMLight *PipelineLight [GM_MAX_LIGHT_COUNT] = {0};
GMint PipelineRenderIndex = 0;
GMint PipelineRenderType = 0;
GMint PipelineMatrixMode = GM_MODELVIEW_MATRIX;
GMint PipelineTextureLayer = 0;
GMint PipelineMaxTextureLayer = 1;
GMint PipelineUpdateVerticesMatrix = GM_TRUE;
GMint PipelineRenderStatus[GM_STATUS_COUNT] = { GM_FALSE };
GMint PipelinePixelRect [4] = {0,0,0,0};
GMint PipelineViewportRect[4] = {0,0,0,0};
GMint PipelineScissorRect [4] = {0,0,0,0};
GMfloat PipelinePlaneNear = 1.0f;
GMfloat PipelineRangeNear = 1.0 / 1.0f;
GMfloat PipelinePlaneFar = 100.0f;
GMfloat PipelineRangeFar = 1.0 / 100.0f;
GMfloat PipelineFieldOfView = 640 / 2;
GMMatrix4f PipelineRenderMatrix[GM_MATRIX_COUNT] = {0};
GMint PipelineTextureID = 0;
GMint PipelineTextureMipMapLevel = 0;
_GMpixel_t *PipelinePixelBuffer = 0;
GMfloat *PipelineDepthBuffer = 0;
GMint PipelineScreenWidth = 0;
GMint PipelineScreenHeight = 0;
GMint PipelineDepthBufferSize = 0;
GMint PipelinePixelBufferSize = 0;
GMBlendProc PipelineBlendFunc = &GMBlendNormal;
GMint PipelineRenderFace = GM_RENDER_FRONT;
GMenum PipelineLastError = 0;
GMColor4i PipelineClearColor;
GMMappingGen PipelineMappingGenType[GM_TEXTURE_LAYER_SIZE];
/*
* These annoying piles of shits need to be implemented now.
* all the internal utility functions.
*/
GMINLINE void GMClampf(GMfloat *val, const GMfloat min, const GMfloat max) {
*val = (*val < min) ? min : (*val > max) ? max : *val;
}
GMINLINE void GMClampi(GMint *val, const GMint min, const GMint max) {
*val = (*val < min) ? min : (*val > max) ? max : *val;
}
GMINLINE GMint GMPixInside2D(const GMint x, const GMint y) {
return x >= PipelinePixelRect[0] &&
y >= PipelinePixelRect[1] &&
x < PipelinePixelRect[2] &&
y < PipelinePixelRect[3];
}
GMINLINE GMint GMPixInside3D(const GMint x, const GMint y, const GMfloat z, const GMint i) {
return (x >= PipelinePixelRect[0] &&
y >= PipelinePixelRect[1] &&
x < PipelinePixelRect[2] &&
y < PipelinePixelRect[3]) &&
(!PipelineRenderStatus[GM_DEPTH_TEST] ||
(x < PipelineRangeNear &&
z > PipelineRangeFar &&
z >= PipelineDepthBuffer[i]));
}
GMINLINE GMint GMCheckPlotDepth(const GMint i, const GMfloat z) {
if (PipelineRenderStatus[GM_DEPTH_TEST] &&
!(z < PipelineRangeNear &&
z > PipelineRangeFar &&
z >= PipelineDepthBuffer[i] - 1.0e-6))
return 0;
return 1;
}
GMINLINE void GMPlotPixel3f(GMint i, const _GMcolor_t r, const _GMcolor_t g, const _GMcolor_t b) {
i <<= 2;
PipelinePixelBuffer[i+0] = r;
PipelinePixelBuffer[i+1] = g;
PipelinePixelBuffer[i+2] = b;
}
GMINLINE void GMPlotPixel4f(GMint i, const _GMcolor_t r, const _GMcolor_t g, const _GMcolor_t b, const _GMcolor_t a) {
i <<= 2;
PipelinePixelBuffer[i+0] = r * a / 255 + PipelinePixelBuffer[i+0] * (255 - a) / 255;
PipelinePixelBuffer[i+1] = g * a / 255 + PipelinePixelBuffer[i+1] * (255 - a) / 255;
PipelinePixelBuffer[i+2] = b * a / 255 + PipelinePixelBuffer[i+2] * (255 - a) / 255;
}
GMINLINE void GMPlotDepth(const GMint i, const GMfloat z) {
PipelineDepthBuffer[i] = z;
}
GMINLINE void GMPlotIndexed(GMint i, const GMfloat z, _GMcolor_t r, _GMcolor_t g, _GMcolor_t b, _GMcolor_t a) {
if (a == 0)
return;
PipelineDepthBuffer[i] = z; /* depth */
i <<= 2;
if (PipelineRenderStatus[GM_BLEND_TEST]) {
PipelineBlendFunc (
&r,&g,&b,&a,
&PipelinePixelBuffer[i+0],
&PipelinePixelBuffer[i+1],
&PipelinePixelBuffer[i+2],
&PipelinePixelBuffer[i+3]
);
}
/* plot the pixel */
if (a == 255 || !PipelineRenderStatus[GM_BLEND]) {
PipelinePixelBuffer[i+0] = r;
PipelinePixelBuffer[i+1] = g;
PipelinePixelBuffer[i+2] = b;
} else {
PipelinePixelBuffer[i+0] = r * a / 255 + PipelinePixelBuffer[i+0] * (255 - a) / 255;
PipelinePixelBuffer[i+1] = g * a / 255 + PipelinePixelBuffer[i+1] * (255 - a) / 255;
PipelinePixelBuffer[i+2] = b * a / 255 + PipelinePixelBuffer[i+2] * (255 - a) / 255;
}
}
GMINLINE void GMPlotLocated(GMint x, GMint y, GMfloat z, _GMcolor_t r, _GMcolor_t g, _GMcolor_t b, _GMcolor_t a) {
if (x < PipelinePixelRect[0] || y < PipelinePixelRect[1] ||
x >= PipelinePixelRect[2] || y >= PipelinePixelRect[3])
return;
GMPlotIndexed(y * PipelineScreenWidth + x, z, r, g, b, a);
}
GMINLINE GMint GMTriangleFaceVisible() {
if (PipelineRenderFace != GM_RENDER_BOTH) {
GMfloat d =
(PipelineVertex[1]->pos.x - PipelineVertex[0]->pos.x) *
(PipelineVertex[2]->pos.y - PipelineVertex[0]->pos.y) -
(PipelineVertex[1]->pos.y - PipelineVertex[0]->pos.y) *
(PipelineVertex[2]->pos.x - PipelineVertex[0]->pos.x);
if ((PipelineRenderFace == GM_RENDER_FRONT && d <= 0.0f) ||
(PipelineRenderFace == GM_RENDER_BACK && d >= 0.0f))
return 0;
}
return 1;
}
GMINLINE GMint GMTriangleVisible() {
GMfloat far = 1.0 / PipelineRangeFar;
return !((PipelineVertex[0]->pos.x < 0 &&
PipelineVertex[1]->pos.x < 0 &&
PipelineVertex[2]->pos.x < 0) ||
(PipelineVertex[0]->pos.x > PipelineScreenWidth &&
PipelineVertex[1]->pos.x > PipelineScreenWidth &&
PipelineVertex[2]->pos.x > PipelineScreenWidth) ||
(PipelineVertex[0]->pos.y < 0 &&
PipelineVertex[1]->pos.y < 0 &&
PipelineVertex[2]->pos.y < 0) ||
(PipelineVertex[0]->pos.y > PipelineScreenHeight &&
PipelineVertex[1]->pos.y > PipelineScreenHeight &&
PipelineVertex[2]->pos.y > PipelineScreenHeight) ||
(PipelineVertex[0]->pos.z > far &&
PipelineVertex[1]->pos.z > far &&
PipelineVertex[2]->pos.z > far));
}
GMINLINE void GMGetTriangleArea(GMint *beg, GMint *end) {
#define MIN(A, B, C) \
( A <= B && A <= C ? A : ( B <= A && B <= C ? B : C ) )
#define MAX(A, B, C) \
( A >= B && A >= C ? A : ( B >= A && B >= C ? B : C ) )
*beg = (GMint)MIN(PipelineVertex[0]->pos.y, PipelineVertex[1]->pos.y, PipelineVertex[2]->pos.y);
*end = (GMint)MAX(PipelineVertex[0]->pos.y, PipelineVertex[1]->pos.y, PipelineVertex[2]->pos.y);
/* clamp */
if (*beg < PipelinePixelRect[1]) *beg = PipelinePixelRect[1];
if (*end > PipelinePixelRect[3]) *end = PipelinePixelRect[3];
}
/*
* Vector/Matrix manipulation (mult, normalize, etc)
* TODO:
* Optimize these with SIMD?
*/
GMINLINE void GMMultMatrix3f(const GMMatrix4f *mat, GMVector3f *vec) {
GMfloat \
x=vec->x*mat->m[0]+vec->y*mat->m[4]+vec->z*mat->m[8] +mat->m[12],
y=vec->x*mat->m[1]+vec->y*mat->m[5]+vec->z*mat->m[9] +mat->m[13],
z=vec->x*mat->m[2]+vec->y*mat->m[6]+vec->z*mat->m[10]+mat->m[14];
vec->x = x;
vec->y = y;
vec->z = z;
}
GMINLINE void GMMultMatrix4f(const GMMatrix4f *mat, GMVector4f *vec) {
GMfloat \
x=vec->x*mat->m[0]+vec->y*mat->m[4]+vec->z*mat->m[8] +vec->w*mat->m[12],
y=vec->x*mat->m[1]+vec->y*mat->m[5]+vec->z*mat->m[9] +vec->w*mat->m[13],
z=vec->x*mat->m[2]+vec->y*mat->m[6]+vec->z*mat->m[10]+vec->w*mat->m[14],
w=vec->x*mat->m[3]+vec->y*mat->m[7]+vec->z*mat->m[11]+vec->w*mat->m[15];
vec->x = x;
vec->y = y;
vec->z = z;
vec->w = w;
}
GMINLINE void GMNormalizeVec(GMVector3f *vec) {
const GMfloat l = 1.0 / sqrtf(
vec->x*vec->x +
vec->y*vec->y +
vec->z*vec->z
);
vec->x *= l;
vec->y *= l;
vec->z *= l;
}
GMINLINE void GMProject2DTo3D(GMfloat *x, GMfloat *y) {
*x = (GMfloat)((GMfloat)( *x-PipelineScreenWidth /2)/(PipelineScreenWidth /2));
*y = (GMfloat)((GMfloat)(-*y+PipelineScreenHeight/2)/(PipelineScreenHeight/2));
}
GMINLINE void GMProject3DTo2D(GMVector4f *vec) {
const GMfloat z = vec->z;
GMMultMatrix4f(&PipelineRenderMatrix[GM_PROJECTION_MATRIX], vec);
const GMfloat w = 1.0f/vec->w;
vec->x = w*(vec->x*PipelineRenderMatrix[GM_PROJECTION_MATRIX].m[0] +
vec->w*PipelineRenderMatrix[GM_PROJECTION_MATRIX].m[12]);
vec->y = w*(vec->y*PipelineRenderMatrix[GM_PROJECTION_MATRIX].m[5] +
vec->w*PipelineRenderMatrix[GM_PROJECTION_MATRIX].m[13]);
vec->z = z;
}
GMINLINE void GMSwapVertex(GMVertex **v1, GMVertex **v2) {
GMVertex *t = *v1;
*v1 = *v2,*v2 = t;
}
GMINLINE GMfloat GMComputeAngle(const GMVector4f *v1, const GMVector4f *v2) {
return acos((v1->x*v2->x+v1->y*v2->y+v1->z*v2->z)/(
sqrtf(v1->x*v1->x+v1->y*v1->y+v1->z*v1->z)*
sqrtf(v2->x*v2->x+v2->y*v2->y+v2->z*v2->z)))*180.0f/M_PI;
}
GMINLINE void GMInterpolateColors(GMColor4i *c, const GMColor4i *a, const GMColor4i *b, const GMfloat *t) {
c->r = a->r + (GMint)((GMfloat)(b->r-a->r) * *t);
c->g = a->g + (GMint)((GMfloat)(b->g-a->g) * *t);
c->b = a->b + (GMint)((GMfloat)(b->b-a->b) * *t);
c->a = a->a + (GMint)((GMfloat)(b->a-a->a) * *t);
}
GMINLINE void GMClampTexCoords(GMint w, GMint h, GMfloat *u, GMfloat *v) {
*u -= (GMint)(*u/w)*w;
*v -= (GMint)(*v/h)*h;
if (*u < 0) *u = w + *u;
if (*v < 0) *v = h + *v;
}
GMINLINE void GMGetTexturePixelLinear(GMColor4i *c, GMVector2f *map) {
GMClampTexCoords(
PipelineTexture[PipelineTextureLayer]->width,
PipelineTexture[PipelineTextureLayer]->height,
&map->x,
&map->y
);
GMint i = (
(((GMint)map->y) >> PipelineTextureMipMapLevel) * (PipelineTexture[PipelineTextureLayer]->width >> PipelineTextureMipMapLevel) +
(((GMint)map->x) >> PipelineTextureMipMapLevel)
) << 2;
GM_GET_TEXEL(c, i);
}
GMINLINE void GMGetTexturePixelSmooth(GMColor4i *p, GMVector2f *m1) {
GMint i;
/* pointer version for consistency */
GMVector2f e;
GMVector2f *m2=&e;
GMClampTexCoords (
PipelineTexture[PipelineTextureLayer]->width,
PipelineTexture[PipelineTextureLayer]->height,
&m1->x,
&m1->y
);
m2->x = m1->x + (1 << PipelineTextureMipMapLevel);
m2->y = m1->y + (1 << PipelineTextureMipMapLevel);
if (m2->x > PipelineTexture[PipelineTextureLayer]->width) m2->x = 0;
if (m2->y > PipelineTexture[PipelineTextureLayer]->height) m2->y = 0;
GMColor4i c[4];
GMint o[2] = {
(((GMint)m2->y)>>PipelineTextureMipMapLevel) *
(PipelineTexture[PipelineTextureLayer]->width >> PipelineTextureMipMapLevel),
(((GMint)m1->y)>>PipelineTextureMipMapLevel) *
(PipelineTexture[PipelineTextureLayer]->width >> PipelineTextureMipMapLevel)
};
GM_GET_TEXEL(&c[0],(o[0]+(((GMint)m1->x)>>PipelineTextureMipMapLevel))<<2);
GM_GET_TEXEL(&c[1],(o[0]+(((GMint)m2->x)>>PipelineTextureMipMapLevel))<<2);
GM_GET_TEXEL(&c[2],(o[1]+(((GMint)m2->x)>>PipelineTextureMipMapLevel))<<2);
GM_GET_TEXEL(&c[3],(o[1]+(((GMint)m1->x)>>PipelineTextureMipMapLevel))<<2);
/* compute mix */
m1->x /= (GMfloat)(1 << PipelineTextureMipMapLevel);
m1->y /= (GMfloat)(1 << PipelineTextureMipMapLevel);
GMint ratiox = (GMint)((m1->x - (GMint)m1->x) * 255);
GMint ratioy = (GMint)((m1->y - (GMint)m1->y) * 255);
GMint iatiox = 255 - ratiox;
GMint iatioy = 255 - ratioy;
#define INTERPOLATE(E) \
p->E = (c[0].E*iatiox/255+c[1].E*ratiox/255)*iatioy/255 \
+ (c[3].E*iatiox/255+c[2].E*ratiox/255)*ratioy/255
INTERPOLATE(r), INTERPOLATE(g);
INTERPOLATE(b), INTERPOLATE(a);
#undef INTERPOLATE
}
GMINLINE GMint GMCheckColorEquality(const GMColor4i *c1, const GMColor4i *c2, const GMColor4i *c3) {
if (c1->r != c2->r || c1->r != c3->r ||
c1->g != c2->g || c1->g != c3->g ||
c1->b != c2->b || c1->b != c3->b ||
c1->a != c2->a || c1->a != c3->a)
return 0;
return 1;
}
GMINLINE void GMGetFreeVisibleVertexIndex(GMint i, GMint *v) {
switch (i) {
case 0: v[0]=1,v[1]=2; break;
case 1: v[0]=2,v[1]=0; break;
case 2: v[0]=0,v[1]=1; break;
}
}
GMINLINE GMint GMGetFreeVisibleVertexIndexi(GMint i, GMint v) {
#define BLOCK(A,B) { \
switch (v) { \
case A:{return B;} \
case B:{return A;} \
} break; \
}
switch (i) {
case 0: BLOCK(1,2)
case 1: BLOCK(0,2)
case 2: BLOCK(0,1)
}
#undef BLOCK
return -1;
}
GMINLINE GMint GMGetVertexClippingPlane(GMVertex **a, const GMVertex *b) {
GMfloat fct = (b->pos.z - (*a)->pos.z) / (b->pos.z - PipelinePlaneNear);
(*a)->pos.x = b->pos.x + ((*a)->pos.y - b->pos.y) * fct;
(*a)->pos.y = b->pos.y + ((*a)->pos.x - b->pos.x) * fct;
(*a)->map->x = b->map->x + ((*a)->map->x - b->map->x) * fct;
(*a)->map->y = b->map->y + ((*a)->map->y - b->map->y) * fct;
}
GMINLINE void GMComputeMipMapLevel(GMvoid) {
GMfloat size = fabs(GM_COMPUTE_TRIANGLE_FACE_SIZE(
PipelineVertex[0]->pos,
PipelineVertex[1]->pos,
PipelineVertex[2]->pos
));
GMVector2f map[3];
map[0].x = PipelineVertex[0]->map[PipelineTextureLayer].x * PipelineTexture[PipelineTextureLayer]->width;
map[0].y = PipelineVertex[0]->map[PipelineTextureLayer].y * PipelineTexture[PipelineTextureLayer]->height;
map[1].x = PipelineVertex[1]->map[PipelineTextureLayer].x * PipelineTexture[PipelineTextureLayer]->width;
map[1].y = PipelineVertex[1]->map[PipelineTextureLayer].y * PipelineTexture[PipelineTextureLayer]->height;
map[2].x = PipelineVertex[2]->map[PipelineTextureLayer].x * PipelineTexture[PipelineTextureLayer]->width;
map[2].y = PipelineVertex[2]->map[PipelineTextureLayer].y * PipelineTexture[PipelineTextureLayer]->height;
/* clamp */
PipelineTextureMipMapLevel = (GMint)(size/fabs(GM_COMPUTE_TRIANGLE_FACE_SIZE(map[0], map[1], map[2])));
GMClampi (
&PipelineTextureMipMapLevel,
0,
1024
);
/* TODO: optimize */
GMint c = 0;
while ((PipelineTextureMipMapLevel >> 2) > 0)
PipelineTextureMipMapLevel >>=2, ++c;
PipelineTextureMipMapLevel = c;
/* clamp */
GMClampi(
&PipelineTextureMipMapLevel,
0,
PipelineTexture[PipelineTextureLayer]->mipcount - 1
);
}
GMINLINE void GMUpdateMappingGen(GMint type, GMfloat pos, GMfloat nml, GMfloat *map) {
switch (type) {
case GM_OBJECT_LINEAR: break;
case GM_EYE_LINEAR: *map = pos; break;
case GM_SPHERE_MAP: *map = nml / 2 + 0.5; break;
}
}
/*
* The actual rasterizer (renderer) is implemented from here down.
* These functions are internal. API functions are marked as such
* with lowercase gm.
*/
void GMBlendNormal(GM_BLENDPROC_PARAMS) { }
void GMBlendBright(GM_BLENDPROC_PARAMS) {
*srcR = *dstR + (*srcR * *srcA / 255);
*srcG = *dstG + (*srcG * *srcA / 255);
*srcB = *dstB + (*srcB * *srcA / 255);
GMClampi(srcR, 0, 255);
GMClampi(srcG, 0, 255);
GMClampi(srcB, 0, 255);
}
void GMBlendDark (GM_BLENDPROC_PARAMS) {
*srcR = *dstR - 255 + (*srcR * *srcA / 255);
*srcG = *dstG - 255 + (*srcG * *srcA / 255);
*srcB = *dstB - 255 + (*srcB * *srcA / 255);
GMClampi(srcR, 0, 255);
GMClampi(srcG, 0, 255);
GMClampi(srcB, 0, 255);
}
void GMBlendLight(GM_BLENDPROC_PARAMS) {
*srcR = *dstR * *srcR / 255;
*srcG = *dstG * *srcG / 255;
*srcB = *dstB * *srcB / 255;
}
static void GMUpdatePixelRect(GMvoid) {
if (PipelineRenderStatus[GM_SCISSOR_TEST]) {
PipelinePixelRect[0]=(PipelineViewportRect[0]<PipelineScissorRect[0]?PipelineViewportRect[0]:PipelineScissorRect[0]);
PipelinePixelRect[1]=(PipelineViewportRect[1]<PipelineScissorRect[1]?PipelineViewportRect[1]:PipelineScissorRect[1]);
PipelinePixelRect[2]=(PipelineViewportRect[2]<PipelineScissorRect[2]?PipelineViewportRect[2]:PipelineScissorRect[2]);
PipelinePixelRect[3]=(PipelineViewportRect[3]<PipelineScissorRect[3]?PipelineViewportRect[3]:PipelineScissorRect[3]);
} else {
PipelinePixelRect[0] = PipelineViewportRect[0];
PipelinePixelRect[1] = PipelineViewportRect[1];
PipelinePixelRect[2] = PipelineViewportRect[2];
PipelinePixelRect[3] = PipelineViewportRect[3];
}
GMClampi(&PipelinePixelRect[0], 0, PipelineScreenWidth);
GMClampi(&PipelinePixelRect[1], 0, PipelineScreenHeight);
GMClampi(&PipelinePixelRect[2], 0, PipelineScreenWidth);
GMClampi(&PipelinePixelRect[3], 0, PipelineScreenHeight);
/* silly swap */
GMint t;
if (PipelinePixelRect[0] > PipelinePixelRect[2]) {
t = PipelinePixelRect[0];
PipelinePixelRect[0] = PipelinePixelRect[2];
PipelinePixelRect[2] = t;
}
if (PipelinePixelRect[1] > PipelinePixelRect[3]) {
t = PipelinePixelRect[1];
PipelinePixelRect[1] = PipelinePixelRect[3];
PipelinePixelRect[3] = t;
}
}
/* TODO: bitwise hack me? */
static GMint GMRound2(GMint v) {
GMint i;
for (i=1; i < v; i <<= 1)
if (i - v <= v - i / 2)
return i;
return i / 2;
}
static void GMClampTextureSize(GMTexture2D *tex, GMuint lvl) {
GMint w = GMRound2(tex->width);
GMint h = GMRound2(tex->height);
/* NPOT not allowed */
if (w != tex->width || h != tex->height) {
GMvoid *i = (GMvoid*)tex->image[lvl];
/* TODO: this */
#if 0
gmScaleImage2D(
i,
tex->width,
tex->height,
w,
h,
4,
GM_UNSIGNED_BYTE
);
#endif
tex->image[lvl] = (_GMpixel_t*)i;
tex->width = w;
tex->height = h;
}
}
/*
* The actual exported API now begins.
*/
GMAPI const GMbyte *gmGetString(GMenum name) {
switch (name) {
case GM_VENDOR: return "Dale Weiler's Renderer";
case GM_RENDERER: return "gmrast (Software Renderer)";
case GM_VERSION: return "0.1";
}
return "";
}
GMAPI GMenum gmGetError(GMvoid) {
return PipelineLastError;
}
GMAPI void gmGetValue(GMenum type, GMvoid *d) {
#define CASE(T,X,V) \
case X: { \
T *i = (T*)d; \
(V); \
break; \
}
#define COPY(T,X,V) \
CASE(T,X,(i[0]=V[0],i[1]=V[1],i[2]=V[2],i[3]=V[3]))
switch(type) {
COPY(GMint, GM_SCISSOR, PipelineScissorRect );
COPY(GMint, GM_VIEWPORT, PipelineViewportRect);
CASE(GMfloat, GM_PERSPECTIVE,(
i[0] = PipelinePlaneNear,
i[1] = PipelinePlaneFar,
i[2] = PipelineFieldOfView
));
}
#undef CASE
#undef COPY
}
/* states/status (set/get/on/off) */
GMAPI void gmEnable(GMenum thing) {
PipelineRenderStatus[thing] = GM_TRUE;
}
GMAPI void gmDisable(GMenum thing) {
PipelineRenderStatus[thing] = GM_FALSE;
}
GMAPI void gmSetStatus(GMenum thing, GMint test) {
PipelineRenderStatus[thing] = test;
}
GMAPI GMint gmGetStatus(GMenum thing) {
return PipelineRenderStatus[thing];
}
GMAPI void gmScissor(GMint x, GMint y, GMsizei w, GMsizei h) {
PipelineScissorRect[0] = x;
PipelineScissorRect[1] = y;
PipelineScissorRect[2] = x + w;
PipelineScissorRect[3] = y + h;
GMUpdatePixelRect();
}
GMAPI void gmViewport(GMint x, GMint y, GMsizei w, GMsizei h) {
PipelineViewportRect[0] = x;
PipelineViewportRect[1] = y;
PipelineViewportRect[2] = x + w;
PipelineViewportRect[3] = y + h;
GMUpdatePixelRect();
}
GMAPI void gmPerspective(GMfloat near, GMfloat far, GMfloat fovy) {
PipelinePlaneNear = near;
PipelinePlaneFar = far;
PipelineRangeNear = 1.0 / near;
PipelineRangeFar = 1.0 / far;
PipelineFieldOfView = fovy;
}
GMAPI void gmRenderMode(GMenum mode) {
PipelineRenderFace = mode;
}
GMAPI void gmBlendFunc(GMenum mode) {
switch (mode) {
case GM_BLEND_NORMAL: PipelineBlendFunc = &GMBlendNormal; break;
case GM_BLEND_BRIGHT: PipelineBlendFunc = &GMBlendBright; break;
case GM_BLEND_DARK: PipelineBlendFunc = &GMBlendDark; break;
}
}
/* lighting */
#define GM_LIGHT_CHECK(X) \
do { \
if(((X) -= GM_LIGHT0) < 0 || (X) >= GM_MAX_LIGHT_COUNT) { \
return; \
} \
} while (0)
GMAPI void gmLighti(GMenum index, GMenum type, const GMint param) {
GM_LIGHT_CHECK(index);
GMLight *light = PipelineLight[index];
switch (type) {
case GM_LIGHT_TYPE:
light->type = param;
break;
}
}
GMAPI void gmLightfv(GMenum index, GMenum type, const GMfloat *params) {
GM_LIGHT_CHECK(index);
GMLight *light = PipelineLight[index];
switch (type) {
#define GM_LIGHT_COPY(NAME, E) \
memcmp(&light-> NAME, params, sizeof(light->E)); \
break
#define GM_LIGHT_RGBA(NAME) \
light-> NAME .r = (_GMcolor_t)(params[0] * 255); \
light-> NAME .g = (_GMcolor_t)(params[1] * 255); \
light-> NAME .b = (_GMcolor_t)(params[2] * 255); \
light-> NAME .a = (_GMcolor_t)(params[3] * 255); \
break
case GM_DIFFUSE: GM_LIGHT_RGBA(diffuse);
case GM_AMBIENT: GM_LIGHT_RGBA(ambient);
case GM_TRANSFORMATION: GM_LIGHT_COPY(mat.m[0], mat);
case GM_DIRECTION: GM_LIGHT_COPY(dir.x, dir);
#undef GM_LIGHT_COPY
#undef GM_LIGHT_RGBA
}
}
#undef GM_LIGHT_CHECK
/* texturing */
GMAPI void gmGenTexture(GMuint *id) {
GMTexture2D *t = (GMTexture2D*)malloc(sizeof(GMTexture2D));
t->width = 0;
t->height = 0;
t->mipcount = 0;
t->image = (_GMpixel_t**)calloc(GM_MAX_MIPMAPLEVELS, sizeof(_GMpixel_t*));
memset(t->image, 0, GM_MAX_MIPMAPLEVELS);
id = (GMuint*)t;
}
GMAPI void gmDeleteTexture(GMuint *id) {
GMint i = 0;
if (id) {
GMTexture2D *t = (GMTexture2D*)id;
if (t) {
if (t->image) {
for(; i < t->mipcount; ++i)
if (t->image[i])
free(t->image[i]);
free(t->image);
}
free(t);
}
id = 0;
}
}
GMAPI void gmBindTexture(GMuint id) {
if (id)
PipelineTexture[PipelineTextureLayer] = (GMTexture2D*)id;
}
GMAPI void gmUnbindTexture(GMvoid) {
PipelineTexture[PipelineTextureLayer] = 0;
}
GMAPI GMuint gmGetCurTexture(GMvoid) {
return (GMuint)PipelineTexture[PipelineTextureLayer];
}
GMAPI void gmTexGen(GMint c, GMint t) {
switch (c) {
case GM_TEXTURE_GEN_U: PipelineMappingGenType[PipelineTextureLayer].u = t; break;
case GM_TEXTURE_GEN_V: PipelineMappingGenType[PipelineTextureLayer].v = t; break;
}
}
/*
* all of the following functions need optimization. SIMD perhaps?
* future prospect please.
*/
GMAPI void gmTexSubImage2D(GMuint id, GMuint lvl, GMint fmt, GMenum type, const GMvoid *data) {
if (!id || !data || fmt < 1 || fmt > 4 || type < GM_INTEGER || type > GM_FLOAT)
return;
GMTexture2D *t = (GMTexture2D*)id;
/* TODO: error */
#if 0
if (!t->image[lvl])
PipelineLastError = 1;
#endif
GMint w = t->width >> lvl;
GMint h = t->height >> lvl;
GMint s = (w*h) << 2;
/* register for speed */
register GMint x;
register GMint y;
register GMint i = 0;
register GMint j = 0;
#define SUB(V, T, X1, X2, X3, X4) \
case V: { \
const T *ref = (const T*)data; \
for (y = 0; y < h; ++y) { \
for (x = 0; x < w; ++x, i += 4, j += fmt) { \
switch (fmt) { \
case 1: (X1); break; \
case 2: (X2); break; \
case 3: (X3); break; \
case 4: (X4); break; \
} \
} \
} \
break; \
}
#define ASG(X1,X2,X3,X4) \
t->image[lvl][i+0] = (X1),t->image[lvl][i+1] = (X2), \
t->image[lvl][i+0] = (X3),t->image[lvl][i+1] = (X4)
switch (type) {
SUB(GM_INTEGER, GMint,
(ASG(ref[j], ref[j], ref[j], 255 )),
(ASG(ref[j], ref[j], ref[j], ref[j+1])),
(ASG(ref[j+0], ref[j+1], ref[j+2], 255 )),
(ASG(ref[j+0], ref[j+1], ref[j+2], ref[j+3]))
)
SUB(GM_FLOAT, GMfloat,
(ASG((GMint)(ref[j] *255), (GMint)(ref[j] *255), (GMint)(ref[j] *255), 255)),
(ASG((GMint)(ref[j+0]*255), (GMint)(ref[j+0]*255), (GMint)(ref[j+0]*255), (GMint)(ref[j+1]*255))),
(ASG((GMint)(ref[j+0]*255), (GMint)(ref[j+1]*255), (GMint)(ref[j+2]*255), 255)),
(ASG((GMint)(ref[j+0]*255), (GMint)(ref[j+1]*255), (GMint)(ref[j+2]*255), (GMint)(ref[j+3]*255)))
)
SUB(GM_UNSIGNED_BYTE, GMubyte,
(ASG(ref[j], ref[j], ref[j], 255 )),
(ASG(ref[j], ref[j], ref[j], ref[j+1])),
(ASG(ref[j+0], ref[j+1], ref[j+2], 255 )),
(ASG(ref[j+0], ref[j+1], ref[j+2], ref[j+3]))
)
default: {
memset(t->image[lvl], 0, sizeof(_GMpixel_t) * s);
break;
}
#undef SUB
#undef ASG
}
#if 0
GMClampTextureSize(t, lvl);
#endif
}
GMAPI void gmTexImage2D(GMuint id, GMuint lvl, GMint w, GMint h, GMint fmt, GMenum type, const GMvoid *data) {
if (!id || !data || w <= 0 || h <= 0 || fmt < 1 || fmt > 4 || type < GM_INTEGER || type > GM_FLOAT)
return;
GMTexture2D *t = (GMTexture2D*)id;
/* delete old */
if (t->image[lvl])
free(t->image[lvl]);
GMint s = (w*h) << 2;
if (lvl == 0)
t->width = w, t->height = h;
if (t->mipcount < lvl + 1)
t->mipcount = lvl + 1;
/* allocate */
t->image[lvl] = (_GMpixel_t*)calloc(s, sizeof(_GMpixel_t));
gmTexSubImage2D(id, lvl, fmt, type, data);
}
GMAPI void gmCopyTexSubImage2D(GMuint id, GMuint lvl, GMint ox, GMint oy, GMint x, GMint y, GMenum comps) {
GMTexture2D *t = (GMTexture2D*)id;
GMint ix,iy;
GMint i,j,k;
GMfloat z;
GMint w = t->width >> lvl;
GMint h = t->height >> lvl;
/* loop increment need true initialization of zero */
i = 0;
j = 0;
k = 0;
/* clamp */
GMClampi(&x, 0, PipelineScreenWidth);
GMClampi(&y, 0, PipelineScreenHeight);
GMClampi(&w, 0, PipelineScreenWidth);
GMClampi(&h, 0, PipelineScreenHeight);
if (x + w > PipelineScreenWidth)
w -= PipelineScreenWidth - (x + w);
if (y + h > PipelineScreenHeight)
h -= PipelineScreenHeight - (y + h);
GMClampi(&ox, 0, t->width);
GMClampi(&oy, 0, t->height);
GMClampi(&w, 0, t->width);
GMClampi(&h, 0, t->height);
if (ox + w > t->width)
w -= t->width - (ox + w);
if (oy + h > t->height)
h -= t->height - (oy + h);
/* all lines */
if (comps == GM_RGBA) {
for (iy = y; iy < y + h; ++iy) {
i = (iy * PipelineScreenWidth + x) << 2;
j = ((iy - y + oy) * t->width + ox) << 2;
for (ix = x; ix < (x + w) << 2; ++ix, ++i, +j)
t->image[lvl][j] = PipelinePixelBuffer[i];
}
} else if (comps == GM_DEPTH) {
for (iy = y; iy < y + h; ++iy) {
i = iy * PipelineScreenWidth + x;
j = ((iy - y +oy)*t->width +ox) << 2;
z = PipelineDepthBuffer[i] * 255.0f;
GMClampf(&z, 0.0f, 255.0f);
/* all texels */
for (ix = x; ix < (x + w) << 2; ++ix, ++j) {
t->image[lvl][j] = (k==3) ? 255 : (_GMcolor_t)z;
if (++k > 4) {
k = 0;
z = PipelineDepthBuffer[++i] * 255.0f;
GMClampf(&z, 0.0f, 255.0f);
}
}
}
}
}
/*
* TODO: complete texture stuff. I suspect all the specifics of this
* will be later splitted into it's own file. gmTexture.c, gmLight.c
* gmMatrix.c etc etc.
*/
#define GM_VERTEX_IMPL(END, ARGS, X, Y, Z, W) \
GMAPI void gmVertex##END ARGS { \
PipelineVertex[PipelineRenderIndex]->pos.x = X; \
PipelineVertex[PipelineRenderIndex]->pos.y = Y; \
PipelineVertex[PipelineRenderIndex]->pos.z = Z; \
PipelineVertex[PipelineRenderIndex]->pos.w = W; \
++PipelineRenderIndex; \
}
#define GM_NORMAL_IMPL(END, ARGS, X, Y, Z) \
GMAPI void gmNormal##END ARGS { \
PipelineVertex[PipelineRenderIndex]->nrm.x = X; \
PipelineVertex[PipelineRenderIndex]->nrm.y = Y; \
PipelineVertex[PipelineRenderIndex]->nrm.z = Z; \
}
#define GM_COLOUR_IMPL(END, ARGS, R, G, B, A) \
GMAPI void gmColor##END ARGS { \
PipelineVertex[PipelineRenderIndex]->clr.r = R; \
PipelineVertex[PipelineRenderIndex]->clr.g = G; \
PipelineVertex[PipelineRenderIndex]->clr.b = B; \
PipelineVertex[PipelineRenderIndex]->clr.a = A; \
}
GM_VERTEX_IMPL(2i, (GMint x, GMint y), x,y,0,1)
GM_VERTEX_IMPL(3i, (GMint x, GMint y, GMint z), x,y,z,1)
GM_VERTEX_IMPL(4i, (GMint x, GMint y, GMint z, GMint w), x,y,z,w)
GM_VERTEX_IMPL(2f, (GMfloat x, GMfloat y), x,y,0,1)
GM_VERTEX_IMPL(3f, (GMfloat x, GMfloat y, GMfloat z), x,y,z,1)
GM_VERTEX_IMPL(4f, (GMfloat x, GMfloat y, GMfloat z, GMfloat w), x,y,z,w)
GM_NORMAL_IMPL(3i, (GMint x, GMint y, GMint z), x,y,z)
GM_NORMAL_IMPL(3f, (GMfloat x, GMfloat y, GMfloat z), x,y,z)
GM_COLOUR_IMPL(3i, (GMint r, GMint g, GMint b), r,g,b,255)
GM_COLOUR_IMPL(4i, (GMint r, GMint g, GMint b, GMint a), r,g,b,a)
GM_COLOUR_IMPL(3f, (GMfloat r, GMfloat g, GMfloat b), r*255,g*255,b*255, 255)
GM_COLOUR_IMPL(4f, (GMfloat r, GMfloat g, GMfloat b, GMfloat a), r*255,g*255,b*255,a*255)
GMAPI void gmTexCoord2i(GMint layer, GMint x, GMint y) {
PipelineVertex[PipelineRenderIndex]->map[layer].x = x;
PipelineVertex[PipelineRenderIndex]->map[layer].y = y;
}
GMAPI void gmTexCoord2f(GMint layer, GMfloat x, GMfloat y) {
const GMfloat *m = PipelineRenderMatrix[GM_TEXTURE_MATRIX].m;
PipelineVertex[PipelineRenderIndex]->map[layer].x = x*m[0] + y*m[4] + m[12];
PipelineVertex[PipelineRenderIndex]->map[layer].y = x*m[1] + y*m[5] + m[13];
}
#undef GM_VERTEX_IMPL
#undef GM_NORMAL_IMPL
#undef GM_COLOUR_IMPL
/* matrices */
GMAPI void gmMatrixMode(GMint mat) {
PipelineMatrixMode = mat;
}
#define GM_COPY_MATRIX(M,X) \
do { \
X [0] = M [0]; \
X [1] = M [1]; \
X [2] = M [2]; \
X [3] = M [3]; \
X [4] = M [4]; \
X [5] = M [5]; \
X [6] = M [6]; \
X [7] = M [7]; \
X [8] = M [8]; \
X [9] = M [9]; \
X [10] = M [10]; \
X [11] = M [11]; \
X [12] = M [12]; \
X [13] = M [13]; \
X [14] = M [14]; \
X [15] = M [15]; \
} while (0)
GMAPI void gmLoadIdentity(GMvoid) {
/*
* constant folded thanks to the macro not being a function and
* this data being static const.
*/
static const GMfloat mat[16] = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
GM_COPY_MATRIX (
mat,
PipelineRenderMatrix[PipelineMatrixMode].m
);
}
GMAPI void gmLoadMatrix(const GMfloat *mat) {
/*
* not constant folded, runtime acomplished. This is done
* with the same macro.
*/
GM_COPY_MATRIX (
mat,
PipelineRenderMatrix[PipelineMatrixMode].m
);
}
GMAPI void gmGetMatrix(GMfloat *mat) {
GM_COPY_MATRIX (
PipelineRenderMatrix[PipelineMatrixMode].m,
mat
);
}
GMAPI void gmUpdateNormalMatrix(GMvoid) {
#define MODELVIEW(X) \
PipelineRenderMatrix[GM_MODELVIEW_MATRIX].m[(X)]
GMfloat src[16] = {
MODELVIEW(0), MODELVIEW(1), MODELVIEW(2), MODELVIEW(3),
MODELVIEW(4), MODELVIEW(5), MODELVIEW(6), MODELVIEW(7),
MODELVIEW(8), MODELVIEW(9), MODELVIEW(10), MODELVIEW(11),
0.0f, 0.0f, 0.0f, MODELVIEW(15)
};
GM_COPY_MATRIX (
src,
PipelineRenderMatrix[GM_NORMAL_MATRIX].m
);
}
#undef GM_COPY_MATRIX
#undef MODELVIEW
/* others */
GMAPI void gmClearColor(GMint r, GMint g, GMint b) {
PipelineClearColor.r = r;
PipelineClearColor.g = g;
PipelineClearColor.b = b;
}
GMAPI void gmClear(GMbyte targ) {
register GMint i = 0;
/* clear pixel buffer */
if (targ & GM_CLEAR_COLOR) {
while (i < PipelinePixelBufferSize) {
PipelinePixelBuffer[i+0] = PipelineClearColor.r;
PipelinePixelBuffer[i+1] = PipelineClearColor.g;
PipelinePixelBuffer[i+2] = PipelineClearColor.b;
PipelinePixelBuffer[i+3] = 255;
i += 4;
}
}
/* clear depth buffer */
if (targ & GM_CLEAR_DEPTH) {
i = 0;
while (i < PipelineDepthBufferSize) {
PipelineDepthBuffer[i] = 0;
++ i;
}
}
}
/*
* Actual primitive rendering routins take place here. This is where most
* of the rasterization takes place. The previous stuff is just a finite
* state machine for various attributes of textured geometry rendering.
*/
GMint PipelineOptXStart = 0, PipelineOptXEnd = 0;
GMint PipelineOptYStart = 0, PipelineOptYEnd = 0;
GMint PipelineXStart = 0, PipelineXEnd = 0;
GMint PipelineYStart = 0, PipelineYEnd = 0;
GMint PipelineMiddle = 0, PipelineMiddleStart = 0;
GMint PipelineEndMiddle = 0, PipelineEndStart = 0;
GMfloat PipelineFactor = 0;
GMint PipelineTempi = 0;
GMfloat PipelineTempf = 0;
GMint PipelineV0 = 0, PipelineV1 = 1;
GMPolygonSide PipelineLSide, PipelineRSide, PipelineStep;
#define GM_ComputeScanLineDimension \
if (y < PipelineMiddle) \
PipelineXStart=(GMint)(PipelineVertex[0]->pos.x+(PipelineVertex[1]->pos.x-PipelineVertex[0]->pos.x)*(y-PipelineYStart)/PipelineMiddleStart); \
else if (y > PipelineMiddle) \
PipelineXStart=(GMint)(PipelineVertex[1]->pos.x+(PipelineVertex[2]->pos.x-PipelineVertex[1]->pos.x)*(y-PipelineMiddle)/PipelineEndMiddle); \
else PipelineXStart=(GMint)(PipelineVertex[1]->pos.x); \
PipelineXEnd = (GMint)(PipelineVertex[0]->pos.x+(PipelineVertex[2]->pos.x-PipelineVertex[0]->pos.x)*(y-PipelineYStart)/PipelineEndStart);
#define GM_ClampScalineDimension \
PipelineOptXStart = (PipelineXStart < PipelinePixelRect[0] ? PipelinePixelRect[0] : PipelineXStart); \
PipelineOptXEnd = (PipelineXEnd >= PipelinePixelRect[2] ? PipelinePixelRect[2] : PipelineXEnd)
#define GM_Swapi(A,B) PipelineTempi = A; A = B; B = PipelineTempi
#define GM_Swapf(A,B) PipelineTempf = A; A = B; B = PipelineTempf
#define GM_PointInside(P) \
P.x < 0 || P.x >= PipelineScreenWidth || P.y < 0 || P.y > PipelineScreenHeight
#define GM_CheckPlotDepth \
if (!GMCheckPlotDepth(i, z)) continue
#define GM_ComputePolygonSidesDepth(A,B) \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[2]->pos.y - PipelineVertex[0]->pos.y); \
A.z = PipelineVertex[0]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
if (y < PipelineMiddle) { \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[1]->pos.y - PipelineVertex[0]->pos.y); \
B.z = PipelineVertex[0]->pos.z + (PipelineVertex[1]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
} else { \
PipelineFactor = (GMfloat)(y - PipelineMiddle) / (PipelineVertex[2]->pos.y - PipelineVertex[1]->pos.y); \
B.z = PipelineVertex[1]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[1]->pos.z) * PipelineFactor; \
}
#define GM_ComputePolygonSidesDepthColor(A,B) \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[2]->pos.y - PipelineVertex[0]->pos.y); \
A.z = PipelineVertex[0]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
GMInterpolateColors ( \
&A.c, \
&PipelineVertex[0]->clr, \
&PipelineVertex[2]->clr, \
&PipelineFactor \
); \
if (y < PipelineMiddle) { \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[1]->pos.y - PipelineVertex[0]->pos.y); \
B.z = PipelineVertex[0]->pos.z + (PipelineVertex[1]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
GMInterpolateColors ( \
&B.c, \
&PipelineVertex[0]->clr, \
&PipelineVertex[1]->clr, \
&PipelineFactor \
); \
} else { \
PipelineFactor = (GMfloat)(y - PipelineMiddle) / (PipelineVertex[2]->pos.y - PipelineVertex[1]->pos.y); \
B.z = PipelineVertex[1]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[1]->pos.z) * PipelineFactor; \
GMInterpolateColors ( \
&B.c, \
&PipelineVertex[1]->clr, \
&PipelineVertex[2]->clr, \
&PipelineFactor \
); \
}
#define uvm map[PipelineTextureLayer]
#define GM_ComputePolygonSidesDepthTexCoords(A,B) \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[2]->pos.y - PipelineVertex[0]->pos.y); \
A.z = PipelineVertex[0]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
A.u = PipelineVertex[0]->uvm.x + (PipelineVertex[2]->uvm.x - PipelineVertex[0]->uvm.x) * PipelineFactor; \
A.v = PipelineVertex[0]->uvm.y + (PipelineVertex[2]->uvm.y - PipelineVertex[0]->uvm.y) * PipelineFactor; \
if (y < PipelineMiddle) { \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[1]->pos.y - PipelineVertex[0]->pos.y); \
B.z = PipelineVertex[0]->pos.z + (PipelineVertex[1]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
B.u = PipelineVertex[0]->uvm.x + (PipelineVertex[1]->uvm.x - PipelineVertex[0]->uvm.x) * PipelineFactor; \
B.v = PipelineVertex[0]->uvm.y + (PipelineVertex[1]->uvm.y - PipelineVertex[0]->uvm.y) * PipelineFactor; \
} else { \
PipelineFactor = (GMfloat)(y - PipelineMiddle) / (PipelineVertex[2]->pos.y - PipelineVertex[1]->pos.y); \
B.z = PipelineVertex[1]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[1]->pos.z) * PipelineFactor; \
B.u = PipelineVertex[1]->uvm.x + (PipelineVertex[2]->uvm.x - PipelineVertex[1]->uvm.x) * PipelineFactor; \
B.v = PipelineVertex[1]->uvm.y + (PipelineVertex[2]->uvm.y - PipelineVertex[1]->uvm.y) * PipelineFactor; \
}
#define GM_ComputePolygonSidesDepthTexCoordsColor(A,B) \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[2]->pos.y - PipelineVertex[0]->pos.y); \
A.z = PipelineVertex[0]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
A.u = PipelineVertex[0]->uvm.x + (PipelineVertex[2]->uvm.x - PipelineVertex[0]->uvm.x) * PipelineFactor; \
A.v = PipelineVertex[0]->uvm.y + (PipelineVertex[2]->uvm.y - PipelineVertex[0]->uvm.y) * PipelineFactor; \
GMInterpolateColors ( \
&A.c, \
&PipelineVertex[0]->clr, \
&PipelineVertex[2]->clr, \
&PipelineFactor \
); \
if (y < PipelineMiddle) { \
PipelineFactor = (GMfloat)(y - PipelineYStart) / (PipelineVertex[1]->pos.y - PipelineVertex[0]->pos.y); \
B.z = PipelineVertex[0]->pos.z + (PipelineVertex[1]->pos.z - PipelineVertex[0]->pos.z) * PipelineFactor; \
B.u = PipelineVertex[0]->uvm.x + (PipelineVertex[1]->uvm.x - PipelineVertex[0]->uvm.x) * PipelineFactor; \
B.v = PipelineVertex[0]->uvm.y + (PipelineVertex[1]->uvm.y - PipelineVertex[0]->uvm.y) * PipelineFactor; \
GMInterpolateColors ( \
&B.c, \
&PipelineVertex[0]->clr, \
&PipelineVertex[1]->clr, \
&PipelineFactor \
); \
} else { \
PipelineFactor = (GMfloat)(y - PipelineMiddle) / (PipelineVertex[2]->pos.y - PipelineVertex[1]->pos.y); \
B.z = PipelineVertex[1]->pos.z + (PipelineVertex[2]->pos.z - PipelineVertex[1]->pos.z) * PipelineFactor; \
B.u = PipelineVertex[1]->uvm.x + (PipelineVertex[2]->uvm.x - PipelineVertex[1]->uvm.x) * PipelineFactor; \
B.v = PipelineVertex[1]->uvm.y + (PipelineVertex[2]->uvm.y - PipelineVertex[1]->uvm.y) * PipelineFactor; \
GMInterpolateColors ( \
&B.c, \
&PipelineVertex[1]->clr, \
&PipelineVertex[2]->clr, \
&PipelineFactor \
); \
}
#define GM_UpdateScanlinePart(FUNC) \
GM_ComputeScanLineDimension; \
if (PipelineXStart > PipelineXEnd) { \
GM_Swapi(PipelineXStart, PipelineXEnd); \
FUNC(PipelineLSide, PipelineRSide); \
} else { \
FUNC(PipelineRSide, PipelineLSide); \
} \
GM_ClampScalineDimension
#define GM_ResizeTextureCoordinate(V) \
PipelineVertex[V]->uvm.x *= PipelineTexture[PipelineTextureLayer]->width * PipelineVertex[V]->pos.z; \
PipelineVertex[V]->uvm.y *= PipelineTexture[PipelineTextureLayer]->height * PipelineVertex[V]->pos.z
#define GM_ResizeTextureCoordinates \
GM_ResizeTextureCoordinate(0); \
GM_ResizeTextureCoordinate(1); \
GM_ResizeTextureCoordinate(2); \
GM_ResizeTextureCoordinate(3)
#define GM_SetColorIntensity(C, I) \
C.r = C.r * I / 255, C.g = C.g * I / 255, C.b = C.b * I / 255
#define GM_LineBaseAlgorithm \
GMint i , t; \
GMint x , y; \
GMint dx , dy; \
GMint incx, incy; \
GMint pdx , pdy; \
GMint ddx , ddy; \
GMint es , el; \
GMint err; \
\
GMfloat z = PipelineVertex[PipelineV0]->pos.z; \
GMint x1 = (GMint)PipelineVertex[PipelineV0]->pos.x, y1 = (GMint)PipelineVertex[PipelineV0]->pos.y; \
GMint x2 = (GMint)PipelineVertex[PipelineV1]->pos.x, y2 = (GMint)PipelineVertex[PipelineV1]->pos.y; \
\
dx = x2 - x1; \
dy = y2 - y1; \
\
incx = (dx > 0) ? 1 : (dx < 0) ? -1 : 0; \
incy = (dy > 0) ? 1 : (dy < 0) ? -1 : 0; \
\
if (dx < 0) dx = -dx; \
if (dy < 0) dy = -dy; \
\
if (dx > dy) { \
pdx = incx, pdy = 0; \
ddx = incx, ddy = incy; \
es = dy, el = dx; \
} else { \
pdx = 0, pdy = incy; \
ddx = incx, ddy = incy; \
es = dx, el = dy; \
} \
\
if (el == 0) { \
return; \
} \
\
x = x1; \
y = y1; \
err = el/2
#define GM_LineInterpolate \
err -= es; \
if (err < 0) { \
err += el; \
x += ddx; \
y += ddy; \
} else { \
x += pdx; \
y += pdy; \
}
#define GM_LinePlot \
GMPlotLocated ( \
x, y, z, \
clr.r * PipelineVertex[PipelineV0]->clr.r / 255, \
clr.g * PipelineVertex[PipelineV0]->clr.g / 255, \
clr.b * PipelineVertex[PipelineV0]->clr.b / 255, \
clr.a * PipelineVertex[PipelineV0]->clr.a / 255 \
)
#define GM_ClampRectangle \
if (right < 0 || left >= PipelineScreenWidth || bottom < 0 || top >= PipelineScreenHeight) { \
return; \
} \
GMClampi(&left, 0, PipelineScreenWidth); \
GMClampi(&right, 0, PipelineScreenWidth); \
GMClampi(&top, 0, PipelineScreenHeight); \
GMClampi(&bottom,0, PipelineScreenHeight); \
\
i = top * PipelineScreenWidth + left
/* ==================================================================== */
/* POINT */
/* ==================================================================== */
GMINLINE void GMRenderPointColor(GMvoid) {
GMPlotLocated (
(GMint)PipelineVertex[0]->pos.x,
(GMint)PipelineVertex[0]->pos.y,
(GMint)PipelineVertex[0]->pos.z,
PipelineVertex[0]->clr.r,
PipelineVertex[0]->clr.g,
PipelineVertex[0]->clr.b,
PipelineVertex[0]->clr.a
);
}
GMINLINE void GMRenderPointTexture(GMvoid) {
GMColor4i clr;
GMVector2f map;
PipelineVertex[0]->uvm.x *= PipelineTexture[PipelineTextureLayer]->width * PipelineVertex[0]->pos.z;
PipelineVertex[0]->uvm.y *= PipelineTexture[PipelineTextureLayer]->height * PipelineVertex[0]->pos.z;
map.x = PipelineVertex[0]->uvm.x / PipelineVertex[0]->pos.z;
map.y = PipelineVertex[0]->uvm.y / PipelineVertex[0]->pos.z;
GMGetTexturePixelLinear(&clr, &map);
GMPlotLocated (
(GMint)PipelineVertex[0]->pos.x,
(GMint)PipelineVertex[0]->pos.y,
(GMint)PipelineVertex[0]->pos.z,
clr.r * PipelineVertex[0]->clr.r / 255,
clr.g * PipelineVertex[0]->clr.g / 255,
clr.b * PipelineVertex[0]->clr.b / 255,
clr.a * PipelineVertex[0]->clr.a / 255
);
}
/* ==================================================================== */
/* LINE */
/* ==================================================================== */
GMINLINE void GMRenderLineSolidColorLinear(GMvoid) {
GM_LineBaseAlgorithm;
/* step computation */
PipelineStep.z = (PipelineVertex[PipelineV1]->pos.z - PipelineVertex[PipelineV0]->pos.z) / el;
/* first pixel plot */
GMPlotLocated (
x,y,z,
PipelineVertex[PipelineV0]->clr.r,
PipelineVertex[PipelineV0]->clr.g,
PipelineVertex[PipelineV0]->clr.b,
PipelineVertex[PipelineV0]->clr.a
);
/* plot the rest */
for (t = 0; t < el; ++t) {
GM_LineInterpolate;
/* step it */
z += PipelineStep.z;
/* plot the pixel */
GMPlotLocated (
x, y, z,
PipelineVertex[PipelineV0]->clr.r,
PipelineVertex[PipelineV0]->clr.g,
PipelineVertex[PipelineV0]->clr.b,
PipelineVertex[PipelineV0]->clr.a
);
}
}
GMINLINE void GMRenderLineTextureColorLinear(GMvoid) {
GM_LineBaseAlgorithm;
GMColor4i clr;
GMVector2f map;
GMVector2f inv;
/* step computation */
PipelineStep.z = (PipelineVertex[PipelineV1]->pos.z - PipelineVertex[PipelineV0]->pos.z) / el;
PipelineStep.u = (PipelineVertex[PipelineV1]->uvm.x - PipelineVertex[PipelineV0]->uvm.x) / el;
PipelineStep.v = (PipelineVertex[PipelineV1]->uvm.y - PipelineVertex[PipelineV0]->uvm.y) / el;
map.x = PipelineVertex[PipelineV0]->uvm.x;
map.y = PipelineVertex[PipelineV0]->uvm.y;
/* first pixel plot */
GM_LinePlot;
/* plot the rest */
for (t = 0; t < el; ++t) {
GM_LineInterpolate;
z += PipelineStep.z;
map.x += PipelineStep.u;
map.y += PipelineStep.v;
inv.x = map.x / z;
inv.y = map.y / z;
/* get texel coloring */
GMGetTexturePixelLinear(&clr, &inv);
GM_LinePlot;
}
}
GMINLINE void GMRenderLineTextureColorSmooth(GMvoid) { /* TODO: implement ! */ }
GMINLINE void GMRenderLineSolidColorSmooth (GMvoid) { /* TODO: implement ! */ }
/* ==================================================================== */
/* RECT */
/* ==================================================================== */
GMINLINE void GMRenderRectangleColorLinear(GMvoid) {
GMint x,y,i;
GMint left = (GMint)PipelineVertex[0]->pos.x;
GMint right = (GMint)PipelineVertex[1]->pos.x;
GMint top = (GMint)PipelineVertex[0]->pos.y;
GMint bottom = (GMint)PipelineVertex[1]->pos.y;
GM_ClampRectangle;
/* don't bother plotting alpha */
if (PipelineVertex[0]->clr.a == 255) {
/* all pixels */
for (y = top; y < bottom; ++y) {
/* scan line plot */
for (x = left; x < right; ++x, ++i) {
GMPlotPixel3f(
i,
PipelineVertex[0]->clr.r,
PipelineVertex[0]->clr.g,
PipelineVertex[0]->clr.b
);
}
/*
* This is a mind confuzzler. This pushes the
* indices to the next step for the next iteration
* for y. Note that the x iteration will step i
* as well so you can easily get confused how it's
* plotting these pixels.
*/
i += PipelineScreenWidth - (right - left);
}
} else {
/* all pixels */
for (y = top; y < bottom; ++y) {
/* scan line plot */
for (x = left; x < right; ++x, ++i) {
GMPlotPixel4f(
i,
PipelineVertex[0]->clr.r,
PipelineVertex[0]->clr.g,
PipelineVertex[0]->clr.b,
PipelineVertex[0]->clr.a
);
}
i += PipelineScreenWidth - (right - left);
}
}
}
GMINLINE void GMRenderRectangleColorSmooth (GMvoid) { /* TODO: implement ! */ }
GMINLINE void GMRenderRectangleTextureSizePixelLinear(GMvoid) { /* TODO: implement ! */ }
GMINLINE void GMRenderRectangleTextureSizePixelSmooth(GMvoid) { /* TODO: implement ! */ }
/* ==================================================================== */
/* TRIANGLE */
/* ==================================================================== */
GMINLINE void GMRenderTriangleSolidColorLinear(GMvoid) {
GMint x,y,i;
GMfloat z;
for (y = PipelineOptYStart; y < PipelineOptYEnd; ++y) {
/* Expands to a mess, you've been warned. */
GM_UpdateScanlinePart (
GM_ComputePolygonSidesDepth
);
GM_ClampScalineDimension;
/* compute step for linear interpolation */
PipelineStep.z = (PipelineRSide.z - PipelineLSide.z) / (PipelineXEnd - PipelineXStart);
/* left side linear interpolation */
i = y * PipelineScreenWidth + PipelineOptXStart;
z = PipelineLSide.z + PipelineStep.z * (PipelineOptXStart - PipelineOptXStart);
/* loop, check depth, plot all pixels in current scanline */
for (x = PipelineOptXStart; x < PipelineOptXStart; ++x, ++i) {
z += PipelineStep.z; /* TODO: move to for statment? */
GMCheckPlotDepth;
GMPlotIndexed(
i,z,
PipelineVertex[2]->clr.r,
PipelineVertex[2]->clr.g,
PipelineVertex[2]->clr.b,
PipelineVertex[2]->clr.a
);
}
}
}
GMINLINE void GMRenderTriangleSolidColorSmooth(GMvoid) {
GMint x,y,i;
GMfloat z;
GMint tmp;
GMColor4i clr, stp;
/* all lines */
printf("PipelineOptYStart = %d\n", PipelineOptYStart);
printf("PipelineOptYEnd = %d\n", PipelineOptYEnd);
for (y = PipelineOptYStart; y < PipelineOptYEnd; ++y) {
printf("entered\n");
/* Expands to a mess, you've been warned. */
GM_UpdateScanlinePart(GM_ComputePolygonSidesDepthColor);
if (PipelineXEnd - PipelineXStart == 0)
continue;
/* clamp because oh noes */
GM_ClampScalineDimension;
/* linear interpolation step computation */
tmp = PipelineXEnd - PipelineXStart;
PipelineStep.z = (PipelineRSide.z - PipelineLSide.z) / tmp;
stp.r = (GMfloat)(PipelineRSide.c.r - PipelineLSide.c.r)/tmp;
stp.g = (GMfloat)(PipelineRSide.c.g - PipelineLSide.c.g)/tmp;
stp.b = (GMfloat)(PipelineRSide.c.b - PipelineLSide.c.b)/tmp;
stp.a = (GMfloat)(PipelineRSide.c.a - PipelineLSide.c.a)/tmp;
/* left side linear interpolation */
tmp = PipelineOptXStart - PipelineXStart;
i = y * PipelineScreenWidth + PipelineOptXStart;
z = PipelineLSide.z + PipelineStep.z * tmp;
clr.r = PipelineRSide.c.r + stp.r * tmp;
clr.g = PipelineRSide.c.g + stp.g * tmp;
clr.b = PipelineRSide.c.b + stp.b * tmp;
clr.a = PipelineRSide.c.a + stp.a * tmp;
/* plot for all on current scan line */
for (x = PipelineOptXStart; x < PipelineOptXEnd; ++x, ++i) {
z += PipelineStep.z; /* TODO: mode to for loop? */
clr.r += stp.r;
clr.g += stp.g;
clr.b += stp.b;
clr.a += stp.a;
GM_CheckPlotDepth;
GMPlotIndexed (
i,z,
(_GMcolor_t)clr.r,
(_GMcolor_t)clr.g,
(_GMcolor_t)clr.b,
(_GMcolor_t)clr.a
);
}
}
}
GMINLINE void GMRenderTriangleTexturePixelLinearColorLinear(GMvoid) {
GMint x,y,i;
GMfloat z;
GMint tmp;
GMColor4i clr;
GMVector2f map,inv;
GM_ResizeTextureCoordinates;
/* all lines */
for (y = PipelineOptYStart; y < PipelineOptYEnd; ++y) {
GM_UpdateScanlinePart(GM_ComputePolygonSidesDepthTexCoords);
GM_ClampScalineDimension;
/* step computation for linear interpolation */
tmp = PipelineXEnd - PipelineXStart;
PipelineStep.z = (PipelineRSide.z - PipelineLSide.z) / tmp;
PipelineStep.u = (PipelineRSide.u - PipelineLSide.u) / tmp;
PipelineStep.v = (PipelineRSide.v - PipelineLSide.v) / tmp;
/* left side linear interpolation */
tmp = PipelineOptXStart - PipelineXStart;
i = y * PipelineScreenWidth + PipelineOptXStart;
z = PipelineLSide.z + PipelineStep.z * tmp;
map.x = PipelineLSide.u + PipelineStep.u * tmp;
map.y = PipelineLSide.v + PipelineStep.v * tmp;
/* all pixels in scan line */
for (x = PipelineOptXStart; x < PipelineOptXEnd; ++x, ++i) {
x += PipelineStep.z;
map.x += PipelineStep.u;
map.y += PipelineStep.v;
GM_CheckPlotDepth;
inv.x = map.x / z;
inv.y = map.y / z;
GMGetTexturePixelLinear(&clr, &inv);
GMPlotIndexed (
i,z,
clr.r * PipelineVertex[2]->clr.r / 255,
clr.g * PipelineVertex[2]->clr.g / 255,
clr.b * PipelineVertex[2]->clr.b / 255,
clr.a * PipelineVertex[2]->clr.a / 255
);
}
}
}
GMINLINE void GMRenderTriangleTexturePixelLinearColorSmooth(GMvoid) {
GMint x,y,i;
GMfloat z;
GMint tmp;
GMColor4i mcl;
GMColor4i clr,stp;
GMVector2f map,inv;
GM_ResizeTextureCoordinates;
/* all lines */
for (y = PipelineOptYStart; y < PipelineOptYEnd; ++y) {
GM_UpdateScanlinePart(GM_ComputePolygonSidesDepthTexCoordsColor);
GM_ClampScalineDimension;
/* step computation for linear interpolation */
tmp = PipelineXEnd - PipelineXStart;
PipelineStep.z = (PipelineRSide.z - PipelineLSide.z) / tmp;
PipelineStep.u = (PipelineRSide.u - PipelineLSide.u) / tmp;
PipelineStep.v = (PipelineRSide.v - PipelineLSide.v) / tmp;
stp.r = (GMfloat)(PipelineRSide.c.r - PipelineLSide.c.r)/tmp;
stp.g = (GMfloat)(PipelineRSide.c.g - PipelineLSide.c.g)/tmp;
stp.b = (GMfloat)(PipelineRSide.c.b - PipelineLSide.c.b)/tmp;
stp.a = (GMfloat)(PipelineRSide.c.a - PipelineLSide.c.a)/tmp;
/* left side linear interpolation */
tmp = PipelineOptXStart - PipelineXStart;
i = y * PipelineScreenWidth + PipelineOptXStart;
z = PipelineLSide.z + PipelineStep.z * tmp;
map.x = PipelineLSide.u + PipelineStep.u * tmp;
map.y = PipelineLSide.v + PipelineStep.v * tmp;
clr.r = PipelineRSide.c.r + stp.r * tmp;
clr.g = PipelineRSide.c.g + stp.g * tmp;
clr.b = PipelineRSide.c.b + stp.b * tmp;
clr.a = PipelineRSide.c.a + stp.a * tmp;
/* all pixels in scan line */
for (x = PipelineOptXStart; x < PipelineOptXEnd; ++x, ++i) {
x += PipelineStep.z;
map.x += PipelineStep.u;
map.y += PipelineStep.v;
clr.r += stp.r;
clr.g += stp.g;
clr.b += stp.b;
clr.a += stp.a;
GM_CheckPlotDepth;
inv.x = map.x / z;
inv.y = map.y / z;
GMGetTexturePixelLinear(&mcl, &inv);
GMPlotIndexed (
i,z,
mcl.r * (_GMcolor_t)clr.r / 255,
mcl.g * (_GMcolor_t)clr.g / 255,
mcl.b * (_GMcolor_t)clr.b / 255,
mcl.a * (_GMcolor_t)clr.a / 255
);
}
}
}
GMINLINE void GMRenderTriangleTexturePixelSmoothColorLinear(GMvoid) {
GMint x,y,i;
GMfloat z;
GMint tmp;
GMColor4i clr;
GMVector2f map,inv;
GM_ResizeTextureCoordinates;
/* all lines */
for (y = PipelineOptYStart; y < PipelineOptYEnd; ++y) {
GM_UpdateScanlinePart(GM_ComputePolygonSidesDepthTexCoords);
GM_ClampScalineDimension;
/* step computation for linear interpolation */
tmp = PipelineXEnd - PipelineXStart;
PipelineStep.z = (PipelineRSide.z - PipelineLSide.z) / tmp;
PipelineStep.u = (PipelineRSide.u - PipelineLSide.u) / tmp;
PipelineStep.v = (PipelineRSide.v - PipelineLSide.v) / tmp;
/* left side linear interpolation */
tmp = PipelineOptXStart - PipelineXStart;
i = y * PipelineScreenWidth + PipelineOptXStart;
z = PipelineLSide.z + PipelineStep.z * tmp;
map.x = PipelineLSide.u + PipelineStep.u * tmp;
map.y = PipelineLSide.v + PipelineStep.v * tmp;
/* all pixels in scan line */
for (x = PipelineOptXStart; x < PipelineOptXEnd; ++x, ++i) {
x += PipelineStep.z;
map.x += PipelineStep.u;
map.y += PipelineStep.v;
GM_CheckPlotDepth;
inv.x = map.x / z;
inv.y = map.y / z;
GMGetTexturePixelSmooth(&clr, &inv);
GMPlotIndexed (
i,z,
clr.r * PipelineVertex[2]->clr.r / 255,
clr.g * PipelineVertex[2]->clr.g / 255,
clr.b * PipelineVertex[2]->clr.b / 255,
clr.a * PipelineVertex[2]->clr.a / 255
);
}
}
}
GMINLINE void GMRenderTriangleTexturePixelSmoothColorSmooth(GMvoid) {
GMint x,y,i;
GMfloat z;
GMint tmp;
GMColor4i mcl;
GMColor4i clr,stp;
GMVector2f map,inv;
GM_ResizeTextureCoordinates;
/* all lines */
for (y = PipelineOptYStart; y < PipelineOptYEnd; ++y) {
GM_UpdateScanlinePart(GM_ComputePolygonSidesDepthTexCoordsColor);
GM_ClampScalineDimension;
/* step computation for linear interpolation */
tmp = PipelineXEnd - PipelineXStart;
PipelineStep.z = (PipelineRSide.z - PipelineLSide.z) / tmp;
PipelineStep.u = (PipelineRSide.u - PipelineLSide.u) / tmp;
PipelineStep.v = (PipelineRSide.v - PipelineLSide.v) / tmp;
stp.r = (GMfloat)(PipelineRSide.c.r - PipelineLSide.c.r)/tmp;
stp.g = (GMfloat)(PipelineRSide.c.g - PipelineLSide.c.g)/tmp;
stp.b = (GMfloat)(PipelineRSide.c.b - PipelineLSide.c.b)/tmp;
stp.a = (GMfloat)(PipelineRSide.c.a - PipelineLSide.c.a)/tmp;
/* left side linear interpolation */
tmp = PipelineOptXStart - PipelineXStart;
i = y * PipelineScreenWidth + PipelineOptXStart;
z = PipelineLSide.z + PipelineStep.z * tmp;
map.x = PipelineLSide.u + PipelineStep.u * tmp;
map.y = PipelineLSide.v + PipelineStep.v * tmp;
clr.r = PipelineRSide.c.r + stp.r * tmp;
clr.g = PipelineRSide.c.g + stp.g * tmp;
clr.b = PipelineRSide.c.b + stp.b * tmp;
clr.a = PipelineRSide.c.a + stp.a * tmp;
/* all pixels in scan line */
for (x = PipelineOptXStart; x < PipelineOptXEnd; ++x, ++i) {
x += PipelineStep.z;
map.x += PipelineStep.u;
map.y += PipelineStep.v;
clr.r += stp.r;
clr.g += stp.g;
clr.b += stp.b;
clr.a += stp.a;
GM_CheckPlotDepth;
inv.x = map.x / z;
inv.y = map.y / z;
GMGetTexturePixelSmooth(&mcl, &inv);
GMPlotIndexed (
i,z,
mcl.r * (_GMcolor_t)clr.r / 255,
mcl.g * (_GMcolor_t)clr.g / 255,
mcl.b * (_GMcolor_t)clr.b / 255,
mcl.a * (_GMcolor_t)clr.a / 255
);
}
}
}
GMINLINE void GMUpdateVertices(GMvoid) {
/* normal to be normalized? */
if (PipelineRenderStatus[GM_LIGHTING] || PipelineRenderStatus[GM_TEXGEN]) {
/* multiply normals by normal matrix */
GMMultMatrix3f(&PipelineRenderMatrix[GM_NORMAL_MATRIX], &PipelineVertex[0]->nrm);
GMMultMatrix3f(&PipelineRenderMatrix[GM_NORMAL_MATRIX], &PipelineVertex[1]->nrm);
GMMultMatrix3f(&PipelineRenderMatrix[GM_NORMAL_MATRIX], &PipelineVertex[2]->nrm);
/* normalize them normals otherwise they're not normals are they :P */
if (PipelineRenderStatus[GM_NORMALIZE]) {
GMNormalizeVec(&PipelineVertex[0]->nrm);
GMNormalizeVec(&PipelineVertex[1]->nrm);
GMNormalizeVec(&PipelineVertex[2]->nrm);
}
}
/* mapping generation */
if (PipelineRenderStatus[GM_TEXGEN]) {
GMint i;
for (i = 0; i < GM_TEXTURE_LAYER_SIZE; ++i) {
#define UPDATE_MAPPING_GEN(X) \
GMUpdateMappingGen ( \
PipelineMappingGenType[i].u, \
PipelineVertex[X]->pos.x, \
PipelineVertex[X]->nrm.x, \
&PipelineVertex[X]->map[i].x \
); \
GMUpdateMappingGen ( \
PipelineMappingGenType[i].v, \
PipelineVertex[X]->pos.y, \
PipelineVertex[X]->nrm.y, \
&PipelineVertex[X]->map[i].y \
)
UPDATE_MAPPING_GEN(0);
UPDATE_MAPPING_GEN(1);
UPDATE_MAPPING_GEN(2);
#undef UPDATE_MAPPING_GEN
}
}
/* lighting intensity control */
if (PipelineRenderStatus[GM_LIGHTING]) {
if (PipelineRenderStatus[GM_COLOR_SMOOTH]) {
#define SET_COLOR_INTENSITY(X) \
GM_SetColorIntensity ( \
PipelineVertex[X]->clr, \
(GMint)((0.2f+fabs(PipelineVertex[X]->nrm.z) * 0.8f) * 255) \
)
SET_COLOR_INTENSITY(0);
SET_COLOR_INTENSITY(1);
SET_COLOR_INTENSITY(2);
#undef SET_COLOR_INTENSITY
} else {
GMint flat = (GMint)((0.2f+fabs(PipelineVertex[0]->nrm.z)*0.8f)*255);
GM_SetColorIntensity(PipelineVertex[0]->clr, flat);
GM_SetColorIntensity(PipelineVertex[1]->clr, flat);
GM_SetColorIntensity(PipelineVertex[2]->clr, flat);
}
}
}
/*
* Drawing functions now begin:
* these are length and I should be shot for code duplication.
*/
GMAPI void gmDrawPoint(GMvoid) {
if (PipelineRenderStatus[GM_LIGHTING] || PipelineRenderStatus[GM_TEXGEN]) {
GMMultMatrix3f(&PipelineRenderMatrix[GM_NORMAL_MATRIX], &PipelineVertex[0]->nrm);
/* normalise teh normalz!! all of dem! */
if (PipelineRenderStatus[GM_NORMALIZE])
GMNormalizeVec(&PipelineVertex[0]->nrm);
}
/* mapping generation */
if (PipelineRenderStatus[GM_TEXGEN]) {
GMint i;
for (i = 0; i < GM_TEXTURE_LAYER_SIZE; ++i) {
/* u then v */
GMUpdateMappingGen(PipelineMappingGenType[i].u, PipelineVertex[0]->pos.x, PipelineVertex[0]->nrm.x, &PipelineVertex[0]->map[i].x);
GMUpdateMappingGen(PipelineMappingGenType[i].v, PipelineVertex[0]->pos.y, PipelineVertex[0]->nrm.y, &PipelineVertex[0]->map[i].y);
}
}
/* lighting */
if (PipelineRenderStatus[GM_LIGHTING]) {
GM_SetColorIntensity (
PipelineVertex[0]->clr,
(GMint)((0.5f + fabs(PipelineVertex[0]->nrm.z) / 2) * 255)
);
}
/*
* Rasterization of the point now begins. This JUST for a point
* more work is required for lines, and triangles.
*/
PipelineRenderIndex = 0;
PipelineTextureLayer = 0;
PipelineTextureMipMapLevel = 0;
/*
* We need to multiply yhe vertices against the modelview
* matrix otherwise they won't display at all.
*/
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[0]->pos);
/*
* Abort thy captin, abort thy mission. Cannon balls everywhere!!
* thy detriment of floating is ye peak imminent deaths, yar scurvy!?
*/
if (PipelineVertex[0]->pos.z <= 0)
return;
/*
* Project the 2D window coordinates against the vertex position
* since this is planar.
*/
GMProject3DTo2D(&PipelineVertex[0]->pos);
/* invert z */
PipelineVertex[0]->pos.z = 1.0 / \
PipelineVertex[0]->pos.z;
/* store blend function */
GMBlendProc blend = PipelineBlendFunc;
do {
if (PipelineTexture[PipelineTextureLayer])
GMRenderPointTexture();
else if(PipelineTextureLayer == 0)
GMRenderPointColor ();
/* we stored because we overwrite here */
PipelineBlendFunc = GMBlendLight;
} while (++PipelineTextureLayer < PipelineMaxTextureLayer);
/* restore blend function */
PipelineBlendFunc = blend;
}
GMAPI void gmDrawLine(GMvoid) {
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[0]->pos);
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[1]->pos);
GMUpdateVertices();
PipelineRenderIndex = 0;
PipelineTextureLayer = 0;
PipelineTextureMipMapLevel = 0;
if (PipelineVertex[0]->pos.z <= 0 || PipelineVertex[1]->pos.z <= 0)
return;
GMProject3DTo2D(&PipelineVertex[0]->pos);
GMProject3DTo2D(&PipelineVertex[1]->pos);
if (GM_PointInside(PipelineVertex[0]->pos) || GM_PointInside(PipelineVertex[1]->pos))
return;
PipelineVertex[0]->pos.z = 1.0 / PipelineVertex[0]->pos.z;
PipelineVertex[1]->pos.z = 1.0 / PipelineVertex[1]->pos.z;
GMBlendProc blend = PipelineBlendFunc;
do {
if (PipelineTexture[PipelineTextureLayer]) {
GM_ResizeTextureCoordinates;
if (PipelineRenderStatus[GM_COLOR_SMOOTH])
GMRenderLineTextureColorSmooth();
else
GMRenderLineTextureColorLinear();
} else if (PipelineTextureLayer == 0) {
if (PipelineRenderStatus[GM_COLOR_SMOOTH])
GMRenderLineSolidColorSmooth();
else
GMRenderLineSolidColorLinear();
}
PipelineBlendFunc = GMBlendLight;
} while (++PipelineTextureLayer < PipelineMaxTextureLayer);
PipelineBlendFunc = blend;
}
GMAPI void gmDrawLineStrip(GMvoid) {
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[0]->pos);
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[1]->pos);
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[2]->pos);
GMUpdateVertices();
PipelineRenderIndex = 0;
PipelineTextureLayer = 0;
if (PipelineVertex[0]->pos.z <= 0 || PipelineVertex[1]->pos.z <= 0 || PipelineVertex[2]->pos.z <= 0)
return;
GMProject3DTo2D(&PipelineVertex[0]->pos);
GMProject3DTo2D(&PipelineVertex[1]->pos);
GMProject3DTo2D(&PipelineVertex[2]->pos);
if (GM_PointInside(PipelineVertex[0]->pos) ||
GM_PointInside(PipelineVertex[1]->pos) ||
GM_PointInside(PipelineVertex[2]->pos))
return;
PipelineVertex[0]->pos.z = 1.0 / PipelineVertex[0]->pos.z;
PipelineVertex[1]->pos.z = 1.0 / PipelineVertex[1]->pos.z;
PipelineVertex[2]->pos.z = 1.0 / PipelineVertex[2]->pos.z;
GMint smooth = !GMCheckColorEquality
(
&PipelineVertex[0]->clr,
&PipelineVertex[1]->clr,
&PipelineVertex[2]->clr
);
GMBlendProc blend = PipelineBlendFunc;
#define LINE(P) \
do { \
PipelineV0 = 0, PipelineV1 = 2, P(); \
PipelineV0 = 1, PipelineV1 = 2, P(); \
PipelineV0 = 2, PipelineV1 = 0, P(); \
} while (0)
do {
if (PipelineTexture[PipelineTextureLayer]) {
GMComputeMipMapLevel();
GM_ResizeTextureCoordinates;
if (PipelineRenderStatus[GM_COLOR_SMOOTH] && smooth)
LINE(GMRenderLineTextureColorSmooth);
else
LINE(GMRenderLineTextureColorLinear);
} else if (PipelineTextureLayer == 0) {
if (PipelineRenderStatus[GM_COLOR_SMOOTH] && smooth)
LINE(GMRenderLineSolidColorSmooth);
else
LINE(GMRenderLineSolidColorLinear);
}
#undef LINE
PipelineBlendFunc = GMBlendLight;
} while (++PipelineTextureLayer < PipelineMaxTextureLayer);
PipelineV0 = 0;
PipelineV1 = 1;
PipelineBlendFunc = blend;
}
GMAPI void gmDrawRectangle(GMvoid) {
PipelineRenderIndex = 0;
PipelineTextureMipMapLevel = 0;
if (PipelineTexture[0]) {
if (PipelineRenderStatus[GM_TEXTURE_SMOOTH])
GMRenderRectangleTextureSizePixelSmooth();
else
GMRenderRectangleTextureSizePixelLinear();
} else {
if (PipelineRenderStatus[GM_TEXTURE_SMOOTH])
GMRenderRectangleColorSmooth();
else
GMRenderRectangleColorLinear();
}
}
GMAPI void gmDrawTriangle(GMvoid) {
PipelineRenderIndex = 0;
PipelineTextureLayer = 0;
if (PipelineUpdateVerticesMatrix == GM_TRUE) {
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[0]->pos);
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[1]->pos);
GMMultMatrix4f(&PipelineRenderMatrix[GM_MODELVIEW_MATRIX], &PipelineVertex[2]->pos);
GMUpdateVertices();
}
/* check depth */
if ((PipelineVertex[0]->pos.z < PipelinePlaneNear &&
PipelineVertex[1]->pos.z < PipelinePlaneNear &&
PipelineVertex[2]->pos.z < PipelinePlaneNear)||
(PipelineVertex[0]->pos.z > PipelinePlaneFar &&
PipelineVertex[1]->pos.z > PipelinePlaneFar &&
PipelineVertex[2]->pos.z > PipelinePlaneFar))
return;
GMint vert[2];
GMint outv = 0;
if (PipelineVertex[0]->pos.z < PipelinePlaneNear) vert[outv++] = 0;
if (PipelineVertex[1]->pos.z < PipelinePlaneNear) vert[outv++] = 1;
if (PipelineVertex[2]->pos.z < PipelinePlaneNear) vert[outv++] = 2;
if (outv == 1) {
GMint verts[2];
GMint third;
GMGetFreeVisibleVertexIndex(*vert, verts);
memcpy (
&PipelineVertex[3] ->pos.x,
&PipelineVertex[vert[0]]->pos.x,
sizeof(GMVertex)
);
GMGetVertexClippingPlane(
&PipelineVertex[vert [0]],
PipelineVertex[verts[0]]
);
GMGetVertexClippingPlane(
&PipelineVertex[3],
PipelineVertex[verts[1]]
);
/* third index for second triangle */
if ((third = *vert - 1) < 0) third = 2;
/* save all */
memcpy(&PipelineMemVertex[0]->pos.x, &PipelineVertex[third]->pos.x, sizeof(GMVertex));
memcpy(&PipelineMemVertex[1]->pos.x, &PipelineVertex[3] ->pos.x, sizeof(GMVertex));
memcpy(&PipelineMemVertex[2]->pos.x, &PipelineVertex[*vert]->pos.x, sizeof(GMVertex));
} else if (outv == 2) {
GMint second = GMGetFreeVisibleVertexIndexi(vert[0], vert[1]);
/* clamp */
GMGetVertexClippingPlane(&PipelineVertex[vert[0]], PipelineVertex[second]);
GMGetVertexClippingPlane(&PipelineVertex[vert[1]], PipelineVertex[second]);
}
/* project it */
GMProject3DTo2D(&PipelineVertex[0]->pos);
GMProject3DTo2D(&PipelineVertex[1]->pos);
GMProject3DTo2D(&PipelineVertex[2]->pos);
/* visible? */
if (!GMTriangleFaceVisible() || !GMTriangleVisible())
return;
/* sort */
if (PipelineVertex[0]->pos.y > PipelineVertex[1]->pos.y)
GMSwapVertex(&PipelineVertex[0], &PipelineVertex[1]);
if (PipelineVertex[0]->pos.y > PipelineVertex[2]->pos.y)
GMSwapVertex(&PipelineVertex[0], &PipelineVertex[2]);
if (PipelineVertex[1]->pos.y > PipelineVertex[2]->pos.y)
GMSwapVertex(&PipelineVertex[1], &PipelineVertex[2]);
/* clamp */
GMGetTriangleArea(&PipelineOptYStart, &PipelineOptYEnd);
PipelineYStart = (GMint)PipelineVertex[0]->pos.y;
PipelineMiddle = (GMint)PipelineVertex[1]->pos.y;
PipelineYEnd = (GMint)PipelineVertex[2]->pos.y;
/* compute dimensions */
PipelineMiddleStart = PipelineMiddle - PipelineYStart;
PipelineEndMiddle = PipelineYEnd - PipelineMiddle;
PipelineEndStart = PipelineYEnd - PipelineYStart;
/* invert z */
PipelineVertex[0]->pos.z = 1.0 / PipelineVertex[0]->pos.z;
PipelineVertex[1]->pos.z = 1.0 / PipelineVertex[1]->pos.z;
PipelineVertex[2]->pos.z = 1.0 / PipelineVertex[2]->pos.z;
GMint smooth = !GMCheckColorEquality (
&PipelineVertex[0]->clr,
&PipelineVertex[1]->clr,
&PipelineVertex[2]->clr
);
GMBlendProc blend = PipelineBlendFunc;
//printf("entering loop\n");
do {
//printf("entered loop\n");
if (PipelineTexture[PipelineTextureLayer]) {
GMComputeMipMapLevel();
if (PipelineRenderStatus[GM_TEXTURE_SMOOTH]) {
if (PipelineRenderStatus[GM_COLOR_SMOOTH] && smooth)
GMRenderTriangleTexturePixelSmoothColorSmooth();
else
GMRenderTriangleTexturePixelSmoothColorLinear();
} else {
if (PipelineRenderStatus[GM_COLOR_SMOOTH] && smooth)
GMRenderTriangleTexturePixelLinearColorSmooth();
else
GMRenderTriangleTexturePixelLinearColorLinear();
}
} else {
if (PipelineRenderStatus[GM_COLOR_SMOOTH] && smooth)
GMRenderTriangleSolidColorSmooth();
else
GMRenderTriangleSolidColorLinear();
}
PipelineBlendFunc = GMBlendLight;
} while(++PipelineTextureLayer < PipelineMaxTextureLayer);
PipelineBlendFunc = blend;
if (outv == 1) {
GMSwapVertex(&PipelineVertex[0], &PipelineMemVertex[0]);
GMSwapVertex(&PipelineVertex[1], &PipelineMemVertex[1]);
GMSwapVertex(&PipelineVertex[2], &PipelineMemVertex[2]);
PipelineUpdateVerticesMatrix = GM_FALSE;
gmDrawTriangle();
PipelineUpdateVerticesMatrix = GM_TRUE;
}
}
/* init and shutdown for the rasterizer */
GMAPI void gmCreate(GMint w, GMint h) {
GMint i;
PipelineScreenWidth = w;
PipelineScreenHeight = h;
PipelineDepthBufferSize = w * h;
PipelinePixelBufferSize = PipelineDepthBufferSize << 2;
PipelinePixelBuffer = (_GMpixel_t*)calloc(PipelinePixelBufferSize, sizeof(_GMpixel_t));
PipelineDepthBuffer = (GMfloat*) calloc(PipelineDepthBufferSize, sizeof(GMfloat));
memset(PipelinePixelBuffer, 0, sizeof(_GMpixel_t) * PipelinePixelBufferSize);
memset(PipelineDepthBuffer, 0, sizeof(GMfloat) * PipelineDepthBufferSize);
/* vertex render pipeline allocations */
for (i = 0; i < GM_RENDER_PIPELINE_SIZE; ++i) {
PipelineVertex [i] = (GMVertex*)malloc(sizeof(GMVertex));
PipelineMemVertex[i] = (GMVertex*)malloc(sizeof(GMVertex));
memset(PipelineVertex [i], 0, sizeof(GMVertex));
memset(PipelineMemVertex[i], 0, sizeof(GMVertex));
}
/* lighting allocations */
for (i = 0; i < GM_MAX_LIGHT_COUNT; ++i) {
PipelineLight[i] = (GMLight*)malloc(sizeof(GMLight));
memset(PipelineLight[i], 0, sizeof(GMLight));
}
/* default texture settings */
for (i = 0; i < GM_TEXTURE_LAYER_SIZE; ++i) {
PipelineMappingGenType[i].u = GM_OBJECT_LINEAR;
PipelineMappingGenType[i].v = GM_OBJECT_LINEAR;
}
memset(&PipelineClearColor, 0, sizeof(GMint) * 4);
/* default global render */
gmRenderMode (GM_RENDER_FRONT);
gmBlendFunc (GM_BLEND_NORMAL);
gmViewport (0,0, PipelineScreenWidth, PipelineScreenHeight);
gmPerspective(1.0f, 100.0f, PipelineScreenWidth/2);
/* matrix states */
for (i = GM_MODELVIEW_MATRIX; i <= GM_TEXTURE_MATRIX; ++i) {
gmMatrixMode (GM_MODELVIEW_MATRIX);
gmLoadIdentity();
}
}
GMAPI void gmDestroy(GMvoid) {
GMint i;
free(PipelinePixelBuffer);
free(PipelineDepthBuffer);
for (i = 0; i < GM_RENDER_PIPELINE_SIZE; ++i) {
free(PipelineVertex [i]);
free(PipelineMemVertex[i]);
}
for (i = 0; i < GM_MAX_LIGHT_COUNT; ++i) {
free(PipelineLight[i]);
}
}
/*
* TODO: make work like OpenGL
*/
GMAPI void gmBegin() { }
GMAPI void gmEnd(GMvoid) {
gmDrawRectangle();
}
#ifndef _GMNOTEST
#include <stdio.h>
int main() {
int i;
gmCreate(10, 10);
gmEnable(GM_NORMALIZE);
gmEnable(GM_COLOR_SMOOTH);
for (i = 0; i < 255; i++) {
gmClear(GM_CLEAR_COLOR | GM_CLEAR_DEPTH);
gmLoadIdentity();
gmMatrixMode(GM_MODELVIEW_MATRIX);
gmBegin();
gmColor3i ( 1, 0, 0);
gmVertex3i( 0, 1, 5);
gmColor3i ( 0, 1, 0);
gmVertex3i( 1, -1, 5);
gmColor3i ( 0, 0, 1);
gmVertex3i(-1, -1, 5);
gmEnd();
}
for (i = 0; i < 100; i++)
printf("%d, ", PipelinePixelBuffer[i]);
gmDestroy();
printf("Vendor: %s\n", gmGetString(GM_VENDOR));
printf("Renderer: %s\n", gmGetString(GM_RENDERER));
printf("Version: %s\n", gmGetString(GM_VERSION));
return 0;
}
#endif
/*
* gmrast a public domain opengl like software rasterizer.
*/
#ifndef GMRAST_HDR_USED__
#define GMRAST_HDR_USED__
#pragma once
/* cplusplus handling */
#if defined(__cplusplus)
extern "C" {
#endif
/*
* Proper export if creating a dynamically linked library.
* This should be extended for other calling convention support.
*/
#if defined(GMRAST_LIBRARY)
# if defined(WIN32) || defined(_WIN32)
# if defined(__cplusplus)
# define GMAPI extern "C" __declspec(dllexport)
# else
# define GMAPI __declspec(dllexport)
# endif
# else defined(__linux__) || defined(linux)
# define GMAPI /* nothing */
# endif
#else
# define GMAPI /* nothing */
#endif
/*
* TODO:
* selecting alternitive inline keywords for compilers that
* don't support inline (C99).
*/
#define GMINLINE inline
/* internal types */
typedef unsigned char _GMpixel_t;
typedef int _GMcolor_t;
/* external types */
typedef int GMenum;
typedef unsigned int GMuint;
typedef unsigned char GMubyte;
typedef void GMvoid;
typedef _Bool GMboolean;
typedef char GMbyte;
typedef short GMshort;
typedef int GMint;
typedef float GMfloat;
typedef double GMdouble;
typedef unsigned int GMsizei;
#define GM_TRUE 1
#define GM_FALSE 0
/* status */
#define GM_DEPTH_TEST 0
#define GM_ALPHA_TEST 1
#define GM_SCISSOR_TEST 2
#define GM_BLEND_TEST 3
#define GM_NORMALIZE 4
#define GM_LIGHTING 5
#define GM_BLEND 6
#define GM_TEXGEN 7
#define GM_COLOR_SMOOTH 8
#define GM_TEXTURE_SMOOTH 9
#define GM_TEXTURE_MIPMAPS 10
/* todo more lights */
#define GM_LIGHT0 11
#define GM_STATUS_COUNT 12
/* clear */
#define GM_CLEAR_COLOR 0x01
#define GM_CLEAR_DEPTH 0x02
/* face modes */
#define GM_RENDER_FRONT 0
#define GM_RENDER_BACK 1
#define GM_RENDER_BOTH 2
/* blending */
#define GM_BLEND_NORMAL 0
#define GM_BLEND_BRIGHT 1
#define GM_BLEND_DARK 2
#define GM_MODELVIEW_MATRIX 0
#define GM_PROJECTION_MATRIX 1
#define GM_NORMAL_MATRIX 2
#define GM_TEXTURE_MATRIX 3
#define GM_VIEWPORT_MATRIX 4
#define GM_MATRIX_COUNT (GM_VIEWPORT_MATRIX+1)
#define GM_INTEGER 0
#define GM_UNSIGNED_BYTE 1
#define GM_FLOAT 2
/* data comps */
#define GM_DEPTH 0
#define GM_GRAY 1
#define GM_GRAYALPHA 2
#define GM_RGB 3
#define GM_RGBA 4
/* texcoord */
#define GM_TEXTURE_GEN_U 0
#define GM_TEXTURE_GEN_V 1
/* mapping generation types */
#define GM_OBJECT_LINEAR 0
#define GM_EYE_LINEAR 1
#define GM_SPHERE_MAP 2
/*
* TODO:
* GM_NORMAL_MAP
* GM_REFLECTION_MAP
*/
/*
* These are just candy. The version is the only actual thing worth
* implementing. But to stay OpenGL-like we have implement these.
*/
#define GM_VENDOR 0
#define GM_RENDERER 1
#define GM_VERSION 2
/* rectangle types */
#define GM_RECT_LINEAR 0
#define GM_RECT_SMOOTH 1
#define GM_RECT_TEXTURELINEAR 2
#define GM_RECT_TEXTURESMOOTH 3
/* information types */
#define GM_SCISSOR 0
#define GM_VIEWPORT 1
#define GM_PERSPECTIVE 2
/* lighting types */
#define GM_LIGHT_DIRECTIONAL 0
#define GM_LIGHT_POINT 1
#define GM_LIGHT_SPOT 2
/* attributes (used internally) */
#define GM_LIGHT_TYPE 0
#define GM_DIFFUSE 1
#define GM_AMBIENT 2
#define GM_TRANSFORMATION 3
#define GM_DIRECTION 4
/*
* API interface:
* TODO: a lot
*/
GMAPI const GMbyte *gmGetString (GMenum);
GMAPI GMenum gmGetError (GMvoid);
GMAPI void gmGetValue (GMenum, GMvoid *);
GMAPI void gmEnable (GMenum);
GMAPI void gmDisable (GMenum);
GMAPI void gmSetStatus (GMenum, GMint);
GMAPI GMint gmGetStatus (GMenum);
GMAPI void gmScissor (GMint, GMint, GMsizei, GMsizei);
GMAPI void gmViewport (GMint, GMint, GMsizei, GMsizei);
GMAPI void gmPerspective (GMfloat, GMfloat, GMfloat);
GMAPI void gmRenderMode (GMenum);
GMAPI void gmBlendFunc (GMenum);
GMAPI void gmLighti (GMenum, GMenum, const GMint);
GMAPI void gmLightfv (GMenum, GMenum, const GMfloat*);
GMAPI void gmGenTexture (GMuint*);
GMAPI void gmDeleteTexture(GMuint*);
/*
* gmBindTexture doesn't have support for target textures.
* it's assumed GL_TEXTURE_2D always. This may change in
* the future.
*
* TODO:
* Target texture for binding.
* GM_TEXTURE_1D
* GM_TEXTURE_2D
* GM_TEXTURE_3D
* GM_TEXTURE_CUBE_MAP (GL 1.3 or greater)
*/
GMAPI void gmBindTexture (GMuint);
/*
* This only unbinds the current bound texture. OpenGL doesn't have this
* instead you call glBindTexture(NULL) or glDisable(GL_TEXTURE_2D) to
* unbind a texture.
*
* TODO: remove and implement proper GL_TEXTURE_2D state enabling / disabling
* as well as NULL for gmBindTexture. This will change when gmBindTexture
* supports target texture selection.
*
* The gmGetCurTexture is a convience function. This will be deprecated
* eventually once the texture state system allows target texture binding
*/
GMAPI void gmUnbindTexture(GMvoid);
GMAPI GMuint gmGetCurTexture(GMvoid);
/*
* TODO: Implement params as per OpenGL spec.
* Specifies a pointer to an array of texture generation parameters. If pname is
* GL_TEXTURE_GEN_MODE, then the array must contain a single symbolic constant, one of
* GL_OBJECT_LINEAR, GL_EYE_LINEAR, or
* GL_SPHERE_MAP. Otherwise, params holds the coefficients for the
* texture-coordinate generation function specified by pname.
*/
GMAPI void gmTexGen (GMint, GMint);
/*
* gm{Copy}Tex{Sub}Image Functions begin. These are slow functions. TODO
* SIMD optimized variants. Perhaps eihrul will help me with that.
*/
GMAPI void gmTexSubImage2D (GMuint, GMuint, GMint, GMenum, const GMvoid*);
GMAPI void gmCopyTexSubImage2D(GMuint, GMuint, GMint, GMint, GMint, GMint, GMenum);
GMAPI void gmTexImage2D (GMuint, GMuint, GMint, GMint, GMint, GMenum, const GMvoid*);
GMAPI void gmCopyTexImage2D (GMuint, GMuint, GMint, GMint, GMint, GMint, GMint, GMint);
/*
* gm{Vertex/Normal/TexCoord/Color}{2/3/4}{i/f} definitions begin.
* TODO: *fv variants.
*/
GMAPI void gmVertex2i (GMint,GMint);
GMAPI void gmVertex3i (GMint,GMint,GMint);
GMAPI void gmVertex4i (GMint,GMint,GMint,GMint);
GMAPI void gmVertex2f (GMfloat,GMfloat);
GMAPI void gmVertex3f (GMfloat,GMfloat,GMfloat);
GMAPI void gmVertex4f (GMfloat,GMfloat,GMfloat,GMfloat);
GMAPI void gmNormal3i (GMint,GMint,GMint);
GMAPI void gmNormal3f (GMfloat,GMfloat,GMfloat);
/*
* These don't work like OpenGL functions at all, the first argument
* is the current layer you want to the texcoords for.
*
* TODO:
* proper emulation
*/
GMAPI void gmTexCoord2i(GMint, GMint, GMint);
GMAPI void gmTexCoord2f(GMint, GMfloat, GMfloat);
/*
* TODO:
*
* standard functions
* gmColor3b
* gmColor3s
* gmColor3d
* gmColor3ub
* gmColor3us
* gmColor3ui
* gmColor4b
* gmColor4s
* gmColor4d
* gmColor4ub
* gmColor4us
* gmColor4ui
*
* pointer versions
* gmColor3bv
* gmColor3sv
* gmColor3iv
* gmColor3fv
* gmColor3dv
* gmColor3usv
* gmColor3uiv
* gmColor4bv
* gmColor4sv
* gmColor4iv
* gmColor4fv
* gmColor4dv
* gmColor4ubv
* gmColor4usv
* gmColor4uiv
*/
GMAPI void gmColor3i (GMint, GMint, GMint);
GMAPI void gmColor3f (GMfloat,GMfloat,GMfloat);
GMAPI void gmColor4i (GMint, GMint, GMint, GMint);
GMAPI void gmColor4f (GMfloat,GMfloat,GMfloat,GMfloat);
GMAPI void gmMatrixMode (GMint);
GMAPI void gmLoadIdentity(GMvoid);
GMAPI void gmLoadMatrix (const GMfloat*);
/*
* This doesn't work like OpenGL at all this simply returns the
* modelview matrix to the argument. This doesn't use GLenum to
* select the matrix type and return it.
*
* TODO
* proper emulation
*/
GMAPI void gmGetMatrix (GMfloat*);
/*
* This updates the normal matrix. The FSM doesn't do it because
* explicit control allows for more fine-tuned update. This is because
* updating the matrix is pointless-ly expensive. Which when done
* every-frame would be a detriment to the speed of this rasterizer.
*
* TODO:
* optimize update inside FSM via SIMD?
*/
GMAPI void gmUpdateNormalMatrix(GMvoid);
GMAPI void gmClearColor(GMint, GMint, GMint);
GMAPI void gmClear (GMbyte);
GMAPI void gmFinish (GMvoid);
/*
* gmBegin isn't implemented. Instead the following functions are used
* gmDrawPoint () -- glBegin(GL_POINTS)
* gmDrawLine () -- glBegin(GL_LINES)
* gmDrawLineStrip() -- glBegin(GL_LINE_STRIP)
* gmDrawRectangle() -- glBegin(GL_QUAD)
* gmDrawTriangle () -- glBegin(GL_TRIANGLE)
*
* TODO:
* gmDrawTriangleStrip
* gmDrawRectangleStrip
* gmDrawLineLoop
* gmDrawTringleFan
*
* However -D_GMBEGIN or #define _GMBEGIN will enable a macro that emulates
* the functionality of glBegin.
*/
GMAPI void gmDrawPoint (GMvoid);
GMAPI void gmDrawLine (GMvoid);
GMAPI void gmDrawLineStrip(GMvoid);
GMAPI void gmDrawRectangle(GMvoid);
GMAPI void gmDrawTriangle (GMvoid);
#if defined(__cplusplus)
/* extern "C" { */
}
#endif
#endif /* !GMRAST_HDR_USED__ */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment