Changeset View
Changeset View
Standalone View
Standalone View
source/graphics/ModelAbstract.cpp
/* Copyright (C) 2011 Wildfire Games. | /* Copyright (C) 2022 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU General Public License for more details. | * GNU General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. | ||||
*/ | */ | ||||
#include "precompiled.h" | #include "precompiled.h" | ||||
#include "ModelAbstract.h" | #include "ModelAbstract.h" | ||||
#include "graphics/PropPoint.h" | |||||
#include "ps/CLogger.h" | #include "ps/CLogger.h" | ||||
const CBoundingBoxOriented& CModelAbstract::GetSelectionBox() | void CModelAbstract::Clone(CModelAbstract& o) const | ||||
{ | |||||
for (const Prop& prop : m_Props) | |||||
o.AddProp(prop.m_Point, prop.m_Model->Clone(), prop.m_ObjectEntry, prop.m_MinHeight, prop.m_MaxHeight, prop.m_Selectable); | |||||
} | |||||
void CModelAbstract::AddProp(const SPropPoint* point, CModelAbstract* model, CObjectEntry* objectentry, float minHeight, float maxHeight, bool selectable) | |||||
{ | |||||
// this next call will invalidate the bounds of "model", which will in turn also invalidate the selection box | |||||
model->m_Parent = this; | |||||
model->SetParentRelativeTransform(point->m_Transform); | |||||
Prop prop; | |||||
prop.m_Point = point; | |||||
prop.m_Model = model; | |||||
prop.m_ObjectEntry = objectentry; | |||||
prop.m_MinHeight = minHeight; | |||||
prop.m_MaxHeight = maxHeight; | |||||
prop.m_Selectable = selectable; | |||||
m_Props.push_back(prop); | |||||
} | |||||
const CBoundingBoxAligned CModelAbstract::GetObjectBounds() | |||||
{ | |||||
CBoundingBoxAligned objBounds = CSelectableObject::GetObjectBounds(); // updates the (children-not-included) object-space bounds if necessary | |||||
// now extend these bounds to include the props' selection bounds (if any) | |||||
for (size_t i = 0; i < m_Props.size(); ++i) | |||||
{ | |||||
const Prop& prop = m_Props[i]; | |||||
if (prop.m_Hidden || !prop.m_Selectable) | |||||
continue; // prop is hidden from rendering, so it also shouldn't be used for selection | |||||
CBoundingBoxAligned propSelectionBounds = prop.m_Model->GetObjectBounds(); | |||||
if (propSelectionBounds.IsEmpty()) | |||||
continue; // submodel does not wish to participate in selection box, exclude it | |||||
// We have the prop's bounds in its own object-space; now we need to transform them so they can be properly added | |||||
// to the bounds in our object-space. For that, we need the transform of the prop attachment point. | |||||
// | |||||
// We have the prop point information; however, it's not trivial to compute its exact location in our object-space | |||||
// since it may or may not be attached to a bone (see SPropPoint), which in turn may or may not be in the middle of | |||||
// an animation. The bone matrices might be of interest, but they're really only meant to be used for the animation | |||||
// system and are quite opaque to use from the outside (see @ref ValidatePosition). | |||||
// | |||||
// However, a nice side effect of ValidatePosition is that it also computes the absolute world-space transform of | |||||
// our props and sets it on their respective models. In particular, @ref ValidatePosition will compute the prop's | |||||
// world-space transform as either | |||||
// | |||||
// T' = T x B x O | |||||
// or | |||||
// T' = T x O | |||||
// | |||||
// where T' is the prop's world-space transform, T is our world-space transform, O is the prop's local | |||||
// offset/rotation matrix, and B is an optional transformation matrix of the bone the prop is attached to | |||||
// (taking into account animation and everything). | |||||
// | |||||
// From this, it is clear that either O or B x O is the object-space transformation matrix of the prop. So, | |||||
// all we need to do is apply our own inverse world-transform T^(-1) to T' to get our desired result. Luckily, | |||||
// this is precomputed upon setting the transform matrix (see @ref SetTransform), so it is free to fetch. | |||||
CMatrix3D propObjectTransform = prop.m_Model->m_ParentRelativeTransform; // T' | |||||
//propObjectTransform.Concatenate(GetInvTransform()); // T^(-1) x T' | |||||
// Transform the prop's bounds into our object coordinate space | |||||
CBoundingBoxAligned transformedPropSelectionBounds; | |||||
propSelectionBounds.Transform(propObjectTransform, transformedPropSelectionBounds); | |||||
objBounds += transformedPropSelectionBounds; | |||||
} | |||||
return objBounds; | |||||
} | |||||
const CBoundingBoxOriented& CSelectableObject::GetSelectionBox() | |||||
{ | { | ||||
if (!m_SelectionBoxValid) | if (!m_SelectionBoxValid) | ||||
{ | { | ||||
CalcSelectionBox(); | CalcSelectionBox(); | ||||
m_SelectionBoxValid = true; | m_SelectionBoxValid = true; | ||||
} | } | ||||
return m_SelectionBox; | return m_SelectionBox; | ||||
} | } | ||||
void CModelAbstract::CalcSelectionBox() | void CSelectableObject::CalcSelectionBox() | ||||
{ | { | ||||
if (m_CustomSelectionShape) | if (m_CustomSelectionShape) | ||||
{ | { | ||||
// custom shape | // custom shape | ||||
switch(m_CustomSelectionShape->m_Type) | switch(m_CustomSelectionShape->m_Type) | ||||
{ | { | ||||
case CustomSelectionShape::BOX: | case CustomSelectionShape::BOX: | ||||
{ | { | ||||
Show All 26 Lines | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// standard method | // standard method | ||||
// Get the object-space bounds that should be used to construct this model (and its children)'s selection box | // Get the object-space bounds that should be used to construct this model (and its children)'s selection box | ||||
CBoundingBoxAligned objBounds = GetObjectSelectionBoundsRec(); | CBoundingBoxAligned objBounds = GetObjectBounds(); | ||||
if (objBounds.IsEmpty()) | if (objBounds.IsEmpty()) | ||||
{ | { | ||||
m_SelectionBox.SetEmpty(); // model does not wish to participate in selection | m_SelectionBox.SetEmpty(); // model does not wish to participate in selection | ||||
return; | return; | ||||
} | } | ||||
// Prevent the bounding box from extending through the terrain; clip the lower plane at Y=0 in object space. | // Prevent the bounding box from extending through the terrain; clip the lower plane at Y=0 in object space. | ||||
if (objBounds[1].Y > 0.f) // should always be the case, unless the models are defined really weirdly | if (objBounds[1].Y > 0.f) // should always be the case, unless the models are defined really weirdly | ||||
objBounds[0].Y = std::max(0.f, objBounds[0].Y); | objBounds[0].Y = std::max(0.f, objBounds[0].Y); | ||||
// transform object-space axis-aligned bounds to world-space arbitrary-aligned box | // transform object-space axis-aligned bounds to world-space arbitrary-aligned box | ||||
objBounds.Transform(GetTransform(), m_SelectionBox); | objBounds.Transform(GetTransform(), m_SelectionBox); | ||||
} | } | ||||
} | } |
Wildfire Games · Phabricator