// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdpch.h"
//
#include "displayer_visual_shape.h"
#include "instance.h"
#include "editor.h"
#include "r2_config.h"
#include "tool.h"
//
#include "../entity_cl.h"
#include "../global.h"
#include "../misc.h"
#include "../pacs_client.h"
#include "verbose_clock.h"
//
#include "nel/3d/u_instance_material.h"
#include "nel/3d/u_shape_bank.h"
#include "nel/3d/u_visual_collision_manager.h"
#include "nel/3d/u_visual_collision_entity.h"
using namespace NLMISC;
extern uint SkipFrame;
namespace R2
{
// *********************************************************************************************************
CDisplayerVisualShape::CDisplayerVisualShape(const std::string &shapeName, float scale, bool worldMapDisplay)
{
_ShapeName = shapeName;
_Scale = scale;
_Touched = true;
_BadShapeName = false;
_Active = false;
_VisualSnapToGroundDone = false;
_VisualCollisionEntity = NULL;
_WorldMapDisplay = worldMapDisplay;
}
// *********************************************************************************************************
CDisplayerVisualShape::~CDisplayerVisualShape()
{
deleteShape();
deleteVisualCollisionEntity();
}
// *********************************************************************************************************
void CDisplayerVisualShape::deleteVisualCollisionEntity()
{
//H_AUTO(R2_CDisplayerVisualShape_deleteVisualCollisionEntity)
if (CollisionManager)
{
if (_VisualCollisionEntity)
{
CollisionManager->deleteEntity(_VisualCollisionEntity);
_VisualCollisionEntity = NULL;
}
}
}
// *********************************************************************************************************
void CDisplayerVisualShape::deleteShape()
{
//H_AUTO(R2_CDisplayerVisualShape_deleteShape)
if (!_Instance.empty())
{
Scene->deleteInstance(_Instance);
}
if (_MapDeco.isAddedToMap())
{
CGroupMap *gm = CTool::getWorldMap();
if (gm)
{
gm->removeDeco(&_MapDeco);
}
}
}
// *********************************************************************************************************
void CDisplayerVisualShape::onPreRender()
{
//H_AUTO(R2_CDisplayerVisualShape_onPreRender)
if (SkipFrame != 0)
{
// a tp was done -> invalidate visual collision entity
deleteVisualCollisionEntity();
}
if (!_Instance.empty())
{
bool inIsland = false;
CIslandCollision &col = getEditor().getIslandCollision();
R2::CScenarioEntryPoints::CCompleteIsland *currIsland = col.getCurrIslandDesc();
if (currIsland)
{
inIsland = currIsland->isIn(getWorldPos2f());
}
if (getActualVisibility() && inIsland)
{
_Instance.show();
if (_WorldMapDisplay && !_MapDeco.getActive())
{
_MapDeco.setActive(true);
_Touched = true;
}
}
else
{
_Instance.hide();
_MapDeco.setActive(false);
}
}
if (!getActualVisibility()) return;
if (_BadShapeName) return;
if (_Active)
{
if (testNeedZEval()) _VisualSnapToGroundDone = false;
if (!_VisualSnapToGroundDone && SkipFrame == 0)
{
snapToGround(); // force visual snap to ground if not already done + icon update
}
}
if (!_Touched)
{
if (!_Instance.empty()) _BBoxMatrix = _Instance.getMatrix();
else _BBoxMatrix = CMatrix::Identity;
return;
}
if (_Active)
{
updateMapDeco();
if (_Instance.empty())
{
sint64 startTime = 0;
static volatile bool bench = false;
if (bench)
{
startTime = CTime::getPerformanceTime();
}
_Instance = Scene->createInstance(_ShapeName);
if (bench)
{
sint64 endTime = CTime::getPerformanceTime();
nlwarning("clip time = %.2f ms", 1000.f * CTime::ticksToSecond(endTime - startTime));
}
if (_Instance.empty())
{
_BadShapeName = true;
return;
}
_Instance.setTransformMode(NL3D::UTransform::DirectMatrix);
_Instance.enableCastShadowMap(true);
}
CMatrix instanceMat;
instanceMat.setScale(_Scale);
instanceMat.setPos(getWorldPos().asVector());
_Instance.setMatrix(instanceMat);
_VisualSnapToGroundDone = false;
visualSnapToGround(); // force visual snap to ground if not already done
}
else
{
if (!_Instance.empty())
{
Scene->deleteInstance(_Instance);
}
}
if (!_Instance.empty())
{
_BBoxMatrix = _Instance.getMatrix(); // ensure that bbox and shape displayed at same pos
// (events that modify the instance pos may be received after the display of this frame)
}
_MapDeco.setInvalidPosFlag(getDisplayFlag(FlagBadPos));
_Touched = false;
}
// *********************************************************************************************************
void CDisplayerVisualShape::drawBBox(NLMISC::CRGBA color) const
{
//H_AUTO(R2_CDisplayerVisualShape_drawBBox)
if (getRotateInProgress()) return; // no drawn while drawing (bbox moved one frame too late, must solve this)
NLMISC::CAABBox bbox;
_Instance.getShapeAABBox(bbox);
Driver->setModelMatrix(_BBoxMatrix);
::drawBox(bbox.getMin(), bbox.getMax(), color);
}
// *********************************************************************************************************
void CDisplayerVisualShape::onPostRender()
{
//H_AUTO(R2_CDisplayerVisualShape_onPostRender)
if (!_Active || !getActualVisibility()) return;
if (_BadShapeName) return;
if (!_Instance.empty())
{
if (getDisplayFlag(FlagSelected))
{
//visualSnapToGround();
drawBBox(CRGBA::Green);
setEmissive(_Instance, getBlinkColor(CV_SelectedInstanceColor.get()));
}
else if (getDisplayFlag(FlagHasFocus))
{
//visualSnapToGround();
drawBBox(CRGBA::White);
setEmissive(_Instance, getBlinkColor(CV_FocusedInstanceColor.get()));
}
else
{
CRGBA color = getDisplayModeColorInScene();
setEmissive(_Instance, color);
::makeInstanceTransparent(_Instance, color.A, color.A != 255);
}
}
CDisplayerVisual::onPostRender();
}
// *********************************************************************************************************
void CDisplayerVisualShape::onAttrModified(const std::string &name, sint32 index)
{
//H_AUTO(R2_CDisplayerVisualShape_onAttrModified)
CDisplayerVisual::onAttrModified(name, index);
if (name == "Position")
{
_VisualSnapToGroundDone = false;
_Touched = true;
}
/*else if (name == "DisplayMode")
{
updateMapDeco();
}*/
}
// *********************************************************************************************************
void CDisplayerVisualShape::setDisplayMode(sint32 mode)
{
//H_AUTO(R2_CDisplayerVisualShape_setDisplayMode)
CDisplayerVisual::setDisplayMode(mode);
updateMapDeco();
}
// *********************************************************************************************************
void CDisplayerVisualShape::onParentDisplayModeChanged()
{
//H_AUTO(R2_CDisplayerVisualShape_onParentDisplayModeChanged)
updateMapDeco();
}
// *********************************************************************************************************
void CDisplayerVisualShape::onFocus(bool focused)
{
//H_AUTO(R2_CDisplayerVisualShape_onFocus)
CDisplayerVisual::onFocus(focused);
updateMapDeco();
}
// *********************************************************************************************************
void CDisplayerVisualShape::onSelect(bool selected)
{
//H_AUTO(R2_CDisplayerVisualShape_onSelect)
CDisplayerVisual::onSelect(selected);
updateMapDeco();
}
// *********************************************************************************************************
void CDisplayerVisualShape::setActive(bool active)
{
//H_AUTO(R2_CDisplayerVisualShape_setActive)
if (active == _Active) return;
if (!active)
{
deleteShape();
}
else
{
if (!_MapDeco.isAddedToMap() && _WorldMapDisplay)
{
CGroupMap *gm = CTool::getWorldMap();
if (gm)
{
_MapDeco.setDisplayedInstance(getDisplayedInstance(), false);
gm->addDeco(&_MapDeco);
_MapDeco.invalidateCoords();
}
}
}
_Touched = true;
_Active = active;
}
// *********************************************************************************************************
void CDisplayerVisualShape::updateMapDeco()
{
//H_AUTO(R2_CDisplayerVisualShape_updateMapDeco)
if (_MapDeco.isAddedToMap())
{
CGroupMap *gm = CTool::getWorldMap();
if (gm)
{
_MapDeco.onUpdate(*gm);
_MapDeco.invalidateCoords();
}
}
}
// *********************************************************************************************************
bool CDisplayerVisualShape::getActive() const
{
//H_AUTO(R2_CDisplayerVisualShape_getActive)
return _Active;
}
// *********************************************************************************************************
bool CDisplayerVisualShape::init(const CLuaObject ¶meters)
{
//H_AUTO(R2_CDisplayerVisualSh_initSape)
_ShapeName = parameters["ShapeName"].toString();
if (parameters["Scale"].isNumber())
{
_Scale = parameters["Scale"];
}
NL3D::UShapeBank *shapeBank= Driver->getShapeBank();
if (shapeBank)
{
shapeBank->buildSystemGeometryForshape(_ShapeName);
}
return CDisplayerVisual::init(parameters);
}
// *********************************************************************************************************
bool CDisplayerVisualShape::getLastClip() const
{
//H_AUTO(R2_CDisplayerVisualShape_getLastClip)
if (_Instance.empty()) return true;
return !_Instance.getLastClippedState();
}
// *********************************************************************************************************
NLMISC::CAABBox CDisplayerVisualShape::getSelectBox() const
{
//H_AUTO(R2_CDisplayerVisualShape_getSelectBox)
if (_Instance.empty()) return CDisplayerVisual::getSelectBox();
// TODO nico : cache the bbox
NLMISC::CAABBox bbox;
_Instance.getShapeAABBox(bbox);
bbox.setMinMax(_Scale * bbox.getMin(), _Scale * bbox.getMax());
return bbox;
}
// *********************************************************************************************************
float CDisplayerVisualShape::preciseIntersectionTest(const NLMISC::CVector &worldRayStart,const NLMISC::CVector &worldRayDir) const
{
//H_AUTO(R2_CDisplayerVisualShape_preciseIntersectionTest)
if (_Instance.empty()) return FLT_MAX;
if(_Instance.supportFastIntersect())
{
float dist2D, distZ;
if (const_cast(_Instance).fastIntersect(worldRayStart, worldRayDir, dist2D, distZ, false))
{
if (dist2D == 0.f)
{
return distZ;
}
}
}
return FLT_MAX;
}
// *********************************************************************************************************
const NLMISC::CMatrix &CDisplayerVisualShape::getInvertedMatrix() const
{
//H_AUTO(R2_CDisplayerVisualShape_getInvertedMatrix)
// no rot part for now
_InvertedMatrix.setPos(- getWorldPos().asVector());
return _InvertedMatrix;
}
// *********************************************************************************************************
void CDisplayerVisualShape::updateWorldPos()
{
//H_AUTO(R2_CDisplayerVisualShape_updateWorldPos)
CDisplayerVisual::updateWorldPos();
// must always snap pos (if world pos was modified because of parent move,
// in this case our relative pos remains unmodified)
_VisualSnapToGroundDone = false;
snapToGround();
updateMapDeco();
}
// *********************************************************************************************************
void CDisplayerVisualShape::snapToGround()
{
//H_AUTO(R2_CDisplayerVisualShape_snapToGround)
if (!GR)
{
return;
}
NLPACS::UGlobalPosition gpos = GR->retrievePosition(getWorldPos());
if (gpos.InstanceId != -1)
{
CVector snappedPos = GR->getGlobalPosition(gpos);
// locally modify the z
_WorldPos.z = snappedPos.z;
//setDisplayFlag(FlagBadPos, false);
}
else
{
//setDisplayFlag(FlagBadPos, true);
}
visualSnapToGround();
_Touched = true;
}
// *********************************************************************************************************
void CDisplayerVisualShape::visualSnapToGround()
{
//H_AUTO(R2_CDisplayerVisualShape_visualSnapToGround)
if (!_VisualSnapToGroundDone)
{
if (!_Instance.empty())
{
CMatrix mat = _Instance.getMatrix();
CVector pos3f = _WorldPos.asVector();
// VisualCollisionEntity's 'snap to ground' requires that the entity is not farther than 100 meters from it
// Eval first coarse height from height map + coarse collision mesh
CTool::TRayIntersectionType interType = CTool::NoIntersection;
static volatile bool coarseSnap = true;
if (coarseSnap)
{
CVector inter;
CTool::TRayIntersectionType interType = CTool::computeWorldMapIntersection(pos3f.x, pos3f.y, inter);
if (interType != CTool::NoIntersection)
{
pos3f.z = inter.z;
}
}
bool snapped = false;
if (CollisionManager)
{
// create if necessary
if (!_VisualCollisionEntity)
{
_VisualCollisionEntity = CollisionManager->createEntity();
}
// eval finer pos from previous pos
snapped = _VisualCollisionEntity->snapToGround(pos3f); // refine z from current pos
}
//
if (snapped || interType != CTool::NoIntersection) // take precise version or else default to coarsest one
{
_WorldPos.z = pos3f.z;
}
_VisualSnapToGroundDone = true;
mat.setPos(_WorldPos);
_Instance.setMatrix(mat);
}
}
}
// *********************************************************************************************************
NLMISC::CVector CDisplayerVisualShape::evalLinkPoint(bool /* leader */)
{
//H_AUTO(R2_CDisplayerVisualShape_evalLinkPoint)
if (!_Instance.empty())
{
visualSnapToGround();
// use the visual snapped pos instead
return _Instance.getMatrix().getPos();
}
// return position that is visually snapped to the ground
return _WorldPos;
}
// *********************************************************************************************************
void CDisplayerVisualShape::onAdd(CGroupMap &/* owner */)
{
//H_AUTO(R2_CDisplayerVisualShape_onAdd)
}
// *********************************************************************************************************
void CDisplayerVisualShape::onRemove(CGroupMap &/* owner */)
{
//H_AUTO(R2_CDisplayerVisualShape_onRemove)
}
// *********************************************************************************************************
void CDisplayerVisualShape::onPreRender(CGroupMap &/* owner */)
{
//H_AUTO(R2_CDisplayerVisualShape_onPreRender)
}
// *********************************************************************************************************
void CDisplayerVisualShape::onUpdate(CGroupMap &/* owner */)
{
//H_AUTO(R2_CDisplayerVisualShape_onUpdate)
//
}
} // R2