Created
September 29, 2012 17:49
-
-
Save graphitemaster/3804702 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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