home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CDPD Public Domain Collection for CDTV 3
/
CDPDIII.bin
/
pd
/
graphics
/
3d
/
icoons
/
source
/
render.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-23
|
14KB
|
499 lines
/* :ts=8 */ /* Yes some of us still use vi ! */
/************************************************************************/
/* */
/* This file contains code which is called from tesselate.c to render */
/* the current object. */
/* */
/************************************************************************/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
/* SIPP include files: */
#include "sipp.h"
#include "shaders.h"
#include "general.h"
#include "globals.h"
#include "intui.h"
#include "spl_gfx.h"
#include "tesselate.h"
#include "render.h"
static Strauss_desc Strauss_Shader_Data;
static double Min_Value;
static double Max_Value;
static double View_Distance;
static SObject *SIPP_Object = NULL;
static int Current_Pen = -1;
static Byte_T *Error_Diffusion_Table = NULL;
static
void Error_Diffusion_Free()
/************************************************************************/
/* */
/* Free work space used by the Floyd-Steinberg algorithm. */
/* */
/************************************************************************/
{
int i;
if (Error_Diffusion_Table == NULL) return;
free(Error_Diffusion_Table);
Error_Diffusion_Table = NULL;
} /* Error_Diffusion_Free */
static
void Error_Diffusion_Init()
/************************************************************************/
/* */
/* Allocate and initialize workspace used by the Floyd-Steinberg */
/* algorithm. */
/* */
/************************************************************************/
{
int i;
Error_Diffusion_Free();
Error_Diffusion_Table = (Byte_T *)
malloc(Screen_Width * sizeof(Byte_T)+1);
if (Error_Diffusion_Table == NULL) {
Display_Error_Message("Couldn't allocate memory");
CloseStuff();
exit(2);
} /* if */
for (i = 0; i < Screen_Width; i++) Error_Diffusion_Table[i] = 0;
} /* Error_Diffusion_Init */
static
short Error_Diffusion_Correct(int X, short Value)
/************************************************************************/
/* */
/* Compute and return actual pen id to be used to plot a color at column*/
/* X with the value 'Value' (0 - 255). */
/* We use a slightly modified Floyd-Steinberg error diffusion algoritm */
/* to do this. */
/* */
/* The modification is that the below-right term is ignored. */
/* */
/************************************************************************/
{
short Pen, E, E_Right, E_BelowLeft, E_Below;
Value += Error_Diffusion_Table[X]; /* Correct for earlier error */
Pen = (Value + 8) >> 4;
E = Value - (Pen << 4); /* Error */
E_Right = ((7 * E) + 8) >> 4;
E_BelowLeft = ((3 * E) + 8) >> 4;
E_Below = E - E_Right - E_BelowLeft;
Error_Diffusion_Table[X+1] += E_Right; /* right */
Error_Diffusion_Table[X-1] += E_BelowLeft; /* Below left */
Error_Diffusion_Table[X] = E_Below; /* Below */
return(Pen);
} /* Error_Diffusion_Correct */
static
void Set_Color_Table_Gray(Boolean_T On)
/************************************************************************/
/* */
/* If On is TRUE, then set the colortable to a grayscale, otherwise */
/* restore the colortable. */
/* */
/************************************************************************/
{
int i;
if (On) {
for (i = 0; i < 16; i++)
SetRGB4(Windows[Id_Active_Window].ViewPort, i, i, i, i);
} else {
for (i = 0; i < 16 && ScreenColors[i].ColorIndex >= 0; i++)
SetRGB4(Windows[Id_Active_Window].ViewPort,
ScreenColors[i].ColorIndex,
ScreenColors[i].Red,
ScreenColors[i].Green,
ScreenColors[i].Blue);
}
} /* Set_Color_Table_Gray */
static
void Draw_Line(char *Data, /* Not used */
int Col1, int Row1, /* Start pos */
int Col2, int Row2) /* End pos */
/************************************************************************/
/* */
/* Draw a line from (Col1, Row1) to (Col2, Row2). */
/* */
/************************************************************************/
{
short X1, Y1, X2, Y2;
if (Current_Pen < 0) {
Current_Pen = 1;
SetAPen(Windows[Id_Active_Window].RastPort, Current_Pen);
}
X1 = Views[V_P].X_Min + (Col1 << 1);
Y1 = Views[V_P].Y_Min + Row1;
X2 = Views[V_P].X_Min + (Col2 << 1);
Y2 = Views[V_P].Y_Min + Row2;
Move(Windows[Id_Active_Window].RastPort, X1, Y1);
Draw(Windows[Id_Active_Window].RastPort, X2, Y2);
} /* Draw_Line */
static
void Draw_Pixel(char *Data, /* Not used */
int Col, int Row, /* Position */
unsigned char R, /* Red */
unsigned char G, /* Green */
unsigned char B) /* Blue */
/************************************************************************/
/* */
/* Draw a pixel on position (Col, Row) with the color given by R, G, B. */
/* */
/* Simplifications: */
/* */
/* We know that we're working with a monochrome picture, so we only */
/* look at R (red). */
/* */
/* We disregard aspect ratio, but plot 2 horizontal pixels each time */
/* (corresponding to an aspect ration of 0.91 in PAL). */
/* */
/* Actual pen is chosen based on a slightly modified Floyd - Steinberg */
/* error diffusion algoritm. */
/* */
/************************************************************************/
{
short X, Y;
int Pen;
static int Old_Row = -1;
if (Old_Row != Row) {
sprintf(Error_Msg, "Rendering row %d", Row);
Display_Status(Error_Msg);
Old_Row = Row;
}
if (R == 0) {
/* Trick to speed up for black color: */
/* Ignore error correction for this pixel, and return */
/* immediately, as we know that all pixels are black by */
/* default. */
Error_Diffusion_Table[Col] = 0;
return; /* Not entirely correct, but faster */
} else Pen = Error_Diffusion_Correct(Col, R);
/* We have already set full screen to black, so there's */
/* no need to actually plot the black pixels. */
if (Pen == 0) return;
if (Pen != Current_Pen) {
SetAPen(Windows[Id_Active_Window].RastPort, Pen);
Current_Pen = Pen;
}
/* Plot 2 pixels for each column. */
X = Views[V_P].X_Min + (Col << 1);
Y = Views[V_P].Y_Min + Row;
WritePixel(Windows[Id_Active_Window].RastPort, X, Y);
WritePixel(Windows[Id_Active_Window].RastPort, X+1, Y);
} /* Draw_Pixel */
static
void Create_SIPP_Polygon(Vector_T Point0, Vector_T Point1,
Vector_T Point2, Vector_T Point3)
/************************************************************************/
/* */
/* Add the square given by the four points to polygon stack. */
/* */
/************************************************************************/
{
Boolean_T Face1_Ok, Face2_Ok;
int i;
Face1_Ok = Face_Is_Ok(Point0, Point1, Point2);
Face2_Ok = Face_Is_Ok(Point1, Point2, Point3);
if (Face1_Ok) {
vertex_push(Point0[0], Point0[1], Point0[2]);
vertex_push(Point1[0], Point1[1], Point1[2]);
vertex_push(Point2[0], Point2[1], Point2[2]);
polygon_push();
for (i = 0; i < 3; i++) {
if (Point0[i] < Min_Value) Min_Value = Point0[i];
if (Point0[i] > Max_Value) Max_Value = Point0[i];
if (Point1[i] < Min_Value) Min_Value = Point1[i];
if (Point1[i] > Max_Value) Max_Value = Point1[i];
if (Point2[i] < Min_Value) Min_Value = Point2[i];
if (Point2[i] > Max_Value) Max_Value = Point2[i];
} /* for */
} /* if */
if (Face2_Ok) {
vertex_push(Point2[0], Point2[1], Point2[2]);
vertex_push(Point1[0], Point1[1], Point1[2]);
vertex_push(Point3[0], Point3[1], Point3[2]);
polygon_push();
for (i = 0; i < 3; i++) {
if (Point1[i] < Min_Value) Min_Value = Point1[i];
if (Point1[i] > Max_Value) Max_Value = Point1[i];
if (Point2[i] < Min_Value) Min_Value = Point2[i];
if (Point2[i] > Max_Value) Max_Value = Point2[i];
if (Point3[i] < Min_Value) Min_Value = Point3[i];
if (Point3[i] > Max_Value) Max_Value = Point3[i];
} /* for */
} /* if */
} /* Create_SIPP_Polygon */
Boolean_T Render_Setup()
/************************************************************************/
/* */
/* Tesselate the current object, and add faces to SIPP. */
/* This can take quite some time... */
/* */
/************************************************************************/
{
Tesselate_Info_T TI;
Boolean_T Error;
Surface *SIPP_Surface;
static Boolean_T First_Time = TRUE;
Render_Cleanup();
Min_Value = 999999.9;
Max_Value = -999999.9;
TI.Patch_Begin = NULL;
TI.Patch_Generate_Face = Create_SIPP_Polygon;
TI.Patch_End = NULL;
if (First_Time) {
First_Time = FALSE;
sipp_init();
} /* if */
Error = Tesselate_Object(&TI);
if (Error) return(Error);
Strauss_Shader_Data.ambient = 0.0; /* 0 - 1 */
Strauss_Shader_Data.smoothness = 0.5;
Strauss_Shader_Data.metalness = 0.5;
Strauss_Shader_Data.color.red = 1.0;
Strauss_Shader_Data.color.grn = 1.0;
Strauss_Shader_Data.color.blu = 1.0;
Strauss_Shader_Data.opacity.red = 1.0;
Strauss_Shader_Data.opacity.grn = 1.0;
Strauss_Shader_Data.opacity.blu = 1.0;
Display_Status("Creating surface");
SIPP_Surface = surface_create(&Strauss_Shader_Data, strauss_shader);
Display_Status("Creating object");
SIPP_Object = object_create();
object_add_surface(SIPP_Object, SIPP_Surface);
object_add_subobj(sipp_world, SIPP_Object);
Min_Value = ABS(Min_Value);
Max_Value = ABS(Max_Value);
View_Distance = (Min_Value > Max_Value) ? Min_Value : Max_Value;
View_Distance = View_Distance * 3.0;
Display_Status("Render setup done");
return(FALSE);
} /* Render_Setup */
void Render_Cleanup()
/************************************************************************/
/* */
/* Free memory etc. used in rendering. */
/* */
/************************************************************************/
{
if (SIPP_Object == NULL) return;
object_sub_subobj(sipp_world, SIPP_Object);
object_delete(SIPP_Object);
SIPP_Object = NULL;
} /* Render_Cleanup */
Boolean_T Render_Object()
/************************************************************************/
/* */
/* render the current object. */
/* */
/* Render_Setup should be called before this routine. */
/* Render_Cleanup should be called later to release memory etc. */
/* */
/************************************************************************/
{
Vector_T P, RP;
Boolean_T Error;
Lightsource *SIPP_Lightsource;
int Width, Height;
int Mode;
if (SIPP_Object == NULL) return(TRUE);
Set_Pointer(TRUE);
Error_Diffusion_Init();
switch (Rendering_Mode) {
case RM_Phong : Mode = PHONG; break;
case RM_Gouraud : Mode = GOURAUD; break;
case RM_Flat : Mode = FLAT; break;
case RM_Line : Mode = LINE; break;
default : Mode = LINE; break;
} /* switch */
P[0] = 0.0;
P[1] = -View_Distance;
P[2] = 0.0;
Compute_Inverse_Rotation_Matrix(M_InvRotation, Rotation_Vector);
Matrix_MultV(RP, M_InvRotation, P);
sipp_show_backfaces(!Backface_Culling);
camera_params(sipp_camera,
RP[0], RP[1], RP[2],
0.0, 0.0, 0.0, /* Look at */
0.0, 0.0, 1.0, /* Up vector */
0.4); /* Focal factor */
SIPP_Lightsource = lightsource_create(
2.0*RP[0], 2.0*RP[1], 2.0*RP[2], /* Position */
1.0, 1.0, 1.0, /* Color (RGB) */
LIGHT_DIRECTION);
Display_Status("Rendering image");
Current_Pen = -1;
Width = Views[Id_Active_Window].X_Max -
Views[Id_Active_Window].X_Min + 1;
Height = Views[Id_Active_Window].Y_Max -
Views[Id_Active_Window].Y_Min + 1;
Width = Width / 2;
if (Mode != LINE) {
Set_Color_Table_Gray(TRUE);
Clear_All(What_P);
render_image_func(
Width, /* Width */
Height, /* Height */
Draw_Pixel, /* Function */
0, /* Data */
Mode, /* Mode */
1); /* Oversampling */
Display_Status("Rendering Done. Press spacebar");
while (Wait_For_Key() != ' ') ;
Set_Color_Table_Gray(FALSE);
Redraw_Mask |= What_All;
} else {
Clear_All(What_P);
render_image_func(
Width, /* Width */
Height, /* Height */
Draw_Line, /* Function */
0, /* Data */
LINE, /* Mode */
1); /* Oversampling */
Display_Status("Rendering Done. Press spacebar");
while (Wait_For_Key() != ' ') ;
Redraw_Mask |= What_All;
} /* if .. else .. */
Display_Status(NULL);
light_destruct(SIPP_Lightsource);
Error_Diffusion_Free();
Set_Pointer(FALSE);
return(FALSE);
} /* Render_Object */
Boolean_T Render_Tesselate_Object()
/************************************************************************/
/* */
/* Tesselate the current object, and render it. */
/* */
/************************************************************************/
{
Boolean_T Error;
Set_Pointer(TRUE);
if (Render_Setup()) return(TRUE);
Error = Render_Object();
return(Error);
} /* Render_Tesselate_Object */