scummvm/engines/watchmaker/3d/loader.cpp

654 lines
22 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "watchmaker/3d/loader.h"
#include "common/stream.h"
#include "watchmaker/3d/geometry.h"
#include "watchmaker/3d/light.h"
#include "watchmaker/3d/math/llmath.h"
#include "watchmaker/3d/t3d_body.h"
#include "watchmaker/3d/t3d_mesh.h"
#include "watchmaker/game.h"
#include "watchmaker/ll/ll_mesh.h"
#include "watchmaker/ll/ll_system.h"
#include "watchmaker/renderer.h"
#include "watchmaker/t3d.h"
#include "watchmaker/types.h"
#include "watchmaker/utils.h"
#include "watchmaker/windows_hacks.h"
#include "watchmaker/work_dirs.h"
namespace Watchmaker {
#define T3DFILEVERSION 11
t3dV3F CharCorrection;
t3dF32 CurFloorY;
int32 t3dCurTime = 900, t3dCurOliSet = 0;
t3dPathCamera::t3dPathCamera(Common::SeekableReadStream &stream) {
NumCamera = stream.readByte();
PathIndex = stream.readByte();
Direction = stream.readByte();
}
t3dCAMERA::t3dCAMERA(Common::SeekableReadStream &stream) {
Index = stream.readByte();
Source = t3dV3F(stream) * SCALEFACTOR;
Target = t3dV3F(stream) * SCALEFACTOR;
t3dVectCopy(&MaxTarget, &Target);
// Camera[camera].Fov=t3dReadReal();
Fov = RADIANS_TO_DEGREE(stream.readFloatLE()); //FOV
NearClipPlane = stream.readFloatLE() * SCALEFACTOR;
FarClipPlane = stream.readFloatLE() * SCALEFACTOR;
int numPaths = stream.readByte();
CameraPaths.reserve(numPaths);
for (int i = 0; i < numPaths; i++) {
CameraPaths.push_back(t3dPathCamera(stream));
}
}
t3dCAMERAPATH::t3dCAMERAPATH(Common::SeekableReadStream &stream) {
int numPoints = stream.readSint16LE();
CarrelloDist = stream.readSint32LE();
PList.resize(numPoints);
for (int j = 0; j < numPoints; j++) {
PList[j].x = stream.readFloatLE() * SCALEFACTOR;
PList[j].y = stream.readFloatLE() * SCALEFACTOR;
PList[j].z = stream.readFloatLE() * SCALEFACTOR;
}
}
void decodeLoaderFlags(uint32 flags) {
warning("%d: T3D_GENERATESHADOWMAPS", flags & T3D_GENERATESHADOWMAPS);
warning("%d: T3D_NOLIGHTMAPS", flags & T3D_NOLIGHTMAPS);
warning("%d: T3D_NORECURSION", flags & T3D_NORECURSION);
warning("%d: T3D_HALFTEXTURESIZE", flags & T3D_HALFTEXTURESIZE);
warning("%d: T3D_FULLSCREEN", flags & T3D_FULLSCREEN);
warning("%d: T3D_FASTRENDERING", flags & T3D_FASTRENDERING);
warning("%d: T3D_OUTDOORLIGHTS", flags & T3D_OUTDOORLIGHTS);
warning("%d: T3D_NOVOLUMETRICLIGHTS", flags & T3D_NOVOLUMETRICLIGHTS);
warning("%d: T3D_NOBOUNDS", flags & T3D_NOBOUNDS);
warning("%d: T3D_NOCAMERAS", flags & T3D_NOCAMERAS);
warning("%d: T3D_NONEXCLUSIVEMOUSE", flags & T3D_NONEXCLUSIVEMOUSE);
warning("%d: T3D_RECURSIONLEVEL1", flags & T3D_RECURSIONLEVEL1);
warning("%d: T3D_SKY", flags & T3D_SKY);
warning("%d: T3D_PRELOAD_RXT", flags & T3D_PRELOAD_RXT);
warning("%d: T3D_STATIC_SET0", flags & T3D_STATIC_SET0);
warning("%d: T3D_STATIC_SET1", flags & T3D_STATIC_SET1);
warning("%d: T3D_NOSHADOWS", flags & T3D_NOSHADOWS);
warning("%d: T3D_NOICONS", flags & T3D_NOICONS);
warning("%d: T3D_NOSOUND", flags & T3D_NOSOUND);
warning("%d: T3D_PRELOADBASE", flags & T3D_PRELOADBASE);
warning("%d: T3D_NOMUSIC", flags & T3D_NOMUSIC);
warning("%d: T3D_DEBUGMODE", flags & T3D_DEBUGMODE);
warning("%d: T3D_FASTFILE", flags & T3D_FASTFILE);
warning("%d: T3D_HIPOLYPLAYERS", flags & T3D_HIPOLYPLAYERS);
warning("%d: T3D_HIPOLYCHARACTERS", flags & T3D_HIPOLYCHARACTERS);
}
Common::String constructPath(const Common::String &prefix, const Common::String &filename, const char *suffix) {
Common::String Name = prefix + filename;
uint16 len = Name.size();
if (suffix != nullptr) {
uint16 suffixLen = strlen(suffix);
Name = Name.substr(0, len - suffixLen) + suffix;
assert(suffixLen == 3);
}
return Common::String(Name);
}
class RoomManagerImplementation : public RoomManager {
WGame *_game;
#define MAX_LOADED_FILES 100
RecStruct LoadedFiles[MAX_LOADED_FILES];
uint16 NumLoadedFiles = 0;
public:
RoomManagerImplementation(WGame *game) : _game(game) {}
#define MAX_T3D_LOADLIST_ITEMS 50
struct _t3dLOADLIST {
Common::String pname = {};
uint32 LoaderFlags = 0;
t3dMESH *m = nullptr;
};
_t3dLOADLIST t3dLoadList[MAX_T3D_LOADLIST_ITEMS] = {};
void addToLoadList(t3dMESH *m, const Common::String &pname, uint32 _LoaderFlags) override {
if (!pname.empty()) {
int32 a;
for (a = 0; a < MAX_T3D_LOADLIST_ITEMS; a++) {
if (t3dLoadList[a].pname.empty()) {
t3dLoadList[a].LoaderFlags = _LoaderFlags;
t3dLoadList[a].m = m;
t3dLoadList[a].pname = pname;
break;
}
}
if (a >= MAX_T3D_LOADLIST_ITEMS)
warning("Cannot add %s to LoadList", pname.c_str());
} else {
warning("Invalid parameters invoking AddToLoadList()");
warning("Mesh (%s), pname %s", m->name.c_str(), pname.c_str());
}
}
_t3dLOADLIST* getFromLoadList() {
for (int a = 0; a < MAX_T3D_LOADLIST_ITEMS; a++) {
if (!t3dLoadList[a].pname.empty())
return &t3dLoadList[a];
}
return nullptr;
}
t3dBODY *getRoomIfLoaded(const Common::String &roomname) override {
t3dBODY *t = nullptr;
for (int i = 0; i < NumLoadedFiles; i++)
if ((LoadedFiles[i].b != nullptr) && LoadedFiles[i].b->name.equalsIgnoreCase(roomname))
t = LoadedFiles[i].b;
return t;
}
t3dBODY* loadRoom(const Common::String &pname, t3dBODY *b, uint16 *NumBody, uint32 LoaderFlags) override;
t3dBODY* loadSingleRoom(const Common::String &pname, uint16 *NumBody, uint32 LoaderFlags);
void hideRoomMeshesMatching(const Common::String &pname) override {
for (int i = 0; i < NumLoadedFiles; i++)
if (LoadedFiles[i].b)
if (LoadedFiles[i].b->name.equalsIgnoreCase(pname)) {
HideRoomMeshes(_game->init, LoadedFiles[i].b);
}
}
Common::Array<t3dBODY*> getLoadedFiles() override {
// TODO: This won't need to be a copy if we maintain a Common::Array only containing the valid ones.
Common::Array<t3dBODY*> files;
for (int i = 0; i < NumLoadedFiles; i++)
if (LoadedFiles[i].b)
files.push_back(LoadedFiles[i].b);
return files;
}
t3dBODY *checkIfAlreadyLoaded(const Common::String &Name) override {
if (Name.empty()) return nullptr;
for (uint16 i = 0; i < NumLoadedFiles; i++) {
if ((LoadedFiles[i].b != nullptr) && /*(LoadedFiles[i].Name != nullptr) &&*/ (!LoadedFiles[i].name.empty()))
if (LoadedFiles[i].name.equalsIgnoreCase(Name))
return LoadedFiles[i].b;
}
return nullptr;
}
t3dMESH *linkMeshToStr(Init &init, const Common::String &str) override {
if (str.empty()) return nullptr;
// Cerca tra le camere
if (str.equalsIgnoreCase("camera"))
return &init._globals._invVars.CameraDummy;
// Cerca tra i personaggi
for (uint16 i = 0; i < T3D_MAX_CHARACTERS; i++)
if ((Character[i]) && (str.equalsIgnoreCase((char *)init.Obj[i].meshlink[0])))
return Character[i]->Mesh;
// Cerca nelle stanze caricate
for (uint16 i = 0; i < NumLoadedFiles; i++) {
if (LoadedFiles[i].b)
for (uint16 j = 0; j < LoadedFiles[i].b->NumMeshes(); j++) {
if (str.equalsIgnoreCase(LoadedFiles[i].b->MeshTable[j].name))
return &LoadedFiles[i].b->MeshTable[j];
}
}
return nullptr;
}
void releaseBody(const Common::String &name, const Common::String &altName) override {
for (int j = 0; j < NumLoadedFiles; j++) {
if (LoadedFiles[j].name.equalsIgnoreCase(name) || LoadedFiles[j].name.equalsIgnoreCase(altName)) {
t3dReleaseBody(LoadedFiles[j].b);
LoadedFiles[j].b = nullptr;
break;
}
}
}
void releaseLoadedFiles(uint32 exceptFlag) override {
for (int i = 0; i < NumLoadedFiles; i++) {
if (LoadedFiles[i].b && !(LoadedFiles[i].Flags & exceptFlag)) {
t3dReleaseBody(LoadedFiles[i].b);
LoadedFiles[i] = RecStruct(); // TODO: Deduplicate.
}
}
}
private:
void loadFromList();
};
RoomManager *RoomManager::create(WGame *game) {
return new RoomManagerImplementation(game);
}
/* -----------------10/06/99 16.04-------------------
* t3dLoadRoom
* --------------------------------------------------*/
t3dBODY* RoomManagerImplementation::loadRoom(const Common::String &pname, t3dBODY *b, uint16 *NumBody, uint32 _LoaderFlags) {
warning("t3dLoadRoom(%s, b, %d, %d)", pname.c_str(), *NumBody, _LoaderFlags);
struct _t3dLOADLIST *l;
t3dBODY *r = nullptr;
t3dBODY *rez = nullptr;
// reset everything that was previously in the load list
for (int i = 0; i < MAX_T3D_LOADLIST_ITEMS; i++) {
t3dLoadList[i] = _t3dLOADLIST();
}
// Add the base stanza to the upload list
addToLoadList(nullptr, pname, _LoaderFlags);
while ((l = getFromLoadList())) {
uint16 num = 0;
if (l->m) {
if ((rez = _vm->_roomManager->checkIfAlreadyLoaded(l->pname)))
l->m->PortalList = rez;
else {
// if (l->m->Flags&T3D_MESH_PREPROCESSPORTAL)
// body=l->m->PortalList = t3dLoadSingleRoom( l->pname, l->m->PortalList, &num, (l->LoaderFlags|T3D_HALFTEXTURESIZE) );
// else
// TODO: This should increase some refcount on the PortalList
warning("TODO: Handle refcounts on PortalList");
l->m->PortalList = loadSingleRoom(l->pname, &num, l->LoaderFlags);
}
} else
r = loadSingleRoom(l->pname, NumBody, l->LoaderFlags);
*l = _t3dLOADLIST();
}
if (!(_LoaderFlags & T3D_NORECURSION)) {
for (uint16 i = 0; i < NumLoadedFiles; i++)
if (LoadedFiles[i].b)
t3dCalcRejectedMeshFromPortal(LoadedFiles[i].b);
}
warning("Room loaded");
return r;
}
t3dBODY* RoomManagerImplementation::loadSingleRoom(const Common::String &_pname, uint16 *NumBody, uint32 _LoaderFlags) {
//warning("t3dLoadSingleRoom(workDirs, %s, b, %d, %d)", _pname, *NumBody, _LoaderFlags);
//decodeLoaderFlags(_LoaderFlags);
Common::String pname(_pname);
WorkDirs &workdirs = _game->workDirs;
if (pname.equalsIgnoreCase("r1c.t3d"))
if (((t3dCurTime >= 1300) && (t3dCurTime <= 1310)) || (t3dCurTime >= 1800)) //se viene cambiato l'orario cambiarlo anche in UpdateRoomVis...
pname = "r1c-notte.t3d";
if (pname.equalsIgnoreCase("r15.t3d"))
if (((t3dCurTime >= 1300) && (t3dCurTime <= 1310)) || (t3dCurTime >= 1800))
pname = "r15-notte.t3d";
auto name = constructPath(workdirs._t3dDir, pname);
//warning("t3dLoadSingleRoom opening(%s)", name.c_str());
auto stream = openFile(name);
if (!(stream)) { // Apre file
warning("t3dLoadSingleRoom: Failed to open(%s)", name.c_str());
return nullptr;
}
if (*NumBody != 0) {
// TODO: This currently means that we never free the dependant bodies.
warning("Loading a dependant body, should also be deleted alongside the base body");
}
t3dBODY *b = new t3dBODY();
//warning("Loading %s ...", name.c_str());
*b = t3dBODY(); // Azzera Body
uint16 fileVersion = stream->readByte();
if (fileVersion != T3DFILEVERSION) { // Controlla la versione del file
warning("%s file incompatible: current version: %d.\tFile version: %d", name.c_str(), T3DFILEVERSION, fileVersion);
delete b;
return nullptr;
}
{
uint16 j = 1;
while (j < MAX_LOADED_FILES && LoadedFiles[j].b != nullptr) {
j++;
}
if (j >= MAX_LOADED_FILES) {
warning("Too many t3d files loaded!");
delete b;
return nullptr;
}
if ((j + 1) > NumLoadedFiles)
NumLoadedFiles = j + 1;
LoadedFiles[j].name = _pname; // Aggiunge il file alla lista
LoadedFiles[j].Flags = LoaderFlags; // Aggiunge Flags alla lista
LoadedFiles[j].b = b; // Aggiunge Body alla lista
j = 0;
}
b->loadFromStream(*_game, pname, *stream, _LoaderFlags);
return b;
}
t3dParticle::t3dParticle(Common::SeekableReadStream &stream) {
t3dF32 difR1, difG1, difB1;
t3dF32 difR2, difG2, difB2;
Num = (uint32)stream.readFloatLE();
Lung = stream.readFloatLE();
Size = stream.readFloatLE();
Seg1 = stream.readFloatLE();
Seg2 = stream.readFloatLE();
Dim1 = stream.readFloatLE() / 1000.0f;
Dim2 = stream.readFloatLE() / 1000.0f;
Speed = stream.readFloatLE() / 10.0f;
Speed1 = stream.readFloatLE() / 10.0f;
Speed2 = stream.readFloatLE() / 10.0f;
Caos = stream.readFloatLE() / 10;
Caos1 = stream.readFloatLE() / 10;
Caos2 = stream.readFloatLE() / 10;
Delay = (uint32)stream.readFloatLE();
OR1 = (uint8)stream.readSint32LE();
R1 = (uint8)stream.readSint32LE();
G1 = (uint8)stream.readSint32LE();
B1 = (uint8)stream.readSint32LE();
R2 = (uint8)stream.readSint32LE();
G2 = (uint8)stream.readSint32LE();
B2 = (uint8)stream.readSint32LE();
R3 = (uint8)stream.readSint32LE();
G3 = (uint8)stream.readSint32LE();
B3 = (uint8)stream.readSint32LE();
Type = (uint8)stream.readSint32LE();
#ifndef WMGEN
ParticleIndex = t3dCreateSmokeParticle(Num,
Type,
OR1);
#endif
difR1 = (R2 - R1) / (Seg1 / Speed1);
difG1 = (G2 - G1) / (Seg1 / Speed1);
difB1 = (B2 - B1) / (Seg1 / Speed1);
difR2 = (R3 - R2) / (Seg2 / Speed2);
difG2 = (G3 - G2) / (Seg2 / Speed2);
difB2 = (B3 - B2) / (Seg2 / Speed2);
R2 = difR1;
G2 = difG1;
B2 = difB1;
R3 = difR2;
G3 = difG2;
B3 = difB2;
}
/* -----------------24/05/00 10.24-------------------
* t3dPrecalcLight
* --------------------------------------------------*/
void t3dPrecalcLight(t3dBODY *b, uint8 *sun) {
t3dV3F tmp, l, *normal;
t3dVERTEX vv;
t3dF32 nlight;
int32 k, aa, rr, gg, bb;
uint32 i, j, cv;
for (i = 0, cv = 0; i < b->NumMeshes(); i++) { // Si ripassa tutte le mesh
t3dMESH &Mesh = b->MeshTable[i];
#ifndef WMGEN
Mesh.VBptr = Mesh.VertexBuffer;
#endif
for (j = 0; j < Mesh.NumVerts; j++, cv++) { // Si passa tutti i vertici
rr = RGBA_GETRED(Mesh.VBptr[j].diffuse);
gg = RGBA_GETGREEN(Mesh.VBptr[j].diffuse);
bb = RGBA_GETBLUE(Mesh.VBptr[j].diffuse);
aa = RGBA_GETALPHA(Mesh.VBptr[j].diffuse);
tmp.x = Mesh.VBptr[j].x;
tmp.y = Mesh.VBptr[j].y;
tmp.z = Mesh.VBptr[j].z;
for (k = 0; k < (int)b->NumLights(); k++) { // Si passa tutte le luci
if ((b->LightTable[k].Type & T3D_LIGHT_REALTIME) || (b->LightTable[k].Type & T3D_LIGHT_FLARE) || !(b->LightTable[k].Type & T3D_LIGHT_LIGHTON)) continue;
t3dVectSub(&l, &b->LightTable[k].Source, &tmp); // Calcola vettore luce->vertice
t3dVectNormalize(&l); // lo normalizza
normal = &Mesh.NList[j]->n; // Calcola normale
if ((nlight = t3dVectDot(normal, &l)) >= 0) {
if (LightVertex(&vv, &tmp, &b->LightTable[k])) {
if ((sun) && (!sun[cv]) && (b->LightTable[k].Type & T3D_LIGHT_SUN)) {
rr += t3dFloatToInt(vv.r * nlight * 0.50f);
gg += t3dFloatToInt(vv.g * nlight * 0.35f);
bb += t3dFloatToInt(vv.b * nlight * 0.27f);
} else {
rr += t3dFloatToInt(vv.r * nlight);
gg += t3dFloatToInt(vv.g * nlight);
bb += t3dFloatToInt(vv.b * nlight);
}
}
}
}
if (rr < 0) rr = 0;
if (gg < 0) gg = 0;
if (bb < 0) bb = 0;
if (rr > 255) rr = 255;
if (gg > 255) gg = 255;
if (bb > 255) bb = 255;
Mesh.VBptr[j].diffuse = RGBA_MAKE(rr, gg, bb, aa);
}
#ifndef WMGEN
Mesh.VBptr = nullptr;
#endif
}
}
/* -----------------10/06/99 16.06-------------------
* t3dLoadSky
* --------------------------------------------------*/
void t3dLoadSky(WGame &game, t3dBODY * /*body*/) {
t3dF32 Skyminx, Skyminy, Skyminz, Skymaxx, Skymaxy, Skymaxz;
uint16 n = 0, i;
gVertex *gv;
// t3dF32 Tile=1.5f;
t3dF32 div;
if (!(t3dSky = _vm->_roomManager->loadRoom("sky.t3d", t3dSky, &n, T3D_NORECURSION | T3D_NOLIGHTMAPS | T3D_NOVOLUMETRICLIGHTS | T3D_NOCAMERAS | T3D_NOBOUNDS | T3D_STATIC_SET0))) {
warning("Error during t3dLoadRoom: Sky not loaded");
}
GetBoundaries(t3dSky, &Skyminx, &Skyminy, &Skyminz, &Skymaxx, &Skymaxy, &Skymaxz);
for (i = 0; i < t3dSky->NumMeshes(); i++) {
gv = t3dSky->MeshTable[i].VertexBuffer;
for (n = 0; n < t3dSky->MeshTable[i].NumVerts; n++, gv++) {
gv->x -= Skyminx + ((Skymaxx - Skyminx) / 2.0f);
gv->y -= Skyminy + ((Skymaxy - Skyminy) / 2.0f);
gv->z -= Skyminz + ((Skymaxz - Skyminz) / 2.0f);
div = (t3dF32)sqrt(gv->x * gv->x + gv->y * gv->y + gv->z * gv->z);
gv->x /= div;
gv->y /= div;
gv->z /= div;
gv->x *= 15000;
gv->y *= 500;
gv->z *= 15000;
gv->u1 *= 1.0f;
gv->v1 *= 1.0f;
}
t3dSky->MeshTable[0].Radius = 15000.0f * 2;
}
for (n = 0; n < t3dSky->NumNormals; n++) {
t3dSky->NList[n]->dist *= 15000.0f;
}
for (n = 0; n < t3dSky->NumMaterials(); n++) {
t3dSky->MatTable[n]->addProperty(T3D_MATERIAL_SKY);
t3dSky->MatTable[n]->addProperty(T3D_MATERIAL_NOLIGHTMAP);
}
}
/* -----------------02/08/99 15.40-------------------
* t3dAddVertexBuffer
* --------------------------------------------------*/
Common::SharedPtr<VertexBuffer> t3dAddVertexBuffer(t3dBODY *b, uint32 numv) {
return b->addVertexBuffer();
}
/* -----------------10/06/99 16.03-------------------
* t3dOptimizeMaterialList
* --------------------------------------------------*/
void t3dOptimizeMaterialList(t3dBODY *b) {
for (uint32 i = 0; i < b->NumMaterials(); i++) { // Scorre tutti materilai di un body
MaterialPtr Mat = b->MatTable[i];
if ((Mat == nullptr) || /*(!Mat->Texture->Name) ||*/ (Mat->Movie) || (Mat->hasFlag(T3D_MATERIAL_MOVIE))) // Se non esiste o non ha texture
continue; // esce
for (uint32 j = 0; j < b->NumMaterials(); j++) { // Cerca materiali uguali
MaterialPtr CurMat = b->MatTable[j];
if (Mat == CurMat)
continue;
if (CurMat == nullptr /*|| (!CurMat->Texture->Name)*/)
continue;
if (Mat->Texture->name.equalsIgnoreCase(CurMat->Texture->name)) { // Se ha lo setsso nome di texture
//warning("TODO: Implement Material-merging");
// This is currently broken.
Mat = rMergeMaterial(Mat, CurMat); // Unisce i due materiali
for (uint32 k = 0; k < b->NumMeshes(); k++) { // Aggiorna in tutte le mesh id materiale
auto &m = b->MeshTable[k];
for (int q = 0; q < m.NumFaces(); q++) {
auto &f = m.FList[q];
if (f.getMaterialIndex() == j)
f.setMaterialIndex(i);
}
}
warning("Deduplicating: %s (%d v %d", Mat->Texture->name.c_str(), i, j);
b->MatTable[j] = nullptr; // TODO: This should probably happen in rMergeMaterial
}
}
}
// TODO: The optimization leaves a bunch of materials as nullptr, we need to update all the
// references to them. Currently we do this by subtracting 1 from all references that were above
// a removed material. This works, but isn't really optimal.
//int subtract = 0;
for (uint32 i = 0; i < b->NumMaterials(); i++) {
if (!b->MatTable[i]) {
b->MatTable.remove_at(i);
//subtract++;
for (uint32 k = 0; k < b->NumMeshes(); k++) {
auto &m = b->MeshTable[k];
for (int q = 0; q < m.NumFaces(); q++) {
auto &f = m.FList[q];
if (f.getMaterialIndex() >= i) {
f.setMaterialIndex(f.getMaterialIndex() - 1);
}
}
}
i--;
}
}
}
/* -----------------30/07/99 10.55-------------------
* t3dFinalizeMaterialList
* --------------------------------------------------*/
void t3dFinalizeMaterialList(t3dBODY *b) {
for (uint32 i = 0; i < b->NumMeshes(); i++) {
t3dMESH &Mesh = b->MeshTable[i];
#ifndef WMGEN
Mesh.VBptr = Mesh.VertexBuffer;
#endif
for (uint32 j = 0; j < Mesh.NumFaces(); j++) {
t3dFACE &Face = Mesh.FList[j];
MaterialPtr Mat = Face.getMaterial();
if (Face.lightmap) {
Mat = nullptr;
for (const auto &material : Face.getMaterial()->AddictionalMaterial) {
if (material->Texture->ID == Face.lightmap->Texture->ID) {
Mat = material;
break;
}
}
if (Mat == nullptr) {
warning("%s: Can't find Lightmap Sub-Material!", Mesh.name.c_str());
warning("%d %d", Face.getMaterial()->NumAddictionalMaterial, Face.lightmap->Texture->ID);
for (const auto &material : Face.getMaterial()->AddictionalMaterial) {
warning("%d", material->Texture->ID);
}
continue;
}
}
uint32 k = 0;
for (k = 0; k < (uint32)Mat->NumAllocatedMesh; k++)
if (Mat->FlagsList[k] == &Mesh.Flags)
break;
if (k >= (uint32)Mat->NumAllocatedMesh) {
Mat->FlagsList.push_back(&Mesh.Flags);
Mat->NumAllocatedMesh++;
Mesh.Flags |= T3D_MESH_UPDATEVB;
}
Mesh.Flags |= T3D_MESH_UPDATEVB;
for (uint32 h = 0; h < 3; h++) {
for (k = 0; k < (uint32)Mat->NumAllocatedVerts(); k++)
if (Mat->VertsList[k] == &Mesh.VBptr[Face.VertexIndex[h]])
break;
if (k >= (uint32)Mat->NumAllocatedVerts()) {
Mat->VertsList.push_back(&Mesh.VBptr[Face.VertexIndex[h]]);
}
assert(k < Mat->VertsList.size());
Face.setMatVertexIndex(h, k);
}
}
#ifndef WMGEN
Mesh.VBptr = nullptr;
#endif
}
for (uint32 i = 0; i < b->NumMaterials(); i++) {
auto &Mat = b->MatTable[i];
if (!Mat) {
warning("nullptr");
}
Mat->VBO = b->addVertexBuffer(); // t3dAddVertexBuffer(b, Mat->NumAllocatedVerts);
for (int j = 0; j < Mat->NumAddictionalMaterial; j++)
Mat->AddictionalMaterial[j]->VBO = t3dAddVertexBuffer(b, Mat->AddictionalMaterial[j]->NumAllocatedVerts());
}
}
} // End of namespace Watchmaker