2012-02-03 16:03:52 -05:00
/*
2018-01-01 00:40:17 +01:00
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
2012-02-03 16:03:52 -05:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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 2 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/>.
*/
2012-02-18 16:52:08 +01:00
2012-02-03 16:03:52 -05:00
#include "VMapFactory.h"
#include "VMapManager2.h"
#include "VMapDefinitions.h"
#include "WorldModel.h"
#include "GameObjectModel.h"
#include "Log.h"
2018-03-29 20:49:19 +02:00
#include "MapTree.h"
2015-07-25 15:37:35 +02:00
#include "Timer.h"
2012-02-03 16:03:52 -05:00
using G3D :: Vector3 ;
using G3D :: Ray ;
using G3D :: AABox ;
struct GameobjectModelData
{
2018-04-07 21:56:19 +02:00
GameobjectModelData ( char const * name_ , uint32 nameLength , Vector3 const & lowBound , Vector3 const & highBound , bool isWmo_ ) :
bound ( lowBound , highBound ), name ( name_ , nameLength ), isWmo ( isWmo_ ) { }
2012-02-03 16:03:52 -05:00
AABox bound ;
std :: string name ;
2018-04-07 21:56:19 +02:00
bool isWmo ;
2012-02-03 16:03:52 -05:00
};
2014-05-01 10:10:13 +02:00
typedef std :: unordered_map < uint32 , GameobjectModelData > ModelList ;
2012-02-03 16:03:52 -05:00
ModelList model_list ;
2015-07-25 15:37:35 +02:00
void LoadGameObjectModelList ( std :: string const & dataPath )
2012-02-03 16:03:52 -05:00
{
2012-02-23 15:56:53 +01:00
uint32 oldMSTime = getMSTime ();
2013-02-03 14:11:59 +01:00
2015-07-25 15:37:35 +02:00
FILE * model_list_file = fopen (( dataPath + "vmaps/" + VMAP :: GAMEOBJECT_MODELS ). c_str (), "rb" );
2012-02-03 16:03:52 -05:00
if ( ! model_list_file )
2012-02-23 15:56:53 +01:00
{
2018-04-02 20:05:08 +02:00
TC_LOG_ERROR ( "misc" , "Unable to open '%s' file." , VMAP :: GAMEOBJECT_MODELS );
2012-02-03 16:03:52 -05:00
return ;
2012-02-23 15:56:53 +01:00
}
2012-02-03 16:03:52 -05:00
2018-04-07 21:56:19 +02:00
char magic [ 8 ];
if ( fread ( magic , 1 , 8 , model_list_file ) != 8
|| memcmp ( magic , VMAP :: VMAP_MAGIC , 8 ) != 0 )
{
TC_LOG_ERROR ( "misc" , "File '%s' has wrong header, expected %s." , VMAP :: GAMEOBJECT_MODELS , VMAP :: VMAP_MAGIC );
2018-04-07 22:20:30 +02:00
fclose ( model_list_file );
2018-04-07 21:56:19 +02:00
return ;
}
2012-02-03 16:03:52 -05:00
uint32 name_length , displayId ;
2018-04-07 21:56:19 +02:00
uint8 isWmo ;
2012-02-03 16:03:52 -05:00
char buff [ 500 ];
2012-02-23 15:56:53 +01:00
while ( true )
2012-02-03 16:03:52 -05:00
{
2012-02-16 13:56:08 +01:00
Vector3 v1 , v2 ;
2012-02-23 15:56:53 +01:00
if ( fread ( & displayId , sizeof ( uint32 ), 1 , model_list_file ) != 1 )
if ( feof ( model_list_file )) // EOF flag is only set after failed reading attempt
break ;
2018-04-07 21:56:19 +02:00
if ( fread ( & isWmo , sizeof ( uint8 ), 1 , model_list_file ) != 1
|| fread ( & name_length , sizeof ( uint32 ), 1 , model_list_file ) != 1
2012-02-16 13:56:08 +01:00
|| name_length >= sizeof ( buff )
|| fread ( & buff , sizeof ( char ), name_length , model_list_file ) != name_length
|| fread ( & v1 , sizeof ( Vector3 ), 1 , model_list_file ) != 1
|| fread ( & v2 , sizeof ( Vector3 ), 1 , model_list_file ) != 1 )
2012-02-03 16:03:52 -05:00
{
2018-04-02 20:05:08 +02:00
TC_LOG_ERROR ( "misc" , "File '%s' seems to be corrupted!" , VMAP :: GAMEOBJECT_MODELS );
2012-02-03 16:03:52 -05:00
break ;
}
2015-01-31 22:57:08 +01:00
if ( v1 . isNaN () || v2 . isNaN ())
{
2018-04-02 20:05:08 +02:00
TC_LOG_ERROR ( "misc" , "File '%s' Model '%s' has invalid v1%s v2%s values!" , VMAP :: GAMEOBJECT_MODELS , std :: string ( buff , name_length ). c_str (), v1 . toString (). c_str (), v2 . toString (). c_str ());
2015-01-31 22:57:08 +01:00
continue ;
}
2018-04-07 21:56:19 +02:00
model_list . emplace ( std :: piecewise_construct , std :: forward_as_tuple ( displayId ), std :: forward_as_tuple ( & buff [ 0 ], name_length , v1 , v2 , isWmo != 0 ));
2012-02-03 16:03:52 -05:00
}
2012-02-23 15:56:53 +01:00
2012-02-03 16:03:52 -05:00
fclose ( model_list_file );
2018-04-02 20:05:08 +02:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u GameObject models in %u ms" , uint32 ( model_list . size ()), GetMSTimeDiffToNow ( oldMSTime ));
2012-02-03 16:03:52 -05:00
}
GameObjectModel ::~ GameObjectModel ()
{
if ( iModel )
(( VMAP :: VMapManager2 * ) VMAP :: VMapFactory :: createOrGetVMapManager ()) -> releaseModelInstance ( name );
}
2015-07-25 15:37:35 +02:00
bool GameObjectModel :: initialize ( std :: unique_ptr < GameObjectModelOwnerBase > modelOwner , std :: string const & dataPath )
2012-02-03 16:03:52 -05:00
{
2015-07-25 15:37:35 +02:00
ModelList :: const_iterator it = model_list . find ( modelOwner -> GetDisplayId ());
2012-02-03 16:03:52 -05:00
if ( it == model_list . end ())
return false ;
G3D :: AABox mdl_box ( it -> second . bound );
// ignore models with no bounds
if ( mdl_box == G3D :: AABox :: zero ())
{
2018-04-02 20:05:08 +02:00
TC_LOG_ERROR ( "misc" , "GameObject model %s has zero bounds, loading skipped" , it -> second . name . c_str ());
2012-02-03 16:03:52 -05:00
return false ;
}
2015-07-25 15:37:35 +02:00
iModel = (( VMAP :: VMapManager2 * ) VMAP :: VMapFactory :: createOrGetVMapManager ()) -> acquireModelInstance ( dataPath + "vmaps/" , it -> second . name );
2012-02-03 16:03:52 -05:00
if ( ! iModel )
return false ;
name = it -> second . name ;
2015-07-25 15:37:35 +02:00
iPos = modelOwner -> GetPosition ();
iScale = modelOwner -> GetScale ();
2012-02-03 16:03:52 -05:00
iInvScale = 1.f / iScale ;
2015-07-25 15:37:35 +02:00
G3D :: Matrix3 iRotation = G3D :: Matrix3 :: fromEulerAnglesZYX ( modelOwner -> GetOrientation (), 0 , 0 );
2012-02-03 16:03:52 -05:00
iInvRot = iRotation . inverse ();
// transform bounding box:
mdl_box = AABox ( mdl_box . low () * iScale , mdl_box . high () * iScale );
AABox rotated_bounds ;
for ( int i = 0 ; i < 8 ; ++ i )
rotated_bounds . merge ( iRotation * mdl_box . corner ( i ));
2014-03-23 00:23:30 +01:00
iBound = rotated_bounds + iPos ;
2012-02-03 16:03:52 -05:00
#ifdef SPAWN_CORNERS
// test:
for ( int i = 0 ; i < 8 ; ++ i )
{
Vector3 pos ( iBound . corner ( i ));
2015-07-25 15:37:35 +02:00
modelOwner -> DebugVisualizeCorner ( pos );
2012-02-03 16:03:52 -05:00
}
#endif
2015-07-25 15:37:35 +02:00
owner = std :: move ( modelOwner );
2012-02-03 16:03:52 -05:00
return true ;
}
2015-07-25 15:37:35 +02:00
GameObjectModel * GameObjectModel :: Create ( std :: unique_ptr < GameObjectModelOwnerBase > modelOwner , std :: string const & dataPath )
2012-02-03 16:03:52 -05:00
{
GameObjectModel * mdl = new GameObjectModel ();
2015-07-25 15:37:35 +02:00
if ( ! mdl -> initialize ( std :: move ( modelOwner ), dataPath ))
2012-02-03 16:03:52 -05:00
{
delete mdl ;
return NULL ;
}
return mdl ;
}
2018-01-28 11:37:20 +01:00
bool GameObjectModel :: intersectRay ( G3D :: Ray const & ray , float & maxDist , bool stopAtFirstHit , PhaseShift const & phaseShift ) const
2012-02-03 16:03:52 -05:00
{
2017-05-05 20:48:08 +02:00
if ( ! isCollisionEnabled () || ! owner -> IsSpawned ())
return false ;
2018-01-28 11:37:20 +01:00
if ( ! owner -> IsInPhase ( phaseShift ))
2012-02-03 16:03:52 -05:00
return false ;
float time = ray . intersectionTime ( iBound );
2014-08-21 16:56:11 +02:00
if ( time == G3D :: finf ())
2012-02-03 16:03:52 -05:00
return false ;
// child bounds are defined in object space:
Vector3 p = iInvRot * ( ray . origin () - iPos ) * iInvScale ;
Ray modRay ( p , iInvRot * ray . direction ());
2017-05-05 20:48:08 +02:00
float distance = maxDist * iInvScale ;
bool hit = iModel -> IntersectRay ( modRay , distance , stopAtFirstHit );
2012-06-30 16:07:09 +02:00
if ( hit )
2012-02-03 16:03:52 -05:00
{
distance *= iScale ;
2017-05-05 20:48:08 +02:00
maxDist = distance ;
2012-02-03 16:03:52 -05:00
}
return hit ;
2012-02-16 13:56:08 +01:00
}
2014-03-23 00:23:30 +01:00
2018-03-28 22:01:22 +02:00
void GameObjectModel :: intersectPoint ( G3D :: Vector3 const & point , VMAP :: AreaInfo & info , PhaseShift const & phaseShift ) const
{
if ( ! isCollisionEnabled () || ! owner -> IsSpawned ())
return ;
if ( ! owner -> IsInPhase ( phaseShift ))
return ;
if ( ! iBound . contains ( point ))
return ;
// child bounds are defined in object space:
Vector3 pModel = iInvRot * ( point - iPos ) * iInvScale ;
Vector3 zDirModel = iInvRot * Vector3 ( 0.f , 0.f , - 1.f );
float zDist ;
if ( iModel -> IntersectPoint ( pModel , zDirModel , zDist , info ))
{
Vector3 modelGround = pModel + zDist * zDirModel ;
float world_Z = (( modelGround * iInvRot ) * iScale + iPos ). z ;
if ( info . ground_Z < world_Z )
{
info . ground_Z = world_Z ;
info . adtId = owner -> GetNameSetId ();
}
}
}
2015-06-03 19:53:28 +02:00
bool GameObjectModel :: UpdatePosition ()
2014-03-23 00:23:30 +01:00
{
if ( ! iModel )
return false ;
2015-06-03 19:53:28 +02:00
ModelList :: const_iterator it = model_list . find ( owner -> GetDisplayId ());
2014-03-23 00:23:30 +01:00
if ( it == model_list . end ())
return false ;
G3D :: AABox mdl_box ( it -> second . bound );
// ignore models with no bounds
if ( mdl_box == G3D :: AABox :: zero ())
{
2018-04-02 20:05:08 +02:00
TC_LOG_ERROR ( "misc" , "GameObject model %s has zero bounds, loading skipped" , it -> second . name . c_str ());
2014-03-23 00:23:30 +01:00
return false ;
}
2015-07-25 15:37:35 +02:00
iPos = owner -> GetPosition ();
2014-03-23 00:23:30 +01:00
2015-06-03 19:53:28 +02:00
G3D :: Matrix3 iRotation = G3D :: Matrix3 :: fromEulerAnglesZYX ( owner -> GetOrientation (), 0 , 0 );
2014-03-23 00:23:30 +01:00
iInvRot = iRotation . inverse ();
// transform bounding box:
mdl_box = AABox ( mdl_box . low () * iScale , mdl_box . high () * iScale );
AABox rotated_bounds ;
for ( int i = 0 ; i < 8 ; ++ i )
rotated_bounds . merge ( iRotation * mdl_box . corner ( i ));
iBound = rotated_bounds + iPos ;
#ifdef SPAWN_CORNERS
// test:
for ( int i = 0 ; i < 8 ; ++ i )
{
Vector3 pos ( iBound . corner ( i ));
2015-07-25 15:37:35 +02:00
owner -> DebugVisualizeCorner ( pos );
2014-03-23 00:23:30 +01:00
}
#endif
return true ;
}