cleanup: spelling, comments, alignment
[blender.git] / source / gameengine / Rasterizer / RAS_BucketManager.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file gameengine/Rasterizer/RAS_BucketManager.cpp
29  *  \ingroup bgerast
30  */
31
32 #ifdef _MSC_VER
33    /* don't show these anoying STL warnings */
34 #  pragma warning (disable:4786)
35 #endif
36
37 #include "RAS_MaterialBucket.h"
38 #include "RAS_MeshObject.h"
39 #include "RAS_Polygon.h"
40 #include "RAS_IPolygonMaterial.h"
41 #include "RAS_IRasterizer.h"
42
43 #include "RAS_BucketManager.h"
44
45 #include <algorithm>
46 /* sorting */
47
48 struct RAS_BucketManager::sortedmeshslot
49 {
50 public:
51         MT_Scalar m_z;                                  /* depth */
52         RAS_MeshSlot *m_ms;                             /* mesh slot */
53         RAS_MaterialBucket *m_bucket;   /* buck mesh slot came from */
54
55         sortedmeshslot() {}
56
57         void set(RAS_MeshSlot *ms, RAS_MaterialBucket *bucket, const MT_Vector3& pnorm)
58         {
59                 // would be good to use the actual bounding box center instead
60                 MT_Point3 pos(ms->m_OpenGLMatrix[12], ms->m_OpenGLMatrix[13], ms->m_OpenGLMatrix[14]);
61
62                 m_z = MT_dot(pnorm, pos);
63                 m_ms = ms;
64                 m_bucket = bucket;
65         }
66 };
67
68 struct RAS_BucketManager::backtofront
69 {
70         bool operator()(const sortedmeshslot &a, const sortedmeshslot &b)
71         {
72                 return (a.m_z < b.m_z) || (a.m_z == b.m_z && a.m_ms < b.m_ms);
73         }
74 };
75
76 struct RAS_BucketManager::fronttoback
77 {
78         bool operator()(const sortedmeshslot &a, const sortedmeshslot &b)
79         {
80                 return (a.m_z > b.m_z) || (a.m_z == b.m_z && a.m_ms > b.m_ms);
81         }
82 };
83
84 /* bucket manager */
85
86 RAS_BucketManager::RAS_BucketManager()
87 {
88
89 }
90
91 RAS_BucketManager::~RAS_BucketManager()
92 {
93         BucketList::iterator it;
94
95         for (it = m_SolidBuckets.begin(); it != m_SolidBuckets.end(); it++)
96                 delete (*it);
97
98         for (it = m_AlphaBuckets.begin(); it != m_AlphaBuckets.end(); it++)
99                 delete(*it);
100         
101         m_SolidBuckets.clear();
102         m_AlphaBuckets.clear();
103 }
104
105 void RAS_BucketManager::OrderBuckets(const MT_Transform& cameratrans, BucketList& buckets, vector<sortedmeshslot>& slots, bool alpha)
106 {
107         BucketList::iterator bit;
108         list<RAS_MeshSlot>::iterator mit;
109         size_t size = 0, i = 0;
110
111         /* Camera's near plane equation: pnorm.dot(point) + pval,
112          * but we leave out pval since it's constant anyway */
113         const MT_Vector3 pnorm(cameratrans.getBasis()[2]);
114
115         for (bit = buckets.begin(); bit != buckets.end(); ++bit)
116         {
117                 SG_DList::iterator<RAS_MeshSlot> mit((*bit)->GetActiveMeshSlots());
118                 for (mit.begin(); !mit.end(); ++mit)
119                         size++;
120         }
121
122         slots.resize(size);
123
124         for (bit = buckets.begin(); bit != buckets.end(); ++bit)
125         {
126                 RAS_MaterialBucket* bucket = *bit;
127                 RAS_MeshSlot* ms;
128                 // remove the mesh slot from the list, it culls them automatically for next frame
129                 while ((ms = bucket->GetNextActiveMeshSlot())) {
130                         slots[i++].set(ms, bucket, pnorm);
131                 }
132         }
133                 
134         if (alpha)
135                 sort(slots.begin(), slots.end(), backtofront());
136         else
137                 sort(slots.begin(), slots.end(), fronttoback());
138 }
139
140 void RAS_BucketManager::RenderAlphaBuckets(const MT_Transform& cameratrans, RAS_IRasterizer* rasty)
141 {
142         vector<sortedmeshslot> slots;
143         vector<sortedmeshslot>::iterator sit;
144
145         // Having depth masks disabled/enabled gives different artifacts in
146         // case no sorting is done or is done inexact. For compatibility, we
147         // disable it.
148         if (rasty->GetDrawingMode() != RAS_IRasterizer::KX_SHADOW)
149                 rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_DISABLED);
150
151         OrderBuckets(cameratrans, m_AlphaBuckets, slots, true);
152         
153         for (sit=slots.begin(); sit!=slots.end(); ++sit) {
154                 rasty->SetClientObject(sit->m_ms->m_clientObj);
155
156                 while (sit->m_bucket->ActivateMaterial(cameratrans, rasty))
157                         sit->m_bucket->RenderMeshSlot(cameratrans, rasty, *(sit->m_ms));
158
159                 // make this mesh slot culled automatically for next frame
160                 // it will be culled out by frustum culling
161                 sit->m_ms->SetCulled(true);
162         }
163
164         rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_ENABLED);
165 }
166
167 void RAS_BucketManager::RenderSolidBuckets(const MT_Transform& cameratrans, RAS_IRasterizer* rasty)
168 {
169         BucketList::iterator bit;
170
171         rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_ENABLED);
172
173         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
174 #if 1
175                 RAS_MaterialBucket* bucket = *bit;
176                 RAS_MeshSlot* ms;
177                 // remove the mesh slot from the list, it culls them automatically for next frame
178                 while ((ms = bucket->GetNextActiveMeshSlot())) {
179                         rasty->SetClientObject(ms->m_clientObj);
180                         while (bucket->ActivateMaterial(cameratrans, rasty))
181                                 bucket->RenderMeshSlot(cameratrans, rasty, *ms);
182
183                         // make this mesh slot culled automatically for next frame
184                         // it will be culled out by frustum culling
185                         ms->SetCulled(true);
186                 }
187 #else
188                 list<RAS_MeshSlot>::iterator mit;
189                 for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
190                         if (mit->IsCulled())
191                                 continue;
192
193                         rasty->SetClientObject(rasty, mit->m_clientObj);
194
195                         while ((*bit)->ActivateMaterial(cameratrans, rasty))
196                                 (*bit)->RenderMeshSlot(cameratrans, rasty, *mit);
197
198                         // make this mesh slot culled automatically for next frame
199                         // it will be culled out by frustum culling
200                         mit->SetCulled(true);
201                 }
202 #endif
203         }
204         
205         /* this code draws meshes order front-to-back instead to reduce overdraw.
206          * it turned out slower due to much material state switching, a more clever
207          * algorithm might do better. */
208 #if 0
209         vector<sortedmeshslot> slots;
210         vector<sortedmeshslot>::iterator sit;
211
212         OrderBuckets(cameratrans, m_SolidBuckets, slots, false);
213
214         for (sit=slots.begin(); sit!=slots.end(); ++sit) {
215                 rendertools->SetClientObject(rasty, sit->m_ms->m_clientObj);
216
217                 while (sit->m_bucket->ActivateMaterial(cameratrans, rasty))
218                         sit->m_bucket->RenderMeshSlot(cameratrans, rasty, *(sit->m_ms));
219         }
220 #endif
221 }
222
223 void RAS_BucketManager::Renderbuckets(const MT_Transform& cameratrans, RAS_IRasterizer* rasty)
224 {
225         /* beginning each frame, clear (texture/material) caching information */
226         rasty->ClearCachingInfo();
227
228         RenderSolidBuckets(cameratrans, rasty);
229         RenderAlphaBuckets(cameratrans, rasty);
230
231         /* If we're drawing shadows and bucket wasn't rendered (outside of the lamp frustum or doesn't cast shadows)
232          * then the mesh is still modified, so we don't want to set MeshModified to false yet (it will mess up
233          * updating display lists). Just leave this step for the main render pass.
234          */
235         if (rasty->GetDrawingMode() != RAS_IRasterizer::KX_SHADOW) {
236                 /* All meshes should be up to date now */
237                 /* Don't do this while processing buckets because some meshes are split between buckets */
238                 BucketList::iterator bit;
239                 list<RAS_MeshSlot>::iterator mit;
240                 for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
241                         for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
242                                 mit->m_mesh->SetMeshModified(false);
243                         }
244                 }
245                 for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit) {
246                         for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
247                                 mit->m_mesh->SetMeshModified(false);
248                         }
249                 }
250         }
251         
252
253         rasty->SetClientObject(NULL);
254 }
255
256 RAS_MaterialBucket *RAS_BucketManager::FindBucket(RAS_IPolyMaterial *material, bool &bucketCreated)
257 {
258         BucketList::iterator it;
259
260         bucketCreated = false;
261
262         for (it = m_SolidBuckets.begin(); it != m_SolidBuckets.end(); it++)
263                 if (*(*it)->GetPolyMaterial() == *material)
264                         return *it;
265         
266         for (it = m_AlphaBuckets.begin(); it != m_AlphaBuckets.end(); it++)
267                 if (*(*it)->GetPolyMaterial() == *material)
268                         return *it;
269         
270         RAS_MaterialBucket *bucket = new RAS_MaterialBucket(material);
271         bucketCreated = true;
272
273         if (bucket->IsAlpha())
274                 m_AlphaBuckets.push_back(bucket);
275         else
276                 m_SolidBuckets.push_back(bucket);
277         
278         return bucket;
279 }
280
281 void RAS_BucketManager::OptimizeBuckets(MT_Scalar distance)
282 {
283         BucketList::iterator bit;
284         
285         distance = 10.0f;
286
287         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit)
288                 (*bit)->Optimize(distance);
289         for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit)
290                 (*bit)->Optimize(distance);
291 }
292
293 void RAS_BucketManager::ReleaseDisplayLists(RAS_IPolyMaterial *mat)
294 {
295         BucketList::iterator bit;
296         list<RAS_MeshSlot>::iterator mit;
297
298         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
299                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
300                         for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
301                                 if (mit->m_DisplayList) {
302                                         mit->m_DisplayList->Release();
303                                         mit->m_DisplayList = NULL;
304                                 }
305                         }
306                 }
307         }
308         
309         for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit) {
310                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
311                         for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
312                                 if (mit->m_DisplayList) {
313                                         mit->m_DisplayList->Release();
314                                         mit->m_DisplayList = NULL;
315                                 }
316                         }
317                 }
318         }
319 }
320
321 void RAS_BucketManager::ReleaseMaterials(RAS_IPolyMaterial * mat)
322 {
323         BucketList::iterator bit;
324         list<RAS_MeshSlot>::iterator mit;
325
326         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
327                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
328                         (*bit)->GetPolyMaterial()->ReleaseMaterial();
329                 }
330         }
331         
332         for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit) {
333                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
334                         (*bit)->GetPolyMaterial()->ReleaseMaterial();
335                 }
336         }
337 }
338
339 /* frees the bucket, only used when freeing scenes */
340 void RAS_BucketManager::RemoveMaterial(RAS_IPolyMaterial * mat)
341 {
342         BucketList::iterator bit, bitp;
343         list<RAS_MeshSlot>::iterator mit;
344         int i;
345
346
347         for (i=0; i<m_SolidBuckets.size(); i++) {
348                 RAS_MaterialBucket *bucket = m_SolidBuckets[i];
349                 if (mat == bucket->GetPolyMaterial()) {
350                         m_SolidBuckets.erase(m_SolidBuckets.begin()+i);
351                         delete bucket;
352                         i--;
353                 }
354         }
355
356         for (int i=0; i<m_AlphaBuckets.size(); i++) {
357                 RAS_MaterialBucket *bucket = m_AlphaBuckets[i];
358                 if (mat == bucket->GetPolyMaterial()) {
359                         m_AlphaBuckets.erase(m_AlphaBuckets.begin()+i);
360                         delete bucket;
361                         i--;
362                 }
363         }
364 }
365
366 //#include <stdio.h>
367
368 void RAS_BucketManager::MergeBucketManager(RAS_BucketManager *other, SCA_IScene *scene)
369 {
370         /* concatenate lists */
371         // printf("BEFORE %d %d\n", GetSolidBuckets().size(), GetAlphaBuckets().size());
372
373         GetSolidBuckets().insert( GetSolidBuckets().end(), other->GetSolidBuckets().begin(), other->GetSolidBuckets().end() );
374         other->GetSolidBuckets().clear();
375
376         GetAlphaBuckets().insert( GetAlphaBuckets().end(), other->GetAlphaBuckets().begin(), other->GetAlphaBuckets().end() );
377         other->GetAlphaBuckets().clear();
378         //printf("AFTER %d %d\n", GetSolidBuckets().size(), GetAlphaBuckets().size());
379 }
380