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