Added Imgui AF editor by Stephen Pridham

This commit is contained in:
Robert Beckebans 2022-09-15 11:03:22 +02:00
parent f4e84b50db
commit 25b7680cfb
15 changed files with 2164 additions and 18 deletions

1
.gitignore vendored
View file

@ -35,6 +35,7 @@ base/sound/
base/strings/
base/video/
base/wads/
base/renderprogs2/
GPATH
GRTAGS

View file

@ -69,7 +69,9 @@ option(USE_DX12
"Use DirectX 12" ON)
option(USE_NVRHI_VULKAN
"Use Vulkan" OFF)
"Use Vulkan" ON)
set(NVRHI_INSTALL OFF)
set(CPU_TYPE "" CACHE STRING "When set, passes this string as CPU-ID which will be embedded into the binary.")
@ -203,7 +205,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
add_compile_options(-fno-strict-aliasing)
# SRS - Make sure OSX can find system headers and add support for minimum OSX runtime version
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(APPLE)
# SRS - Also add -fasm-blocks otherwise Xcode complains and -Qunused-arguments to silence MMX/SSE unused arg warnings when compiling for Apple arm64
add_definitions(-fasm-blocks -Qunused-arguments)
if(CMAKE_OSX_SYSROOT)
@ -368,7 +370,7 @@ if(USE_VULKAN)
# which causes all kinds of weird segmentation faults because struct sizes don't match
# SRS - Set default VULKAN_SDK location if environment variable not defined on OSX
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT DEFINED ENV{VULKAN_SDK})
if(APPLE AND NOT DEFINED ENV{VULKAN_SDK})
if(NOT USE_MoltenVK)
# SRS - Vulkan SDK installer copies standard vulkan headers and libs to /usr/local on OSX
set(ENV{VULKAN_SDK} /usr/local)
@ -425,7 +427,7 @@ if(USE_VULKAN)
add_definitions(-DUSE_VULKAN)
include_directories($ENV{VULKAN_SDK}/include)
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(APPLE)
# SRS - Enable Beta extensions for VULKAN_SDK portability subset features on OSX
add_definitions(-DVK_ENABLE_BETA_EXTENSIONS)
# SRS - Optionally use MoltenVK headers/library for runtime config functions on OSX
@ -450,7 +452,7 @@ elseif(USE_NVRHI_VULKAN)
# which causes all kinds of weird segmentation faults because struct sizes don't match
# SRS - Set default VULKAN_SDK location if environment variable not defined on OSX
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT DEFINED ENV{VULKAN_SDK})
if(APPLE AND NOT DEFINED ENV{VULKAN_SDK})
if(NOT USE_MoltenVK)
# SRS - Vulkan SDK installer copies standard vulkan headers and libs to /usr/local on OSX
set(ENV{VULKAN_SDK} /usr/local)
@ -465,6 +467,37 @@ elseif(USE_NVRHI_VULKAN)
find_package(Vulkan)
endif()
if(NOT Vulkan_FOUND)
message(FATAL_ERROR "Could not find Vulkan library!")
else()
include_directories($ENV{VULKAN_SDK}/include)
if(APPLE)
# SRS - Enable Beta extensions for VULKAN_SDK portability subset features on OSX
add_definitions(-DVK_ENABLE_BETA_EXTENSIONS)
# SRS - Optionally use MoltenVK headers/library for runtime config functions on OSX
if(USE_MoltenVK)
add_definitions(-DUSE_MoltenVK)
include_directories($ENV{VULKAN_SDK}/../MoltenVK/include)
set(Vulkan_LIBRARY $ENV{VULKAN_SDK}/../MoltenVK/dylib/macOS/libMoltenVK.dylib CACHE FILEPATH "Path to MoltenVK library." FORCE)
endif()
endif()
message(STATUS "Using Vulkan: " ${Vulkan_LIBRARY})
endif()
# Eric: For use with SDL2/Vulkan
if(UNIX)
# SRS - For UNIX-like target OSs use the generic SDL Vulkan platform
add_definitions(-DUSE_NVRHI_SDL)
elseif(WIN32)
add_definitions(-DVK_USE_PLATFORM_WIN32_KHR)
endif()
# Use FindVulkan module added with CMAKE 3.7
if(NOT CMAKE_VERSION VERSION_LESS 3.7.0)
message( STATUS "Using module to find Vulkan" )
find_package(Vulkan)
endif()
if(NOT Vulkan_FOUND)
message(FATAL_ERROR "Could not find Vulkan library!")
else()
@ -1172,7 +1205,7 @@ file(GLOB SDL_SOURCES sys/sdl/*.cpp)
# Eric: Utilize either the Vulkan or GL implementation of SDL
if(UNIX)
if(USE_VULKAN)
if(USE_VULKAN OR USE_NVRHI)
get_filename_component(sdl_glimp_cpp_full_path ${CMAKE_CURRENT_SOURCE_DIR}/sys/sdl/sdl_glimp.cpp ABSOLUTE)
list(REMOVE_ITEM SDL_SOURCES "${sdl_glimp_cpp_full_path}")
else()
@ -1520,6 +1553,19 @@ set(RBDOOM3_SOURCES
${GAMED3XP_SCRIPT_SOURCES}
)
macro(AddSubdirectory dirName)
file(GLOB DIR_INCLUDES ${dirName}/*.h)
file(GLOB DIR_SOURCES ${dirName}/*.cpp)
source_group(${dirName} FILES ${DIR_INCLUDES})
source_group(${dirName} FILES ${DIR_SOURCES})
list(APPEND RBDOOM3_INCLUDES ${DIR_INCLUDES})
list(APPEND RBDOOM3_SOURCES ${DIR_SOURCES})
endmacro()
AddSubdirectory( tools/imgui/afeditor )
if(DOOM_CLASSIC)
add_definitions(-DUSE_DOOMCLASSIC)
@ -1782,7 +1828,7 @@ else()
set(SDLx_LIBRARY ${SDL_LIBRARY})
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(APPLE)
list(REMOVE_ITEM POSIX_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sys/posix/platform_linux.cpp)
else()
list(REMOVE_ITEM POSIX_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/sys/posix/platform_osx.cpp)
@ -1797,7 +1843,7 @@ else()
add_definitions(-DUSE_OPENAL)
# SRS - Added support for OpenAL Soft headers on OSX (vs default macOS SDK headers)
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT OPENAL_INCLUDE_DIR MATCHES "SDKs/MacOSX.*\.sdk")
if(APPLE AND NOT OPENAL_INCLUDE_DIR MATCHES "SDKs/MacOSX.*\.sdk")
include_directories(${OPENAL_INCLUDE_DIR})
add_definitions(-DUSE_OPENAL_SOFT_INCLUDES)
endif()
@ -1834,7 +1880,13 @@ else()
list(APPEND Vulkan_LIBRARIES glslang-default-resource-limits)
endif()
endif()
elseif(USE_NVRHI)
list(APPEND RBDOOM3_INCLUDES ${RENDERER_NVRHI_INCLUDES})
list(APPEND RBDOOM3_SOURCES ${RENDERER_NVRHI_SOURCES})
set(OpenGL_LIBRARIES
opengl32
glu32)
else()
find_package(OpenGL REQUIRED)
include_directories(${OPENGL_INCLUDE_DIRS})
@ -1915,6 +1967,9 @@ else()
add_executable(RBDoom3BFG WIN32 ${RBDOOM3_SOURCES})
if(USE_NVRHI_VULKAN)
add_dependencies(RBDoom3BFG nvrhi_vk)
endif()
if (USE_PRECOMPILED_HEADERS)
# make sure precompiled header is created before executable is compiled
add_dependencies(RBDoom3BFG precomp_header_rbdoom3bfg)
@ -1924,7 +1979,7 @@ else()
endif()
if(NOT WIN32)
if(NOT "${CMAKE_SYSTEM}" MATCHES "Darwin")
if(NOT APPLE)
set(RT_LIBRARY rt)
endif()
@ -1946,12 +2001,6 @@ else()
endif()
endif()
if(USE_DX11)
#target_sources(RBDoom3BFG PRIVATE src/app/dx11/DeviceManager_DX11.cpp)
target_compile_definitions(RBDoom3BFG PUBLIC USE_DX11=1)
target_link_libraries(RBDoom3BFG nvrhi_d3d11)
endif()
if(USE_DX12)
target_compile_definitions(RBDoom3BFG PUBLIC USE_DX12=1)
target_link_libraries(RBDoom3BFG nvrhi_d3d12)

View file

@ -31,7 +31,7 @@ If you have questions concerning this license or the applicable additional terms
// *INDENT-OFF*
#if USE_GPU_SKINNING
StructuredBuffer<float4> matrices : register(t11);
StructuredBuffer<float4> matrices : register( t11 );
#endif
struct VS_IN

View file

@ -31,8 +31,8 @@ If you have questions concerning this license or the applicable additional terms
// *INDENT-OFF*
Texture2D t_CurrentRender : register( t0 VK_DESCRIPTOR_SET( 0 ) );
Texture2D t_HeatMap : register( t1 VK_DESCRIPTOR_SET( 0 ) );
Texture2D t_CurrentRender : register( t0 VK_DESCRIPTOR_SET( 0 ) );
Texture2D t_HeatMap : register( t1 VK_DESCRIPTOR_SET( 0 ) );
SamplerState samp0 : register( s0 VK_DESCRIPTOR_SET( 1 ) ); // texture 0 is _currentRender
SamplerState samp1 : register( s1 VK_DESCRIPTOR_SET( 1 ) ); // texture 1 is heatmap

View file

@ -157,6 +157,8 @@ void DrawToolWindows();
void LightEditorInit( const idDict* dict, idEntity* entity );
void AfEditorInit();
}
#endif /* !__EDIT_PUBLIC_H__ */

View file

@ -0,0 +1,356 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "AfBodyEditor.h"
#include "../imgui/BFGimgui.h"
static const char* bodyTypeNames[] =
{
"none",
"box",
"octahedron",
"dedecahedron",
"cylinder",
"cone",
"bone",
"polygon",
"polygonvolume",
"custom"
};
static const char* modifyJointsNames[] =
{
"axis",
"origin",
"both"
};
static const char* afVecTypeNames[] =
{
"coordinates",
"joint",
"bone center",
};
static bool ModelTypeItemGetter( void* data, int index, const char** out );
namespace ImGuiTools
{
AfBodyEditor::AfBodyEditor( idDeclAF* newDecl, idDeclAF_Body* newBody )
: decl( newDecl )
, body( newBody )
, positionType( 0 )
, modifyJointType( 0 )
, comboJoint1( 0 )
, comboJoint2( 0 )
, originBoneCenterJoint1( 0 )
, originBoneCenterJoint2( 0 )
, originJoint( 0 )
, contentWidget( MakePhysicsContentsSelector() )
{
InitJointLists();
}
AfBodyEditor::~AfBodyEditor()
{
}
bool AfBodyEditor::Do()
{
ImGui::PushID( body->name );
bool changed = false;
ImGui::InputText( "##rename", &renameBody );
ImGui::SameLine();
if( ImGui::Button( "Rename" ) )
{
if( renameBody.Allocated() > 0 )
{
decl->RenameBody( body->name, renameBody.c_str() );
renameBody.Clear();
}
changed = true;
}
if( ImGui::CollapsingHeader( "Collision Model" ) )
{
changed = CollisionModel() || changed;
}
if( ImGui::CollapsingHeader( "Position" ) )
{
changed = Position() || changed;
}
if( ImGui::CollapsingHeader( "Collision Detection" ) )
{
changed = ImGui::Checkbox( "Self Collision", &body->selfCollision ) || changed;
changed = DoMultiSelect( &contentWidget, &body->contents ) || changed;
}
if( ImGui::CollapsingHeader( "Friction" ) )
{
changed = ImGui::DragFloat( "Linear", &body->linearFriction ) || changed;
changed = ImGui::DragFloat( "Angular", &body->angularFriction ) || changed;
changed = ImGui::DragFloat( "Contact", &body->contactFriction ) || changed;
changed = InputAfVector( "Friction Direction", &body->frictionDirection ) || changed;
changed = InputAfVector( "Motor Direction", &body->contactMotorDirection ) || changed;
}
if( ImGui::CollapsingHeader( "Joints" ) )
{
if( ImGui::Combo( "Modified Joint", &comboJoint1, StringListItemGetter, &joints, joints.Num() ) )
{
body->jointName = joints[comboJoint1];
changed = true;
}
changed = ImGui::Combo( "Modify", ( int* )&body->jointMod, modifyJointsNames, ARRAY_COUNT( modifyJointsNames ) ) || changed;
changed = ImGui::InputText( "Contained Joints", &body->containedJoints ) || changed;
}
ImGui::PopID();
return changed;
}
void AfBodyEditor::InitJointLists()
{
const idRenderModel* model = gameEdit->ANIM_GetModelFromName( decl->model.c_str() );
if( !model )
{
return;
}
const int numJoints = model->NumJoints();
for( int i = 0; i < numJoints; i++ )
{
const char* jointName = model->GetJointName( ( jointHandle_t )i );
joints[joints.Append( jointName )].ToLower();
}
}
bool AfBodyEditor::Position()
{
bool changed = false;
ImGui::Columns( 2, "positionColumns2" );
changed = ImGui::Combo( "Type", ( int* )&body->origin.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed;
ImGui::NextColumn();
changed = PositionProperty() || changed;
ImGui::NextColumn();
changed = PitchYawRoll() || changed;
ImGui::Columns( 1 );
return changed;
}
bool AfBodyEditor::PositionProperty()
{
bool changed = false;
idAFVector* afVec = &body->origin;
switch( afVec->type )
{
case idAFVector::VEC_COORDS:
changed = ImGui::DragFloat3( "Coordinates", ( float* )&afVec->ToVec3() ) || changed;
break;
case idAFVector::VEC_JOINT:
if( ImGui::BeginCombo( "Joint", afVec->joint1.c_str() ) )
{
for( int n = 0; n < joints.Num(); n++ )
{
bool is_selected = ( afVec->joint1 == joints[n] );
if( ImGui::Selectable( joints[n], is_selected ) )
{
changed = true;
afVec->joint1 = joints[n];
}
if( is_selected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
break;
case idAFVector::VEC_BONECENTER:
case idAFVector::VEC_BONEDIR:
if( ImGui::BeginCombo( "Joint 1", afVec->joint1.c_str() ) )
{
for( int n = 0; n < joints.Num(); n++ )
{
bool is_selected = ( afVec->joint1 == joints[n] );
if( ImGui::Selectable( joints[n], is_selected ) )
{
changed = true;
afVec->joint1 = joints[n];
}
if( is_selected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
if( ImGui::BeginCombo( "Joint 2", afVec->joint2.c_str() ) )
{
for( int n = 0; n < joints.Num(); n++ )
{
bool is_selected = ( afVec->joint2 == joints[n] );
if( ImGui::Selectable( joints[n], is_selected ) )
{
changed = true;
afVec->joint2 = joints[n];
}
if( is_selected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
break;
default:
ImGui::Text( "*Unknown Type*" );
}
return changed;
}
bool AfBodyEditor::PitchYawRoll()
{
bool changed = false;
const ImGuiStyle& style = ImGui::GetStyle();
const float w_full = ImGui::CalcItemWidth();
const float square_sz = ImGui::GetFrameHeight();
const float w_button = square_sz + style.ItemInnerSpacing.x;
const float w_inputs = w_full - w_button;
const float widthItemOne = Max( 1.0f, ( float )( int )( ( w_inputs - ( style.ItemInnerSpacing.x ) * 2 ) / 2.0f ) );
const float widthItemLast = Max( 1.0f, ( float )( int )( w_inputs - ( widthItemOne + style.ItemInnerSpacing.x ) ) );
ImGui::SetNextItemWidth( widthItemOne );
changed = ImGui::DragFloat( "##p", &body->angles.pitch, 1.0f, 0.0f, 360.0f, "pitch: %.3f" ) || changed;
ImGui::SameLine();
ImGui::SetNextItemWidth( widthItemOne );
changed = ImGui::DragFloat( "##y", &body->angles.yaw, 1.0f, 0.0f, 360.0f, "yaw: %.3f" ) || changed;
ImGui::SameLine();
ImGui::SetNextItemWidth( widthItemLast );
changed = ImGui::DragFloat( "##r", &body->angles.roll, 1.0f, 0.0f, 360.0f, "roll: %.3f" );
return changed;
}
bool AfBodyEditor::CollisionModel()
{
bool changed = false;
ImGui::PushID( "CollisionModel" );
ImGui::Columns( 2, "collisonColumns2" );
changed = ImGui::Combo( "Model Type", &body->modelType, ModelTypeItemGetter, nullptr, ARRAY_COUNT( bodyTypeNames ) ) || changed;
changed = ImGui::DragFloat( "Density", &body->density ) || changed;
ImGui::NextColumn();
changed = CollisionModelSize() || changed;
ImGui::Columns( 1 );
ImGui::PopID();
return changed;
}
bool AfBodyEditor::CollisionModelSize()
{
bool changed = false;
switch( body->modelType )
{
case TRM_INVALID:
ImGui::Text( "Provide a valid model type." );
break;
case TRM_BOX:
case TRM_OCTAHEDRON:
case TRM_DODECAHEDRON:
changed = InputAfVector( "min", &body->v1 ) || changed;
changed = InputAfVector( "max", &body->v2 ) || changed;
ImGui::NewLine();
break;
case TRM_CYLINDER:
case TRM_CONE:
changed = InputAfVector( "min", &body->v1 ) || changed;
changed = InputAfVector( "max", &body->v2 ) || changed;
changed = ImGui::InputInt( "Sides", &body->numSides ) || changed;
break;
case TRM_BONE:
changed = InputAfVector( "min", &body->v1 ) || changed;
changed = InputAfVector( "max", &body->v2 ) || changed;
changed = ImGui::DragFloat( "Width", &body->width ) || changed;
break;
case TRM_POLYGON:
ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Polygon models are not supported :(" );
break;
case TRM_POLYGONVOLUME:
ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Polygon Volume models are not supported :(" );
break;
case TRM_CUSTOM:
ImGui::TextColored( ImVec4( 1, 0, 0, 1 ), "Custom models are not supported :(" );
break;
}
return changed;
}
bool AfBodyEditor::InputAfVector( const char* label, idAFVector* vec )
{
return ImGui::DragFloat3( label, ( float* )&vec->ToVec3() );
}
}
static bool ModelTypeItemGetter( void* data, int index, const char** out )
{
if( index < 0 || index >= ARRAY_COUNT( bodyTypeNames ) )
{
return false;
}
*out = bodyTypeNames[index];
return true;
}

View file

@ -0,0 +1,72 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma once
#include "../util/Imgui_IdWidgets.h"
namespace ImGuiTools
{
class AfBodyEditor
{
public:
AfBodyEditor( idDeclAF* newDecl, idDeclAF_Body* newBody );
~AfBodyEditor();
bool Do();
private:
void InitJointLists();
bool Position();
bool PositionProperty();
bool PitchYawRoll();
bool CollisionModel();
bool CollisionModelSize();
bool InputAfVector( const char* label, idAFVector* vec );
idDeclAF* decl;
idDeclAF_Body* body;
int positionType;
int modifyJointType;
int comboJoint1;
int comboJoint2;
int originBoneCenterJoint1;
int originBoneCenterJoint2;
int originJoint;
MultiSelectWidget contentWidget;
idStr modifiedJoint;
idStr renameBody;
idStrList joints;
};
}

View file

@ -0,0 +1,409 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "AfConstraintEditor.h"
#include "../imgui/BFGimgui.h"
static const char* constraintTypeNames[] =
{
"Invalid",
"Fixed",
"Ball and Socket Joint",
"Universal Joint",
"Hinge",
"Slider",
"Spring"
};
static const char* anchorTypeNames[] =
{
"joint",
"coorindates"
};
static const char* jointPointTypeNames[] =
{
"bone",
"angles"
};
static const char* afVecTypeNames[] =
{
"coordinates",
"joint",
"bone center",
"bone direction"
};
static const char* constraintLimitTypeNames[] =
{
"none",
"cone",
"pyramid"
};
namespace ImGuiTools
{
static bool BodyItemGetter( void* data, int index, const char** out );
static bool ConstraintLimitTypeGetter( void* data, int index, const char** out );
AfConstraintEditor::AfConstraintEditor( idDeclAF* newDecl, idDeclAF_Constraint* newConstraint )
: decl( newDecl )
, constraint( newConstraint )
, anchorType( 0 )
, shaft1Type( 0 )
, shaft2Type( 0 )
, limitOrientationType( 0 )
, body1( 0 )
, body2( 0 )
, jointAnchor1( 0 )
, jointAnchor2( 0 )
, jointShaft1( 0 )
{
InitJointLists();
}
AfConstraintEditor::~AfConstraintEditor()
{
}
bool AfConstraintEditor::Do()
{
ImGui::PushID( constraint->name.c_str() );
bool changed = false;
ImGui::InputText( "##rename", &renameConstraint );
ImGui::SameLine();
if( ImGui::Button( "Rename" ) )
{
if( renameConstraint.Allocated() > 0 )
{
decl->RenameConstraint( constraint->name, renameConstraint.c_str() );
renameConstraint.Clear();
}
changed = true;
}
if( ImGui::CollapsingHeader( "General" ) )
{
ImGui::Columns( 2, "constraintGeneral2" );
changed = ImGui::Combo( "Type##General", ( int* )&constraint->type, constraintTypeNames, ARRAY_COUNT( constraintTypeNames ) ) || changed;
changed = ImGui::DragFloat( "Friction##Friction", &constraint->friction ) || changed;
if( constraint->type == DECLAF_CONSTRAINT_SPRING )
{
changed = ImGui::DragFloat( "Stretch", &constraint->stretch ) || changed;
ImGui::SameLine();
HelpMarker( "Spring constant when the spring is stretched" );
changed = ImGui::DragFloat( "Compress", &constraint->compress, 0.001f, 0.0f, 1.0f ) || changed;
ImGui::SameLine();
HelpMarker( "Spring constant when the spring is compressed" );
changed = ImGui::DragFloat( "Damping", &constraint->damping, 0.001f, 0.0f, 1.0f ) || changed;
ImGui::SameLine();
HelpMarker( "Spring damping" );
changed = ImGui::DragFloat( "Length", &constraint->restLength, 0.001f, 0.0f, 1.0f ) || changed;
ImGui::SameLine();
HelpMarker( "Rest length of the spring" );
changed = ImGui::DragFloat( "Min Length", &constraint->minLength, 0.001f, 0.0f, 1.0f ) || changed;
ImGui::SameLine();
HelpMarker( "Minimum length of the spring" );
changed = ImGui::DragFloat( "Max Length", &constraint->maxLength, 0.001f, 0.0f, 1.0f ) || changed;
ImGui::SameLine();
HelpMarker( "Maximum length of the spring" );
}
ImGui::NextColumn();
if( ImGui::BeginCombo( "Body 1", constraint->body1.c_str() ) )
{
for( int n = 0; n < decl->bodies.Num(); n++ )
{
bool isSelected = ( constraint->body1 == decl->bodies[n]->name );
if( ImGui::Selectable( decl->bodies[n]->name, isSelected ) )
{
constraint->body1 = decl->bodies[n]->name;
changed = true;
}
if( isSelected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
if( ImGui::BeginCombo( "Body 2", constraint->body2.c_str() ) )
{
for( int n = 0; n < decl->bodies.Num(); n++ )
{
bool isSelected = ( constraint->body2 == decl->bodies[n]->name );
if( ImGui::Selectable( decl->bodies[n]->name, isSelected ) )
{
constraint->body2 = decl->bodies[n]->name;
changed = true;
}
if( isSelected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
ImGui::Columns( 1 );
}
if( ImGui::CollapsingHeader( "Anchor" ) )
{
ImGui::PushID( "Anchor1" );
ImGui::Columns( 2, "anchorColumn2" );
changed = ImGui::Combo( "Type##anchor", ( int* )&constraint->anchor.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed;
ImGui::NextColumn();
changed = InputAfVector( &constraint->anchor ) || changed;
ImGui::Columns( 1 );
ImGui::PopID();
}
if( constraint->type == DECLAF_CONSTRAINT_SPRING )
{
if( ImGui::CollapsingHeader( "Anchor2" ) )
{
ImGui::PushID( "Anchor2" );
ImGui::Columns( 2, "anchorColumn2" );
changed = ImGui::Combo( "Type##anchor", ( int* )&constraint->anchor2.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed;
ImGui::NextColumn();
changed = InputAfVector( &constraint->anchor2 ) || changed;
ImGui::Columns( 1 );
ImGui::PopID();
}
}
if( constraint->type == DECLAF_CONSTRAINT_UNIVERSALJOINT )
{
if( ImGui::CollapsingHeader( "Shaft 1" ) )
{
ImGui::PushID( "Shaft 1" );
ImGui::Columns( 2, "shaft12" );
changed = ImGui::Combo( "Type", ( int* )&constraint->shaft[0].type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed;
ImGui::NextColumn();
changed = InputAfVector( &constraint->shaft[0] ) || changed;
ImGui::Columns( 1 );
ImGui::PopID();
}
if( ImGui::CollapsingHeader( "Shaft 2" ) )
{
ImGui::PushID( "Shaft 2" );
ImGui::Columns( 2, "shaft22" );
changed = ImGui::Combo( "Type", ( int* )&constraint->shaft[1].type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed;
ImGui::NextColumn();
changed = InputAfVector( &constraint->shaft[1] ) || changed;
ImGui::Columns( 1 );
ImGui::PopID();
}
}
if( constraint->type == DECLAF_CONSTRAINT_HINGE )
{
if( ImGui::CollapsingHeader( "Limit Orientation" ) )
{
ImGui::DragFloat3( "Limit Angles", &constraint->limitAngles[0] );
ImGui::SameLine();
HelpMarker( "Specifies a V-shaped limit."
"\nThe first angle specifies the center of the V-shaped limit."
"\nThe angle which specifies the width of the V-shaped limit follows."
"\nThe V-shape is attached to body2. Next a shaft is specified which"
"\nis attached to body1 and is constrained to always stay within the V-shape."
"\nThe orientation of this shaft is specified with the third angle." );
}
}
else
{
if( ImGui::CollapsingHeader( "Limit Type" ) )
{
ImGui::PushID( "LimitType" );
ImGui::Columns( 2, "limit2" );
changed = ImGui::Combo( "Type", ( int* )&constraint->limit, ConstraintLimitTypeGetter, nullptr, ARRAY_COUNT( constraintLimitTypeNames ) - 1 ) || changed;
ImGui::NextColumn();
switch( constraint->limit )
{
case idDeclAF_Constraint::LIMIT_NONE:
break;
case idDeclAF_Constraint::LIMIT_CONE:
changed = ImGui::DragFloat3( "Cone##Limit", ( float* )&constraint->limitAngles[0], 0, -359.0f, 360.0f ) || changed;
break;
case idDeclAF_Constraint::LIMIT_PYRAMID:
changed = ImGui::DragFloat3( "Pyramid##Limit", ( float* )&constraint->limitAngles[0], 0, -359.0f, 360.0f ) || changed;
break;
}
ImGui::Columns( 1 );
ImGui::PopID();
}
if( ImGui::CollapsingHeader( "Limit Orientation" ) )
{
ImGui::PushID( "LimitO" );
ImGui::Columns( 2, "limitOrientation2" );
changed = ImGui::Combo( "Type", ( int* )&constraint->limitAxis.type, afVecTypeNames, ARRAY_COUNT( afVecTypeNames ) ) || changed;
ImGui::NextColumn();
changed = InputAfVector( &constraint->limitAxis ) || changed;
ImGui::Columns( 1 );
ImGui::PopID();
}
}
ImGui::PopID();
return changed;
}
bool AfConstraintEditor::InputAfVector( idAFVector* afVec )
{
bool changed = false;
switch( afVec->type )
{
case idAFVector::VEC_COORDS:
changed = changed || ImGui::DragFloat3( "Coordinates", ( float* )&afVec->ToVec3() );
break;
case idAFVector::VEC_JOINT:
if( ImGui::BeginCombo( "Joint", afVec->joint1.c_str() ) )
{
for( int n = 0; n < joints.Num(); n++ )
{
bool is_selected = ( afVec->joint1 == joints[n] );
if( ImGui::Selectable( joints[n], is_selected ) )
{
afVec->joint1 = joints[n];
changed = true;
}
if( is_selected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
break;
case idAFVector::VEC_BONECENTER:
case idAFVector::VEC_BONEDIR:
if( ImGui::BeginCombo( "Joint 1", afVec->joint1.c_str() ) )
{
for( int n = 0; n < joints.Num(); n++ )
{
bool is_selected = ( afVec->joint1 == joints[n] );
if( ImGui::Selectable( joints[n], is_selected ) )
{
afVec->joint1 = joints[n];
changed = true;
}
if( is_selected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
if( ImGui::BeginCombo( "Joint 2", afVec->joint2.c_str() ) )
{
for( int n = 0; n < joints.Num(); n++ )
{
bool is_selected = ( afVec->joint2 == joints[n] );
if( ImGui::Selectable( joints[n], is_selected ) )
{
afVec->joint2 = joints[n];
changed = true;
}
if( is_selected )
{
ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch)
}
}
ImGui::EndCombo();
}
break;
default:
ImGui::Text( "*Unknown Type*" );
}
return changed;
}
void AfConstraintEditor::InitJointLists()
{
const idRenderModel* model = gameEdit->ANIM_GetModelFromName( decl->model.c_str() );
if( !model )
{
return;
}
joints.Clear();
const int numJoints = model->NumJoints();
for( int i = 0; i < numJoints; i++ )
{
const char* jointName = model->GetJointName( ( jointHandle_t )i );
joints[joints.Append( jointName )].ToLower();
}
}
static bool BodyItemGetter( void* data, int index, const char** out )
{
idDeclAF* decl = reinterpret_cast<idDeclAF*>( data );
if( index < 0 || index >= decl->bodies.Num() )
{
return false;
}
idDeclAF_Body* body = decl->bodies[index];
*out = body->name.c_str();
return true;
}
bool ConstraintLimitTypeGetter( void* data, int index, const char** out )
{
index += 1;
if( index < 0 || index > ARRAY_COUNT( constraintLimitTypeNames ) )
{
*out = constraintLimitTypeNames[0];
return false;
}
*out = constraintLimitTypeNames[index];
return true;
}
}

View file

@ -0,0 +1,65 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma once
#include "../util/Imgui_IdWidgets.h"
namespace ImGuiTools
{
class AfConstraintEditor
{
public:
AfConstraintEditor( idDeclAF* newDecl, idDeclAF_Constraint* newConstraint );
~AfConstraintEditor();
bool Do();
bool InputAfVector( idAFVector* afVec );
void InitJointLists();
private:
idDeclAF* decl;
idDeclAF_Constraint* constraint;
int anchorType;
int shaft1Type;
int shaft2Type;
int limitOrientationType;
int body1;
int body2;
int jointAnchor1;
int jointAnchor2;
int jointShaft1;
idStr renameConstraint;
idStrList joints;
};
}

View file

@ -0,0 +1,679 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "AfEditor.h"
#include "../imgui/BFGimgui.h"
#include "../framework/DeclAF.h"
#include "../util/Imgui_IdWidgets.h"
extern idCVar af_useLinearTime;
extern idCVar af_useImpulseFriction;
extern idCVar af_useJointImpulseFriction;
extern idCVar af_useSymmetry;
extern idCVar af_skipSelfCollision;
extern idCVar af_skipLimits;
extern idCVar af_skipFriction;
extern idCVar af_forceFriction;
extern idCVar af_maxLinearVelocity;
extern idCVar af_showTimings;
extern idCVar af_showConstraints;
extern idCVar af_showConstraintNames;
extern idCVar af_showConstrainedBodies;
extern idCVar af_showPrimaryOnly;
extern idCVar af_showTrees;
extern idCVar af_showLimits;
extern idCVar af_showBodies;
extern idCVar af_showBodyNames;
extern idCVar af_showMass;
extern idCVar af_showTotalMass;
extern idCVar af_showInertia;
extern idCVar af_showVelocity;
extern idCVar af_showActive;
extern idCVar af_testSolid;
namespace
{
static bool isShown = false;
static idDeclAF emptyDecl;
static idDeclAF_Body emptyBody;
static idDeclAF_Constraint emptyConstraint;
static int linearTolerance = 10;
static int angularTolerance = 10;
static idStrStatic<256> currentBody;
static int bodyLength = 8;
static int bodyWidth = 16;
static int bodyHeight = 20;
static const char* bodyTypeNames[] =
{
"box",
"octahedron",
"dedecahedron",
"cylinder",
"cone",
"bone",
"polygon",
"polygonvolume",
"custom"
};
int positionType = 0;
static const char* positionTypeNames[3] =
{
"coordinates",
"bone center",
"joint"
};
bool bodyContentsSelected[5] =
{
false,
false,
false,
false,
};
MultiSelectWidget w1 = MakePhysicsContentsSelector();
MultiSelectWidget w2 = MakePhysicsContentsSelector();
int modifyJointType = 0;
static const char* modifyJointsNames[3] =
{
"orientation",
"position",
"both"
};
int constraintType = 2;
static const char* constraintTypeNames[DECLAF_CONSTRAINT_SPRING] =
{
"fixed",
"ballandsocket",
"universal",
"hinge",
"slider",
"spring"
};
int constraintLimitType = 1;
static const char* constraintLimitTypeNames[3] =
{
"none",
"cone",
"pyramid"
};
int shaft1 = 1;
int shaft2 = 1;
int limitOrientation = 1;
static const char* jointPointTypeNames[2] =
{
"bone",
"angles"
};
int anchorType = 0;
static const char* anchorTypeNames[2] =
{
"joint",
"coorindates"
};
}
namespace ImGuiTools
{
static bool BodyItemGetter( void* data, int index, const char** items );
static bool ConstraintItemGetter( void* data, int index, const char** items );
static bool CVarCheckBox( const char* label, idCVar* cvar );
AfEditor::AfEditor()
: isShown( false )
, fileSelection( 0 )
, currentAf( 0 )
, currentConstraint( 0 )
, currentBodySelection( 0 )
, currentEntity( 0 )
, decl( nullptr )
, body( nullptr )
, constraint( nullptr )
, propertyEditor( nullptr )
{
}
AfEditor::~AfEditor()
{
if( propertyEditor )
{
delete propertyEditor;
}
bodyEditors.DeleteContents();
constraintEditors.DeleteContents();
}
void AfEditor::Init()
{
}
void AfEditor::Draw()
{
bool showTool = isShown;
static char buff[256];
if( ImGui::Begin( "AF Editor", &showTool, ImGuiWindowFlags_MenuBar ) )
{
impl::SetReleaseToolMouse( true );
bool changedAf = false;
bool openedAfBrowser = false;
bool clickedNew = false;
if( ImGui::BeginMenuBar() )
{
if( ImGui::BeginMenu( "File" ) )
{
if( ImGui::MenuItem( "New", "Ctrl+N" ) )
{
clickedNew = true;
}
if( ImGui::MenuItem( "Open..", "Ctrl+O" ) )
{
afList.shouldPopulate = true;
openedAfBrowser = true;
}
if( ImGui::MenuItem( "Save", "Ctrl+S" ) )
{
if( decl )
{
decl->Save();
}
}
// When the editor is closed. it should also set g_editEntities with ~= EDITOR_AF.
if( ImGui::MenuItem( "Close", "Ctrl+W" ) )
{
showTool = false;
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
if( clickedNew )
{
ImGui::OpenPopup( "New Articulated Figure" );
}
if( ImGui::BeginPopupModal( "New Articulated Figure" ) )
{
if( afFiles.Num() == 0 )
{
idFileList* files = fileSystem->ListFiles( "af", ".af", true, true );
for( int i = 0; i < files->GetNumFiles(); i++ )
{
afFiles.Append( files->GetFile( i ) );
}
fileSystem->FreeFileList( files );
}
ImGui::ListBoxHeader( "##afFileSelect" );
for( int i = 0; i < afFiles.Num(); i++ )
{
if( ImGui::ListBox( "Files", &fileSelection, StringListItemGetter, &afFiles, afFiles.Num() ) )
{
fileName = afFiles[fileSelection];
}
}
ImGui::ListBoxFooter();
ImGui::SameLine();
ImGui::SmallButton( "New File" );
ImGui::InputText( "AF Name", &fileName );
if( ImGui::Button( "Create" ) )
{
idStr afName = fileName;
afName.StripPath();
afName.StripFileExtension();
if( !afName.IsEmpty() )
{
idDeclAF* newDecl = static_cast<idDeclAF*>( const_cast<idDecl*>( declManager->FindType( DECL_AF, afName.c_str(), false ) ) );
if( !newDecl )
{
// create it
newDecl = static_cast<idDeclAF*>( declManager->CreateNewDecl( DECL_AF, afName.c_str(), fileName.c_str() ) );
}
afList.names.Append( afName );
currentAf = afList.names.Num() - 1;
OnNewDecl( newDecl );
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
if( ImGui::Button( "Close" ) )
{
afFiles.Clear();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if( openedAfBrowser )
{
ImGui::OpenPopup( "Articulated Figure Browser" );
}
if( ImGui::BeginPopup( "Articulated Figure Browser" ) )
{
afList.populate();
afList.shouldPopulate = false;
if( afList.names.Num() > 0 )
{
ImGui::Combo( "Articulated Figures", &currentAf, &StringListItemGetter, &afList, afList.names.Num() );
if( ImGui::Button( "Select" ) )
{
idDeclAF* newDecl = static_cast<idDeclAF*>( const_cast<idDecl*>( declManager->FindType( DECL_AF, afList.names[currentAf], false ) ) );
if( newDecl )
{
OnNewDecl( newDecl );
}
ImGui::CloseCurrentPopup();
}
}
else
{
ImGui::Text( "There are no articulated figures!" );
}
ImGui::SameLine();
if( ImGui::Button( "Close" ) )
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if( decl )
{
if( ImGui::BeginTabBar( "Tab Bar" ) )
{
if( ImGui::BeginTabItem( "View" ) )
{
CVarCheckBox( "Show Bodies", &af_showBodies );
CVarCheckBox( "Show Body Names", &af_showBodyNames );
CVarCheckBox( "Show Constraints", &af_showConstraints );
CVarCheckBox( "Show Constraint Names", &af_showConstraintNames );
CVarCheckBox( "Show Limits", &af_showLimits );
CVarCheckBox( "Show Active ", &af_showActive );
CVarCheckBox( "Show Trees", &af_showTrees );
CVarCheckBox( "Show Mass", &af_showMass );
ImGui::EndTabItem();
}
if( ImGui::BeginTabItem( "Properties" ) )
{
if( propertyEditor )
{
changedAf = changedAf || propertyEditor->Do();
}
ImGui::EndTabItem();
}
if( ImGui::BeginTabItem( "Bodies##Tab" ) )
{
if( decl->bodies.Num() > 0 )
{
ImGui::Combo( "Body", &currentBodySelection, BodyItemGetter, decl, decl->bodies.Num() );
}
ImGui::SameLine();
static char bodyName[256] = { '\0' };
if( ImGui::Button( "New Body" ) )
{
ImGui::OpenPopup( "Create a New Body" );
}
if( ImGui::BeginPopup( "Create a New Body" ) )
{
ImGui::Text( "Create a new body for the articulated figure" );
ImGui::InputText( "Body Name", &bodyName[0], 256 );
if( ImGui::Button( "Create" ) )
{
// added a new body
if( strlen( bodyName ) > 0 )
{
// TODO: Should do some data validation on the body name.
decl->NewBody( bodyName );
bodyName[0] = '\0';
currentBodySelection = decl->bodies.Num() - 1;
bodyEditors.Append( new AfBodyEditor( decl, decl->bodies[currentBodySelection] ) );
ImGui::CloseCurrentPopup();
}
}
ImGui::SameLine();
if( ImGui::Button( "Cancel" ) )
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::Separator();
if( bodyEditors.Num() > currentBodySelection )
{
changedAf = changedAf || bodyEditors[currentBodySelection]->Do();
if( ImGui::Button( "Delete Body" ) )
{
ImGui::OpenPopup( "Delete Body##2" );
}
if( ImGui::BeginPopupModal( "Delete Body##2" ) )
{
ImGui::Text( "Are you sure you want to delete body %s?", decl->bodies[currentBodySelection]->name.c_str() );
if( ImGui::Button( "Yes" ) )
{
delete bodyEditors[currentBodySelection];
bodyEditors.RemoveIndex( currentBodySelection );
decl->DeleteBody( decl->bodies[currentBodySelection]->name.c_str() );
currentBodySelection -= 1;
if( currentBodySelection < 0 )
{
currentBodySelection = 0;
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if( ImGui::Button( "No" ) )
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
ImGui::EndTabItem();
}
if( ImGui::BeginTabItem( "Constraints" ) )
{
if( decl->constraints.Num() > 0 )
{
ImGui::Combo( "Constraint", &currentConstraint, ConstraintItemGetter, decl, decl->constraints.Num() );
}
static char constraintName[256] = { '\0' };
if( ImGui::Button( "New" ) )
{
ImGui::OpenPopup( "New Constraint" );
}
if( ImGui::BeginPopupModal( "New Constraint" ) )
{
ImGui::Text( "Create a new body for the articulated figure" );
ImGui::InputText( "Constraint Name", &constraintName[0], 256 );
if( ImGui::Button( "Create" ) )
{
// added a new body
if( strlen( constraintName ) > 0 )
{
decl->NewConstraint( constraintName );
constraintEditors.Append( new AfConstraintEditor( decl, decl->constraints[decl->constraints.Num() - 1] ) );
currentConstraint = constraintEditors.Num() - 1;
}
constraintName[0] = '\0';
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if( ImGui::Button( "Cancel" ) )
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
if( ImGui::Button( "Delete" ) )
{
ImGui::OpenPopup( "Delete Constraint##2" );
}
if( ImGui::BeginPopupModal( "Delete Constraint##2" ) )
{
ImGui::Text( "Are you sure you want to delete constraint %s?", decl->constraints[currentConstraint]->name.c_str() );
if( ImGui::Button( "Yes" ) )
{
delete constraintEditors[currentConstraint];
constraintEditors.RemoveIndex( currentConstraint );
decl->DeleteConstraint( decl->constraints[currentConstraint]->name.c_str() );
currentConstraint -= 1;
if( currentConstraint < 0 )
{
currentConstraint = 0;
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if( ImGui::Button( "No" ) )
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
if( decl->constraints.Num() > currentConstraint )
{
changedAf = constraintEditors[currentConstraint]->Do();
}
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::NewLine();
ImGui::Separator();
ImGui::NewLine();
try
{
if( ImGui::Button( "Spawn" ) )
{
if( afList.names.Num() > currentAf )
{
gameEdit->AF_SpawnEntity( afList.names[currentAf].c_str() );
}
}
ImGui::SameLine();
if( ImGui::Button( "Kill" ) )
{
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "deleteSelected\n" );
}
if( changedAf )
{
if( afList.names.Num() > currentAf )
{
gameEdit->AF_UpdateEntities( afList.names[currentAf].c_str() );
}
}
}
catch( idException& exception )
{
common->DWarning( "AfEditor exception %s", exception.GetError() );
}
}
}
ImGui::End();
if( isShown && !showTool )
{
// TODO: do the same as when pressing cancel?
isShown = showTool;
impl::SetReleaseToolMouse( false );
}
}
void AfEditor::OnNewDecl( idDeclAF* newDecl )
{
decl = newDecl;
if( propertyEditor )
{
delete propertyEditor;
}
bodyEditors.DeleteContents();
constraintEditors.DeleteContents();
propertyEditor = new AfPropertyEditor( newDecl );
for( int i = 0; i < decl->bodies.Num(); i++ )
{
bodyEditors.Append( new AfBodyEditor( decl, decl->bodies[i] ) );
}
for( int i = 0; i < decl->constraints.Num(); i++ )
{
constraintEditors.Append( new AfConstraintEditor( decl, decl->constraints[i] ) );
}
}
AfEditor& AfEditor::Instance()
{
static AfEditor instance;
return instance;
}
void AfEditor::Enable( const idCmdArgs& args )
{
AfEditor::Instance().ShowIt( true );
}
void AfEditor::AfList::populate()
{
if( !shouldPopulate )
{
return;
}
names.Clear();
int count = declManager->GetNumDecls( DECL_AF );
for( int i = 0; i < count; i++ )
{
names.Append( static_cast<const idDeclAF*>( declManager->DeclByIndex( DECL_AF, i, false ) )->GetName() );
}
}
bool BodyItemGetter( void* data, int index, const char** itemName )
{
idDeclAF* decl = reinterpret_cast<idDeclAF*>( data );
if( !decl )
{
return false;
}
if( index > decl->bodies.Num() )
{
return false;
}
idDeclAF_Body* body = decl->bodies[index];
*itemName = body->name.c_str();
return true;
}
bool ConstraintItemGetter( void* data, int index, const char** items )
{
idDeclAF* decl = reinterpret_cast<idDeclAF*>( data );
if( !decl )
{
return false;
}
if( index > decl->constraints.Num() )
{
return false;
}
idDeclAF_Constraint* constraint = decl->constraints[index];
*items = constraint->name.c_str();
return true;
}
static bool CVarCheckBox( const char* label, idCVar* cvar )
{
bool value = cvar->GetBool();
if( ImGui::Checkbox( label, &value ) )
{
cvar->SetBool( value );
return true;
}
return false;
}
}

View file

@ -0,0 +1,122 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef EDITOR_TOOLS_AFEDITOR_H_
#define EDITOR_TOOLS_AFEDITOR_H_
#include "../../edit_public.h"
#include "AfBodyEditor.h"
#include "AfConstraintEditor.h"
#include "AfPropertyEditor.h"
namespace ImGuiTools
{
/**
* Articulated figure imgui editor.
*/
class AfEditor
{
public:
virtual ~AfEditor();
void Init();
void ShowIt( bool show );
bool IsShown() const;
void Draw();
public:
static AfEditor& Instance();
static void Enable( const idCmdArgs& args );
public:
AfEditor( AfEditor const& ) = delete;
void operator=( AfEditor const& ) = delete;
private:
struct AfList
{
AfList() : names(), shouldPopulate( false ) {}
void populate();
idList<idStr> names;
bool shouldPopulate;
};
AfEditor();
void OnNewDecl( idDeclAF* newDecl );
bool isShown;
int fileSelection;
int currentAf;
int currentConstraint;
int currentBodySelection;
int currentEntity;
idDeclAF* decl;
idDeclAF_Body* body;
idDeclAF_Constraint* constraint;
// Editor dialogs
AfPropertyEditor* propertyEditor;
idList<AfBodyEditor*> bodyEditors;
idList<AfConstraintEditor*> constraintEditors;
AfList afList; // list with idDeclAF names
idList<idStr> afFiles;
idStr fileName;
struct IndexEntityDef
{
int index;
idStr name;
};
idList<IndexEntityDef> entities;
};
inline void AfEditor::ShowIt( bool show )
{
isShown = show;
}
inline bool AfEditor::IsShown() const
{
return isShown;
}
}
#endif

View file

@ -0,0 +1,139 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "AfPropertyEditor.h"
#include "../imgui/BFGimgui.h"
namespace ImGuiTools
{
AfPropertyEditor::AfPropertyEditor( idDeclAF* newDecl )
: decl( newDecl )
, contentWidget( MakePhysicsContentsSelector() )
, clipMaskWidget( MakePhysicsContentsSelector() )
, linearTolerance( 0 )
, angularTolerance( 0 )
, currentModel( 0 )
{
contentWidget.UpdateWithBitFlags( decl->contents );
clipMaskWidget.UpdateWithBitFlags( decl->clipMask );
UpdateModelDefList();
}
AfPropertyEditor::~AfPropertyEditor()
{
}
bool AfPropertyEditor::Do()
{
bool changed = false;
if( ImGui::CollapsingHeader( "MD5" ) )
{
if( ImGui::Combo( "Models", &currentModel, StringListItemGetter, &modelDefs, modelDefs.Num() ) )
{
decl->model = modelDefs[currentModel];
changed = true;
}
ImGui::InputText( "Skin", &decl->skin, ImGuiInputTextFlags_CharsNoBlank );
}
if( ImGui::CollapsingHeader( "Default Collision Detection" ) )
{
ImGui::Checkbox( "Self Collision", &decl->selfCollision );
ImGui::ListBoxHeader( "Contents" );
changed = DoMultiSelect( &contentWidget, &decl->contents ) || changed;
ImGui::ListBoxFooter();
ImGui::ListBoxHeader( "Clip Mask" );
changed = DoMultiSelect( &clipMaskWidget, &decl->clipMask ) || changed;
ImGui::ListBoxFooter();
}
if( ImGui::CollapsingHeader( "Default Friction" ) )
{
changed = ImGui::DragFloat( "Linear", &decl->defaultLinearFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed;
changed = ImGui::DragFloat( "Angular", &decl->defaultAngularFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed;
changed = ImGui::DragFloat( "Contact", &decl->defaultContactFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed;
changed = ImGui::DragFloat( "Constraint", &decl->defaultConstraintFriction, 0.01f, -100000.f, 100000.f, "%.2f" ) || changed;
}
if( ImGui::CollapsingHeader( "Mass" ) )
{
changed = changed || ImGui::InputFloat( "Total Mass", &decl->totalMass, 1.0f, 50.0f, "%.0f" );
ImGui::SameLine();
HelpMarker( "The total mass of the articulated figure.\n"
"If the total mass is set to a value greater than\n"
"zero then the mass of each body is scaled such that\n"
"the total mass of the articulated figure equals the given mass." );
}
if( ImGui::CollapsingHeader( "Suspend Speed" ) )
{
changed = changed || ImGui::InputFloat2( "Linear Velocity", ( float* )&decl->suspendVelocity, "%.0f" );
changed = changed || ImGui::InputFloat2( "Linear Acceleration", ( float* )&decl->suspendAcceleration, "%.0f" );
}
// TODO(Stephen): Figure out what these properties are for.
//if (ImGui::CollapsingHeader("Suspend Movement"))
//{
// ImGui::InputInt("No Move Time", (int*)&decl->noMoveTime);
// ImGui::InputInt("Linear Tolerance", &linearTolerance);
// ImGui::InputInt("Angular Tolerance", &angularTolerance);
// ImGui::InputInt("Minimum Move Time", (int*)&decl->minMoveTime);
// ImGui::InputInt("Maximum Move Time", (int*)&decl->maxMoveTime);
//}
return changed;
}
void AfPropertyEditor::UpdateModelDefList()
{
// update the model list.
const int totalModelDefs = declManager->GetNumDecls( DECL_MODELDEF );
modelDefs.AssureSize( totalModelDefs );
for( int i = 0; i < totalModelDefs; i++ )
{
const idDecl* modelDef = declManager->DeclByIndex( DECL_MODELDEF, i, false );
if( modelDef )
{
modelDefs[i] = modelDef->GetName();
if( decl->model == modelDef->GetName() )
{
currentModel = i;
}
}
}
}
}

View file

@ -0,0 +1,62 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef EDITOR_TOOLS_AFPROPERTYEDITOR_H_
#define EDITOR_TOOLS_AFPROPERTYEDITOR_H_
#include "../util/Imgui_IdWidgets.h"
namespace ImGuiTools
{
class AfPropertyEditor
{
public:
AfPropertyEditor( idDeclAF* newDecl );
~AfPropertyEditor();
bool Do();
private:
void UpdateModelDefList();
idDeclAF* decl;
MultiSelectWidget contentWidget;
MultiSelectWidget clipMaskWidget;
int linearTolerance;
int angularTolerance;
int currentModel;
idStrList modelDefs;
};
}
#endif

View file

@ -0,0 +1,131 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "../imgui/imgui.h"
#include "Imgui_IdWidgets.h"
static const char* bodyContentsNames[5] =
{
"solid",
"body",
"corpse",
"playerclip",
"monsterclip"
};
static int contentMappingFlags[5] =
{
CONTENTS_SOLID,
CONTENTS_BODY,
CONTENTS_CORPSE,
CONTENTS_PLAYERCLIP,
CONTENTS_MONSTERCLIP
};
MultiSelectWidget::MultiSelectWidget( const char** aNames, int* contentMapping, int aNumEntries )
: names( aNames )
, contentMapping( contentMapping )
, numEntries( aNumEntries )
, selectables( nullptr )
{
selectables = ( bool* )Mem_Alloc( numEntries * sizeof( bool ), TAG_AF );
std::fill( selectables, selectables + numEntries, false );
}
MultiSelectWidget::~MultiSelectWidget()
{
Mem_Free( selectables );
}
void MultiSelectWidget::Update( int index, bool value )
{
assert( index < numEntries );
selectables[index] = value;
}
void MultiSelectWidget::UpdateWithBitFlags( int bitFlags )
{
for( int i = 0; i < numEntries; i++ )
{
Update( i, bitFlags & contentMapping[i] );
}
}
bool DoMultiSelect( MultiSelectWidget* widget, int* contents )
{
bool pressed = false;
for( int i = 0; i < 5; i++ )
{
if( ImGui::Selectable( widget->names[i], &widget->selectables[i] ) )
{
pressed = true;
if( widget->selectables[i] )
{
*contents |= widget->contentMapping[i];
}
else
{
*contents &= ~widget->contentMapping[i];
}
}
}
return pressed;
}
void HelpMarker( const char* desc )
{
ImGui::TextDisabled( "(?)" );
if( ImGui::IsItemHovered() )
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos( ImGui::GetFontSize() * 35.0f );
ImGui::TextUnformatted( desc );
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
bool StringListItemGetter( void* data, int index, const char** outText )
{
idStrList* list = reinterpret_cast<idStrList*>( data );
assert( index < list->Num() );
*outText = ( *list )[index];
return true;
}
MultiSelectWidget MakePhysicsContentsSelector()
{
return MultiSelectWidget( bodyContentsNames, contentMappingFlags, 5 );
}

View file

@ -0,0 +1,59 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
Copyright (C) 2022 Stephen Pridham
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma once
/**
* Holds a list of booleans that correspond to a list of names. This
* class owns the list of bools, not the list of names.
*/
class MultiSelectWidget
{
public:
MultiSelectWidget( const char** aNames, int* contentMapping, int aNumEntries );
~MultiSelectWidget();
void Update( int index, bool value );
void UpdateWithBitFlags( int bitFlags );
public:
const char** names;
int* contentMapping;
int numEntries;
bool* selectables;
};
bool DoMultiSelect( MultiSelectWidget* widget, int* contents );
void HelpMarker( const char* desc );
bool StringListItemGetter( void* data, int index, const char** outText );
MultiSelectWidget MakePhysicsContentsSelector();