#include "viewmanager.h" #include "mxdirectx/mxstopwatch.h" #include "tgl/d3drm/impl.h" #include "viewlod.h" #include <vec.h> DECOMP_SIZE_ASSERT(ViewManager, 0x1bc) // GLOBAL: LEGO1 0x100dbc78 int g_boundingBoxCornerMap[8][3] = {{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {1, 0, 0}, {0, 1, 1}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}}; // GLOBAL: LEGO1 0x100dbcd8 int g_planePointIndexMap[18] = {0, 1, 5, 6, 2, 3, 3, 0, 4, 1, 2, 6, 0, 3, 2, 4, 5, 6}; // GLOBAL: LEGO1 0x10101050 float g_LODScaleFactor = 4.0F; // GLOBAL: LEGO1 0x10101054 float g_minLODThreshold = 0.00097656297; // GLOBAL: LEGO1 0x10101058 int g_maxLODLevels = 6; // GLOBAL: LEGO1 0x1010105c float g_unk0x1010105c = 0.000125F; // GLOBAL: LEGO1 0x10101060 float g_elapsedSeconds = 0; inline void SetAppData(ViewROI* p_roi, LPD3DRM_APPDATA data); inline undefined4 GetD3DRM(IDirect3DRM2*& d3drm, Tgl::Renderer* pRenderer); inline undefined4 GetFrame(IDirect3DRMFrame2*& frame, Tgl::Group* scene); // FUNCTION: LEGO1 0x100a5eb0 ViewManager::ViewManager(Tgl::Renderer* pRenderer, Tgl::Group* scene, const OrientableROI* point_of_view) : scene(scene), flags(c_bit1 | c_bit2 | c_bit3 | c_bit4) { SetPOVSource(point_of_view); prev_render_time = 0.09; GetD3DRM(d3drm, pRenderer); GetFrame(frame, scene); width = 0.0; height = 0.0; view_angle = 0.0; pov.SetIdentity(); front = 0.0; back = 0.0; memset(transformed_points, 0, sizeof(transformed_points)); seconds_allowed = 1.0; } // FUNCTION: LEGO1 0x100a60c0 ViewManager::~ViewManager() { SetPOVSource(NULL); } // FUNCTION: LEGO1 0x100a6150 // FUNCTION: BETA10 0x10172164 unsigned int ViewManager::IsBoundingBoxInFrustum(const BoundingBox& p_bounding_box) { const Vector3* box[] = {&p_bounding_box.Min(), &p_bounding_box.Max()}; float und[8][3]; int i, j, k; for (i = 0; i < 8; i++) { for (j = 0; j < 3; j++) { und[i][j] = box[g_boundingBoxCornerMap[i][j]]->operator[](j); } } for (i = 0; i < 6; i++) { for (k = 0; k < 8; k++) { if (frustum_planes[i][0] * und[k][0] + frustum_planes[i][2] * und[k][2] + frustum_planes[i][1] * und[k][1] + frustum_planes[i][3] >= 0.0f) { break; } } if (k == 8) { return FALSE; } } return TRUE; } // FUNCTION: LEGO1 0x100a6410 void ViewManager::Remove(ViewROI* p_roi) { for (CompoundObject::iterator it = rois.begin(); it != rois.end(); it++) { if (*it == p_roi) { rois.erase(it); if (p_roi->GetUnknown0xe0() >= 0) { RemoveROIDetailFromScene(p_roi); } const CompoundObject* comp = p_roi->GetComp(); if (comp != NULL) { for (CompoundObject::const_iterator it = comp->begin(); !(it == comp->end()); it++) { if (((ViewROI*) *it)->GetUnknown0xe0() >= 0) { RemoveROIDetailFromScene((ViewROI*) *it); } } } return; } } } // FUNCTION: LEGO1 0x100a64d0 void ViewManager::RemoveAll(ViewROI* p_roi) { if (p_roi == NULL) { for (CompoundObject::iterator it = rois.begin(); it != rois.end(); it++) { RemoveAll((ViewROI*) *it); } rois.erase(rois.begin(), rois.end()); } else { if (p_roi->GetUnknown0xe0() >= 0) { RemoveROIDetailFromScene(p_roi); } p_roi->SetUnknown0xe0(-1); const CompoundObject* comp = p_roi->GetComp(); if (comp != NULL) { for (CompoundObject::const_iterator it = comp->begin(); !(it == comp->end()); it++) { if ((ViewROI*) *it != NULL) { RemoveAll((ViewROI*) *it); } } } } } // FUNCTION: LEGO1 0x100a65b0 void ViewManager::UpdateROIDetailBasedOnLOD(ViewROI* p_roi, int p_und) { if (p_roi->GetLODCount() <= p_und) { p_und = p_roi->GetLODCount() - 1; } int unk0xe0 = p_roi->GetUnknown0xe0(); if (unk0xe0 == p_und) { return; } Tgl::Group* group = p_roi->GetGeometry(); Tgl::MeshBuilder* meshBuilder; ViewLOD* lod; if (unk0xe0 < 0) { lod = (ViewLOD*) p_roi->GetLOD(p_und); if (lod->GetUnknown0x08() & ViewLOD::c_bit4) { scene->Add((Tgl::MeshBuilder*) group); SetAppData(p_roi, reinterpret_cast<LPD3DRM_APPDATA>(p_roi)); } } else { lod = (ViewLOD*) p_roi->GetLOD(unk0xe0); if (lod != NULL) { meshBuilder = lod->GetMeshBuilder(); if (meshBuilder != NULL) { group->Remove(meshBuilder); } } lod = (ViewLOD*) p_roi->GetLOD(p_und); } if (lod->GetUnknown0x08() & ViewLOD::c_bit4) { meshBuilder = lod->GetMeshBuilder(); if (meshBuilder != NULL) { group->Add(meshBuilder); SetAppData(p_roi, reinterpret_cast<LPD3DRM_APPDATA>(p_roi)); p_roi->SetUnknown0xe0(p_und); return; } } p_roi->SetUnknown0xe0(-1); } // FUNCTION: LEGO1 0x100a66a0 void ViewManager::RemoveROIDetailFromScene(ViewROI* p_roi) { const ViewLOD* lod = (const ViewLOD*) p_roi->GetLOD(p_roi->GetUnknown0xe0()); if (lod != NULL) { const Tgl::MeshBuilder* meshBuilder = NULL; Tgl::Group* roiGeometry = p_roi->GetGeometry(); meshBuilder = lod->GetMeshBuilder(); if (meshBuilder != NULL) { roiGeometry->Remove(meshBuilder); } scene->Remove(roiGeometry); } p_roi->SetUnknown0xe0(-1); } // FUNCTION: LEGO1 0x100a66f0 inline void ViewManager::ManageVisibilityAndDetailRecursively(ViewROI* p_roi, int p_und) { if (!p_roi->GetVisibility() && p_und != -2) { ManageVisibilityAndDetailRecursively(p_roi, -2); } else { const CompoundObject* comp = p_roi->GetComp(); if (p_und == -1) { if (p_roi->GetWorldBoundingSphere().Radius() > 0.001F) { float und = ProjectedSize(p_roi->GetWorldBoundingSphere()); if (und < seconds_allowed * g_unk0x1010105c) { if (p_roi->GetUnknown0xe0() == -2) { return; } ManageVisibilityAndDetailRecursively(p_roi, -2); return; } p_und = CalculateLODLevel(und, RealtimeView::GetUserMaxLodPower() * seconds_allowed, p_roi); } } if (p_und == -2) { if (p_roi->GetUnknown0xe0() >= 0) { RemoveROIDetailFromScene(p_roi); p_roi->SetUnknown0xe0(-2); } if (comp != NULL) { for (CompoundObject::const_iterator it = comp->begin(); !(it == comp->end()); it++) { ManageVisibilityAndDetailRecursively((ViewROI*) *it, p_und); } } } else if (comp == NULL) { if (p_roi->GetLODs() != NULL && p_roi->GetLODCount() > 0) { UpdateROIDetailBasedOnLOD(p_roi, p_und); return; } } else { p_roi->SetUnknown0xe0(-1); for (CompoundObject::const_iterator it = comp->begin(); !(it == comp->end()); it++) { ManageVisibilityAndDetailRecursively((ViewROI*) *it, p_und); } } } } // FUNCTION: LEGO1 0x100a6930 void ViewManager::Update(float p_previousRenderTime, float) { MxStopWatch stopWatch; stopWatch.Start(); prev_render_time = p_previousRenderTime; flags |= c_bit1; if (flags & c_bit3) { CalculateFrustumTransformations(); } else if (flags & c_bit2) { UpdateViewTransformations(); } for (CompoundObject::iterator it = rois.begin(); it != rois.end(); it++) { ManageVisibilityAndDetailRecursively((ViewROI*) *it, -1); } stopWatch.Stop(); g_elapsedSeconds = stopWatch.ElapsedSeconds(); } inline int ViewManager::CalculateFrustumTransformations() { flags &= ~c_bit3; if (height == 0.0F || front == 0.0F) { return -1; } else { float fVar7 = tan(view_angle / 2.0F); view_area_at_one = view_angle * view_angle * 4.0F; float fVar1 = front * fVar7; float fVar2 = (width / height) * fVar1; float uVar6 = front; float fVar3 = back + front; float fVar4 = fVar3 / front; float fVar5 = fVar4 * fVar1; fVar4 = fVar4 * fVar2; float* frustumVertices = (float*) this->frustum_vertices; // clang-format off *frustumVertices = fVar2; frustumVertices++; *frustumVertices = fVar1; frustumVertices++; *frustumVertices = uVar6; frustumVertices++; *frustumVertices = fVar2; frustumVertices++; *frustumVertices = -fVar1; frustumVertices++; *frustumVertices = uVar6; frustumVertices++; *frustumVertices = -fVar2; frustumVertices++; *frustumVertices = -fVar1; frustumVertices++; *frustumVertices = uVar6; frustumVertices++; *frustumVertices = -fVar2; frustumVertices++; *frustumVertices = fVar1; frustumVertices++; *frustumVertices = uVar6; frustumVertices++; *frustumVertices = fVar4; frustumVertices++; *frustumVertices = fVar5; frustumVertices++; *frustumVertices = fVar3; frustumVertices++; *frustumVertices = fVar4; frustumVertices++; *frustumVertices = -fVar5; frustumVertices++; *frustumVertices = fVar3; frustumVertices++; *frustumVertices = -fVar4; frustumVertices++; *frustumVertices = -fVar5; frustumVertices++; *frustumVertices = fVar3; frustumVertices++; *frustumVertices = -fVar4; frustumVertices++; *frustumVertices = fVar5; frustumVertices++; *frustumVertices = fVar3; // clang-format on UpdateViewTransformations(); return 0; } } inline int ViewManager::CalculateLODLevel(float p_und1, float p_und2, ViewROI* p_roi) { int result; float i; if (IsROIVisibleAtLOD(p_roi) != 0) { if (p_und1 < g_minLODThreshold) { return 0; } result = 1; } else { result = 0; } for (i = p_und2; result < g_maxLODLevels && p_und1 >= i; i *= g_LODScaleFactor) { result++; } return result; } inline int ViewManager::IsROIVisibleAtLOD(ViewROI* p_roi) { const LODListBase* lods = p_roi->GetLODs(); if (lods != NULL && lods->Size() > 0) { if (((ViewLOD*) p_roi->GetLOD(0))->GetUnknown0x08Test8()) { return 1; } return 0; } const CompoundObject* comp = p_roi->GetComp(); if (comp != NULL) { for (CompoundObject::const_iterator it = comp->begin(); !(it == comp->end()); it++) { const LODListBase* lods = ((ViewROI*) *it)->GetLODs(); if (lods != NULL && lods->Size() > 0) { if (((ViewLOD*) ((ViewROI*) *it)->GetLOD(0))->GetUnknown0x08Test8()) { return 1; } return 0; } } } return 0; } // FUNCTION: LEGO1 0x100a6b90 void ViewManager::UpdateViewTransformations() { flags &= ~c_bit2; int i, j, k; for (i = 0; i < 8; i++) { for (j = 0; j < 3; j++) { transformed_points[i][j] = pov[3][j]; for (k = 0; k < 3; k++) { transformed_points[i][j] += pov[k][j] * frustum_vertices[i][k]; } } } for (i = 0; i < 6; i++) { Vector3 a(transformed_points[g_planePointIndexMap[i * 3]]); Vector3 b(transformed_points[g_planePointIndexMap[i * 3 + 1]]); Vector3 c(transformed_points[g_planePointIndexMap[i * 3 + 2]]); Mx3DPointFloat x; Mx3DPointFloat y; Vector3 normal(frustum_planes[i]); x = c; x -= b; y = a; y -= b; normal.EqualsCross(&x, &y); normal.Unitize(); frustum_planes[i][3] = -normal.Dot(&normal, &a); } flags |= c_bit4; } // FUNCTION: LEGO1 0x100a6d50 void ViewManager::SetResolution(int width, int height) { flags |= c_bit3; this->width = width; this->height = height; } // FUNCTION: LEGO1 0x100a6d70 void ViewManager::SetFrustrum(float fov, float front, float back) { this->front = front; this->back = back; flags |= c_bit3; view_angle = fov * 0.017453292519944444; } // FUNCTION: LEGO1 0x100a6da0 void ViewManager::SetPOVSource(const OrientableROI* point_of_view) { if (point_of_view != NULL) { pov = point_of_view->GetLocal2World(); flags |= c_bit2; } } // FUNCTION: LEGO1 0x100a6dc0 float ViewManager::ProjectedSize(const BoundingSphere& p_bounding_sphere) { // The algorithm projects the radius of bounding sphere onto the perpendicular // plane one unit in front of the camera. That value is simply the ratio of the // radius to the distance from the camera to the sphere center. The projected size // is then the ratio of the area of that projected circle to the view surface area // at Z == 1.0. // float sphere_projected_area = 3.14159265359 * (p_bounding_sphere.Radius() * p_bounding_sphere.Radius()); float square_dist_to_sphere = DISTSQRD3(p_bounding_sphere.Center(), pov[3]); return sphere_projected_area / view_area_at_one / square_dist_to_sphere; } // FUNCTION: LEGO1 0x100a6e00 ViewROI* ViewManager::Pick(Tgl::View* p_view, unsigned long x, unsigned long y) { LPDIRECT3DRMPICKEDARRAY picked = NULL; ViewROI* result = NULL; TglImpl::ViewImpl* view = (TglImpl::ViewImpl*) p_view; IDirect3DRMViewport* d3drm = view->ImplementationData(); if (d3drm->Pick(x, y, &picked) != D3DRM_OK) { return NULL; } if (picked != NULL) { if (picked->GetSize() != 0) { LPDIRECT3DRMVISUAL visual; LPDIRECT3DRMFRAMEARRAY frameArray; D3DRMPICKDESC desc; if (picked->GetPick(0, &visual, &frameArray, &desc) == D3DRM_OK) { if (frameArray != NULL) { int size = frameArray->GetSize(); if (size > 1) { for (int i = 1; i < size; i++) { LPDIRECT3DRMFRAME frame = NULL; if (frameArray->GetElement(i, &frame) == D3DRM_OK) { result = (ViewROI*) frame->GetAppData(); if (result != NULL) { frame->Release(); break; } frame->Release(); } } } visual->Release(); frameArray->Release(); } } } picked->Release(); } return result; } inline void SetAppData(ViewROI* p_roi, LPD3DRM_APPDATA data) { IDirect3DRMFrame2* frame = NULL; if (GetFrame(frame, p_roi->GetGeometry()) == 0) { frame->SetAppData(data); } } inline undefined4 GetD3DRM(IDirect3DRM2*& d3drm, Tgl::Renderer* pRenderer) { d3drm = ((TglImpl::RendererImpl*) pRenderer)->ImplementationData(); return 0; } inline undefined4 GetFrame(IDirect3DRMFrame2*& frame, Tgl::Group* scene) { frame = ((TglImpl::GroupImpl*) scene)->ImplementationData(); return 0; }