mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Recast: implement primitive shape volume editor
Allows for selecting already created volumes and modifying or deleting them.
This commit is contained in:
parent
da66a7b9c1
commit
9e46c8be75
@ -23,6 +23,7 @@
|
||||
#include "DebugUtils/Include/DetourDebugDraw.h"
|
||||
#include "Include/InputGeom.h"
|
||||
#include "DetourTileCache/Include/DetourTileCache.h"
|
||||
#include "include/ShapeVolumeTool.h"
|
||||
|
||||
static void EditorCommon_DrawInputGeometry(duDebugDraw* const dd, const InputGeom* const geom,
|
||||
const float maxSlope, const float textureScale)
|
||||
@ -380,10 +381,17 @@ void Editor_StaticTileMeshCommon::renderTileMeshData()
|
||||
glDepthMask(GL_TRUE);
|
||||
}
|
||||
|
||||
int selectedVolumeIndex = -1;
|
||||
if (m_tool->type() == TOOL_SHAPE_VOLUME)
|
||||
{
|
||||
const ShapeVolumeTool* volTool = (const ShapeVolumeTool*)m_tool;
|
||||
selectedVolumeIndex = volTool->getSelectedVolumeIndex();
|
||||
}
|
||||
|
||||
// TODO: also add flags for this
|
||||
m_geom->drawBoxVolumes(&m_dd, recastDrawOffset);
|
||||
m_geom->drawCylinderVolumes(&m_dd, recastDrawOffset);
|
||||
m_geom->drawConvexVolumes(&m_dd, recastDrawOffset);
|
||||
m_geom->drawBoxVolumes(&m_dd, recastDrawOffset, selectedVolumeIndex);
|
||||
m_geom->drawCylinderVolumes(&m_dd, recastDrawOffset, selectedVolumeIndex);
|
||||
m_geom->drawConvexVolumes(&m_dd, recastDrawOffset, selectedVolumeIndex);
|
||||
|
||||
// NOTE: commented out because this already gets rendered when the off-mesh
|
||||
// connection tool is activated. And if we generated an off-mesh link, this
|
||||
|
@ -317,6 +317,9 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath)
|
||||
&vol->verts[0], &vol->verts[1], &vol->verts[2],
|
||||
&vol->verts[3], &vol->verts[4], &vol->verts[5]);
|
||||
|
||||
vol->hmin = 0.0f;
|
||||
vol->hmax = 0.0f;
|
||||
vol->nverts = 6;
|
||||
vol->type = VOLUME_BOX;
|
||||
}
|
||||
}
|
||||
@ -331,6 +334,9 @@ bool InputGeom::loadGeomSet(rcContext* ctx, const std::string& filepath)
|
||||
&vol->verts[0], &vol->verts[1], &vol->verts[2],
|
||||
&vol->verts[3], &vol->verts[4]);
|
||||
|
||||
vol->hmin = 0.0f;
|
||||
vol->hmax = 0.0f;
|
||||
vol->nverts = 5;
|
||||
vol->type = VOLUME_CYLINDER;
|
||||
}
|
||||
}
|
||||
@ -715,9 +721,11 @@ void InputGeom::addBoxVolume(const float* bmin, const float* bmax,
|
||||
{
|
||||
if (m_volumeCount >= MAX_VOLUMES) return;
|
||||
ShapeVolume* vol = &m_volumes[m_volumeCount++];
|
||||
memset(vol, 0, sizeof(ShapeVolume));
|
||||
rdVcopy(&vol->verts[0], bmin);
|
||||
rdVcopy(&vol->verts[3], bmax);
|
||||
vol->hmin = 0.0f;
|
||||
vol->hmax = 0.0f;
|
||||
vol->nverts = 6;
|
||||
vol->flags = flags;
|
||||
vol->area = area;
|
||||
vol->type = VOLUME_BOX;
|
||||
@ -728,10 +736,12 @@ void InputGeom::addCylinderVolume(const float* pos, const float radius,
|
||||
{
|
||||
if (m_volumeCount >= MAX_VOLUMES) return;
|
||||
ShapeVolume* vol = &m_volumes[m_volumeCount++];
|
||||
memset(vol, 0, sizeof(ShapeVolume));
|
||||
rdVcopy(vol->verts, pos);
|
||||
vol->verts[3] = radius;
|
||||
vol->verts[4] = height;
|
||||
vol->hmin = 0.0f;
|
||||
vol->hmax = 0.0f;
|
||||
vol->nverts = 5;
|
||||
vol->flags = flags;
|
||||
vol->area = area;
|
||||
vol->type = VOLUME_CYLINDER;
|
||||
@ -742,7 +752,6 @@ void InputGeom::addConvexVolume(const float* verts, const int nverts,
|
||||
{
|
||||
if (m_volumeCount >= MAX_VOLUMES) return;
|
||||
ShapeVolume* vol = &m_volumes[m_volumeCount++];
|
||||
memset(vol, 0, sizeof(ShapeVolume));
|
||||
memcpy(vol->verts, verts, sizeof(float)*3*nverts);
|
||||
vol->hmin = minh;
|
||||
vol->hmax = maxh;
|
||||
|
@ -74,8 +74,30 @@ static int convexhull(const float* pts, int npts, int* out)
|
||||
return i;
|
||||
}
|
||||
|
||||
static bool isValidVolumeIndex(const int index, const InputGeom* geom)
|
||||
{
|
||||
return index > -1 && index < geom->getConvexVolumeCount();
|
||||
}
|
||||
|
||||
void handleVolumeFlags(int& flags, const char* buttonId)
|
||||
{
|
||||
ImGui::Text("Poly Flags");
|
||||
ImGui::Indent();
|
||||
|
||||
const int numPolyFlags = V_ARRAYSIZE(g_navMeshPolyFlagNames);
|
||||
char buttonName[64];
|
||||
|
||||
for (int i = 0; i < numPolyFlags; i++)
|
||||
{
|
||||
snprintf(buttonName, sizeof(buttonName), "%s##%s", g_navMeshPolyFlagNames[i], buttonId);
|
||||
ImGui::CheckboxFlags(buttonName, &flags, i == (numPolyFlags - 1) ? DT_POLYFLAGS_ALL : 1 << i);
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
}
|
||||
|
||||
ShapeVolumeTool::ShapeVolumeTool() :
|
||||
m_selectedVolumeIndex(-1),
|
||||
m_editor(0),
|
||||
m_selectedPrimitive(VOLUME_CONVEX),
|
||||
m_areaType(RC_NULL_AREA),
|
||||
@ -88,10 +110,54 @@ ShapeVolumeTool::ShapeVolumeTool() :
|
||||
m_convexHeight(650.0f),
|
||||
m_convexDescent(150.0f),
|
||||
m_npts(0),
|
||||
m_nhull(0)
|
||||
m_nhull(0),
|
||||
m_copiedShapeIndex(-1)
|
||||
{
|
||||
}
|
||||
|
||||
int ShapeVolumeTool::getVolumeAtPos(const float* p)
|
||||
{
|
||||
InputGeom* geom = m_editor->getInputGeom();
|
||||
rdAssert(geom);
|
||||
|
||||
// Delete
|
||||
int nearestIndex = -1;
|
||||
const ShapeVolume* vols = geom->getConvexVolumes();
|
||||
|
||||
for (int i = 0; i < geom->getConvexVolumeCount(); ++i)
|
||||
{
|
||||
const ShapeVolume& vol = vols[i];
|
||||
|
||||
if (vol.type == VOLUME_BOX)
|
||||
{
|
||||
if (rdPointInAABB(p, &vol.verts[0], &vol.verts[3]))
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
else if (vol.type == VOLUME_CYLINDER)
|
||||
{
|
||||
if (rdPointInCylinder(p, &vol.verts[0], vol.verts[3], vol.verts[4]))
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
else if (vol.type == VOLUME_CONVEX)
|
||||
{
|
||||
if (rdPointInPolygon(p, vol.verts, vol.nverts) &&
|
||||
p[2] >= vol.hmin && p[2] <= vol.hmax)
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestIndex != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
return nearestIndex;
|
||||
}
|
||||
|
||||
void ShapeVolumeTool::init(Editor* editor)
|
||||
{
|
||||
m_editor = editor;
|
||||
@ -111,7 +177,9 @@ static const char* s_primitiveNames[] = {
|
||||
|
||||
void ShapeVolumeTool::handleMenu()
|
||||
{
|
||||
if (ImGui::BeginCombo("Primitive", s_primitiveNames[m_selectedPrimitive]))
|
||||
ImGui::Text("Create Shape");
|
||||
|
||||
if (ImGui::BeginCombo("Primitive##ShapeVolumeCreate", s_primitiveNames[m_selectedPrimitive]))
|
||||
{
|
||||
for (int i = 0; i < V_ARRAYSIZE(s_primitiveNames); i++)
|
||||
{
|
||||
@ -132,22 +200,28 @@ void ShapeVolumeTool::handleMenu()
|
||||
switch (m_selectedPrimitive)
|
||||
{
|
||||
case VOLUME_BOX:
|
||||
ImGui::SliderFloat("Box Descent", &m_boxDescent, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Box Ascent", &m_boxAscent, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Descent##ShapeVolumeCreate", &m_boxDescent, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Ascent##ShapeVolumeCreate", &m_boxAscent, 0.1f, 4000);
|
||||
break;
|
||||
case VOLUME_CYLINDER:
|
||||
ImGui::SliderFloat("Cylinder Radius", &m_cylinderRadius, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Cylinder Height", &m_cylinderHeight, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Radius##ShapeVolumeCreate", &m_cylinderRadius, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Height##ShapeVolumeCreate", &m_cylinderHeight, 0.1f, 4000);
|
||||
break;
|
||||
case VOLUME_CONVEX:
|
||||
ImGui::SliderFloat("Convex Height", &m_convexHeight, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Convex Descent", &m_convexDescent, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Convex Offset", &m_convexOffset, 0.0f, 2000);
|
||||
ImGui::SliderFloat("Height##ShapeVolumeCreate", &m_convexHeight, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Descent##ShapeVolumeCreate", &m_convexDescent, 0.1f, 4000);
|
||||
ImGui::SliderFloat("Offset##ShapeVolumeCreate", &m_convexOffset, 0.0f, 2000);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if ((m_npts || m_nhull) && ImGui::Button("Clear Shape##ShapeVolumeCreate"))
|
||||
{
|
||||
m_npts = 0;
|
||||
m_nhull = 0;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("Brushes");
|
||||
@ -155,37 +229,126 @@ void ShapeVolumeTool::handleMenu()
|
||||
|
||||
bool isEnabled = m_areaType == RC_NULL_AREA;
|
||||
|
||||
if (ImGui::Checkbox("Clip", &isEnabled))
|
||||
if (ImGui::Checkbox("Clip##ShapeVolumeCreate", &isEnabled))
|
||||
m_areaType = RC_NULL_AREA;
|
||||
|
||||
isEnabled = m_areaType == DT_POLYAREA_TRIGGER;
|
||||
if (ImGui::Checkbox("Trigger", &isEnabled))
|
||||
m_areaType = DT_POLYAREA_TRIGGER; // todo(amos): also allow setting flags and store this in .gset.
|
||||
if (ImGui::Checkbox("Trigger##ShapeVolumeCreate", &isEnabled))
|
||||
m_areaType = DT_POLYAREA_TRIGGER;
|
||||
|
||||
if (m_areaType == DT_POLYAREA_TRIGGER)
|
||||
{
|
||||
ImGui::Text("Poly Flags");
|
||||
ImGui::Indent();
|
||||
|
||||
const int numPolyFlags = V_ARRAYSIZE(g_navMeshPolyFlagNames);
|
||||
|
||||
for (int i = 0; i < numPolyFlags; i++)
|
||||
{
|
||||
const char* flagName = g_navMeshPolyFlagNames[i];
|
||||
ImGui::CheckboxFlags(flagName, &m_polyFlags, i == (numPolyFlags-1) ? DT_POLYFLAGS_ALL : 1<<i);
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
handleVolumeFlags(m_polyFlags, "ShapeVolumeCreate");
|
||||
}
|
||||
|
||||
ImGui::Unindent();
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("Clear Shape"))
|
||||
InputGeom* geom = m_editor->getInputGeom();
|
||||
|
||||
if (!geom || !isValidVolumeIndex(m_selectedVolumeIndex, geom))
|
||||
return;
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Modify Shape");
|
||||
|
||||
ShapeVolume& vol = geom->getConvexVolumes()[m_selectedVolumeIndex];
|
||||
|
||||
if (m_selectedVolumeIndex != m_copiedShapeIndex)
|
||||
{
|
||||
m_npts = 0;
|
||||
m_nhull = 0;
|
||||
m_shapeCopy = vol;
|
||||
m_copiedShapeIndex = m_selectedVolumeIndex;
|
||||
}
|
||||
|
||||
if (vol.area == DT_POLYAREA_TRIGGER)
|
||||
{
|
||||
int flags = vol.flags;
|
||||
handleVolumeFlags(flags, "ShapeVolumeModify");
|
||||
|
||||
if (flags != vol.flags)
|
||||
vol.flags = (unsigned short)flags;
|
||||
}
|
||||
|
||||
ImGui::PushItemWidth(120.f);
|
||||
|
||||
switch (vol.type)
|
||||
{
|
||||
case VOLUME_BOX:
|
||||
|
||||
ImGui::PushItemWidth(60);
|
||||
ImGui::SliderFloat("##ShapeBoxMinsX", &vol.verts[0], m_shapeCopy.verts[0]-4000, vol.verts[3]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderFloat("##ShapeBoxMinsY", &vol.verts[1], m_shapeCopy.verts[1]-4000, vol.verts[4]);
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderFloat("##ShapeBoxMinsZ", &vol.verts[2], m_shapeCopy.verts[2]-4000, vol.verts[5]);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Mins");
|
||||
|
||||
ImGui::SliderFloat("##ShapeBoxMaxsX", &vol.verts[3], vol.verts[0], m_shapeCopy.verts[3]+4000);
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderFloat("##ShapeBoxMaxsY", &vol.verts[4], vol.verts[1], m_shapeCopy.verts[4]+4000);
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderFloat("##ShapeBoxMaxsZ", &vol.verts[5], vol.verts[2], m_shapeCopy.verts[5]+4000);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Maxs");
|
||||
ImGui::PopItemWidth();
|
||||
break;
|
||||
case VOLUME_CYLINDER:
|
||||
|
||||
ImGui::PushItemWidth(55);
|
||||
ImGui::SliderFloat("##ShapeCylinderX", &vol.verts[0], m_shapeCopy.verts[0]-4000, m_shapeCopy.verts[0]+4000);
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderFloat("##ShapeCylinderY", &vol.verts[1], m_shapeCopy.verts[1]-4000, m_shapeCopy.verts[1]+4000);
|
||||
ImGui::SameLine();
|
||||
ImGui::SliderFloat("##ShapeCylinderZ", &vol.verts[2], m_shapeCopy.verts[2]-4000, m_shapeCopy.verts[2]+4000);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Position");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::SliderFloat("Radius##ShapeVolumeModify", &vol.verts[3], 0.1f, 4000);
|
||||
ImGui::SliderFloat("Height##ShapeVolumeModify", &vol.verts[4], 0.1f, 4000);
|
||||
break;
|
||||
case VOLUME_CONVEX:
|
||||
ImGui::SliderFloat("Descent##ShapeVolumeModify", &vol.hmin, m_shapeCopy.hmin-4000, m_shapeCopy.hmin+4000);
|
||||
ImGui::SliderFloat("Ascent##ShapeVolumeModify", &vol.hmax, m_shapeCopy.hmax-4000, m_shapeCopy.hmax+4000);
|
||||
|
||||
char sliderId[64];
|
||||
|
||||
for (int i = 0; i < vol.nverts; i++)
|
||||
{
|
||||
const int len = snprintf(sliderId, sizeof(sliderId), "##(%d)ShapeConvexX", i);
|
||||
if (len < 0)
|
||||
{
|
||||
rdAssert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PushItemWidth(55);
|
||||
ImGui::SliderFloat(sliderId, &vol.verts[(i*3)+0], m_shapeCopy.verts[(i*3)+0]-4000, m_shapeCopy.verts[(i*3)+0]+4000);
|
||||
ImGui::SameLine();
|
||||
sliderId[len] = 'Y';
|
||||
ImGui::SliderFloat(sliderId, &vol.verts[(i*3)+1], m_shapeCopy.verts[(i*3)+1]-4000, m_shapeCopy.verts[(i*3)+1]+4000);
|
||||
ImGui::SameLine();
|
||||
sliderId[len] = 'Z';
|
||||
ImGui::SliderFloat(sliderId, &vol.verts[(i*3)+2], m_shapeCopy.verts[(i*3)+2]-4000, m_shapeCopy.verts[(i*3)+2]+4000);
|
||||
ImGui::SameLine();
|
||||
|
||||
snprintf(sliderId, sizeof(sliderId), "Vert #%d", i);
|
||||
|
||||
ImGui::Text(sliderId);
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Delete Shape##ShapeVolumeModify"))
|
||||
{
|
||||
geom->deleteConvexVolume(m_selectedVolumeIndex);
|
||||
m_selectedVolumeIndex = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
void ShapeVolumeTool::handleClick(const float* /*s*/, const float* p, bool shift)
|
||||
@ -196,45 +359,7 @@ void ShapeVolumeTool::handleClick(const float* /*s*/, const float* p, bool shift
|
||||
|
||||
if (shift)
|
||||
{
|
||||
// Delete
|
||||
int nearestIndex = -1;
|
||||
const ShapeVolume* vols = geom->getConvexVolumes();
|
||||
|
||||
for (int i = 0; i < geom->getConvexVolumeCount(); ++i)
|
||||
{
|
||||
const ShapeVolume& vol = vols[i];
|
||||
|
||||
if (vol.type == VOLUME_BOX)
|
||||
{
|
||||
if (rdPointInAABB(p, &vol.verts[0], &vol.verts[3]))
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
else if (vol.type == VOLUME_CYLINDER)
|
||||
{
|
||||
if (rdPointInCylinder(p, &vol.verts[0], vol.verts[3], vol.verts[4]))
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
else if (vol.type == VOLUME_CONVEX)
|
||||
{
|
||||
if (rdPointInPolygon(p, vol.verts, vol.nverts) &&
|
||||
p[2] >= vol.hmin && p[2] <= vol.hmax)
|
||||
{
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestIndex != -1)
|
||||
break;
|
||||
}
|
||||
// If end point close enough, delete it.
|
||||
if (nearestIndex != -1)
|
||||
{
|
||||
geom->deleteConvexVolume(nearestIndex);
|
||||
}
|
||||
m_selectedVolumeIndex = getVolumeAtPos(p);
|
||||
}
|
||||
else // Create
|
||||
{
|
||||
|
@ -24,7 +24,8 @@
|
||||
|
||||
enum VolumeType : unsigned char
|
||||
{
|
||||
VOLUME_BOX,
|
||||
VOLUME_INVALID = 0xff,
|
||||
VOLUME_BOX = 0,
|
||||
VOLUME_CYLINDER,
|
||||
VOLUME_CONVEX
|
||||
};
|
||||
@ -40,6 +41,20 @@ enum TraceMask : unsigned int
|
||||
static const int MAX_SHAPEVOL_PTS = 12;
|
||||
struct ShapeVolume
|
||||
{
|
||||
ShapeVolume()
|
||||
{
|
||||
for (int i = 0; i < MAX_SHAPEVOL_PTS; i++)
|
||||
{
|
||||
rdVset(&verts[i*3], 0.f,0.f,0.f);
|
||||
}
|
||||
hmin = 0.f;
|
||||
hmax = 0.f;
|
||||
nverts = 0;
|
||||
flags = 0;
|
||||
area = 0;
|
||||
type = VOLUME_INVALID;
|
||||
}
|
||||
|
||||
float verts[MAX_SHAPEVOL_PTS*3];
|
||||
float hmin, hmax;
|
||||
int nverts;
|
||||
@ -175,7 +190,7 @@ public:
|
||||
/// @name Shape Volumes.
|
||||
///@{
|
||||
int getConvexVolumeCount() const { return m_volumeCount; } // todo(amos): rename to 'getShapeVolumeCount'
|
||||
const ShapeVolume* getConvexVolumes() const { return m_volumes; } // todo(amos): rename to 'getShapeVolumes'
|
||||
ShapeVolume* getConvexVolumes() { return m_volumes; } // todo(amos): rename to 'getShapeVolumes'
|
||||
void addBoxVolume(const float* bmin, const float* bmax,
|
||||
unsigned short flags, unsigned char area);
|
||||
void addCylinderVolume(const float* pos, const float radius,
|
||||
|
@ -27,7 +27,10 @@
|
||||
class ShapeVolumeTool : public EditorTool
|
||||
{
|
||||
Editor* m_editor;
|
||||
|
||||
int m_selectedVolumeIndex;
|
||||
int m_selectedPrimitive;
|
||||
|
||||
int m_areaType;
|
||||
int m_polyFlags;
|
||||
|
||||
@ -45,10 +48,16 @@ class ShapeVolumeTool : public EditorTool
|
||||
int m_npts;
|
||||
int m_hull[MAX_SHAPEVOL_PTS];
|
||||
int m_nhull;
|
||||
|
||||
ShapeVolume m_shapeCopy;
|
||||
int m_copiedShapeIndex;
|
||||
|
||||
public:
|
||||
ShapeVolumeTool();
|
||||
|
||||
int getVolumeAtPos(const float* p);
|
||||
inline int getSelectedVolumeIndex() const { return m_selectedVolumeIndex; };
|
||||
|
||||
virtual int type() { return TOOL_SHAPE_VOLUME; }
|
||||
virtual void init(Editor* editor);
|
||||
virtual void reset();
|
||||
|
Loading…
x
Reference in New Issue
Block a user