1 /***********************************************************************
2     created:    Tue Mar 3 2009
3     author:     Paul D Turner (parts based on original code by Thomas Suter)
4 *************************************************************************/
5 /***************************************************************************
6  *   Copyright (C) 2004 - 2011 Paul D Turner & The CEGUI Development Team
7  *
8  *   Permission is hereby granted, free of charge, to any person obtaining
9  *   a copy of this software and associated documentation files (the
10  *   "Software"), to deal in the Software without restriction, including
11  *   without limitation the rights to use, copy, modify, merge, publish,
12  *   distribute, sublicense, and/or sell copies of the Software, and to
13  *   permit persons to whom the Software is furnished to do so, subject to
14  *   the following conditions:
15  *
16  *   The above copyright notice and this permission notice shall be
17  *   included in all copies or substantial portions of the Software.
18  *
19  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  ***************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 
31 #include "CEGUI/RendererModules/Irrlicht/GeometryBuffer.h"
32 #include "CEGUI/RenderEffect.h"
33 #include "CEGUI/RendererModules/Irrlicht/Texture.h"
34 #include "CEGUI/Vertex.h"
35 #include "CEGUI/Quaternion.h"
36 
37 // Start of CEGUI namespace section
38 namespace CEGUI
39 {
40 //----------------------------------------------------------------------------//
IrrlichtGeometryBuffer(irr::video::IVideoDriver & driver)41 IrrlichtGeometryBuffer::IrrlichtGeometryBuffer(irr::video::IVideoDriver& driver):
42     d_driver(driver),
43     d_activeTexture(0),
44     d_clipRect(0, 0, 0, 0),
45     d_clippingActive(true),
46     d_translation(0, 0, 0),
47     d_rotation(0, 0, 0),
48     d_pivot(0, 0, 0),
49     d_effect(0),
50     d_matrixValid(false),
51 #if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8)
52     d_xViewDir(1.0f),
53 #else
54     d_xViewDir(driver.getDriverType() != irr::video::EDT_OPENGL ? 1.0f : -1.0f),
55 #endif
56     d_texelOffset(driver.getDriverType() != irr::video::EDT_OPENGL ? -0.5f : 0.0f)
57 {
58     d_material.BackfaceCulling = false;
59     d_material.Lighting = false;
60     d_material.ZBuffer = 0;
61     d_material.ZWriteEnable = false;
62 
63     // force upate of blending options to suit the default 'normal' mode
64     d_blendMode = BM_INVALID;
65     setBlendMode(BM_NORMAL);
66 }
67 
68 //----------------------------------------------------------------------------//
draw() const69 void IrrlichtGeometryBuffer::draw() const
70 {
71     if (!d_matrixValid)
72         updateMatrix();
73 
74     d_driver.setTransform(irr::video::ETS_WORLD, d_matrix);
75 
76     const int pass_count = d_effect ? d_effect->getPassCount() : 1;
77     for (int pass = 0; pass < pass_count; ++pass)
78     {
79         // set up RenderEffect
80         if (d_effect)
81             d_effect->performPreRenderFunctions(pass);
82 
83         // draw the batches
84         size_t pos = 0;
85         BatchList::const_iterator i = d_batches.begin();
86         for ( ; i != d_batches.end(); ++i)
87         {
88             if (i->clip)
89                 setupClipping();
90 
91             d_material.setTexture(0, i->texture);
92             d_driver.setMaterial(d_material);
93             d_driver.drawIndexedTriangleList(&d_vertices[pos], i->vertexCount,
94                                              &d_indices[pos], i->vertexCount / 3);
95             pos += i->vertexCount;
96 
97             if (i->clip)
98                 cleanupClipping();
99         }
100     }
101 
102     // clean up RenderEffect
103     if (d_effect)
104         d_effect->performPostRenderFunctions();
105 }
106 
107 //----------------------------------------------------------------------------//
setupClipping() const108 void IrrlichtGeometryBuffer::setupClipping() const
109 {
110     // NB: This is done via viewport & projection manipulation because Irrlicht
111     // does not expose scissoring facilities of underlying APIs.  This has the
112     // unfortunate side effect of being much more expensive to set up.
113     d_savedViewport = d_driver.getViewPort();
114     d_savedProjection = d_driver.getTransform(irr::video::ETS_PROJECTION);
115 
116     const Sizef csz(d_clipRect.getSize());
117     const Sizef tsz(static_cast<float>(d_savedViewport.getWidth()),
118                      static_cast<float>(d_savedViewport.getHeight()));
119 
120     // set modified projection 'scissor' matix that negates scale and
121     // translation that would be done by setting the viewport to the clip area.
122     irr::core::matrix4 scsr(irr::core::matrix4::EM4CONST_IDENTITY);
123     scsr(0, 0) = tsz.d_width / csz.d_width;
124     scsr(1, 1) = tsz.d_height / csz.d_height;
125     scsr(3, 0) = d_xViewDir * (tsz.d_width + 2.0f *
126                    (d_savedViewport.UpperLeftCorner.X -
127                      (d_clipRect.left() + csz.d_width * 0.5f))) / csz.d_width;
128     scsr(3, 1) = -(tsz.d_height + 2.0f *
129                    (d_savedViewport.UpperLeftCorner.Y -
130                      (d_clipRect.top() + csz.d_height * 0.5f))) / csz.d_height;
131 
132     scsr *= d_savedProjection;
133     d_driver.setTransform(irr::video::ETS_PROJECTION, scsr);
134 
135     // set new viewport for the clipping area
136     const irr::core::rect<irr::s32> vp(
137             static_cast<irr::s32>(d_clipRect.left()),
138             static_cast<irr::s32>(d_clipRect.top()),
139             static_cast<irr::s32>(d_clipRect.right()),
140             static_cast<irr::s32>(d_clipRect.bottom()));
141     d_driver.setViewPort(vp);
142 }
143 
144 //----------------------------------------------------------------------------//
cleanupClipping() const145 void IrrlichtGeometryBuffer::cleanupClipping() const
146 {
147     d_driver.setTransform(irr::video::ETS_PROJECTION, d_savedProjection);
148     d_driver.setViewPort(d_savedViewport);
149 }
150 
151 //----------------------------------------------------------------------------//
setTranslation(const Vector3f & v)152 void IrrlichtGeometryBuffer::setTranslation(const Vector3f& v)
153 {
154     d_translation.X = v.d_x;
155     d_translation.Y = v.d_y;
156     d_translation.Z = v.d_z;
157     d_matrixValid = false;
158 }
159 
160 //----------------------------------------------------------------------------//
setRotation(const Quaternion & r)161 void IrrlichtGeometryBuffer::setRotation(const Quaternion& r)
162 {
163     d_rotation.W = -r.d_w;
164     d_rotation.X = r.d_x;
165     d_rotation.Y = r.d_y;
166     d_rotation.Z = r.d_z;
167     d_matrixValid = false;
168 }
169 
170 //----------------------------------------------------------------------------//
setPivot(const Vector3f & p)171 void IrrlichtGeometryBuffer::setPivot(const Vector3f& p)
172 {
173     d_pivot.X = p.d_x;
174     d_pivot.Y = p.d_y;
175     d_pivot.Z = p.d_z;
176     d_matrixValid = false;
177 }
178 
179 //----------------------------------------------------------------------------//
setClippingRegion(const Rectf & region)180 void IrrlichtGeometryBuffer::setClippingRegion(const Rectf& region)
181 {
182     d_clipRect.top(ceguimax(0.0f, region.top()));
183     d_clipRect.bottom(ceguimax(0.0f, region.bottom()));
184     d_clipRect.left(ceguimax(0.0f, region.left()));
185     d_clipRect.right(ceguimax(0.0f, region.right()));
186 }
187 
188 //----------------------------------------------------------------------------//
appendVertex(const Vertex & vertex)189 void IrrlichtGeometryBuffer::appendVertex(const Vertex& vertex)
190 {
191     appendGeometry(&vertex, 1);
192 }
193 
194 //----------------------------------------------------------------------------//
appendGeometry(const Vertex * const vbuff,uint vertex_count)195 void IrrlichtGeometryBuffer::appendGeometry(const Vertex* const vbuff,
196                                             uint vertex_count)
197 {
198     // see if we should start a new batch
199     irr::video::ITexture* t =
200         d_activeTexture ? d_activeTexture->getIrrlichtTexture() : 0;
201 
202     if (d_batches.empty() ||
203         d_batches.back().texture != t ||
204         d_batches.back().clip != d_clippingActive)
205     {
206         const BatchInfo batch = {t, 0, d_clippingActive};
207         d_batches.push_back(batch);
208     }
209 
210     // buffer these vertices
211     const irr::u16 idx_start = d_batches.back().vertexCount;
212     irr::video::S3DVertex v;
213     for (uint i = 0; i < vertex_count; ++i)
214     {
215         const Vertex& vs = vbuff[i];
216         v.Pos.X     = vs.position.d_x + d_texelOffset;
217         v.Pos.Y     = vs.position.d_y + d_texelOffset;
218         v.Pos.Z     = vs.position.d_z;
219         v.TCoords.X = vs.tex_coords.d_x;
220         v.TCoords.Y = vs.tex_coords.d_y;
221         v.Color.set(vs.colour_val.getARGB());
222         d_vertices.push_back(v);
223         d_indices.push_back(idx_start + i);
224     }
225 
226     // update size of current batch
227     d_batches.back().vertexCount += vertex_count;
228 }
229 
230 //----------------------------------------------------------------------------//
setActiveTexture(Texture * texture)231 void IrrlichtGeometryBuffer::setActiveTexture(Texture* texture)
232 {
233     d_activeTexture = static_cast<IrrlichtTexture*>(texture);
234 }
235 
236 //----------------------------------------------------------------------------//
reset()237 void IrrlichtGeometryBuffer::reset()
238 {
239     d_vertices.clear();
240     d_indices.clear();
241     d_batches.clear();
242     d_activeTexture = 0;
243 }
244 
245 //----------------------------------------------------------------------------//
getActiveTexture() const246 Texture* IrrlichtGeometryBuffer::getActiveTexture() const
247 {
248     return d_activeTexture;
249 }
250 
251 //----------------------------------------------------------------------------//
getVertexCount() const252 uint IrrlichtGeometryBuffer::getVertexCount() const
253 {
254     return d_vertices.size();
255 }
256 
257 //----------------------------------------------------------------------------//
getBatchCount() const258 uint IrrlichtGeometryBuffer::getBatchCount() const
259 {
260     return d_batches.size();
261 }
262 
263 //----------------------------------------------------------------------------//
setRenderEffect(RenderEffect * effect)264 void IrrlichtGeometryBuffer::setRenderEffect(RenderEffect* effect)
265 {
266     d_effect = effect;
267 }
268 
269 //----------------------------------------------------------------------------//
getRenderEffect()270 RenderEffect* IrrlichtGeometryBuffer::getRenderEffect()
271 {
272     return d_effect;
273 }
274 
275 //----------------------------------------------------------------------------//
setBlendMode(const BlendMode mode)276 void IrrlichtGeometryBuffer::setBlendMode(const BlendMode mode)
277 {
278     // if blend mode is already set to this, ignore.
279     if (d_blendMode == mode)
280         return;
281 
282     // call default to set mode field (in case we change how that's done)
283     GeometryBuffer::setBlendMode(mode);
284 
285 #if CEGUI_IRR_SDK_VERSION >= 16
286     // FIXME: Here we just use the 'best of a bad situation' option
287     // FIXME: which results in incorrect accumulation of alpha values
288     // FIXME: in texture based targets.  There is no fix for this that is
289     // FIXME: possible with the stock Irrlicht; while we could creata an
290     // FIXME: appropriate custom material, it would involve directly linking
291     // FIXME: with the various underlying rendering APIs - which is fine for
292     // FIXME: an end user, but is definitely not what _we_ want to be doing.
293     //
294     // If anybody knows the above information to be incorrect, and has a fix
295     // for this issue, please let us know! :)
296 
297 /*    if (d_blendMode == BM_RTT_PREMULTIPLIED)
298     {
299         d_material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND;
300         d_material.MaterialTypeParam = irr::video::pack_texureBlendFunc(
301                 irr::video::EBF_ONE,
302                 irr::video::EBF_ONE_MINUS_SRC_ALPHA,
303                 irr::video::EMFN_MODULATE_1X,
304                 irr::video::EAS_NONE);
305     }
306     else */
307     {
308         d_material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND;
309         d_material.MaterialTypeParam =
310 #if IRRLICHT_VERSION_MAJOR > 1 || (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8)
311             irr::video::pack_textureBlendFunc(
312 #else
313             irr::video::pack_texureBlendFunc(
314 #endif
315                 irr::video::EBF_SRC_ALPHA,
316                 irr::video::EBF_ONE_MINUS_SRC_ALPHA,
317                 irr::video::EMFN_MODULATE_1X,
318                 irr::video::EAS_NONE);
319     }
320 #else
321     d_material.MaterialType = irr::video::EMT_TRANSPARENT_ALPHA_CHANNEL;
322     d_material.MaterialTypeParam = 0;
323 #endif
324 }
325 
326 //----------------------------------------------------------------------------//
getMatrix() const327 const irr::core::matrix4& IrrlichtGeometryBuffer::getMatrix() const
328 {
329     return d_matrix;
330 }
331 
332 //----------------------------------------------------------------------------//
updateMatrix() const333 void IrrlichtGeometryBuffer::updateMatrix() const
334 {
335     d_matrix.makeIdentity();
336     d_matrix.setTranslation(d_translation + d_pivot);
337 
338     irr::core::matrix4 ptrans;
339     ptrans.setTranslation(-d_pivot);
340 
341     d_matrix *= d_rotation.getMatrix();
342     d_matrix *= ptrans;
343 
344     d_matrixValid = true;
345 }
346 
347 //----------------------------------------------------------------------------//
getMaterial()348 irr::video::SMaterial& IrrlichtGeometryBuffer::getMaterial()
349 {
350     return const_cast<irr::video::SMaterial&>(
351         static_cast<const IrrlichtGeometryBuffer*>(this)->getMaterial());
352 }
353 
354 //----------------------------------------------------------------------------//
getMaterial() const355 const irr::video::SMaterial& IrrlichtGeometryBuffer::getMaterial() const
356 {
357     return d_material;
358 }
359 
360 //----------------------------------------------------------------------------//
setClippingActive(const bool active)361 void IrrlichtGeometryBuffer::setClippingActive(const bool active)
362 {
363     d_clippingActive = active;
364 }
365 
366 //----------------------------------------------------------------------------//
isClippingActive() const367 bool IrrlichtGeometryBuffer::isClippingActive() const
368 {
369     return d_clippingActive;
370 }
371 
372 //----------------------------------------------------------------------------//
373 //
374 } // End of  CEGUI namespace section
375