1 /* Stereograph 0.13, 31/03/2000; 2 * renderer, stereographer's engine; 3 * Copyright (c) 2000 by Fabian Januszewski <fabian.linux@januszewski.de> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 */ 19 20 #define _GNU_SOURCE 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <math.h> 25 26 #include "renderer.h" 27 28 29 /* here we go... */ 30 31 struct GFX_DATA Base, Texture, Stereo; 32 struct RENDERER_DATA Renderer; 33 struct PARAMS Param; 34 35 36 /* 1st of all: send all releavant data (pointers) to foreign functions */ 37 int Get_GFX_Pointers(struct PARAMS **pParam, struct GFX_DATA **pBase, struct GFX_DATA **pTexture, struct GFX_DATA **pStereo) { 38 (*pParam) = &Param; 39 (*pBase) = &Base; 40 (*pTexture) = &Texture; 41 (*pStereo) = &Stereo; 42 return 0; 43 } 44 45 46 /* render a mere line */ 47 int ProcessLine(int base_line) { 48 return RenderLine(Base.Data + Base.Width * base_line, Texture.Data + Texture.Width * ((base_line + Param.Starty) % Texture.Height), Stereo.Data + Stereo.Width*base_line*Renderer.nzoom); 49 } 50 51 52 /* destroy memory reservations, internals; NOTE: Stereo also belongs to the renderer! */ 53 void Clear_Renderer(void) 54 { 55 free(Renderer.right_change_map); 56 free(Renderer.left_change_map); 57 free(Renderer.scanline); 58 free(Stereo.Data); 59 } 60 61 62 /* initialize internal rendering engine data */ 63 int Initialize_Renderer(void) 64 { 65 Renderer.startx = Param.Startx; 66 Renderer.max_change_tex_width = (int)((Texture.Width - 1) * Param.Front); 67 Renderer.right_change_map = (float*)malloc(sizeof(float) * (Base.Width + 1)); 68 Renderer.left_change_map = (float*)malloc(sizeof(float) * (Base.Width + 1)); 69 Renderer.naa = Param.AA + 0; 70 Renderer.nzoom = Param.Zoom + 0; 71 Renderer.realdist = Texture.Width * (Param.Distance + 1); 72 Renderer.internal_a = (1.0 / (Texture.Width * Texture.Width)) - (0.25 / (Renderer.realdist * Renderer.realdist)); 73 Renderer.eyeshift = Param.Eyeshift*0.5; 74 if( 75 ((Param.Distance + 1) > 1.0) && ((Param.Distance + 1) <= 9.0) && 76 (Renderer.eyeshift >= -0.5) && (Renderer.eyeshift <= 0.5) && 77 (Renderer.naa > 0) && (Renderer.naa <= 32) && 78 (Renderer.nzoom > 0) && (Renderer.nzoom <= 32) && 79 (Renderer.startx >= 0) && (Renderer.startx < (Base.Width - Texture.Width)) && 80 (Param.Starty >= 0) && 81 (Param.Front > 0.0) && (Param.Front <= 1.0) 82 ) { 83 Renderer.nfactor = Renderer.naa * Renderer.nzoom; 84 if(Param.AA - 1) 85 Renderer.scanline = (int*)malloc(sizeof(int) * (Base.Width * Renderer.nfactor)); 86 else 87 Renderer.scanline = NULL; 88 89 Stereo.Width = Base.Width * Renderer.nzoom; 90 Stereo.Height = Base.Height * Renderer.nzoom; 91 Stereo.Data = (int*)malloc(sizeof(int) * Stereo.Width * Stereo.Height); 92 93 if(Renderer.right_change_map && Renderer.left_change_map && Stereo.Data && Base.Data && Texture.Data) { 94 if(Base.Width >= Texture.Width) 95 return 0; 96 else 97 return -2; 98 } else 99 return -3; 100 } else { 101 return -1; 102 } 103 } 104 105 106 /* Render a mere line of data, may be completely independent of any gfx_data structure */ 107 int RenderLine(int *base_data, int *texture_data, int *stereo_data) 108 { 109 InitMap(Renderer.left_change_map, 0, Base.Width, (float)(Texture.Width + 1)); 110 111 FillMapsLeftToRight(base_data); 112 113 if(Param.AA - 1) 114 { 115 if(Param.Zoom) 116 return ZoomAAScan(stereo_data, texture_data); 117 else 118 return AAScan(stereo_data, texture_data); 119 } else if(Param.Zoom) 120 return ZoomScan(stereo_data, texture_data); 121 else 122 return NormalScan(stereo_data, texture_data); 123 } 124 125 126 /* intialize changemap internals */ 127 int InitMap(float *change_map, int a, int b, float v) 128 { 129 for( ; a < b; a++) 130 change_map[a] = v; 131 return 0; 132 } 133 134 135 /* set changevalues */ 136 int FillMapsLeftToRight(int *base_data) 137 { 138 int z, scanx, i_c; 139 float c; 140 141 /* advanced full centered perspective; reimplemented february 4th 2000 */ 142 /* added eyeshift february 11th 2000 */ 143 for (scanx = 0; scanx < (Base.Width + Texture.Width); scanx++) 144 { 145 if((scanx + (int)(c = GetChange(scanx, base_data))) > 0) { 146 i_c = (int) (scanx + c*(0.5 - Renderer.eyeshift)) + (Texture.Width*Renderer.eyeshift*2.0); 147 if (i_c < Base.Width) { 148 Renderer.right_change_map[i_c] = -c; 149 z = i_c + Renderer.right_change_map[i_c]; 150 if ((z >= 0) && (z < Base.Width)) 151 if (Renderer.left_change_map[z] > c) 152 Renderer.left_change_map[z] = c; 153 } 154 } 155 } 156 157 /* checking for construction errors - `black holes' */ 158 if (Renderer.left_change_map[0] > Texture.Width) 159 Renderer.left_change_map[0] = Texture.Width; 160 for (z = 1; z < Base.Width; z++) 161 if (Renderer.left_change_map[z] > Texture.Width) 162 Renderer.left_change_map[z] = Renderer.left_change_map[z - 1]; 163 return 0; 164 } 165 166 167 /* This is float because of high precision effects such as zoom'n'anti-aliasing */ 168 /* In a simple reference implementation an integer value should be suffisant */ 169 float GetChange(int x, int *base_data) 170 { 171 float s, b, d; 172 /* extracting out of 24 bit RGB data the brightness/intensitiy indicating information */ 173 /* value is extended from 0-255 up to 0-765 */ 174 s = ((base_data[x] & 255) + ((base_data[x] >> 8) & 255) + ((base_data[x] >> 16) & 255)) / 765.0; 175 d = Renderer.realdist - (float) Renderer.max_change_tex_width * s; 176 b = 1.0 / sqrt(Renderer.internal_a + 0.25 / (d * d)); 177 return b; 178 } 179 180 181 /* reference renderer without any special effects */ 182 int NormalScan(int *stereo_data, int *texture_data) 183 { 184 float z; 185 int scanx; 186 187 /* insert the texture at x */ 188 memcpy(stereo_data + Renderer.startx, texture_data, Texture.Width * sizeof(int)); 189 190 /* we've to scan into two directions, beginning @ startx */ 191 for (scanx = Renderer.startx; scanx < (Renderer.startx + Texture.Width); scanx++) 192 { 193 z = scanx + Renderer.right_change_map[scanx]; 194 if (((int) z >= Renderer.startx) && ((int) z < Base.Width)) 195 stereo_data[scanx] = stereo_data[(int) z]; 196 } 197 for (scanx = Renderer.startx + Texture.Width; scanx < Base.Width; scanx++) 198 { 199 z = scanx + Renderer.right_change_map[scanx]; 200 if( ((int) z < Base.Width) && ((int) z >= 0)) 201 stereo_data[scanx] = stereo_data[(int) z]; 202 } 203 for (scanx = Renderer.startx - 1; scanx >= 0; scanx--) 204 { 205 z = scanx + Renderer.left_change_map[scanx]; 206 /* 'cause of problems without the round function - internal round, it's a cut - it is NOT symmetric */ 207 if (z != ((int) z)) 208 z++; 209 if (((int) z >= 0) && ((int) z < Base.Width)) 210 stereo_data[scanx] = stereo_data[(int) z]; 211 } 212 return 0; 213 } 214 215 216 /* First and most effective of all special: anti-aliasing */ 217 int AAScan(int *stereo_data, int *texture_data) 218 { 219 int scanx,a; 220 float z; 221 int r, g, b; 222 223 /* insert the texture without any special fx */ 224 for (scanx = 0; scanx < Texture.Width; scanx++) 225 for (a = 0; a < Renderer.nfactor; a++) 226 Renderer.scanline[((scanx + Renderer.startx) * Renderer.nfactor) + a] = texture_data[scanx]; 227 228 /* scanning... */ 229 for (scanx = Renderer.startx; scanx < (Renderer.startx + Texture.Width); scanx++) 230 { 231 z = scanx + Renderer.right_change_map[scanx]; 232 if ((z >= Renderer.startx) && (z < Base.Width)) 233 for (a = 0; a < Renderer.nfactor; a++) 234 Renderer.scanline[(scanx * Renderer.nfactor) + a] = Renderer.scanline[((int) (z * Renderer.nfactor) + a)]; 235 } 236 for (scanx = Renderer.startx + Texture.Width; scanx < Base.Width; scanx++) 237 { 238 z = scanx + Renderer.right_change_map[scanx]; 239 if( (z < Base.Width) && (z >= 0)) 240 for (a = 0; a < Renderer.nfactor; a++) 241 Renderer.scanline[(scanx * Renderer.nfactor) + a] = Renderer.scanline[((int) (z * Renderer.nfactor) + a)]; 242 } 243 for (scanx = Renderer.startx; scanx >= 0; scanx--) 244 { 245 z = scanx + Renderer.left_change_map[scanx]; 246 if((z < Base.Width) && (z >= 0)) 247 { 248 z *= Renderer.nfactor; 249 if (z != ((int) z)) 250 z++; 251 for (a = 0; a < Renderer.nfactor; a++) 252 Renderer.scanline[(scanx * Renderer.nfactor) + a] = Renderer.scanline[((int) z + a)]; 253 } 254 } 255 256 /* flush mem back into stereo_data, fixed */ 257 for (scanx = 0; scanx < Base.Width; scanx++) 258 { 259 stereo_data[scanx] = r = g = b = 0; 260 for (a = 0; a < Renderer.naa; a++) 261 { 262 r += Renderer.scanline[scanx * Renderer.naa + a] & 255; 263 g += (Renderer.scanline[scanx * Renderer.naa + a] >> 8) & 255; 264 b += (Renderer.scanline[scanx * Renderer.naa + a] >> 16) & 255; 265 } 266 stereo_data[scanx] += (r / Renderer.naa) & 255; 267 stereo_data[scanx] += ((g / Renderer.naa) & 255) << 8; 268 stereo_data[scanx] += ((b / Renderer.naa) & 255) << 16; 269 } 270 return 0; 271 } 272 273 274 /* Second effect: zoom */ 275 int ZoomScan(int *stereo_data, int *texture_data) 276 { 277 int scanx, a; 278 float z; 279 280 /* insert the texture without any special fx */ 281 for (scanx = 0; scanx < Texture.Width; scanx++) 282 for (a = 0; a < Renderer.nfactor; a++) 283 stereo_data[((scanx + Renderer.startx) * Renderer.nfactor) + a] = texture_data[scanx]; 284 285 /* already scanning, nothing to prepare */ 286 for (scanx = Renderer.startx; scanx < (Renderer.startx + Texture.Width); scanx++) 287 { 288 z = scanx + Renderer.right_change_map[scanx]; 289 if ((z >= Renderer.startx) && (z < Base.Width)) 290 for (a = 0; a < Renderer.nfactor; a++) 291 stereo_data[(scanx * Renderer.nfactor) + a] = stereo_data[(int) (z * Renderer.nzoom) + a]; 292 } 293 for (scanx = Renderer.startx + Texture.Width; scanx < Base.Width; scanx++) 294 { 295 z = scanx + Renderer.right_change_map[scanx]; 296 if((z < Base.Width) && (z >= 0)) 297 for (a = 0; a < Renderer.nfactor; a++) 298 stereo_data[(scanx * Renderer.nzoom) + a] = stereo_data[(int) (z * Renderer.nzoom) + a]; 299 } 300 301 for (scanx = Renderer.startx; scanx >= 0; scanx--) 302 { 303 z = scanx + Renderer.left_change_map[scanx]; 304 if((z < Base.Width) && (z >= 0)) 305 { 306 z *= Renderer.nfactor; 307 if (z != ((int) z)) 308 z++; 309 for (a = 0; a < Renderer.nfactor; a++) 310 stereo_data[(scanx * Renderer.nzoom) + a] = stereo_data[(int) z + a]; /* attention : z is was already multiplied above!!! */ 311 } 312 } 313 314 /* fill the comple row down with the same values to construct square pixels */ 315 for (a = 1; a < Renderer.nfactor; a++) 316 memcpy(stereo_data + Stereo.Width*a, stereo_data, Stereo.Width * sizeof(int)); 317 return 0; 318 } 319 320 321 /* ZOOM'N'AA */ 322 /* Final effects: fx pair in cooperation */ 323 int ZoomAAScan(int *stereo_data, int *texture_data) 324 { 325 int scanx, a; 326 float z; 327 int r, g, b; 328 329 /* insert the texture without any special fx */ 330 /* the same as anti-aliasing implementation above */ 331 for (scanx = 0; scanx < Texture.Width; scanx++) 332 for (a = 0; a < Renderer.nfactor; a++) 333 Renderer.scanline[((scanx + Renderer.startx) * Renderer.nfactor) + a] = texture_data[scanx]; 334 335 /* scanning */ 336 for (scanx = Renderer.startx; scanx < (Renderer.startx + Texture.Width); scanx++) 337 { 338 z = scanx + Renderer.right_change_map[scanx]; 339 if ((z >= Renderer.startx) && (z < Base.Width)) 340 for (a = 0; a < Renderer.nfactor; a++) 341 Renderer.scanline[(scanx * Renderer.nfactor) + a] = Renderer.scanline[(int) (z * Renderer.nfactor) + a]; 342 } 343 for (scanx = Renderer.startx + Texture.Width; scanx < Base.Width; scanx++) 344 { 345 z = scanx + Renderer.right_change_map[scanx]; 346 if ((z >= 0) && (z < Base.Width)) 347 for (a = 0; a < Renderer.nfactor; a++) 348 Renderer.scanline[(scanx * Renderer.nfactor) + a] = Renderer.scanline[(int) (z * Renderer.nfactor) + a]; 349 } 350 for (scanx = Renderer.startx; scanx >= 0; scanx--) 351 { 352 z = scanx + Renderer.left_change_map[scanx]; 353 if ((z >= 0) && (z < Base.Width)) 354 { 355 z *= Renderer.nfactor; 356 if (z != ((int) z)) 357 z++; 358 for (a = 0; a < Renderer.nfactor; a++) 359 Renderer.scanline[(scanx * Renderer.nfactor) + a] = Renderer.scanline[(int) z + a]; /* z was already multiplied above; */ 360 } 361 } 362 363 /* flush it down, fixed */ 364 for (scanx = 0; scanx < Stereo.Width; scanx++) 365 { 366 stereo_data[scanx] = r = g = b = 0; 367 for (a = 0; a < Renderer.naa; a++) 368 { 369 r += Renderer.scanline[scanx * Renderer.naa + a] & 255; 370 g += (Renderer.scanline[scanx * Renderer.naa + a] >> 8) & 255; 371 b += (Renderer.scanline[scanx * Renderer.naa + a] >> 16) & 255; 372 } 373 stereo_data[scanx] += (r / Renderer.naa) & 255; 374 stereo_data[scanx] += ((g / Renderer.naa) & 255) << 8; 375 stereo_data[scanx] += ((b / Renderer.naa) & 255) << 16; 376 } 377 378 379 /* fill it down to produce square pixels */ 380 for (a = 1; a < Renderer.nzoom; a++) 381 memcpy(stereo_data + Stereo.Width*a, stereo_data, Stereo.Width*sizeof(int)); 382 return 0; 383 } renderer.c contains code to make the program actually process data and stereograph it. |