2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19 * All rights reserved.
21 * The Original Code is: all of this file.
23 * Contributor(s): none yet.
25 * ***** END GPL LICENSE BLOCK *****
28 /** \file gameengine/Rasterizer/RAS_BucketManager.cpp
33 /* don't show these anoying STL warnings */
34 # pragma warning (disable:4786)
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"
43 #include "RAS_BucketManager.h"
48 struct RAS_BucketManager::sortedmeshslot
51 MT_Scalar m_z; /* depth */
52 RAS_MeshSlot *m_ms; /* mesh slot */
53 RAS_MaterialBucket *m_bucket; /* buck mesh slot came from */
57 void set(RAS_MeshSlot *ms, RAS_MaterialBucket *bucket, const MT_Vector3& pnorm)
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]);
62 m_z = MT_dot(pnorm, pos);
68 struct RAS_BucketManager::backtofront
70 bool operator()(const sortedmeshslot &a, const sortedmeshslot &b)
72 return (a.m_z < b.m_z) || (a.m_z == b.m_z && a.m_ms < b.m_ms);
76 struct RAS_BucketManager::fronttoback
78 bool operator()(const sortedmeshslot &a, const sortedmeshslot &b)
80 return (a.m_z > b.m_z) || (a.m_z == b.m_z && a.m_ms > b.m_ms);
86 RAS_BucketManager::RAS_BucketManager()
91 RAS_BucketManager::~RAS_BucketManager()
93 BucketList::iterator it;
95 for (it = m_SolidBuckets.begin(); it != m_SolidBuckets.end(); it++)
98 for (it = m_AlphaBuckets.begin(); it != m_AlphaBuckets.end(); it++)
101 m_SolidBuckets.clear();
102 m_AlphaBuckets.clear();
105 void RAS_BucketManager::OrderBuckets(const MT_Transform& cameratrans, BucketList& buckets, vector<sortedmeshslot>& slots, bool alpha)
107 BucketList::iterator bit;
108 list<RAS_MeshSlot>::iterator mit;
109 size_t size = 0, i = 0;
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]);
115 for (bit = buckets.begin(); bit != buckets.end(); ++bit)
117 SG_DList::iterator<RAS_MeshSlot> mit((*bit)->GetActiveMeshSlots());
118 for (mit.begin(); !mit.end(); ++mit)
124 for (bit = buckets.begin(); bit != buckets.end(); ++bit)
126 RAS_MaterialBucket* bucket = *bit;
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);
135 sort(slots.begin(), slots.end(), backtofront());
137 sort(slots.begin(), slots.end(), fronttoback());
140 void RAS_BucketManager::RenderAlphaBuckets(const MT_Transform& cameratrans, RAS_IRasterizer* rasty)
142 vector<sortedmeshslot> slots;
143 vector<sortedmeshslot>::iterator sit;
145 // Having depth masks disabled/enabled gives different artifacts in
146 // case no sorting is done or is done inexact. For compatibility, we
148 if (rasty->GetDrawingMode() != RAS_IRasterizer::KX_SHADOW)
149 rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_DISABLED);
151 OrderBuckets(cameratrans, m_AlphaBuckets, slots, true);
153 for (sit=slots.begin(); sit!=slots.end(); ++sit) {
154 rasty->SetClientObject(sit->m_ms->m_clientObj);
156 while (sit->m_bucket->ActivateMaterial(cameratrans, rasty))
157 sit->m_bucket->RenderMeshSlot(cameratrans, rasty, *(sit->m_ms));
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);
164 rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_ENABLED);
167 void RAS_BucketManager::RenderSolidBuckets(const MT_Transform& cameratrans, RAS_IRasterizer* rasty)
169 BucketList::iterator bit;
171 rasty->SetDepthMask(RAS_IRasterizer::KX_DEPTHMASK_ENABLED);
173 for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
175 RAS_MaterialBucket* bucket = *bit;
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);
183 // make this mesh slot culled automatically for next frame
184 // it will be culled out by frustum culling
188 list<RAS_MeshSlot>::iterator mit;
189 for (mit = (*bit)->msBegin(); mit != (*bit)->msEnd(); ++mit) {
193 rasty->SetClientObject(rasty, mit->m_clientObj);
195 while ((*bit)->ActivateMaterial(cameratrans, rasty))
196 (*bit)->RenderMeshSlot(cameratrans, rasty, *mit);
198 // make this mesh slot culled automatically for next frame
199 // it will be culled out by frustum culling
200 mit->SetCulled(true);
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. */
209 vector<sortedmeshslot> slots;
210 vector<sortedmeshslot>::iterator sit;
212 OrderBuckets(cameratrans, m_SolidBuckets, slots, false);
214 for (sit=slots.begin(); sit!=slots.end(); ++sit) {
215 rendertools->SetClientObject(rasty, sit->m_ms->m_clientObj);
217 while (sit->m_bucket->ActivateMaterial(cameratrans, rasty))
218 sit->m_bucket->RenderMeshSlot(cameratrans, rasty, *(sit->m_ms));
223 void RAS_BucketManager::Renderbuckets(const MT_Transform& cameratrans, RAS_IRasterizer* rasty)
225 /* beginning each frame, clear (texture/material) caching information */
226 rasty->ClearCachingInfo();
228 RenderSolidBuckets(cameratrans, rasty);
229 RenderAlphaBuckets(cameratrans, rasty);
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.
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);
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);
253 rasty->SetClientObject(NULL);
256 RAS_MaterialBucket *RAS_BucketManager::FindBucket(RAS_IPolyMaterial *material, bool &bucketCreated)
258 BucketList::iterator it;
260 bucketCreated = false;
262 for (it = m_SolidBuckets.begin(); it != m_SolidBuckets.end(); it++)
263 if (*(*it)->GetPolyMaterial() == *material)
266 for (it = m_AlphaBuckets.begin(); it != m_AlphaBuckets.end(); it++)
267 if (*(*it)->GetPolyMaterial() == *material)
270 RAS_MaterialBucket *bucket = new RAS_MaterialBucket(material);
271 bucketCreated = true;
273 if (bucket->IsAlpha())
274 m_AlphaBuckets.push_back(bucket);
276 m_SolidBuckets.push_back(bucket);
281 void RAS_BucketManager::OptimizeBuckets(MT_Scalar distance)
283 BucketList::iterator bit;
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);
293 void RAS_BucketManager::ReleaseDisplayLists(RAS_IPolyMaterial *mat)
295 BucketList::iterator bit;
296 list<RAS_MeshSlot>::iterator mit;
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;
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;
321 void RAS_BucketManager::ReleaseMaterials(RAS_IPolyMaterial * mat)
323 BucketList::iterator bit;
324 list<RAS_MeshSlot>::iterator mit;
326 for (bit = m_SolidBuckets.begin(); bit != m_SolidBuckets.end(); ++bit) {
327 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
328 (*bit)->GetPolyMaterial()->ReleaseMaterial();
332 for (bit = m_AlphaBuckets.begin(); bit != m_AlphaBuckets.end(); ++bit) {
333 if (mat == NULL || (mat == (*bit)->GetPolyMaterial())) {
334 (*bit)->GetPolyMaterial()->ReleaseMaterial();
339 /* frees the bucket, only used when freeing scenes */
340 void RAS_BucketManager::RemoveMaterial(RAS_IPolyMaterial * mat)
342 BucketList::iterator bit, bitp;
343 list<RAS_MeshSlot>::iterator mit;
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);
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);
368 void RAS_BucketManager::MergeBucketManager(RAS_BucketManager *other, SCA_IScene *scene)
370 /* concatenate lists */
371 // printf("BEFORE %d %d\n", GetSolidBuckets().size(), GetAlphaBuckets().size());
373 GetSolidBuckets().insert( GetSolidBuckets().end(), other->GetSolidBuckets().begin(), other->GetSolidBuckets().end() );
374 other->GetSolidBuckets().clear();
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());