/* 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 .
*
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_strcat
#define FORBIDDEN_SYMBOL_EXCEPTION_strcpy
#include "watchmaker/3d/animation.h"
#include "watchmaker/3d/geometry.h"
#include "watchmaker/3d/loader.h"
#include "watchmaker/3d/math/llmath.h"
#include "watchmaker/3d/t3d_body.h"
#include "watchmaker/3d/t3d_mesh.h"
#include "watchmaker/file_utils.h"
#include "watchmaker/game.h"
#include "watchmaker/ll/ll_system.h"
#include "watchmaker/t3d.h"
#include "watchmaker/types.h"
#include "watchmaker/utils.h"
#include "watchmaker/windows_hacks.h"
/* -----------------16/12/98 10.32-------------------
* PRELOADEDANIMS
* --------------------------------------------------*/
#define MAX_BONES 40
#define MAX_PRELOADED_ANIMS 8
#define A3DFILEVERSION 5
#define SCALE_DEFAULT_ANIM 1
#define SCALE_ANIM 3
namespace Watchmaker {
struct t3dLOADBONE {
t3dV3F *Trasl;
t3dV3F *Euler;
uint32 NumBone;
};
struct t3dLOADANIM {
Common::String name;
uint32 NumFrames = 0, NumBones = 0, HiBone = 0, LastTime = 0;
t3dF32 *Dist = nullptr;
t3dLOADBONE Bone[MAX_BONES] = {};
};
t3dLOADANIM PreloadedAnim[MAX_PRELOADED_ANIMS];
/* -----------------30/12/98 10.56-------------------
* t3dMatRotXYZ
* --------------------------------------------------*/
void t3dMatRotXYZ(t3dM3X3F *dest, t3dF32 x, t3dF32 y, t3dF32 z) {
t3dM3X3F matrix, matrix_x, matrix_y, matrix_z;
t3dMatIdentity(&matrix_x);
t3dMatIdentity(&matrix_y);
t3dMatIdentity(&matrix_z);
matrix_x.M[4] = (float)cos(x);
matrix_x.M[5] = (float)sin(x);
matrix_x.M[7] = -(float)sin(x);
matrix_x.M[8] = (float)cos(x);
matrix_y.M[0] = (float)cos(y);
matrix_y.M[2] = -(float)sin(y);
matrix_y.M[6] = (float)sin(y);
matrix_y.M[8] = (float)cos(y);
matrix_z.M[0] = (float)cos(z);
matrix_z.M[1] = (float)sin(z);
matrix_z.M[3] = -(float)sin(z);
matrix_z.M[4] = (float)cos(z);
t3dMatMul(&matrix, &matrix_x, &matrix_y);
t3dMatMul(&matrix, &matrix, &matrix_z);
dest->M[0] = matrix.M[0];
dest->M[2] = matrix.M[1];
dest->M[1] = matrix.M[2];
dest->M[6] = matrix.M[3];
dest->M[8] = matrix.M[4];
dest->M[7] = matrix.M[5];
dest->M[3] = matrix.M[6];
dest->M[5] = matrix.M[7];
dest->M[4] = matrix.M[8];
}
Common::Array t3dBODY::getPositionalLight(uint8 pos) {
Common::Array result;
for (const auto &light : PosLightTable) {
if (light.Num == pos) {
result.push_back(light);
}
}
return result;
}
/* -----------------04/07/98 15.52-------------------
* GetLightPosition
* --------------------------------------------------*/
uint8 GetLightPosition(t3dV3F *dest, uint8 pos) {
if (!pos) return 0;
auto pLights = t3dCurRoom->getPositionalLight(pos);
dest->y = CurFloorY;
for (const auto &light : pLights) {
if (light.Pos.x && light.Pos.z) {
dest->x = light.Pos.x;
dest->z = light.Pos.z;
return pos;
}
}
if (pos != 99)
warning("Can't find lpos %d in %s", pos, t3dCurRoom->name.c_str());
return 0;
}
/* -----------------04/07/98 15.52-------------------
* GetLightPosition
* --------------------------------------------------*/
uint8 GetLightDirection(t3dV3F *dest, uint8 pos) {
if (!pos) return 0;
auto pLights = t3dCurRoom->getPositionalLight(pos);
dest->y = CurFloorY;
for (const auto &light : pLights) {
if (light.Dir.x && light.Dir.z) {
dest->x = light.Dir.x;
dest->z = light.Dir.x;
return pos;
}
}
if (pos != 99)
warning("Can't find ldir %d in %s", pos, t3dCurRoom->name.c_str());
return 0;
}
/* -----------------15/12/98 16.26-------------------
* t3dLoadAnimation
* --------------------------------------------------*/
int8 t3dLoadAnimation(WGame &game, const char *s, t3dMESH *mesh, uint16 Flag) {
uint32 nf, nb, i, k, h, older, ScaleAnim, CurPreloadedAnim;
uint32 j = 0;
t3dLOADANIM *p;
t3dLOADBONE *bone;
t3dBONEANIM *db;
t3dBONE *b;
t3dV3F t;
t3dF32 c;
// Prova a vedere se l'ho gia' precaricata
for (CurPreloadedAnim = 0; CurPreloadedAnim < MAX_PRELOADED_ANIMS; CurPreloadedAnim++)
if (PreloadedAnim[CurPreloadedAnim].NumFrames)
if (PreloadedAnim[CurPreloadedAnim].name.equalsIgnoreCase(s))
break;
// Se la devo precaricare, cerco quella piu' vecchia e la scarico
if (CurPreloadedAnim >= MAX_PRELOADED_ANIMS) {
older = 0;
// Prima cerco se ci sono ancora degli slot liberi
for (CurPreloadedAnim = 0; CurPreloadedAnim < MAX_PRELOADED_ANIMS; CurPreloadedAnim++) {
if (!PreloadedAnim[CurPreloadedAnim].NumFrames)
break;
else if (!(older) || (older > PreloadedAnim[CurPreloadedAnim].LastTime))
older = PreloadedAnim[j = CurPreloadedAnim].LastTime;
}
// Se non c'erano slot liberi, rilascia vecchia animazione precaricata
if (CurPreloadedAnim >= MAX_PRELOADED_ANIMS) {
CurPreloadedAnim = j;
//t DebugFile( "Precarico animazione %s nello slot %d occupato da %s", s, CurPreloadedAnim, PreloadedAnim[j].Name );
// Disalloca tutto
for (i = 0; i < MAX_BONES; i++) {
t3dFree(PreloadedAnim[j].Bone[i].Trasl);
t3dFree(PreloadedAnim[j].Bone[i].Euler);
}
delete[] PreloadedAnim[j].Dist;
PreloadedAnim[j] = t3dLOADANIM();
}
//t else
//t DebugFile( "Precarico animazione %s nello slot libero %d", s, CurPreloadedAnim );
p = &PreloadedAnim[CurPreloadedAnim];
p->name = s;
// Carica la nuova animazione
Common::String name = game.workDirs._a3dDir + replaceExtension(s, "a3d");
{
auto stream = game.resolveFile(name.c_str());
if (!stream) {
warning("File %s not found", name.c_str());
return -1;
}
if ((i = stream->readByte()) != A3DFILEVERSION) {
warning("%s file incompatible: current version: %d.\tFile version: %d", name.c_str(), A3DFILEVERSION, i);
return -1;
}
nb = stream->readSint16LE();
nf = stream->readSint16LE();
if (nf == 0) {
warning("%s has N0 frames!", name.c_str());
return -1;
}
if (nb >= MAX_BONES) {
warning("%s has too many bones (%d, MAX is %d)!", name.c_str(), j, MAX_BONES);
return -1;
}
p->NumBones = nb;
p->NumFrames = nf;
for (i = 0; i < nb; i++) {
j = (uint32)(stream->readByte());
if (!(p->HiBone) || (p->HiBone < j))
p->HiBone = j;
bone = &p->Bone[i];
bone->NumBone = j;
bone->Euler = t3dCalloc(nf);
bone->Trasl = t3dCalloc(nf);
for (k = 0; k < nf; k++) {
bone->Euler[k] = t3dV3F(*stream);
}
for (k = 0; k < nf; k++) {
bone->Trasl[k] = t3dV3F(*stream);
}
}
if (stream->readByte()) {
p->Dist = new t3dF32[nf]{};
for (k = 0; k < nf; k++)
p->Dist[k] = stream->readFloatLE();
}
} // Close file
}
//t else
//t DebugFile( "Animazione %s gia' precaricata nello slot %d", s, CurPreloadedAnim );
p = &PreloadedAnim[CurPreloadedAnim];
// Scrive l'ultima volta che l'ho usata
p->LastTime = t3dReadTime();
// Finalmente copia l'animazione precaricata nella mesh
if (Flag & T3D_MESH_DEFAULTANIM) {
db = &mesh->DefaultAnim;
mesh->Flags |= T3D_MESH_DEFAULTANIM;
if (db) mesh->releaseAnim(T3D_MESH_DEFAULTANIM);
if (db) mesh->releaseAnim(0);
ScaleAnim = SCALE_DEFAULT_ANIM;
db->NumFrames = p->NumFrames;
} else {
db = &mesh->Anim;
mesh->Flags &= ~T3D_MESH_DEFAULTANIM;
if (db) mesh->releaseAnim(0);
ScaleAnim = SCALE_ANIM;
db->NumFrames = (p->NumFrames - 2) * ScaleAnim + 2;
}
if (db->BoneTable) mesh->releaseAnim(0);
mesh->NumNormals = 0;
db->NumBones = 0;
db->BoneTable = nullptr;
db->BoneTable = t3dCalloc(p->HiBone + 1);
db->NumBones = p->HiBone + 1;
c = 1.0f / (t3dF32)(ScaleAnim);
for (i = 0; i < p->NumBones; i++) {
bone = &p->Bone[i];
b = &db->BoneTable[bone->NumBone];
b->Matrix = t3dCalloc(db->NumFrames);
b->Trasl = t3dCalloc(db->NumFrames);
for (k = 0; k < db->NumFrames; k++) {
j = ((k - 1) / ScaleAnim) + 1;
h = ((k - 1) % ScaleAnim);
if ((!h) || (k < 1)) {
if (k < 1)
j = k;
t3dMatRotXYZ(&b->Matrix[k], bone->Euler[j].x, bone->Euler[j].y, bone->Euler[j].z);
memcpy(&b->Trasl[k], &bone->Trasl[j], sizeof(t3dV3F));
} else {
t3dVectSub(&t, &bone->Euler[j + 1], &bone->Euler[j]);
if ((t.x < T3D_2PI) && (t.x > T3D_PI)) t.x = t.x - T3D_2PI;
if ((t.x > -T3D_2PI) && (t.x < -T3D_PI)) t.x = t.x + T3D_2PI;
if ((t.y < T3D_2PI) && (t.y > T3D_PI)) t.y = t.y - T3D_2PI;
if ((t.y > -T3D_2PI) && (t.y < -T3D_PI)) t.y = t.y + T3D_2PI;
if ((t.z < T3D_2PI) && (t.z > T3D_PI)) t.z = t.z - T3D_2PI;
if ((t.z > -T3D_2PI) && (t.z < -T3D_PI)) t.z = t.z + T3D_2PI;
t *= (c * (t3dF32)(h));
t3dVectAdd(&t, &bone->Euler[j], &t);
t3dMatRotXYZ(&b->Matrix[k], t.x, t.y, t.z);
t3dVectSub(&t, &bone->Trasl[j + 1], &bone->Trasl[j]);
t *= (c * (t3dF32)(h));
t3dVectAdd(&b->Trasl[k], &bone->Trasl[j], &t);
}
/* if(!(mesh->Flags&T3D_MESH_CHARACTER))
DebugFile("%3d;%3d;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;",k,i,
(bone->Euler[j].x)*180.0f/T3D_PI,(bone->Euler[j].y)*180.0f/T3D_PI,(bone->Euler[j].z)*180.0f/T3D_PI,
b->Matrix[k].M[0],b->Matrix[k].M[1],b->Matrix[k].M[2],
b->Matrix[k].M[3],b->Matrix[k].M[4],b->Matrix[k].M[5],
b->Matrix[k].M[6],b->Matrix[k].M[7],b->Matrix[k].M[8] );
*/
}
b->ModVertices.clear();
// Poi inserisce tutti i vertici modificati nell'array gia' alloocato della dimensione giusta
for (auto &modVertices : mesh->ModVertices) {
if (modVertices.NumBone == bone->NumBone) {
b->ModVertices.push_back(modVertices.NumVert);
}
}
}
if (p->Dist) {
db->Dist = new t3dF32[db->NumFrames]{};
for (k = 0; k < db->NumFrames; k++)
db->Dist[k] = p->Dist[k];
}
return 1;
}
/* -----------------30/12/98 11.27-------------------
* FixupAnim
* --------------------------------------------------*/
void FixupAnim(t3dMESH *mesh, uint8 pos, const char *room) {
t3dBONEANIM *db;
t3dBONE *bone, *bone0;
t3dV3F lp, ld, Frame0Trasl, cc, tmp, tmp1, tmp2, zero;
t3dM3X3F lm, mx, BoneInitMatrix;
uint32 i, k, frame;
t3dBODY *OldCurRoom = t3dCurRoom;
if (mesh->Flags & T3D_MESH_DEFAULTANIM) {
db = &mesh->DefaultAnim;
pos = 0;
} else {
db = &mesh->Anim;
if (pos) {
if (room && (room[0] != '\0')) {
t3dBODY *roomPtr = _vm->_roomManager->getRoomIfLoaded(room);
if (room) {
t3dCurRoom = roomPtr;
}
}
if (!GetLightPosition(&lp, pos) || (lp.x == 0.0f) || (lp.z == 0.0f)) pos = 0;
if (!GetLightDirection(&ld, pos) || (ld.x == 0.0f) || (ld.z == 0.0f)) pos = 0;
t3dCurRoom = OldCurRoom;
t3dVectSub(&ld, &ld, &lp);
ld.z = -ld.z;
t3dVectAdd(&ld, &ld, &lp);
t3dMatView(&lm, &lp, &ld);
if ((!pos) || (mesh->Flags & (T3D_MESH_ABS_ANIM | T3D_MESH_CHARACTER))) {
t3dVectCopy(&lp, &mesh->Trasl);
t3dMatCopy(&lm, &mesh->Matrix);
pos = 99;
}
if (mesh->Flags & T3D_MESH_ABS_ANIM)
t3dVectTransform(&cc, &CharCorrection, &lm);
}
}
// Ora sistema tutte le altre bones 1..32 (mesh) e 33/34 (camera)
for (i = 1; i < db->NumBones; i++) {
if (!(bone = &db->BoneTable[i]) || !(bone->Trasl) || !(bone->Matrix)) continue;
// Salva la prima matrice di ogni bone
t3dMatCopy(&BoneInitMatrix, &bone->Matrix[0]);
// Calcola scostamento iniziale bone per azioni assolute personaggi
if (i == 1) t3dVectSub(&Frame0Trasl, &bone->Trasl[1], &bone->Trasl[0]);
for (k = 0; k < db->NumFrames; k++) {
// Tutte le matrici diventano relative al frame 0
t3dMatMulInv(&bone->Matrix[k], &bone->Matrix[k], &BoneInitMatrix);
// Aggiunge la correzione a:
// - Azioni di default (tutti frames personaggi)
// - Azioni relative (tutti frames, personaggi e oggetti)
// - Azioni assolute (personaggi frame 0)
if ((mesh->Flags & T3D_MESH_DEFAULTANIM) || !(mesh->Flags & T3D_MESH_ABS_ANIM) ||
((!k) && (mesh->Flags & T3D_MESH_ABS_ANIM) && (mesh->Flags & T3D_MESH_CHARACTER)))
t3dVectAdd(&bone->Trasl[k], &CharCorrection, &bone->Trasl[k]);
if (pos) {
// Oggetti relativi
if (!(mesh->Flags & T3D_MESH_CHARACTER) && !(mesh->Flags & T3D_MESH_ABS_ANIM)) {
t3dVectTransform(&ld, &bone->Trasl[k], &lm);
t3dVectAdd(&bone->Trasl[k], &ld, &lp);
}
// Personaggi assoluti
else if ((mesh->Flags & T3D_MESH_ABS_ANIM) && (mesh->Flags & T3D_MESH_CHARACTER) && (k)) {
t3dVectSub(&bone->Trasl[k], &bone->Trasl[k], &Frame0Trasl);
t3dVectTransformInv(&bone->Trasl[k], &bone->Trasl[k], &lm);
t3dVectAdd(&bone->Trasl[k], &bone->Trasl[k], &CharCorrection);
t3dMatMul(&bone->Matrix[k], &bone->Matrix[k], &lm);
}
}
/* if(!(mesh->Flags&T3D_MESH_CHARACTER))
DebugFile("%3d;%3d;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;%9f;",k,i,
bone->Matrix[k].M[0],bone->Matrix[k].M[1],bone->Matrix[k].M[2],
bone->Matrix[k].M[3],bone->Matrix[k].M[4],bone->Matrix[k].M[5],
bone->Matrix[k].M[6],bone->Matrix[k].M[7],bone->Matrix[k].M[8] );
*/
}
}
if (db->Dist)
for (k = 0; k < db->NumFrames; k++)
if ((mesh->Flags & T3D_MESH_CHARACTER) && ((!k) || (mesh->Flags & T3D_MESH_DEFAULTANIM)))
db->Dist[k] -= CharCorrection.z;
if (!(bone0 = &db->BoneTable[0]) || !(bone0->Trasl) || !(bone0->Matrix)) {
bone0->Matrix = t3dCalloc(db->NumFrames);
bone0->Trasl = t3dCalloc(db->NumFrames);
} else
warning("Guarda che il bone0 e' gia' stato allocato nella mesh %s", mesh->name.c_str());
for (k = 0; k < db->NumFrames; k++) {
t3dVectCopy(&bone0->Trasl[k], &mesh->Trasl);
t3dMatCopy(&bone0->Matrix[k], &mesh->Matrix);
}
if ((mesh->Flags & T3D_MESH_CHARACTER) && !(mesh->Flags & T3D_MESH_DEFAULTANIM)) {
if (!(bone = &db->BoneTable[1]) || !(bone->Trasl) || !(bone->Matrix))
return ;
t3dVectInit(&tmp1, bone->Trasl[1].x, 0.0f, bone->Trasl[1].z);
t3dVectInit(&tmp, 0.0f, 0.0f, 1.0f);
t3dVectTransform(&tmp, &tmp, &bone->Matrix[1]);
tmp.y = 0;
t3dVectFill(&zero, 0.0f);
t3dMatView(&mx, &zero, &tmp);
t3dVectTransform(&cc, &CharCorrection, &mx);
t3dVectSub(&tmp1, &tmp1, &cc);
for (frame = 0; frame < db->NumFrames; frame++) {
t3dVectInit(&tmp2, bone->Trasl[frame].x, 0.0f, bone->Trasl[frame].z);
t3dVectInit(&tmp, 0.0f, 0.0f, 1.0f);
t3dVectTransform(&tmp, &tmp, &bone->Matrix[frame]);
tmp.y = 0;
t3dVectFill(&zero, 0.0f);
t3dMatView(&mx, &zero, &tmp);
t3dVectTransform(&cc, &CharCorrection, &mx);
t3dVectSub(&tmp2, &tmp2, &cc);
t3dVectSub(&tmp, &tmp2, &tmp1);
// t3dVectTransform( &tmp, &tmp, &mesh->Matrix );
tmp.x += bone->Trasl[1].x - bone->Trasl[0].x;
tmp.z += bone->Trasl[1].z - bone->Trasl[0].z;
t3dVectCopy(&bone0->Trasl[frame], &tmp);
t3dVectInit(&tmp, 0.0f, 0.0f, -1.0f);
t3dVectTransform(&tmp, &tmp, &bone->Matrix[frame]);
tmp.z = -tmp.z;
tmp.y = 0;
t3dVectFill(&zero, 0.0f);
t3dMatView(&bone0->Matrix[frame], &zero, &tmp);
}
for (i = 1; i < db->NumBones; i++) {
if (!(bone = &db->BoneTable[i]) || !(bone->Trasl) || !(bone->Matrix))
continue;
for (k = 0; k < db->NumFrames; k++) {
t3dVectSub(&bone->Trasl[k], &bone->Trasl[k], &bone0->Trasl[k]);
t3dVectTransform(&bone->Trasl[k], &bone->Trasl[k], &bone0->Matrix[k]);
t3dMatMulInv(&bone->Matrix[k], &bone->Matrix[k], &bone0->Matrix[k]);
}
}
for (k = 0; k < db->NumFrames; k++) {
t3dMatMulInv(&bone0->Matrix[k], &mesh->Matrix, &bone0->Matrix[k]);
t3dVectTransform(&tmp, &bone0->Trasl[k], &mesh->Matrix);
t3dVectAdd(&bone0->Trasl[k], &mesh->Trasl, &tmp);
}
}
}
/* -----------------13/04/99 14.57-------------------
* LoadShadowMeshes
* --------------------------------------------------*/
t3dBODY *LoadShadowMeshes(WGame &game, const char *pname, t3dBODY *Body) {
uint16 ref;
char Name[255];
t3dBODY *shadow = new t3dBODY();
gVertex *Original = Body->MeshTable[0].VertexBuffer;
t3dF32 dist, rez;
strcpy(Name, pname);
strncpy(&Name[strlen(pname) - 4], "_Shadow.t3d\0", 12);
uint16 numBodys = 0;
shadow = _vm->_roomManager->loadRoom(Name, shadow, &numBodys, (T3D_NOLIGHTMAPS | T3D_NORECURSION | T3D_NOVOLUMETRICLIGHTS | T3D_NOCAMERAS | T3D_NOBOUNDS | T3D_STATIC_SET0 | T3D_STATIC_SET1));
if (!shadow) return nullptr;
for (uint16 i = 0; i < shadow->NumMeshes(); i++) {
t3dMESH &m = shadow->MeshTable[i];
m.VBptr = m.VertexBuffer;
for (uint16 j = 0; j < m.NumFaces(); j++) {
t3dFACE &f = m.FList[j];
for (uint16 n = 0; n < 3; n++) {
t3dV3F pnt;
pnt.x = m.VBptr[f.VertexIndex[n]].x;
pnt.y = m.VBptr[f.VertexIndex[n]].y;
pnt.z = m.VBptr[f.VertexIndex[n]].z;
ref = 0;
dist = 999999999.0f;
for (uint16 k = 0; k < Body->MeshTable[0].NumVerts; k++) {
t3dV3F tpnt;
tpnt.x = Original[k].x;
tpnt.y = Original[k].y;
tpnt.z = Original[k].z;
if ((rez = t3dVectDistance(&pnt, &tpnt)) < dist) {
dist = rez;
ref = k;
}
}
f.VertexIndex[n] = ref;
}
}
m.VBptr = nullptr;
delete[] m.VertexBuffer;
m.VertexBuffer = nullptr;
delete[] m.OldVertexBuffer;
m.OldVertexBuffer = nullptr;
delete[] m.SavedVertexBuffer;
m.SavedVertexBuffer = nullptr;
m.VertexBuffer = Body->MeshTable[0].VertexBuffer;
m.NumVerts = Body->MeshTable[0].NumVerts;
m.Flags |= T3D_MESH_CHARACTER; //this is a character
}
return shadow;
}
/* -----------------30/12/98 11.27-------------------
* t3dLoadCharacter
* --------------------------------------------------*/
t3dCHARACTER *t3dLoadCharacter(WGame &game, const char *pname, uint16 num) {
warning("LoadCharacter(%s)", pname);
uint8 Mirror = 1;
uint16 numBody = 0, f;
t3dV3F tmp;
// gVertex *v;
t3dCHARACTER *b = new t3dCHARACTER[1] {};
b->Body = _vm->_roomManager->loadRoom(pname, b->Body, &numBody, (T3D_NOLIGHTMAPS | T3D_NORECURSION | T3D_NOVOLUMETRICLIGHTS | T3D_NOCAMERAS | T3D_STATIC_SET0 | T3D_STATIC_SET1));
if (!b->Body) {
delete[] b;
return nullptr;
}
b->Mesh = &b->Body->MeshTable[0];
b->CurRoom = t3dCurRoom;
b->Flags = T3D_CHARACTER_HIDE | T3D_CHARACTER_REALTIMELIGHTING;
if (num >= 2) b->Flags |= T3D_CHARACTER_BNDHIDE;
//Try to load animation
if (t3dLoadAnimation(game, pname, b->Mesh, T3D_MESH_DEFAULTANIM) == -1) {
warning("t3dLoadCharacter: Error loading %s", pname);
delete[] b;
return nullptr;
}
FixupAnim(b->Mesh, 0, "");
// Zero's all the Normals vars, 'cause I recalc all the normals runtime...
b->Body->NumNormals = 0;
b->Body->NumVerticesNormals = 0;
for (uint16 i = 0; i < b->Body->NumMeshes(); i++) {
t3dMESH &mesh = b->Body->MeshTable[i];
for (f = 0; f < mesh.NumFaces(); f++) {
mesh.FList[f].n = nullptr;
}
//sb
//sb mesh->Flags|=T3D_MESH_CASTREALTIMESHADOWS;
//sb
}
b->Body->NList.clear();
//sb
//sb b->Flags|=T3D_CHARACTER_CASTREALTIMESHADOWS;
//sb
// Per gli specchi
if (Mirror) { // Ogni personaggio potrebbe apparire in uno specchio
b->Body->MirrorMatTable.resize(b->Body->NumMaterials());
rCopyMaterialList(b->Body->MirrorMatTable, b->Body->MatTable, b->Body->NumMaterials());
}
// Per le ombre, altezza e raggio del cilindro
b->Height = (t3dF32)sqrt(b->Mesh->BBox[0].p.x * b->Mesh->BBox[0].p.x + b->Mesh->BBox[0].p.z * b->Mesh->BBox[0].p.z);
b->Radius = (t3dF32)sqrt(b->Mesh->BBox[5].p.x * b->Mesh->BBox[5].p.x + b->Mesh->BBox[5].p.z * b->Mesh->BBox[5].p.z);
if (b->Radius < b->Height) b->Radius = b->Height;
b->Height = (b->Mesh->BBox[0].p.y - b->Mesh->BBox[2].p.y) * 1.2f;
// No bounding box detection
/* t3dVectFill(&b->Mesh->BBox[0].p,0.0f);
t3dVectFill(&b->Mesh->BBox[1].p,0.0f);
t3dVectFill(&b->Mesh->BBox[2].p,0.0f);
t3dVectFill(&b->Mesh->BBox[3].p,0.0f);
t3dVectFill(&b->Mesh->BBox[4].p,0.0f);
t3dVectFill(&b->Mesh->BBox[5].p,0.0f);
t3dVectFill(&b->Mesh->BBox[6].p,0.0f);
t3dVectFill(&b->Mesh->BBox[7].p,0.0f);
b->Mesh->Flags|=T3D_MESH_NOBOUNDBOX;
*/
for (uint16 i = 0; i < b->Body->NumMeshes(); i++) {
b->Body->MeshTable[i].Flags |= T3D_MESH_CHARACTER;
b->Body->MeshTable[i].Flags &= ~T3D_MESH_MIRROR;
}
t3dVectFill(&b->Pos, 0.0f);
t3dVectInit(&tmp, 0.0f, 0.0f, -1.0f);
t3dVectAdd(&tmp, &b->Pos, &tmp);
t3dMatView(&b->Mesh->Matrix, &b->Pos, &tmp);
b->Mesh->Matrix.Flags &= ~T3D_MATRIX_IDENTITY;
b->Mesh->CurFrame = 4;
b->Mesh->LastFrame = 0;
b->Mesh->BlendPercent = 255;
b->Walk.OldPanel = -1;
b->Walk.CurPanel = -1;
b->Walk.NumPathNodes = -1;
t3dVectInit(&b->Dir, 0.0f, 0.0f, -1.0f);
t3dVectTransform(&b->Dir, &b->Dir, &b->Mesh->Matrix); //rotate by Character angle
//sb
if (num < 2)
b->Shadow = LoadShadowMeshes(game, pname, b->Body);
else
b->Shadow = nullptr;
//sb
return b;
}
/* -----------------25/09/98 16.07-------------------
* GetFullLightPosition
* --------------------------------------------------*/
uint8 GetFullLightDirection(t3dV3F *dest, uint8 pos) {
if (!pos) return 0;
auto pLights = t3dCurRoom->getPositionalLight(pos);
for (const auto &light : pLights) {
if (light.Dir.x && light.Dir.z) {
*dest = light.Dir;
return pos;
}
}
if (pos != 99)
DebugLogFile("Can't find fldir %d in %s", pos, t3dCurRoom->name.c_str());
return 0;
}
/* -----------------21/12/98 16.40-------------------
* ReleasePreloadedAnims
* --------------------------------------------------*/
void ReleasePreloadedAnims() {
int32 i, j;
for (j = 0; j < MAX_PRELOADED_ANIMS; j++) {
// Disalloca tutto
for (i = 0; i < MAX_BONES; i++) {
t3dFree(PreloadedAnim[j].Bone[i].Trasl);
t3dFree(PreloadedAnim[j].Bone[i].Euler);
}
delete[] PreloadedAnim[j].Dist;
PreloadedAnim[j] = t3dLOADANIM();
}
}
/* -----------------02/05/00 9.30--------------------
* CompareLightPosition
* --------------------------------------------------*/
uint8 CompareLightPosition(char *roomname, uint8 pos1, t3dV3F *pos2, t3dF32 acceptable_dist) {
t3dV3F p1;
t3dBODY *t;
if ((pos1 <= 0) || (pos2 == nullptr)) return FALSE;
// cerco la stanza
t = nullptr;
if (roomname && (roomname[0] != '\0')) {
t = _vm->_roomManager->getRoomIfLoaded(roomname);
} else t = t3dCurRoom;
if (!t) return FALSE;
auto pLights = t->getPositionalLight(pos1);
bool foundLight = false;
for (auto &light : pLights) {
if (light.Pos.x && light.Pos.z) {
p1.x = light.Pos.x;
p1.y = light.Pos.y;
p1.z = light.Pos.z;
foundLight = true;
break;
}
}
if (!foundLight) return FALSE;
if (t3dVectSquaredDistance(&p1, pos2) <= acceptable_dist) return TRUE;
return FALSE;
}
} // End of namespace Watchmaker