// 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_entity.h" #include "displayer_visual_shape.h" #include "instance.h" #include "editor.h" #include "r2_config.h" // #include "../entity_cl.h" #include "../global.h" #include "../misc.h" #include "../entities.h" #include "../pacs_client.h" #include "../cdb_leaf.h" #include "../interface_v3/interface_manager.h" #include "entity_sorter.h" // #include "verbose_clock.h" // #include "nel/misc/sheet_id.h" #include "nel/misc/line.h" #include "nel/misc/i18n.h" #include "nel/misc/command.h" // #include "nel/3d/u_bone.h" #include "nel/3d/u_material.h" #include "nel/3d/u_particle_system_instance.h" // #include "nel/pacs/u_global_retriever.h" #include "nel/pacs/u_move_primitive.h" // tmp #include "nel/3d/scene_user.h" #include "nel/3d/skeleton_model.h" #include "../interface_v3/interface_3d_scene.h" #include "../interface_v3/character_3d.h" #include "../interface_v3/lua_ihm.h" #include "../pacs_client.h" #include "../time_client.h" // #include "../interface_v3/view_renderer.h" // #include "../sheet_manager.h" using namespace NLMISC; using namespace NL3D; extern NL3D::UMaterial GenericMat; namespace R2 { static bool DisplayR2EntityBoxes = false; #define BENCH(name) CPreciseClock clock__##name(#name); // ********************************************************************************************************* ////////////////////////////// // CDisplayerVisualEntity // ////////////////////////////// // ********************************************************************************************************* CDisplayerVisualEntity::CDisplayerVisualEntity() { _Entity = NULL; _InvertedMatrixTouched = true; _Angle = 0.f; _SelectionDisplayMode = AutoSelection; _RegisteredInEditor = false; _ClipBlend = -1.f; _PlaceHolder = NULL; _CreationTimeSwpBufCount = Driver->getSwapBufferCounter(); } // ********************************************************************************************************* bool CDisplayerVisualEntity::isCreatedThisFrame() const { return _CreationTimeSwpBufCount == Driver->getSwapBufferCounter(); } // ********************************************************************************************************* CDisplayerVisualEntity::~CDisplayerVisualEntity() { setActive(false); nlassert(_PlaceHolder == NULL); } // ********************************************************************************************************* void CDisplayerVisualEntity::deletePlaceHolder() { if (_PlaceHolder) { delete _PlaceHolder; _PlaceHolder = NULL; } } // ********************************************************************************************************* float CDisplayerVisualEntity::getSelectionDecalRadius() const { if (!_Entity) return 1.f; // place holder case //H_AUTO(R2_CDisplayerVisualEntity_getSelectionDecalRadius) float radius = getCylinderRadius(); if (radius != -1.f) return radius; NLMISC::CAABBox selectBox = getSelectBox(); return std::max(selectBox.getHalfSize().x, selectBox.getHalfSize().y); } // ********************************************************************************************************* void CDisplayerVisualEntity::onSelect(bool selected) { //H_AUTO(R2_CDisplayerVisualEntity_onSelect) bool alreadySelected = getDisplayFlag(FlagSelected); if (selected && selected != alreadySelected) { if (_Entity) { // force to snap entity to ground here, because pos is bad when calling "getSelectionDecalRadius" and // local bbox cache is updated with wrong value _Entity->snapToGround(); } if (getActualSelectionDisplayMode() == CircleSelection) { getEditor().addSelectingDecal(getWorldPos(), getSelectionDecalRadius()); } } updateMapDeco(); CDisplayerVisual::onSelect(selected); } // ********************************************************************************************************* void CDisplayerVisualEntity::updateVisibility() { //H_AUTO(R2_CDisplayerVisualEntity_updateVisibility) if (!getActive() || !_Entity) return; // not active, so ignore 'VisibleFlag' bool visible = getActualVisibility(); // _Entity->displayable(visible); if (!visible) _Entity->show(false); _Entity->enableInSceneInterface(visible); _MapDeco.setActive(visible); updateWorldMapPresence(); } // ********************************************************************************************************* void CDisplayerVisualEntity::showSelected() { //H_AUTO(R2_CDisplayerVisualEntity_showSelected) if (!_Entity || getActualSelectionDisplayMode() == CircleSelection) { getEditor().showSelectDecal(getWorldPos(), getSelectionDecalRadius()); } else { nlassert(getSelectionType() == LocalSelectBox || getSelectionType() == GroundProjected); getEditor().showSelectBox(getSelectBox(), getInvertedMatrix().inverted()/*, getBlinkColor(CV_SelectedInstanceColor.get())*/); } } // ********************************************************************************************************* void CDisplayerVisualEntity::showHighlighted() { //H_AUTO(R2_CDisplayerVisualEntity_showHighlighted) if (!_Entity || getActualSelectionDisplayMode() == CircleSelection) { getEditor().showHighlightDecal(getWorldPos(), getSelectionDecalRadius()); } else { getEditor().showHighlightBox(getSelectBox(), getInvertedMatrix().inverted()/*, getBlinkColor(CV_FocusedInstanceColor.get())*/); } } // ********************************************************************************************************* void CDisplayerVisualEntity::setVisualSelectionBlink(bool on, NLMISC::CRGBA color) { //H_AUTO(R2_CDisplayerVisualEntity_setVisualSelectionBlink) if (!_Entity) return; _Entity->setVisualSelectionBlink(on, getBlinkColor(color)); for(uint k = 0; k < _Entity->instances().size(); ++k) { CEntityCL::SInstanceCL &inst = _Entity->instances()[k]; if (!inst.Current.empty()) { UParticleSystemInstance ps; ps.cast(inst.Current); if (!ps.empty()) { ps.setUserColor(on ? color : CRGBA::White); } } } } // ********************************************************************************************************* void CDisplayerVisualEntity::onPreRender() { if (_PlaceHolder) { _PlaceHolder->onPreRender(); if (!_Entity) { if (getDisplayFlag(FlagSelected)) { setVisualSelectionBlink(true, getBlinkColor(CV_SelectedInstanceColor.get())); showSelected(); } else if (getDisplayFlag(FlagHasFocus)) { setVisualSelectionBlink(true, getBlinkColor(CV_FocusedInstanceColor.get())); showHighlighted(); } return; } } //H_AUTO(R2_CDisplayerVisualEntity_onPreRender) if (!_Entity) return; updateVisibility(); if (!getActualVisibility()) return; testNeedZEval(); // TODO nico : put this code in base class for use by CDisplayerVisualShape if (!_Entity->isAsyncLoading()) { if (getActualDisplayMode() == DisplayModeVisible) { if (getDisplayFlag(FlagSelected)) { setVisualSelectionBlink(true, getBlinkColor(CV_SelectedInstanceColor.get())); showSelected(); } else if (getDisplayFlag(FlagHasFocus)) { setVisualSelectionBlink(true, getBlinkColor(CV_FocusedInstanceColor.get())); showHighlighted(); } else { setVisualSelectionBlink(false, getBlinkColor(CV_UnselectedInstanceColor.get())); } _Entity->setDiffuse(true, CRGBA::White); if (_ClipBlend != 0.f) { uint32 oldOpacity = CEntityCL::getOpacityMin(); CEntityCL::setOpacityMin(0); _Entity->makeTransparent(_ClipBlend); CEntityCL::setOpacityMin(oldOpacity); } else { _Entity->makeTransparent(false); } } else { CRGBA col = getDisplayModeColorInScene(); _Entity->setDiffuse(true, col); CRGBA selCol; if (getDisplayFlag(FlagSelected)) { selCol = col; showSelected(); } else if (getDisplayFlag(FlagHasFocus)) { selCol.R = (uint8) (3 * (uint) col.R / 4); selCol.G = (uint8) (3 * (uint) col.G / 4); selCol.B = (uint8) (3 * (uint) col.B / 4); selCol.A = (uint8) (3 * (uint) col.A / 4); showHighlighted(); } else { selCol.R = col.R >> 1; selCol.G = col.G >> 1; selCol.B = col.B >> 1; selCol.A = col.A >> 1; } col.A = (uint8) (_ClipBlend * col.A); setVisualSelectionBlink(true, getBlinkColor(selCol)); uint32 oldOpacity = CEntityCL::getOpacityMin(); CEntityCL::setOpacityMin((uint32) (DEFAULT_ENTITY_MIN_OPACITY * _ClipBlend)); _Entity->makeTransparent(col.A != 255); CEntityCL::setOpacityMin(oldOpacity); } } // _MapDeco.setInvalidPosFlag(getDisplayFlag(FlagBadPos)); } // ********************************************************************************************************* void CDisplayerVisualEntity::drawBBox(const NLMISC::CMatrix &modelMatrix, const NLMISC::CAABBox &bbox, NLMISC::CRGBA colOverZ, NLMISC::CRGBA colUnderZ) { //H_AUTO(R2_CDisplayerVisualEntity_drawBBox) //nlwarning("bbox = (%f, %f, %f) - (%f, %f, %f), color = (%d, %d, %d, %d)", bbox.getMin().x, bbox.getMin().y, bbox.getMin().z, // bbox.getMax().x, bbox.getMax().y, bbox.getMax().z, (int) colOverZ.R, (int) colOverZ.G, (int) colOverZ.B, (int) colOverZ.A); // for z-precision, work with camera at (0, 0, 0) static volatile bool fixMatrixPos = false; CMatrix oldViewMatrix = Driver->getViewMatrix(); Driver->setModelMatrix(modelMatrix); CMatrix viewMat = oldViewMatrix; if (fixMatrixPos) { viewMat.setPos(CVector::Null); } Driver->setViewMatrix(viewMat); // draw below zbuffer UMaterial::ZFunc oldZfunc = GenericMat.getZFunc(); bool oldZWrite = GenericMat.getZWrite(); GenericMat.setZFunc(UMaterial::greater); GenericMat.setZWrite(false); ::drawBox(bbox.getMin(), bbox.getMax(), colUnderZ ); GenericMat.setZFunc(oldZfunc); GenericMat.setZWrite(oldZWrite); ::drawBox(bbox.getMin(), bbox.getMax(), colOverZ); Driver->setViewMatrix(oldViewMatrix); } // ********************************************************************************************************* void CDisplayerVisualEntity::drawBBox(NLMISC::CRGBA colOverZ, NLMISC::CRGBA colUnderZ) { //H_AUTO(R2_CDisplayerVisualEntity_drawBBox) if (!_Entity) return; if (_Entity->isAsyncLoading()) return; NLMISC::CAABBox bbox; switch(getActualSelectionDisplayMode()) { case CircleSelection: { // draw real local version CMatrix modelMatrix = _Entity->dirMatrix(); modelMatrix.setPos(_Entity->pos().asVector()); drawBBox(modelMatrix, getEditor().getLocalSelectBox(*_Entity), colOverZ, colUnderZ); // draw world version colOverZ.avg2(colOverZ, CRGBA::Black); colUnderZ.avg2(colUnderZ, CRGBA::Black); drawBBox(CMatrix::Identity, getEditor().getSelectBox(*_Entity), colOverZ, colUnderZ); } break; case BoxSelection: { // uses local version CMatrix modelMatrix = _Entity->dirMatrix(); modelMatrix.setPos(_Entity->pos().asVector()); drawBBox(modelMatrix, getEditor().getLocalSelectBox(*_Entity), colOverZ, colUnderZ); } break; default: nlassert(0); break; } } // ********************************************************************************************************* void CDisplayerVisualEntity::onPostRender() { if (_PlaceHolder) { // _PlaceHolder->onPostRender(); // do not post render here becauseit would set transparency again, bypassing the changed in 'setClipBlend' } //H_AUTO(R2_CDisplayerVisualEntity_onPostRender) if (!_Entity) return; if (!getActualVisibility()) return; if (getDisplayFlag(FlagSelected)) { // draw a bbox around the entity if (R2::DisplayR2EntityBoxes) { drawBBox(CRGBA::Green, CRGBA(127, 127, 127, 255)); } } else if (getDisplayFlag(FlagHasFocus)) { if (R2::DisplayR2EntityBoxes) { drawBBox(CRGBA::White, CRGBA(127, 127, 127, 255)); } } static volatile bool drawBoneSphere = false; if (drawBoneSphere) { if (_Entity->skeleton()) { CSkeletonModel *sm = _Entity->skeleton()->getObjectPtr(); if (sm) { Driver->setModelMatrix(CMatrix::Identity); std::vector boneSpheres; sm->getWorldMaxBoneSpheres(boneSpheres); for(uint k = 0; k < boneSpheres.size(); ++k) { drawSphere(boneSpheres[k].Center, boneSpheres[k].Radius, CRGBA::Red); } } } } CDisplayerVisual::onPostRender(); } // ********************************************************************************************************* void CDisplayerVisualEntity::setActive(bool active) { //H_AUTO(R2_CDisplayerVisualEntity_setActive) if (active && !getActive()) { if (getEditor().getEntitySorter()) { _CreationTimeSwpBufCount = Driver->getSwapBufferCounter(); _ClipBlend = -1.f; getEditor().getEntitySorter()->registerEntityDisplayer(this); } _RegisteredInEditor = true; updateWorldMapPresence(); } if (!active && getActive()) { if (getEditor().getEntitySorter()) { getEditor().getEntitySorter()->unregisterEntityDisplayer(this); } eraseEntity(); // in map if (_MapDeco.isAddedToMap()) { CGroupMap *gm = CTool::getWorldMap(); if (gm) gm->removeDeco(&_MapDeco); } deletePlaceHolder(); _RegisteredInEditor = false; } } // ********************************************************************************************************* void CDisplayerVisualEntity::setClipBlend(float amount) { if (_ClipBlend == amount) return; //CPreciseClock cl("CDisplayerVisualEntity::setClipBlend"); _ClipBlend = amount; bool visible = _ClipBlend != 0.f; if (!_Entity && visible) { createEntity(); } if (!visible) { eraseEntity(); } if (amount == 1.f) { deletePlaceHolder(); } else { // display the place holder if (!_PlaceHolder) { static volatile float scale = 1.f; _PlaceHolder = new CDisplayerVisualShape("r2_entity_place_holder.shape", scale, false); R2::setDisplayedInstance(_PlaceHolder, getDisplayedInstance()); _PlaceHolder->onPostCreate(); ((CDisplayerVisual *) _PlaceHolder)->setActive(true); ((CDisplayerVisual *) _PlaceHolder)->updateWorldPos(); _PlaceHolder->onPreRender(); // force to create the instance so we can make it transparent at start } if (_PlaceHolder && !_PlaceHolder->getMesh().empty()) { makeInstanceTransparent(_PlaceHolder->getMesh(), (uint8) (255.f * (1.f - amount)), amount > 0.5f); } } } // ********************************************************************************************************* void CDisplayerVisualEntity::createEntity() { //H_AUTO(R2_CDisplayerVisualEntity_createEntity) // in scene nlassert(!_Entity); // framework should never call this twice in a row // assume that pos & rot have been set uint k = 2; for (; k < 255; ++k) // find an empty slot { if (!EntitiesMngr.entity(k)) { nlwarning("Creating entity in slot %d with sheet %s at position %s with angle %f", (int) k, getSheet().c_str(), toString(getPos().asVector()).c_str(), getAngle()); _Entity = CEditor::createEntity(k, NLMISC::CSheetId(getSheet()), getWorldPos(), getAngle(), getPermanentStatutIcon()); break; } } if (!_Entity) { nlwarning("Entity creation failed. This would normally not happeneds. Sheet '%s', slot %u ", getSheet().c_str(), k); return; } // updateName(); updateEntity(); _Entity->updateVisible(T1, NULL); _Entity->updatePos(T1, NULL); } // ********************************************************************************************************* void CDisplayerVisualEntity::updateWorldMapPresence() { //H_AUTO(R2_CDisplayerVisualEntity_updateWorldMapPresence) if (!getActive()) return; // if not in current map then remove // if entity not in that map then ignore bool inIsland = false; CIslandCollision &col = getEditor().getIslandCollision(); R2::CScenarioEntryPoints::CCompleteIsland *currIsland = col.getCurrIslandDesc(); if (currIsland) { inIsland = currIsland->isIn(getWorldPos2f()); } // in map (displayed only if selectable for now) if (getDisplayedInstance()->getSelectable() && inIsland) { if (!_MapDeco.isAddedToMap()) { CGroupMap *gm = CTool::getWorldMap(); if (gm) { _MapDeco.setDisplayedInstance(getDisplayedInstance(), true); // retrieve icon from the displayed object (lua code) CLuaState &ls = getEditor().getLua(); std::string texName = ""; { CLuaStackChecker lsc(&ls); if (getDisplayedInstance()->getLuaProjection().callMethodByNameNoThrow("getSelectBarIcon", 0, 1)) { texName = ls.toString(-1); ls.pop(); } } gm->addDeco(&_MapDeco); _MapDeco.setCloseTexture(texName); _MapDeco.invalidateCoords(); } } } else { if (_MapDeco.isAddedToMap()) { CGroupMap *gm = CTool::getWorldMap(); if (gm) { gm->removeDeco(&_MapDeco); } } } } // ********************************************************************************************************* bool CDisplayerVisualEntity::getActive() const { return _RegisteredInEditor; } // ********************************************************************************************************* void CDisplayerVisualEntity::updateWorldPos() { if (_PlaceHolder) { ((CDisplayerVisual *) _PlaceHolder)->updateWorldPos(); } //H_AUTO(R2_CDisplayerVisualEntity_updateWorldPos) CDisplayerVisual::updateWorldPos(); updateEntityWorldPos(); snapToGround(); updateMapDeco(); } // ********************************************************************************************************* /*void CDisplayerVisualEntity::updateValidPosFlag() { // eval if current pos is valid pacs pos if (!GR) { return; } NLPACS::UGlobalPosition gpos = GR->retrievePosition(getWorldPos().asVector()); setDisplayFlag(FlagBadPos, gpos.InstanceId == -1); }*/ // ********************************************************************************************************* void CDisplayerVisualEntity::updateMapDeco() { //H_AUTO(R2_CDisplayerVisualEntity_updateMapDeco) if (_MapDeco.isAddedToMap()) { CGroupMap *gm = CTool::getWorldMap(); if (gm) { _MapDeco.onUpdate(*gm); _MapDeco.invalidateCoords(); } } } // ********************************************************************************************************* void CDisplayerVisualEntity::onFocus(bool focused) { //H_AUTO(R2_CDisplayerVisualEntity_onFocus) CDisplayerVisual::onFocus(focused); updateMapDeco(); } // ********************************************************************************************************* void CDisplayerVisualEntity::eraseEntity() { //H_AUTO(R2_CDisplayerVisualEntity_eraseEntity) // in scene if (_Entity) { EntitiesMngr.remove(_Entity->slot(), true); _Entity = NULL; } } // ********************************************************************************************************* void CDisplayerVisualEntity::onCreate() { //H_AUTO(R2_CDisplayerVisualEntity_onCreate) CDisplayerVisual::onCreate(); retrieveAngle(); } // ********************************************************************************************************* void CDisplayerVisualEntity::onPostCreate() { CDisplayerVisual::onPostCreate(); } // ********************************************************************************************************* bool CDisplayerVisualEntity::isUserCreated() const { return getDisplayedInstance()->getLuaProjection()["User"]["Select"].toBoolean() == true; } // ********************************************************************************************************* void CDisplayerVisualEntity::onAttrModified(const std::string &name, sint32 index) { if (_PlaceHolder) { _PlaceHolder->onAttrModified(name, index); } //H_AUTO(R2_CDisplayerVisualEntity_onAttrModified) CDisplayerVisual::onAttrModified(name, index); if (name == "Angle") { retrieveAngle(); updateEntityRot(); updateMapDeco(); } else if (name == "Name") { updateName(); } else if (name == "GabaritHeight" || name == "GabaritTorsoWidth" || name == "GabaritArmsWidth" || name == "GabaritLegsWidth" || name == "GabaritBreastSize" || name == "HairType" || name == "HairColor" || name == "Tattoo" || name == "EyesColor" || name == "MorphTarget1" || name == "MorphTarget2" || name == "MorphTarget3" || name == "MorphTarget4" || name == "MorphTarget5" || name == "MorphTarget6" || name == "MorphTarget7" || name == "MorphTarget8" || name == "JacketModel" || name == "TrouserModel" || name == "FeetModel" || name == "HandsModel" || name == "ArmModel" || name == "WeaponRightHand" || name == "WeaponLeftHand" || name == "JacketColor" || name == "ArmColor" || name == "HandsColor" || name == "TrouserColor" || name == "FeetColor") { updateEntity(); } else if (name == "SheetClient") { updateRaceAndSex(); } else if (name == "Selectable") { updateWorldMapPresence(); } /*else if (name == "DisplayMode") { updateMapDeco(); }*/ } // ********************************************************************************************************* void CDisplayerVisualEntity::setDisplayMode(sint32 mode) { //H_AUTO(R2_CDisplayerVisualEntity_setDisplayMode) CDisplayerVisual::setDisplayMode(mode); updateMapDeco(); } // ********************************************************************************************************* void CDisplayerVisualEntity::onParentDisplayModeChanged() { //H_AUTO(R2_CDisplayerVisualEntity_onParentDisplayModeChanged) updateMapDeco(); } // ********************************************************************************************************* void CDisplayerVisualEntity::updateRaceAndSex() { //H_AUTO(R2_CDisplayerVisualEntity_updateRaceAndSex) std::string sheetClient = (std::string) getString(&getProps(), "SheetClient"); _Entity = getEditor().createEntity(_Entity->slot(), CSheetId(sheetClient), getPos().asVector(), getAngle()); updateEntity(); // update sex SPropVisualA vA; const string propNameA = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPA); CCDBNodeLeaf *leafA = CInterfaceManager::getInstance()->getDbProp(propNameA); if (!leafA) { nlwarning("Can't find DB leaf %s", propNameA.c_str()); return; } vA.PropertyA = leafA->getValue64(); string::size_type loc = sheetClient.find("female", 0); vA.PropertySubData.Sex = (uint)(loc != string::npos); EntitiesMngr.updateVisualProperty(0, _Entity->slot(), CLFECOMMON::PROPERTY_VPA); } // ********************************************************************************************************* void CDisplayerVisualEntity::updateEntity() { { //BENCH(updateName) //H_AUTO(R2_CDisplayerVisualEntity_updateEntity) updateName(); } CCDBNodeLeaf *leafA; CCDBNodeLeaf *leafB; CCDBNodeLeaf *leafC; SPropVisualA vA; SPropVisualB vB; SPropVisualC vC; { //BENCH(entitySetup) const string propNameA = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPA); const string propNameB = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPB); const string propNameC = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPC); leafA = CInterfaceManager::getInstance()->getDbProp(propNameA); leafB = CInterfaceManager::getInstance()->getDbProp(propNameB); leafC = CInterfaceManager::getInstance()->getDbProp(propNameC); if (!leafA) { nlwarning("Can't find DB leaf %s", propNameA.c_str()); return; } if (!leafB) { nlwarning("Can't find DB leaf %s", propNameB.c_str()); return; } if (!leafC) { nlwarning("Can't find DB leaf %s", propNameC.c_str()); return; } vA.PropertyA = leafA->getValue64(); vB.PropertyB = leafB->getValue64(); vC.PropertyC = leafC->getValue64(); vC.PropertySubData.CharacterHeight = (uint) getNumber(&getProps(), "GabaritHeight"); vC.PropertySubData.ArmsWidth = (uint) getNumber(&getProps(), "GabaritArmsWidth"); vC.PropertySubData.TorsoWidth = (uint) getNumber(&getProps(), "GabaritTorsoWidth"); vC.PropertySubData.LegsWidth = (uint) getNumber(&getProps(), "GabaritLegsWidth"); vC.PropertySubData.BreastSize = (uint) getNumber(&getProps(), "GabaritBreastSize"); int itemNb = (int) getNumber(&getProps(), "HairType"); std::string itemFileName; if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.HatModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::HEAD_SLOT); } else { vA.PropertySubData.HatModel = 0; } vA.PropertySubData.HatColor = (uint) getNumber(&getProps(), "HairColor"); vC.PropertySubData.Tattoo = (uint) getNumber(&getProps(), "Tattoo"); vC.PropertySubData.EyesColor = (uint) getNumber(&getProps(), "EyesColor"); vC.PropertySubData.MorphTarget1 = (uint) getNumber(&getProps(), "MorphTarget1"); vC.PropertySubData.MorphTarget2 = (uint) getNumber(&getProps(), "MorphTarget2"); vC.PropertySubData.MorphTarget3 = (uint) getNumber(&getProps(), "MorphTarget3"); vC.PropertySubData.MorphTarget4 = (uint) getNumber(&getProps(), "MorphTarget4"); vC.PropertySubData.MorphTarget5 = (uint) getNumber(&getProps(), "MorphTarget5"); vC.PropertySubData.MorphTarget6 = (uint) getNumber(&getProps(), "MorphTarget6"); vC.PropertySubData.MorphTarget7 = (uint) getNumber(&getProps(), "MorphTarget7"); vC.PropertySubData.MorphTarget8 = (uint) getNumber(&getProps(), "MorphTarget8"); itemNb = (int) getNumber(&getProps(), "JacketModel"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.JacketModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::CHEST_SLOT); } else { vA.PropertySubData.JacketModel = 0; } itemNb = (int) getNumber(&getProps(), "TrouserModel"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.TrouserModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::LEGS_SLOT); } else { vA.PropertySubData.TrouserModel = 0; } itemNb = (int) getNumber(&getProps(), "FeetModel"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vB.PropertySubData.FeetModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::FEET_SLOT); } else { vB.PropertySubData.FeetModel = 0; } itemNb = (int) getNumber(&getProps(), "HandsModel"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vB.PropertySubData.HandsModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::HANDS_SLOT); } else { vB.PropertySubData.HandsModel = 0; } itemNb = (int) getNumber(&getProps(), "ArmModel"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.ArmModel = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::ARMS_SLOT); } else { vA.PropertySubData.ArmModel = 0; } itemNb = (int) getNumber(&getProps(), "WeaponRightHand"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.WeaponRightHand = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::RIGHT_HAND_SLOT); } else { vA.PropertySubData.WeaponRightHand = 0; } itemNb = (int) getNumber(&getProps(), "WeaponLeftHand"); if(itemNb>0) { itemFileName = CSheetId(itemNb).toString(); vA.PropertySubData.WeaponLeftHand = (uint) SheetMngr.getVSIndex(itemFileName, SLOTTYPE::LEFT_HAND_SLOT); } else { vA.PropertySubData.WeaponLeftHand = 0; } vA.PropertySubData.JacketColor = (uint) getNumber(&getProps(), "JacketColor"); vA.PropertySubData.TrouserColor = (uint) getNumber(&getProps(), "TrouserColor"); vB.PropertySubData.FeetColor = (uint) getNumber(&getProps(), "FeetColor"); vB.PropertySubData.HandsColor = (uint) getNumber(&getProps(), "HandsColor"); vA.PropertySubData.ArmColor = (uint) getNumber(&getProps(), "ArmColor"); } { //BENCH(entityApply) // Set the database leafA->setValue64(vA.PropertyA); leafB->setValue64(vB.PropertyB); leafC->setValue64(vC.PropertyC); // Force to update properties EntitiesMngr.updateVisualProperty(0, _Entity->slot(), CLFECOMMON::PROPERTY_VPA); EntitiesMngr.updateVisualProperty(0, _Entity->slot(), CLFECOMMON::PROPERTY_VPB); EntitiesMngr.updateVisualProperty(0, _Entity->slot(), CLFECOMMON::PROPERTY_VPC); } } // ********************************************************************************************************* void CDisplayerVisualEntity::updateEntityWorldPos() { //H_AUTO(R2_CDisplayerVisualEntity_updateEntityWorldPos) _InvertedMatrixTouched = true; if (!_Entity) return; _Entity->pacsPos(getWorldPos()); _Entity->updatePos(T1, NULL); _Entity->updateVisible(T1, NULL); } // ********************************************************************************************************* void CDisplayerVisualEntity::updateName() { //H_AUTO(R2_CDisplayerVisualEntity_updateName) if (!_Entity) return; std::string name = getString(&getProps(), "Name"); ucstring ucName; ucName.fromUtf8(name); if (ucName.empty()) { ucName = CI18N::get("uiR2EDNoName"); } std::string actName=std::string(""); // If entity is in an additionnal act, then postfix its name with the name of the act if (getDisplayedInstance()->getParentAct() != getEditor().getBaseAct()) { R2::CObjectTable* act = getDisplayedInstance()->getParentAct()->getObjectTable(); R2::CObjectTable* acts = (CObjectTable *)(act->getParent()); sint actNb = -1; for(uint i=0; igetSize(); i++) { if(acts->getValue(i)->equal(act)) { actNb = i; break; } } std::string firstPart = ""; if(actNb>0) firstPart = CI18N::get("uiR2EDDefaultActTitle").toString() + " " + NLMISC::toString(actNb); if (act->isString("Name")) actName = act->toString("Name"); else actName = act->toString("Title"); //obsolete if(actName!=firstPart && actName!="") actName = firstPart+":"+actName; } else { actName = CI18N::get("uiR2EDBaseAct").toString(); } actName = NLMISC::toString(" [%s]", actName.c_str()); ucstring ucActName; ucActName.fromUtf8(actName); ucName += ucActName; { //BENCH(setEntityName) _Entity->setEntityName(ucName); } { //BENCH(buildInSceneInterface) _Entity->buildInSceneInterface(); } } // ********************************************************************************************************* void CDisplayerVisualEntity::updatePermanentStatutIcon(const string & textureName) { //H_AUTO(R2_CDisplayerVisualEntity_updatePermanentStatutIcon) if (!_Entity) return; setPermanentStatutIcon(textureName); _Entity->setPermanentStatutIcon(textureName); _Entity->buildInSceneInterface(); } // ********************************************************************************************************* void CDisplayerVisualEntity::onPostHrcMove() { //H_AUTO(R2_CDisplayerVisualEntity_onPostHrcMove) CDisplayerVisual::onPostHrcMove(); // update the name, because act may have changed updateName(); } // ********************************************************************************************************* void CDisplayerVisualEntity::updateEntityRot() { //H_AUTO(R2_CDisplayerVisualEntity_updateEntityRot) _InvertedMatrixTouched = true; if (!_Entity) return; float angle = getAngle(); _Entity->front(CVector((float)cos(angle), (float)sin(angle), 0.f), true, true, true); _Entity->dir(_Entity->front(), false, false); NLPACS::UMovePrimitive *prim = _Entity->getPrimitive(); if (prim && prim->getPrimitiveType() == NLPACS::UMovePrimitive::_2DOrientedBox) { prim->setOrientation(angle, dynamicWI); } } // ********************************************************************************************************* void CDisplayerVisualEntity::retrieveAngle() { //H_AUTO(R2_CDisplayerVisualEntity_retrieveAngle) _Angle = (float) getNumber(&getProps(), "Angle"); } // ********************************************************************************************************* std::string CDisplayerVisualEntity::getSheet() const { //H_AUTO(R2_std_getSheet) std::string sheet = getString(&getProps(), "SheetClient"); if (sheet.empty()) { nlwarning("Sheet not found, defaulting to fyros.race_stats"); return "fyros.race_stats"; } return sheet; } // ********************************************************************************************************* std::string CDisplayerVisualEntity::getVisualProperties() const { //H_AUTO(R2_std_getVisualProperties) if( !_Entity ) return ""; const std::string propNameA = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPA); const std::string propNameB = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPB); const std::string propNameC = toString("SERVER:Entities:E%d:P%d", _Entity->slot(), CLFECOMMON::PROPERTY_VPC); CCDBNodeLeaf *leafA = CInterfaceManager::getInstance()->getDbProp(propNameA); CCDBNodeLeaf *leafB = CInterfaceManager::getInstance()->getDbProp(propNameB); CCDBNodeLeaf *leafC = CInterfaceManager::getInstance()->getDbProp(propNameC); if (!leafA) { nlwarning("Can't find DB leaf %s", propNameA.c_str()); return ""; } if (!leafB) { nlwarning("Can't find DB leaf %s", propNameB.c_str()); return ""; } if (!leafC) { nlwarning("Can't find DB leaf %s", propNameC.c_str()); return ""; } uint64 uVPA = leafA->getValue64(); uint64 uVPB = leafB->getValue64(); uint64 uVPC = leafC->getValue64(); const std::string strVPABC = NLMISC::toString( "VPA:%016.16"NL_I64"x\nVPB:%016.16"NL_I64"x\nVPC:%016.16"NL_I64"x", uVPA, uVPB, uVPC ); return strVPABC; } // ********************************************************************************************************* /*NLMISC::CVector CDisplayerVisualEntity::evalLinkPoint(bool leader) const {*/ //if (!_Entity) return CVector::Null; /* static volatile bool alwaysSnap = false; if (_Entity->getLastClip() || alwaysSnap) { _Entity->snapToGround(); } if (_Entity->skeleton()) { sint boneId = _Entity->skeleton()->getBoneIdByName(leader ? "Bip01 Head" : "Bip01 Spine1"); if (boneId != -1) { if (!_Entity->skeleton()->isBoneComputed(boneId)) { _Entity->skeleton()->forceComputeBone(boneId); } NL3D::UBone bone = _Entity->skeleton()->getBone(boneId); const NLMISC::CMatrix &worldMat = bone.getLastWorldMatrixComputed(); return worldMat.getPos(); } } */ //TMP : must solve this : bone pos isn't good until entity is fully loaded /* return _Entity->pos(); }*/ // ********************************************************************************************************* bool CDisplayerVisualEntity::getLastClip() const { //H_AUTO(R2_CDisplayerVisualEntity_getLastClip) if (!_Entity) { return _PlaceHolder == NULL; // is there's a place holder consider entity to be not clipped } if (getActualSelectionDisplayMode() == BoxSelection) return false; return _Entity->getLastClip(); } // ********************************************************************************************************* NLMISC::CAABBox CDisplayerVisualEntity::getSelectBox() const { //H_AUTO(R2_NLMISC_CAABBox ) if (!_Entity) { if (_PlaceHolder) { return _PlaceHolder->getSelectBox(); } return CDisplayerVisual::getSelectBox(); } if (getSelectionType() == ISelectableObject::LocalSelectBox || getSelectionType() == ISelectableObject::GroundProjected ) { if (_Entity->isAsyncLoading()) { NLMISC::CAABBox result; result.setCenter(CVector::Null); result.setHalfSize(CVector::Null); return result; } return getEditor().getLocalSelectBox(*_Entity); } else { return getEditor().getSelectBox(*_Entity); } } // ********************************************************************************************************* float CDisplayerVisualEntity::preciseIntersectionTest(const NLMISC::CVector &worldRayStart, const NLMISC::CVector &worldRayDir) const { if (!_Entity) { if (_PlaceHolder) { return _PlaceHolder->preciseIntersectionTest(worldRayStart, worldRayDir); } return 0.f; } //H_AUTO(R2_CDisplayerVisualEntity_preciseIntersectionTest) return CEditor::preciseEntityIntersectionTest(*_Entity, worldRayStart, worldRayDir); } // ********************************************************************************************************* void CDisplayerVisualEntity::snapToGround() { //H_AUTO(R2_CDisplayerVisualEntity_snapToGround) if (!_Entity) return; _Entity->snapToGround(); // change local z _WorldPos.z = _Entity->pos().z; } // ********************************************************************************************************* const NLMISC::CMatrix &CDisplayerVisualEntity::getInvertedMatrix() const { //H_AUTO(R2_NLMISC_CMatrix ) if (!_InvertedMatrixTouched) return _InvertedMatrix; if (_Entity) { _InvertedMatrix = _Entity->dirMatrix(); _InvertedMatrix.setPos(_Entity->pos()); _InvertedMatrix.invert(); _InvertedMatrixTouched = false; } else if (_PlaceHolder) { return _PlaceHolder->getInvertedMatrix(); } return _InvertedMatrix; } // ********************************************************************************************************* sint32 CDisplayerVisualEntity::getSlotEntity() const { //H_AUTO(R2_CDisplayerVisualEntity_getSlotEntity) if(_Entity) { return (sint32)_Entity->slot(); } return -1; } // ********************************************************************************************************* int CDisplayerVisualEntity::luaUpdateName(CLuaState &ls) { //H_AUTO(R2_CDisplayerVisualEntity_luaUpdateName) const char *funcName = "updateName"; CLuaIHM::checkArgCount(ls, funcName, 0); updateName(); return 0; } // ********************************************************************************************************* int CDisplayerVisualEntity::luaUpdatePermanentStatutIcon(CLuaState &ls) { //H_AUTO(R2_CDisplayerVisualEntity_luaUpdatePermanentStatutIcon) const char *funcName = "updatePermanentStatutIcon"; CLuaIHM::checkArgCount(ls, funcName, 1); updatePermanentStatutIcon(ls.toString(1)); return 0; } // ********************************************************************************************************* bool CDisplayerVisualEntity::isInProjection(const NLMISC::CVector2f &pos) const { //H_AUTO(R2_CDisplayerVisualEntity_isInProjection) if (getActualSelectionDisplayMode() == CircleSelection) return false; nlassert(getSelectionType() == LocalSelectBox || getSelectionType() == GroundProjected); CVector localPos = getInvertedMatrix() * pos; NLMISC::CAABBox selectBox = getSelectBox(); CVector pmin = selectBox.getMin(); CVector pmax = selectBox.getMax(); if (localPos.x < pmin.x || localPos.x > pmax.x || localPos.y < pmin.y || localPos.y > pmax.y) return false; return true; } // ********************************************************************************************************* ISelectableObject::TSelectionType CDisplayerVisualEntity::getSelectionType() const { if (!_Entity && _PlaceHolder) return _PlaceHolder->getSelectionType(); //H_AUTO(R2_ISelectableObject_TSelectionType ) // If box selection was explicitly asked (not auto mode), uses ground selection only if (_SelectionDisplayMode == BoxSelection) return ISelectableObject::GroundProjected; // if there's a cylinder primitive, use world box selection, else use local box (for obstacle like barriers) return getActualSelectionDisplayMode() == BoxSelection ? ISelectableObject::LocalSelectBox : ISelectableObject::WorldSelectBox; } // ********************************************************************************************************* float CDisplayerVisualEntity::getCylinderRadius() const { //H_AUTO(R2_CDisplayerVisualEntity_getCylinderRadius) if (!_Entity) return -1.f; const NLPACS::UMovePrimitive *prim = _Entity->getPrimitive(); if (!prim) return -1; if (prim->getPrimitiveType() == NLPACS::UMovePrimitive::_2DOrientedCylinder) { return prim->getRadius(); } else { return -1; } } // ********************************************************************************************************* CDisplayerVisualEntity::TSelectionDisplayMode CDisplayerVisualEntity::getActualSelectionDisplayMode() const { //H_AUTO(R2_CDisplayerVisualEntity_TSelectionDisplayMode ) // unless the display mode was explicitly given, uses the following rule // If the collision primitive is a rectangle, always display selection as a rectangle on ground // If the collision primitive is a cylinder, use that cylinder to display that selection on the ground, unless // it is too big. In this case ,uses a box instead static volatile float maxRadiusForCircleSelection = 2.f; switch(_SelectionDisplayMode) { case AutoSelection: { float radius = getCylinderRadius(); if (radius == -1.f || radius >= maxRadiusForCircleSelection) return BoxSelection; return CircleSelection; } break; case BoxSelection: return BoxSelection; break; case CircleSelection: return CircleSelection; break; default: nlassert(0); break; } return CircleSelection; } // ********************************************************************************************************* bool CDisplayerVisualEntity::maxVisibleEntityExceeded() const { return _ClipBlend != 1.f && _ClipBlend >= 0.f; } } // R2 NLMISC_COMMAND(toggleR2EntityBoxes, "toggle display bounding boxes for selected R2 entities", "") { if (!args.empty()) return false; R2::DisplayR2EntityBoxes = !R2::DisplayR2EntityBoxes; return true; } NLMISC_COMMAND(showR2EntityBoxes, "display bounding boxes for selected R2 entities", "") { if (!args.empty()) return false; R2::DisplayR2EntityBoxes = true; return true; } NLMISC_COMMAND(hideR2EntityBoxes, "display bounding boxes for selected R2 entities", "") { if (!args.empty()) return false; R2::DisplayR2EntityBoxes = false; return true; }