svn merge ^/trunk/blender -r43124:43160
[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
33 #if defined(WIN32) && !defined(FREE_WINDOWS)
34 // don't show these anoying STL warnings
35 #pragma warning (disable:4786)
36 #endif
37
38 #include "CTR_Map.h"
39 #include "RAS_MaterialBucket.h"
40 #include "STR_HashedString.h"
41 #include "RAS_MeshObject.h"
42 #include "RAS_IRasterizer.h"
43 #include "RAS_IRenderTools.h"
44
45 #include "RAS_BucketManager.h"
46
47 #include <algorithm>
48 #include <set>
49
50 /* sorting */
51
52 struct RAS_BucketManager::sortedmeshslot
53 {
54 public:
55         MT_Scalar m_z;                                  /* depth */
56         RAS_MeshSlot *m_ms;                             /* mesh slot */
57         RAS_MaterialBucket *m_bucket;   /* buck mesh slot came from */
58
59         sortedmeshslot() {}
60
61         void set(RAS_MeshSlot *ms, RAS_MaterialBucket *bucket, const MT_Vector3& pnorm)
62         {
63                 // would be good to use the actual bounding box center instead
64                 MT_Point3 pos(ms->m_OpenGLMatrix[12], ms->m_OpenGLMatrix[13], ms->m_OpenGLMatrix[14]);
65
66                 m_z = MT_dot(pnorm, pos);
67                 m_ms = ms;
68                 m_bucket = bucket;
69         }
70 };
71
72 struct RAS_BucketManager::backtofront
73 {
74         bool operator()(const sortedmeshslot &a, const sortedmeshslot &b)
75         {
76                 return (a.m_z < b.m_z) || (a.m_z == b.m_z && a.m_ms < b.m_ms);
77         }
78 };
79
80 struct RAS_BucketManager::fronttoback
81 {
82         bool operator()(const sortedmeshslot &a, const sortedmeshslot &b)
83         {
84                 return (a.m_z > b.m_z) || (a.m_z == b.m_z && a.m_ms > b.m_ms);
85         }
86 };
87
88 /* bucket manager */
89
90 RAS_BucketManager::RAS_BucketManager()
91 {
92
93 }
94
95 RAS_BucketManager::~RAS_BucketManager()
96 {
97         BucketList::iterator it;
98
99         for (it = m_SolidBuckets.begin(); it != m_SolidBuckets.end(); it++)
100                 delete (*it);
101
102         for (it = m_AlphaBuckets.begin(); it != m_AlphaBuckets.end(); it++)
103                 delete(*it);
104         
105         m_SolidBuckets.clear();
106         m_AlphaBuckets.clear();
107 }
108
109 void RAS_BucketManager::OrderBuckets(const MT_Transform& cameratrans, BucketList& buckets, vector<sortedmeshslot>& slots, bool alpha)
110 {
111         BucketList::iterator bit;
112         list<RAS_MeshSlot>::iterator mit;
113         size_t size = 0, i = 0;
114
115         /* Camera's near plane equation: pnorm.dot(point) + pval,
116          * but we leave out pval since it's constant anyway */
117         const MT_Vector3 pnorm(cameratrans.getBasis()[2]);
118
119         for (bit = buckets.begin(); bit != buckets.end(); ++bit)
120         {
121                 SG_DList::iterator<RAS_MeshSlot> mit((*bit)->GetActiveMeshSlots());
122                 for(mit.begin(); !mit.end(); ++mit)
123                         size++;
124         }
125
126         slots.resize(size);
127
128         for (bit = buckets.begin(); bit != buckets.end(); ++bit)
129         {
130                 RAS_MaterialBucket* bucket = *bit;
131                 RAS_MeshSlot* ms;
132                 // remove the mesh slot form the list, it culls them automatically for next frame
133                 while((ms = bucket->GetNextActiveMeshSlot())) {
134                         slots[i++].set(ms, bucket, pnorm);
135                 }
136         }
137                 
138         if(alpha)
139                 sort(slots.begin(), slots.end(), backtofront());
140         else
141                 sort(slots.begin(), slots.end(), fronttoback());
142 }
143
144 void RAS_BucketManager::RenderAlphaBuckets(
145         const MT_Transform& cameratrans, RAS_IRasterizer* rasty, RAS_IRenderTools* rendertools)
146 {
147         vector<sortedmeshslot> slots;
148         vector<sortedmeshslot>::iterator sit;
149
150         // Having depth masks disabled/enabled gives different artifacts in
151         // case no sorting is done or is done inexact. For compatibility, we
152         // disable it.
153         rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_DISABLED);
154
155         OrderBuckets(cameratrans, m_AlphaBuckets, slots, true);
156         
157         for(sit=slots.begin(); sit!=slots.end(); ++sit) {
158                 rendertools->SetClientObject(rasty, sit->m_ms->m_clientObj);
159
160                 while(sit->m_bucket->ActivateMaterial(cameratrans, rasty, rendertools))
161                         sit->m_bucket->RenderMeshSlot(cameratrans, rasty, rendertools, *(sit->m_ms));
162
163                 // make this mesh slot culled automatically for next frame
164                 // it will be culled out by frustrum culling
165                 sit->m_ms->SetCulled(true);
166         }
167
168         rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_ENABLED);
169 }
170
171 void RAS_BucketManager::RenderSolidBuckets(
172         const MT_Transform& cameratrans, RAS_IRasterizer* rasty, RAS_IRenderTools* rendertools)
173 {
174         BucketList::iterator bit;
175
176         rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_ENABLED);
177
178         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
179 #if 1
180                 RAS_MaterialBucket* bucket = *bit;
181                 RAS_MeshSlot* ms;
182                 // remove the mesh slot form the list, it culls them automatically for next frame
183                 while((ms = bucket->GetNextActiveMeshSlot()))
184                 {
185                         rendertools->SetClientObject(rasty, ms->m_clientObj);
186                         while (bucket->ActivateMaterial(cameratrans, rasty, rendertools))
187                                 bucket->RenderMeshSlot(cameratrans, rasty, rendertools, *ms);
188
189                         // make this mesh slot culled automatically for next frame
190                         // it will be culled out by frustrum culling
191                         ms->SetCulled(true);
192                 }
193 #else
194                 list<RAS_MeshSlot>::iterator mit;
195                 for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
196                         if (mit->IsCulled())
197                                 continue;
198
199                         rendertools->SetClientObject(rasty, mit->m_clientObj);
200
201                         while ((*bit)->ActivateMaterial(cameratrans, rasty, rendertools))
202                                 (*bit)->RenderMeshSlot(cameratrans, rasty, rendertools, *mit);
203
204                         // make this mesh slot culled automatically for next frame
205                         // it will be culled out by frustrum culling
206                         mit->SetCulled(true);
207                 }
208 #endif
209         }
210         
211         /* this code draws meshes order front-to-back instead to reduce overdraw.
212          * it turned out slower due to much material state switching, a more clever
213          * algorithm might do better. */
214 #if 0
215         vector<sortedmeshslot> slots;
216         vector<sortedmeshslot>::iterator sit;
217
218         OrderBuckets(cameratrans, m_SolidBuckets, slots, false);
219
220         for(sit=slots.begin(); sit!=slots.end(); ++sit) {
221                 rendertools->SetClientObject(rasty, sit->m_ms->m_clientObj);
222
223                 while(sit->m_bucket->ActivateMaterial(cameratrans, rasty, rendertools))
224                         sit->m_bucket->RenderMeshSlot(cameratrans, rasty, rendertools, *(sit->m_ms));
225         }
226 #endif
227 }
228
229 void RAS_BucketManager::Renderbuckets(
230         const MT_Transform& cameratrans, RAS_IRasterizer* rasty, RAS_IRenderTools* rendertools)
231 {
232         /* beginning each frame, clear (texture/material) caching information */
233         rasty->ClearCachingInfo();
234
235         RenderSolidBuckets(cameratrans, rasty, rendertools);    
236         RenderAlphaBuckets(cameratrans, rasty, rendertools);    
237
238         rendertools->SetClientObject(rasty, NULL);
239 }
240
241 RAS_MaterialBucket* RAS_BucketManager::FindBucket(RAS_IPolyMaterial * material, bool &bucketCreated)
242 {
243         BucketList::iterator it;
244
245         bucketCreated = false;
246
247         for (it = m_SolidBuckets.begin(); it != m_SolidBuckets.end(); it++)
248                 if (*(*it)->GetPolyMaterial() == *material)
249                         return *it;
250         
251         for (it = m_AlphaBuckets.begin(); it != m_AlphaBuckets.end(); it++)
252                 if (*(*it)->GetPolyMaterial() == *material)
253                         return *it;
254         
255         RAS_MaterialBucket *bucket = new RAS_MaterialBucket(material);
256         bucketCreated = true;
257
258         if (bucket->IsAlpha())
259                 m_AlphaBuckets.push_back(bucket);
260         else
261                 m_SolidBuckets.push_back(bucket);
262         
263         return bucket;
264 }
265
266 void RAS_BucketManager::OptimizeBuckets(MT_Scalar distance)
267 {
268         BucketList::iterator bit;
269         
270         distance = 10.0;
271
272         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit)
273                 (*bit)->Optimize(distance);
274         for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit)
275                 (*bit)->Optimize(distance);
276 }
277
278 void RAS_BucketManager::ReleaseDisplayLists(RAS_IPolyMaterial *mat)
279 {
280         BucketList::iterator bit;
281         list<RAS_MeshSlot>::iterator mit;
282
283         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
284                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
285                         for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
286                                 if(mit->m_DisplayList) {
287                                         mit->m_DisplayList->Release();
288                                         mit->m_DisplayList = NULL;
289                                 }
290                         }
291                 }
292         }
293         
294         for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit) {
295                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
296                         for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
297                                 if(mit->m_DisplayList) {
298                                         mit->m_DisplayList->Release();
299                                         mit->m_DisplayList = NULL;
300                                 }
301                         }
302                 }
303         }
304 }
305
306 void RAS_BucketManager::ReleaseMaterials(RAS_IPolyMaterial * mat)
307 {
308         BucketList::iterator bit;
309         list<RAS_MeshSlot>::iterator mit;
310
311         for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
312                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
313                         (*bit)->GetPolyMaterial()->ReleaseMaterial();
314                 }
315         }
316         
317         for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit) {
318                 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
319                         (*bit)->GetPolyMaterial()->ReleaseMaterial();
320                 }
321         }
322 }
323
324 /* frees the bucket, only used when freeing scenes */
325 void RAS_BucketManager::RemoveMaterial(RAS_IPolyMaterial * mat)
326 {
327         BucketList::iterator bit, bitp;
328         list<RAS_MeshSlot>::iterator mit;
329         int i;
330
331
332         for(i=0; i<m_SolidBuckets.size(); i++) {
333                 RAS_MaterialBucket *bucket = m_SolidBuckets[i];
334                 if (mat == bucket->GetPolyMaterial()) {
335                         m_SolidBuckets.erase(m_SolidBuckets.begin()+i);
336                         delete bucket;
337                         i--;
338                 }
339         }
340
341         for(int i=0; i<m_AlphaBuckets.size(); i++) {
342                 RAS_MaterialBucket *bucket = m_AlphaBuckets[i];
343                 if (mat == bucket->GetPolyMaterial()) {
344                         m_AlphaBuckets.erase(m_AlphaBuckets.begin()+i);
345                         delete bucket;
346                         i--;
347                 }
348         }
349 }
350
351 //#include <stdio.h>
352
353 void RAS_BucketManager::MergeBucketManager(RAS_BucketManager *other, SCA_IScene *scene)
354 {
355         /* concatinate lists */
356         // printf("BEFORE %d %d\n", GetSolidBuckets().size(), GetAlphaBuckets().size());
357         BucketList::iterator it;
358
359         for (it = other->GetSolidBuckets().begin(); it != other->GetSolidBuckets().end(); ++it)
360                 (*it)->GetPolyMaterial()->Replace_IScene(scene);
361
362         GetSolidBuckets().insert( GetSolidBuckets().end(), other->GetSolidBuckets().begin(), other->GetSolidBuckets().end() );
363         other->GetSolidBuckets().clear();
364
365         for (it = other->GetAlphaBuckets().begin(); it != other->GetAlphaBuckets().end(); ++it)
366                 (*it)->GetPolyMaterial()->Replace_IScene(scene);
367
368         GetAlphaBuckets().insert( GetAlphaBuckets().end(), other->GetAlphaBuckets().begin(), other->GetAlphaBuckets().end() );
369         other->GetAlphaBuckets().clear();
370         //printf("AFTER %d %d\n", GetSolidBuckets().size(), GetAlphaBuckets().size());
371 }
372