1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * 3d Library
7 *
8 * PHP versions 5
9 *
10 * LICENSE:
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24 *
25 * @category  Image
26 * @package   Image_3D
27 * @author    Kore Nordmann <3d@kore-nordmann.de>
28 * @copyright 1997-2005 Kore Nordmann
29 * @license   http://www.gnu.org/licenses/lgpl.txt lgpl 2.1
30 * @version   CVS: $Id$
31 * @link      http://pear.php.net/package/PackageName
32 * @since     File available since Release 0.1.0
33 */
34
35// {{{ Image_3D_Renderer
36
37/**
38 * Image_3D_Renderer
39 *
40 * @category  Image
41 * @package   Image_3D
42 * @author    Kore Nordmann <3d@kore-nordmann.de>
43 * @copyright 1997-2005 Kore Nordmann
44 * @license   http://www.gnu.org/licenses/lgpl.txt lgpl 2.1
45 * @version   Release: @package_version@
46 * @link      http://pear.php.net/package/PackageName
47 * @since     Class available since Release 0.1.0
48 */
49abstract class Image_3D_Renderer
50{
51
52    // {{{ properties
53
54    /**
55     * Worlds polygones
56     *
57     * @var array
58     */
59    protected $_polygones;
60
61    /**
62     * Worlds points
63     *
64     * @var array
65     */
66    protected $_points;
67
68    /**
69     * Worlds lights
70     *
71     * @var array
72     */
73    protected $_lights;
74
75    /**
76     * Driver we use
77     *
78     * @var array
79     */
80    protected $_driver;
81
82    /**
83     * Size of the Image
84     *
85     * @var array
86     */
87    protected $_size;
88
89    /**
90     * Backgroundcolol
91     *
92     * @var Image_3D_Color
93     */
94    protected $_background;
95
96    /**
97     * Type of Shading used
98     *
99     * @var integer
100     */
101    protected $_shading;
102
103    // }}}
104    // {{{ Constants
105
106    /*
107     * No Shading
108     */
109    const SHADE_NO = 0;
110
111    /*
112     * Flat Shading
113     */
114    const SHADE_FLAT = 1;
115    /*
116     * Gauroud Shading
117     */
118    const SHADE_GAUROUD = 2;
119    /*
120     * Phong Shading
121     */
122    const SHADE_PHONG = 3;
123
124    // }}}
125    // {{{ __construct()
126
127    /**
128     * Constructor for Image_3D_Renderer
129     *
130     * Initialises the environment
131     *
132     * @return  Image_3D_Renderer           Instance of Renderer
133     */
134    public function __construct()
135    {
136        $this->reset();
137    }
138
139    // }}}
140    // {{{ reset()
141
142    /**
143     * Reset all changeable variables
144     *
145     * Initialises the environment
146     *
147     * @return  void
148     */
149    public function reset()
150    {
151        $this->_objects   = array();
152        $this->_polygones = array();
153
154        $this->_points = array();
155        $this->_lights = array();
156        $this->_size   = array(0, 0);
157
158        $this->_background = null;
159
160        $this->_driver = null;
161
162        $this->_shading = self::SHADE_PHONG;
163    }
164
165    // }}}
166    // {{{ _getPolygones()
167
168    /**
169     * Get and merge polygones
170     *
171     * Get polygones and points from an object and merge them unique to local
172     * polygon- and pointarrays.
173     *
174     * @param Image_3D_Object $object Object to merge
175     *
176     * @return  void
177     */
178    protected function _getPolygones(Image_3D_Object $object)
179    {
180        $newPolygones = $object->getPolygones();
181
182        $this->_polygones = array_merge($this->_polygones, $newPolygones);
183
184        // Add points unique to points-Array
185        foreach ($newPolygones as $polygon) {
186            $points = $polygon->getPoints();
187            foreach ($points as $point) {
188                if (!$point->isProcessed()) {
189                    $point->processed();
190                    array_push($this->_points, $point);
191                }
192            }
193        }
194    }
195
196    // }}}
197    // {{{ _calculateScreenCoordiantes()
198
199    /**
200     * Caclulate Screen Coordinates
201     *
202     * Calculate screen coordinates for a point according to the perspektive
203     * the renderer should display
204     *
205     * @param Image_3D_Point $point Point to process
206     *
207     * @return  void
208     */
209    abstract protected function _calculateScreenCoordiantes(Image_3D_Point $point);
210
211    // }}}
212    // {{{ _sortPolygones()
213
214    /**
215     * Sort polygones
216     *
217     * Set the order the polygones will be displayed
218     *
219     * @return  void
220     */
221    abstract protected function _sortPolygones();
222
223    // }}}
224    // {{{ addObjects()
225
226    /**
227     * Add objects to renderer
228     *
229     * Add objects to renderer. Only objects which are added will be
230     * displayed
231     *
232     * @param array $objects Array of objects
233     *
234     * @return  void
235     */
236    public function addObjects($objects)
237    {
238        if (is_array($objects)) {
239            foreach ($objects as $object) {
240                if ($object instanceof Image_3D_Object) {
241                    $this->_getPolygones($object);
242                }
243            }
244        } elseif ($objects instanceof Image_3D_Object) {
245            $this->_getPolygones($objects);
246        }
247    }
248
249    // }}}
250    // {{{ addObjects()
251
252    /**
253     * Add objects to renderer
254     *
255     * Add objects to renderer. Only objects which are added will be
256     * displayed
257     *
258     * @param array $lights Array of objects
259     *
260     * @return  void
261     */
262    public function addLights($lights)
263    {
264        $this->_lights = array_merge($this->_lights, $lights);
265    }
266
267    // }}}
268    // {{{ setSize()
269
270    /**
271     * Set image size
272     *
273     * Set the size of the destination image.
274     *
275     * @param integer $x Width
276     * @param integer $y Height
277     *
278     * @return  void
279     */
280    public function setSize($x, $y)
281    {
282        $this->_size = array($x / 2, $y / 2);
283    }
284
285    // }}}
286    // {{{ setBackgroundColor()
287
288    /**
289     * Set the Backgroundcolor
290     *
291     * Set the backgroundcolor of the destination image.
292     *
293     * @param Image_3D_Color $color Backgroundcolor
294     *
295     * @return  void
296     */
297    public function setBackgroundColor(Image_3D_Color $color)
298    {
299        $this->_background = $color;
300    }
301
302    // }}}
303    // {{{ setShading()
304
305    /**
306     * Set the quality of the shading
307     *
308     * Set the quality of the shading. Standard value is the maximum shading
309     * quality the driver is able to render.
310     *
311     * @param integer $shading Shading quality
312     *
313     * @return  void
314     */
315    public function setShading($shading)
316    {
317        $this->_shading = min($this->_shading, (int) $shading);
318    }
319
320    // }}}
321    // {{{ setDriver()
322
323    /**
324     * Set the driver
325     *
326     * Set the driver the image should be rendered with
327     *
328     * @param Image_3D_Driver $driver Driver to use
329     *
330     * @return  void
331     */
332    public function setDriver(Image_3D_Driver $driver)
333    {
334        $this->_driver = $driver;
335
336        $this->setShading(max($driver->getSupportedShading()));
337    }
338
339    // }}}
340    // {{{ getPolygonCount()
341
342    /**
343     * Return polygon count
344     *
345     * Return the number of used polygones in this image
346     *
347     * @return  integer     Number of Polygones
348     */
349    public function getPolygonCount()
350    {
351        return count($this->_polygones);
352    }
353
354    // }}}
355    // {{{ getPointCount()
356
357    /**
358     * Return point count
359     *
360     * Return the number of used points in this image
361     *
362     * @return  integer     Number of Points
363     */
364    public function getPointCount()
365    {
366        return count($this->_points);
367    }
368
369    // }}}
370    // {{{ getLightCount()
371
372    /**
373     * Return light count
374     *
375     * Return the number of used lights in this image
376     *
377     * @return  integer     Number of Lights
378     */
379    public function getLightCount()
380    {
381        return count($this->_lights);
382    }
383
384    // }}}
385    // {{{ _calculatePolygonColors()
386
387    /**
388     * Calculate the color of all polygones
389     *
390     * Let each polygon calculate his color based on the lights which are
391     * registered for this image
392     *
393     * @return  void
394     */
395    protected function _calculatePolygonColors()
396    {
397        foreach ($this->_polygones as $polygon) {
398            $polygon->calculateColor($this->_lights);
399        }
400    }
401
402    // }}}
403    // {{{ _calculatePointColors()
404
405    /**
406     * Calculate the colors of all points
407     *
408     * Let each point calculate his color based on his normale which is
409     * calculated on his surrounding polygones and the lights which are
410     * registered for this image
411     *
412     * @return  void
413     */
414    protected function _calculatePointColors()
415    {
416        foreach ($this->_polygones as $polygon) {
417            $normale = $polygon->getNormale();
418            $color   = $polygon->getColor();
419
420            $points = $polygon->getPoints();
421            foreach ($points as $point) {
422                $point->addVector($normale);
423                $point->addColor($color);
424            }
425        }
426
427        foreach ($this->_points as $point) {
428            $point->calculateColor($this->_lights);
429        }
430    }
431
432    // }}}
433    // {{{ _shade()
434
435    /**
436     * Draw all polygones
437     *
438     * Draw all polygones concerning the type of shading wich was set for the renderer
439     *
440     * @return  void
441     */
442    protected function _shade()
443    {
444        switch ($this->_shading) {
445        case self::SHADE_NO:
446            foreach ($this->_polygones as $polygon) {
447                $this->_driver->drawPolygon($polygon);
448            }
449            break;
450
451        case self::SHADE_FLAT:
452            $this->_calculatePolygonColors();
453            foreach ($this->_polygones as $polygon) {
454                $this->_driver->drawPolygon($polygon);
455            }
456            break;
457
458        case self::SHADE_GAUROUD:
459            $this->_calculatePointColors();
460            foreach ($this->_polygones as $polygon) {
461                $this->_driver->drawPolygon($polygon);
462            }
463            break;
464
465        default:
466            throw new Exception('Shading type not supported.');
467            break;
468        }
469    }
470
471    // }}}
472    // {{{ render()
473
474    /**
475     * Render the image
476     *
477     * Render the image into the metioned file
478     *
479     * @param string $file Filename
480     *
481     * @return  void
482     */
483    public function render($file)
484    {
485        if (empty($this->_driver)) {
486            return false;
487        }
488
489        // Calculate screen coordinates
490        foreach ($this->_points as $point) {
491            $this->_calculateScreenCoordiantes($point);
492        }
493
494        $this->_sortPolygones();
495
496        // Draw background
497        $this->_driver->createImage($this->_size[0] * 2, $this->_size[1] * 2);
498        $this->_driver->setBackground($this->_background);
499
500        // Create polygones in driver
501        $this->_shade();
502
503        // Save image
504        $this->_driver->save($file);
505    }
506
507    // }}}
508}
509
510// }}}
511