mirror of
				https://github.com/thunderbrewhq/thunderbrew
				synced 2025-10-31 08:16:03 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			719 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			719 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "model/CM2Scene.hpp"
 | |
| #include "gx/Shader.hpp"
 | |
| #include "gx/Transform.hpp"
 | |
| #include "model/CM2Cache.hpp"
 | |
| #include "model/CM2Light.hpp"
 | |
| #include "model/CM2Model.hpp"
 | |
| #include "model/CM2SceneRender.hpp"
 | |
| #include "model/CM2Shared.hpp"
 | |
| #include "model/M2Internal.hpp"
 | |
| #include "model/M2Sort.hpp"
 | |
| #include <algorithm>
 | |
| #include <cassert>
 | |
| #include <tempest/Math.hpp>
 | |
| 
 | |
| uint32_t CM2Scene::s_optFlags = 0xFFFFFFFF;
 | |
| 
 | |
| void CM2Scene::AnimateThread(void* arg) {
 | |
|     // TODO
 | |
| }
 | |
| 
 | |
| void CM2Scene::ComputeElementShaders(M2Element* element) {
 | |
|     auto model = element->model;
 | |
|     auto batch = element->batch;
 | |
|     auto material = &model->m_shared->m_data->materials[batch->materialIndex];
 | |
|     auto lighting = model->m_currentLighting;
 | |
| 
 | |
|     int32_t shaded;
 | |
|     int32_t lightCount;
 | |
| 
 | |
|     if (material->flags & 0x1 || CM2SceneRender::s_shadedList[material->blendMode] == 0) {
 | |
|         shaded = 0;
 | |
|         lightCount = material->flags & 0x1 ? 0 : lighting->m_lightCount;
 | |
|     } else {
 | |
|         shaded = 1;
 | |
|         lightCount = lighting->m_lightCount;
 | |
|     }
 | |
| 
 | |
|     int32_t boneInfluences = element->skinSection->boneInfluences;
 | |
| 
 | |
|     int32_t v18;
 | |
|     if (material->blendMode == M2BLEND_OPAQUE) {
 | |
|         v18 = 0;
 | |
|     } else if (material->blendMode == M2BLEND_ALPHA_KEY) {
 | |
|         v18 = CMath::fuint(element->alpha * 224.0f);
 | |
|     } else {
 | |
|         v18 = 1;
 | |
|     }
 | |
| 
 | |
|     int32_t v8 = 0;
 | |
|     if (!(material->flags & 0x1) && !(material->flags & 0x100) && lighting->m_flags & 0x10) {
 | |
|         // TODO
 | |
|         // v8 = Sub873FF0();
 | |
| 
 | |
|         if (v8) {
 | |
|             if (lighting->m_flags & 0x8) {
 | |
|                 v8 = 1;
 | |
|             }
 | |
| 
 | |
|             if (element->type == 1) {
 | |
|                 v8 = 0;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     int32_t v9 = v18 && (/* TODO !GxCaps().dword130 ||*/ v8);
 | |
|     int32_t v10 = std::min(boneInfluences, 2);
 | |
|     int32_t v11 = std::min(v8, 2);
 | |
| 
 | |
|     element->vertexPermute = shaded + 2 * (v11 + v10 + 2 * v11 + lightCount + 4 * (v11 + v10 + 2 * v11));
 | |
|     element->pixelPermute = v8 + 4 * (CShaderEffect::s_usePcfFiltering + 2 * v9);
 | |
| 
 | |
|     // TODO
 | |
|     // element->dword3C = v8;
 | |
| }
 | |
| 
 | |
| int32_t CM2Scene::SortOpaque(uint32_t a, uint32_t b, const void* userArg) {
 | |
|     auto elements = static_cast<const CM2Scene*>(userArg)->m_elements.Ptr();
 | |
|     auto elementA = const_cast<M2Element*>(&elements[a]);
 | |
|     auto elementB = const_cast<M2Element*>(&elements[b]);
 | |
| 
 | |
|     if (elementA->type < elementB->type) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->type > elementB->type) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     switch (elementA->type) {
 | |
|         case 0:
 | |
|         case 1:
 | |
|             return CM2Scene::SortOpaqueGeoBatches(elementA, elementB);
 | |
| 
 | |
|         case 3:
 | |
|             return CM2Scene::SortOpaqueRibbons(elementA, elementB);
 | |
| 
 | |
|         case 4:
 | |
|             return CM2Scene::SortOpaqueParticles(elementA, elementB);
 | |
| 
 | |
|         default:
 | |
|             return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| int32_t CM2Scene::SortOpaqueGeoBatches(M2Element* elementA, M2Element* elementB) {
 | |
|     auto modelA = elementA->model;
 | |
|     auto dataA = modelA->m_shared->m_data;
 | |
|     auto batchA = elementA->batch;
 | |
|     auto modelB = elementB->model;
 | |
|     auto dataB = modelB->m_shared->m_data;
 | |
|     auto batchB = elementB->batch;
 | |
| 
 | |
|     if (elementA->type == 0) {
 | |
|         if (batchA->materialLayer < batchB->materialLayer) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (batchA->materialLayer > batchB->materialLayer) {
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         if (elementA->effect && elementB->effect) {
 | |
|             auto effectA = elementA->effect;
 | |
|             auto effectB = elementB->effect;
 | |
|             auto vertexShaderA = effectA->m_vertexShaders[elementA->vertexPermute];
 | |
|             auto pixelShaderA = effectA->m_pixelShaders[elementA->pixelPermute];
 | |
|             auto vertexShaderB = effectB->m_vertexShaders[elementB->vertexPermute];
 | |
|             auto pixelShaderB = effectB->m_pixelShaders[elementB->pixelPermute];
 | |
| 
 | |
|             if (vertexShaderA < vertexShaderB) {
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             if (vertexShaderA > vertexShaderB) {
 | |
|                 return 1;
 | |
|             }
 | |
| 
 | |
|             if (pixelShaderA < pixelShaderB) {
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             if (pixelShaderA > pixelShaderB) {
 | |
|                 return 1;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (modelA->m_shared < modelB->m_shared) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (modelA->m_shared > modelB->m_shared) {
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         if ((elementA->flags & 0x4) < (elementB->flags & 0x4)) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if ((elementA->flags & 0x4) > (elementB->flags & 0x4)) {
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         if (modelA < modelB) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (modelA > modelB) {
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         if (elementA->skinSection->boneComboIndex < elementB->skinSection->boneComboIndex) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (elementA->skinSection->boneComboIndex > elementB->skinSection->boneComboIndex) {
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     auto materialA = &dataA->materials[batchA->materialIndex];
 | |
|     auto materialB = &dataB->materials[batchB->materialIndex];
 | |
| 
 | |
|     if (materialA->blendMode < materialB->blendMode) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (materialA->blendMode > materialB->blendMode) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if ((materialA->flags & 0x1F) < (materialB->flags & 0x1F)) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((materialA->flags & 0x1F) > (materialB->flags & 0x1F)) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (batchA->textureCount > 0 && batchB->textureCount > 0) {
 | |
|         for (int32_t i = 0; i < std::min(batchA->textureCount, batchB->textureCount); i++) {
 | |
|             auto textureIndexA = dataA->textureCombos[batchA->textureComboIndex];
 | |
|             auto textureA = textureIndexA >= dataA->textures.Count() ? 0 : reinterpret_cast<intptr_t>(modelA->m_textures[textureIndexA]);
 | |
|             auto textureIndexB = dataB->textureCombos[batchB->textureComboIndex];
 | |
|             auto textureB = textureIndexB >= dataB->textures.Count() ? 0 : reinterpret_cast<intptr_t>(modelB->m_textures[textureIndexB]);
 | |
| 
 | |
|             if ((textureA - textureB) / sizeof(void*) < 0) {
 | |
|                 return -1;
 | |
|             }
 | |
| 
 | |
|             if ((textureA - textureB) / sizeof(void*) > 0) {
 | |
|                 return 1;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (batchA->textureCount < batchB->textureCount) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (batchA->textureCount > batchB->textureCount) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (batchA < batchB)  {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     return batchA > batchB;
 | |
| }
 | |
| 
 | |
| int32_t CM2Scene::SortOpaqueParticles(M2Element* elementA, M2Element* elementB) {
 | |
|     // TODO
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int32_t CM2Scene::SortOpaqueRibbons(M2Element* elementA, M2Element* elementB) {
 | |
|     // TODO
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int32_t CM2Scene::SortTransparent(uint32_t a, uint32_t b, const void* userArg) {
 | |
|     auto elements = static_cast<const CM2Scene*>(userArg)->m_elements.Ptr();
 | |
|     auto elementA = const_cast<M2Element*>(&elements[a]);
 | |
|     auto elementB = const_cast<M2Element*>(&elements[b]);
 | |
| 
 | |
|     if (elementA->float10 > elementB->float10) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->float10 < elementB->float10) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if ((elementA->flags & 0x1) > (elementB->flags & 0x1)) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((elementA->flags & 0x1) < (elementB->flags & 0x1)) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->priorityPlane < elementB->priorityPlane) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->priorityPlane > elementB->priorityPlane) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->float14 > elementB->float14) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->float14 < elementB->float14) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if ((CM2Scene::s_optFlags & 0x4000)
 | |
|         && (elementA->type != elementB->type || elementA->model != elementB->model)
 | |
|         && elementA->effect
 | |
|         && elementB->effect
 | |
|     ) {
 | |
|         auto effectA = elementA->effect;
 | |
|         auto effectB = elementB->effect;
 | |
|         auto vertexShaderA = effectA->m_vertexShaders[elementA->vertexPermute];
 | |
|         auto pixelShaderA = effectA->m_pixelShaders[elementA->pixelPermute];
 | |
|         auto vertexShaderB = effectB->m_vertexShaders[elementB->vertexPermute];
 | |
|         auto pixelShaderB = effectB->m_pixelShaders[elementB->pixelPermute];
 | |
| 
 | |
|         if (vertexShaderA < vertexShaderB) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (vertexShaderA > vertexShaderB) {
 | |
|             return 1;
 | |
|         }
 | |
| 
 | |
|         if (pixelShaderA < pixelShaderB) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (pixelShaderA > pixelShaderB) {
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (elementA->model < elementB->model) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->model > elementB->model) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->type < elementB->type) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->type > elementB->type) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (elementA->type <= 2) {
 | |
|         if (elementA->batch->materialLayer < elementB->batch->materialLayer) {
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         if (elementA->batch->materialLayer > elementB->batch->materialLayer) {
 | |
|             return 1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!(CM2Scene::s_optFlags & 0x4000) || !elementA->effect || !elementB->effect) {
 | |
|         return CM2Scene::SortOpaque(a, b, userArg);
 | |
|     }
 | |
| 
 | |
|     auto effectA = elementA->effect;
 | |
|     auto effectB = elementB->effect;
 | |
|     auto vertexShaderA = effectA->m_vertexShaders[elementA->vertexPermute];
 | |
|     auto pixelShaderA = effectA->m_pixelShaders[elementA->pixelPermute];
 | |
|     auto vertexShaderB = effectB->m_vertexShaders[elementB->vertexPermute];
 | |
|     auto pixelShaderB = effectB->m_pixelShaders[elementB->pixelPermute];
 | |
| 
 | |
|     if (vertexShaderA < vertexShaderB) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (vertexShaderA > vertexShaderB) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     if (pixelShaderA < pixelShaderB) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (pixelShaderA > pixelShaderB) {
 | |
|         return 1;
 | |
|     }
 | |
| 
 | |
|     return CM2Scene::SortOpaque(a, b, userArg);
 | |
| }
 | |
| 
 | |
| void CM2Scene::AdvanceTime(uint32_t a2) {
 | |
|     this->m_time += a2;
 | |
| 
 | |
|     this->m_cache->UpdateShared();
 | |
|     this->m_cache->GarbageCollect(0);
 | |
| 
 | |
|     this->m_flags |= 0x4;
 | |
|     this->uint10 = a2;
 | |
| 
 | |
|     if (a2) {
 | |
|         for (auto model = this->m_animateList; model; model = model->m_animateNext) {
 | |
|             model->ProcessCallbacksRecursive();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     this->m_flags &= ~0x4;
 | |
| }
 | |
| 
 | |
| void CM2Scene::Animate(const C3Vector& cameraPos) {
 | |
|     this->uint14++;
 | |
| 
 | |
|     uint32_t optFlags = this->m_cache->m_flags & 0xE000;
 | |
|     if (CM2Scene::s_optFlags != optFlags) {
 | |
|         CM2Scene::s_optFlags = optFlags;
 | |
|     }
 | |
| 
 | |
|     GxXformView(this->m_view);
 | |
|     C3Vector invCameraPos = { -cameraPos.x, -cameraPos.y, -cameraPos.z };
 | |
|     this->m_view.Translate(invCameraPos);
 | |
|     this->m_viewInv = this->m_view.Inverse(this->m_view.Determinant());
 | |
| 
 | |
|     if (this->m_cache->m_flags & 0x4) {
 | |
|         // In multithreaded mode, iteration over the animate list is interleaved:
 | |
|         // - the current thread animates entries 0, 2, 4, ...
 | |
|         // - the newly created thread animates entries 1, 3, 5, ...
 | |
| 
 | |
|         this->m_cache->BeginThread(CM2Scene::AnimateThread, this);
 | |
| 
 | |
|         CM2Model* nextModel;
 | |
|         for (auto model = this->m_animateList; model; model = nextModel->m_animateNext) {
 | |
|             if (!model->m_attachParent) {
 | |
|                 if (model->m_flag1000) {
 | |
|                     C3Vector v222 = { 0.0f, 0.0f, 0.0f };
 | |
|                     C3Vector v218 = { 1.0f, 1.0f, 1.0f };
 | |
| 
 | |
|                     model->AnimateMTSimple(&this->m_view, v218, v222, 1.0f, 1.0f);
 | |
|                 } else {
 | |
|                     C3Vector v220 = { 0.0f, 0.0f, 0.0f };
 | |
|                     C3Vector v221 = { 1.0f, 1.0f, 1.0f };
 | |
| 
 | |
|                     model->AnimateMT(&this->m_view, v221, v220, 1.0f, 1.0f);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             nextModel = model->m_animateNext;
 | |
|             if (!nextModel) {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         this->m_cache->WaitThread();
 | |
|     } else {
 | |
|         for (auto model = this->m_animateList; model; model = model->m_animateNext) {
 | |
|             if (!model->m_attachParent) {
 | |
|                 if (model->m_flag1000 != 0) {
 | |
|                     C3Vector v222 = { 0.0f, 0.0f, 0.0f };
 | |
|                     C3Vector v218 = { 1.0f, 1.0f, 1.0f };
 | |
| 
 | |
|                     model->AnimateMTSimple(&this->m_view, v218, v222, 1.0f, 1.0f);
 | |
|                 } else {
 | |
|                     C3Vector v220 = { 0.0f, 0.0f, 0.0f };
 | |
|                     C3Vector v221 = { 1.0f, 1.0f, 1.0f };
 | |
| 
 | |
|                     model->AnimateMT(&this->m_view, v221, v220, 1.0f, 1.0f);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (auto model = this->m_animateList; model; model = model->m_animateNext) {
 | |
|         if (!model->m_attachParent) {
 | |
|             model->AnimateST();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     while (this->m_animateList) {
 | |
|         // TODO
 | |
|         // - this is clearing out the animate list; why? something must reattach things to it...
 | |
|         auto model = this->m_animateList;
 | |
|         this->m_animateList = model->m_animateNext;
 | |
|         model->m_animatePrev = nullptr;
 | |
|         model->m_animateNext = nullptr;
 | |
| 
 | |
|         model->SetupLighting();
 | |
|     }
 | |
| 
 | |
|     this->array44.SetCount(0);
 | |
|     for (int32_t i = 0; i < M2PASS_COUNT; i++) {
 | |
|         this->array54[i].SetCount(0);
 | |
|     }
 | |
| 
 | |
|     this->m_elements.SetCount(0);
 | |
|     int32_t elementIndex = 0;
 | |
| 
 | |
|     while (this->m_drawList) {
 | |
|         auto model = this->m_drawList;
 | |
|         this->m_drawList = model->m_drawNext;
 | |
| 
 | |
|         model->m_flag8 = 0;
 | |
|         model->m_flag10000 = 0;
 | |
|         model->m_drawPrev = nullptr;
 | |
|         model->m_drawNext = nullptr;
 | |
| 
 | |
|         if (!model->IsDrawable(0, 0) || model->m_flag4000) {
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         auto v19 = model->m_currentLighting;
 | |
|         auto data = model->m_shared->m_data;
 | |
|         auto v21 = v19->m_flags & 0x20;
 | |
|         auto v22 = v19->m_flags & 0x40;
 | |
| 
 | |
|         if (v21 && v22) {
 | |
|             // TODO
 | |
|             // - liquid plane stuff
 | |
|         }
 | |
| 
 | |
|         auto skinProfile = model->m_shared->skinProfile;
 | |
|         auto v17 = (this->m_cache->m_flags & 0x1) == 0;
 | |
| 
 | |
|         int32_t v229;
 | |
|         if (v17 || (model->m_flags & 0x1) != 0 || (v17 = (model->m_flag40) == 0, v229 = 1, v17)) {
 | |
|             v229 = 0;
 | |
|         }
 | |
| 
 | |
|         uint32_t batchCount;
 | |
|         if (model->ptr2D0) {
 | |
|             // TODO
 | |
|             // batchCount = (model->ptr2D0 + 4);
 | |
| 
 | |
|             assert(false);
 | |
|         } else {
 | |
|             batchCount = skinProfile->batches.Count();
 | |
|         }
 | |
| 
 | |
|         for (int32_t batchIndex = 0; batchIndex < batchCount; batchIndex++) {
 | |
|             M2Batch* batch;
 | |
|             M2SkinSection* skinSection;
 | |
|             CShaderEffect* effect;
 | |
|             int32_t v221;
 | |
|             int32_t v222;
 | |
| 
 | |
|             if (model->ptr2D0) {
 | |
|                 // TODO
 | |
|                 // batch = &model->m_optGeo->batches[batchIndex];
 | |
|                 // skinSection = model->m_optGeo->skinSections[batch->skinSectionIndex];
 | |
| 
 | |
|                 assert(false);
 | |
|             } else {
 | |
|                 batch = &skinProfile->batches[batchIndex];
 | |
|                 skinSection = &model->m_shared->m_skinSections[batch->skinSectionIndex];
 | |
| 
 | |
|                 if (!model->m_skinSections[batch->skinSectionIndex]) {
 | |
|                     continue;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (batch->shader == 0x8000) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             float alpha = model->alpha19C;
 | |
| 
 | |
|             if (batch->colorIndex < data->colors.Count()) {
 | |
|                 auto& color = model->m_colors[batch->colorIndex];
 | |
|                 alpha *= color.alphaTrack.currentValue;
 | |
|             }
 | |
| 
 | |
|             if (batch->textureCount) {
 | |
|                 auto& textureWeight = model->m_textureWeights[data->textureWeightCombos[batch->textureWeightComboIndex]];
 | |
|                 alpha *= textureWeight.weightTrack.currentValue;
 | |
|             }
 | |
| 
 | |
|             if (alpha < 0.000099999997f) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             M2Material* material = &data->materials[batch->materialIndex];
 | |
| 
 | |
|             auto v17 = (batch->flags & 0x4) == 0;
 | |
|             if (v17 || (v17 = this->uint104 == 0, v222 = 1, v17)) {
 | |
|                 v222 = 0;
 | |
|             }
 | |
| 
 | |
|             M2Material* layerMaterial = batch->materialLayer
 | |
|                 ? &data->materials[batch->materialIndex - batch->materialLayer]
 | |
|                 : &data->materials[batch->materialIndex];
 | |
| 
 | |
|             if (layerMaterial->blendMode > 1 || (v221 = 0, alpha < 0.99998999f)) {
 | |
|                 v221 = 1;
 | |
|             }
 | |
| 
 | |
|             if (model->ptr2D0) {
 | |
|                 // TODO
 | |
|                 // effect = model->m_optGeo->effects[batchIndex];
 | |
| 
 | |
|                 assert(false);
 | |
|             } else {
 | |
|                 effect = model->m_shared->m_batchShaders[batchIndex];
 | |
|             }
 | |
| 
 | |
|             if (!effect) {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             auto element = this->m_elements.New();
 | |
| 
 | |
|             if (v222) {
 | |
|                 element->type = 1;
 | |
|             } else if (!model->IsBatchDoodadCompatible(batch) || v221) {
 | |
|                 element->type = 0;
 | |
|             } else {
 | |
|                 element->type = 2;
 | |
|             }
 | |
| 
 | |
|             element->model = model;
 | |
| 
 | |
|             element->flags = 0x0;
 | |
|             if (v221 == 1 && v21 && v22 && !v222) {
 | |
|                 element->flags |= 0x2;
 | |
|             }
 | |
|             if (model->ptr2D0) {
 | |
|                 element->flags |= 0x4;
 | |
|             }
 | |
| 
 | |
|             element->alpha = alpha;
 | |
|             element->index = batchIndex;
 | |
|             element->priorityPlane = batch->priorityPlane;
 | |
|             element->batch = batch;
 | |
|             element->skinSection = skinSection;
 | |
|             element->effect = effect;
 | |
| 
 | |
|             CM2Scene::ComputeElementShaders(element);
 | |
| 
 | |
|             float v58;
 | |
| 
 | |
|             if (v221 < 1) {
 | |
|                 element->float14 = model->float88;
 | |
|                 v58 = model->float88;
 | |
|             } else if (data->flags & 0x10) {
 | |
|                 element->float14 = (skinSection->sortCenterPosition * model->m_boneMatrices[skinSection->centerBoneIndex]).SquaredMag();
 | |
|                 v58 = model->float88;
 | |
|             } else {
 | |
|                 // TODO other sort position logic
 | |
| 
 | |
|                 v58 = model->float88;
 | |
|             }
 | |
| 
 | |
|             element->float10 = v58;
 | |
| 
 | |
|             if (element->type == 2) {
 | |
|                 // TODO
 | |
|             } else if (v221 == 1) {
 | |
|                 if (v222) {
 | |
|                     if (v22) {
 | |
|                         *this->array54[2].New() = elementIndex;
 | |
|                     } else {
 | |
|                         *this->array54[1].New() = elementIndex;
 | |
|                     }
 | |
|                 } else {
 | |
|                     if (v21) {
 | |
|                         *this->array54[1].New() = elementIndex;
 | |
|                     }
 | |
| 
 | |
|                     if (v22) {
 | |
|                         *this->array54[2].New() = elementIndex;
 | |
|                     }
 | |
|                 }
 | |
|             } else {
 | |
|                 *this->array54[v221].New() = elementIndex;
 | |
|             }
 | |
| 
 | |
|             elementIndex++;
 | |
| 
 | |
|             if (v229 && !v222 && v221 >= 1 && !(material->flags & 0x10)) {
 | |
|                 // TODO
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // TODO
 | |
|         // - ribbons
 | |
| 
 | |
|         // TODO
 | |
|         // - draw callbacks
 | |
|     }
 | |
| 
 | |
|     M2HeapSort(CM2Scene::SortOpaque, this->array54[0].Ptr(), this->array54[0].Count(), this);
 | |
|     M2HeapSort(CM2Scene::SortTransparent, this->array54[1].Ptr(), this->array54[1].Count(), this);
 | |
|     M2HeapSort(CM2Scene::SortTransparent, this->array54[2].Ptr(), this->array54[2].Count(), this);
 | |
| 
 | |
|     // TODO sort additive particles
 | |
| }
 | |
| 
 | |
| CM2Model* CM2Scene::CreateModel(const char* file, uint32_t a3) {
 | |
|     if (!file) {
 | |
|         return nullptr;
 | |
|     }
 | |
| 
 | |
|     CM2Shared* shared = this->m_cache->CreateShared(file, a3);
 | |
|     if (!shared) {
 | |
|         shared = this->m_cache->CreateShared("Spells\\ErrorCube.mdx", 0);
 | |
|     }
 | |
| 
 | |
|     CM2Model* model = nullptr;
 | |
| 
 | |
|     if (shared) {
 | |
|         model = CM2Model::AllocModel(g_modelPool);
 | |
| 
 | |
|         if (model) {
 | |
|             if (!model->Initialize(this, shared, nullptr, a3)) {
 | |
|                 // TODO
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         shared->Release();
 | |
|     }
 | |
| 
 | |
|     return model;
 | |
| }
 | |
| 
 | |
| int32_t CM2Scene::Draw(M2PASS pass) {
 | |
|     // TODO
 | |
|     // - conditional check on this->dword144
 | |
| 
 | |
|     if (CM2Scene::s_optFlags != (this->m_cache->m_flags & 0xE000)) {
 | |
|         CM2Scene::s_optFlags = this->m_cache->m_flags & 0xE000;
 | |
|     }
 | |
| 
 | |
|     CM2SceneRender render(this);
 | |
| 
 | |
|     render.Draw(pass, this->m_elements.m_data, this->array54[pass].m_data, this->array54[pass].Count());
 | |
| 
 | |
|     if (pass == M2PASS_0) {
 | |
|         render.Draw(pass, this->m_elements.m_data, this->array44.m_data, this->array44.Count());
 | |
|     }
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| void CM2Scene::SelectLights(CM2Lighting* lighting) {
 | |
|     for (auto light = this->m_lightList; light; light = light->m_lightNext) {
 | |
|         lighting->AddLight(light);
 | |
|     }
 | |
| 
 | |
|     // TODO
 | |
| }
 | 
