A flexible editor environment that runs on JMgui and JMGraphics. Add your own custom object classes and create an interactive editor that allows the user to manipulate them in 3D, adjust properties in custom menus and view data generated by the objects.
It lives in a JMwindow instance that has customizable gui properties such as colors, fonts and background images. SimObject derived classes can be added to a SimEnvironment instance which can display several different gui sections. These gui sections include a 3D editor environment allowing the objects to be rendered and manipulated, a properties menu allowing the user to edit the properties of the selected object, a data window that displays custom graphics generated by the selected object and a scrollable list of all objects in the SimEnvironment. These gui sections can be placed either at specific pixel coordinates, or aligned to guides that the user can manipulate within defined limits.
Multiple screen states can exist and the user can navigate between them using tabs at the top of the window. These screen states can include both SimEnvironment instances and other custom graphics created using JMgui or JMGraphics.
A built in serialization system allows all SimEnvironments to be saved and loaded from files with a custom file extension.
A pre made example project to use as a starting point: https://github.com/Jasper99m/JMEditorEnvironmentExample
Docs: https://jasper99m.github.io/JMEditorEnvironment/
JMGraphics and JMgui must both be included in your project as seperate submodules for this library to work. https://github.com/Jasper99m/JMGraphics https://github.com/Jasper99m/JMgui
Everything is designed to work with CMake and each library has its own CMakeLists.txt file. If your're using CMake, include these libraries in your CMakeLists.txt file by adding
add_subdirectory(JMgui)
add_subdirectory(JMGraphics)
add_subdirectory(JMEditorEnvironment)
target_link_libraries(YourProjectName
PRIVATE JMEditorEnvironment
)
Some assets must be copied to the build directory for the graphics engine to work so add
add_custom_command(TARGET YourProjectName POST_BUILD
#Needed for JMGraphics
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/JMGraphics/assimp-vc143-mt.dll $<TARGET_FILE_DIR:YourProjectName>
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/JMGraphics/shaders $<TARGET_FILE_DIR:YourProjectName>/JMGraphics/shaders
#Needed for JMgui
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/JMgui/fonts $<TARGET_FILE_DIR:YourProjectName>/JMgui/fonts
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/JMgui/textures $<TARGET_FILE_DIR:YourProjectName>/JMgui/textures
)
If you want your project to statically link MSVC in order to be run as a portable on windows without installing anything, Put this before linking the libraries in your CMakeLists.txt.
if (MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
After JMGraphics, JMgui and JMEditorEnvironment are included in your project, create your screen states that will contain your SimEnvironment instances. Make a class derived from ScreenState and override the setup() and display() functions. setup() is called once by the screen state manager the first time the user views the screen state, and display() is called every frame when the user is viewing that screen state. To create your SimEnvironment in setup()
void DerivedScreenState::setup()
{
SimEnv = new SimEnvironment();
//Optional: set the file extension to use when saving and loading.
SimEnv->getManager()->fileExtension = ".myfileext";
Set the function that generates the properties menu displayed when no object is selected. This function is called by the SimEnvironment, and must be a: std::function<GuiElementHandler*(TabbedMenu*,SimEnvironment*)> that returns the GuiElementHandler containing all of the gui elements you want in your menu. Use the TabbedMenu parameter to get the properties menu dimensions when creating your gui elements. This is a good place to put controls for adding your SimObject derived objects.
SimEnv->setupNoObjSelectedMenu = setupNoObjMenu_editor;
For every SimObject derived class that you want to include in this SimEnvironment, you must register it with the SimEnvironment by calling
SimEnv->registerDerivedSimObject("MY_OBJ", []() {return new MyObjConstructor(); });
This tells the SimEnvironment how to create it when loading from a file. Set your SimObject derived classes objectType string to something unique to this class in its constructor and use it in this register function (MY_OBJ in this example). All derived SimObject classes must also override the readData(), writeData() and cloneInternal() functions. readData() and writeData() must read and write all data unique to the derived class, not base class data. Add spaces between each variable you write to the stream in writeData(), and make sure all data is read from the streem in readData(). cloneInternal() must return a pointer to a new copy of this object and only data unique to the derived class needs to be set. Base class data is set by a wrapper function.
Adding gui guides is recomended. These are used to allign the gui sections and allows the user to move them
SimEnv->addGuideX(0.0f, 0.0f, 0.0f, window);
SimEnv->addGuideX(20.0f, 15.0f, 25.0f, window);
SimEnv->addGuideX(85.0f, 80.0f, 88.0f, window);
SimEnv->addGuideX(100.0f, 100.0f, 100.0f, window);
SimEnv->addGuideY(2.0f, 2.0f, 2.0f, window);
SimEnv->addGuideY(80.0f, 70.0f, 85.0f, window, 2, 3);
SimEnv->addGuideY(100.0f, 100.0f, 100.0f, window);
Now create the gui sections alligned to the guides. Guides are selected by index.
SimEnv->setupViewer_toGuides(window, 1, 2, 0, 2);
SimEnv->setupSelector_toGuides(window, 2, 3, 1, 2);
SimEnv->setupPropertiesMenu_toGuides(window, 0, 1, 0, 2);
SimEnv->setupObjectDataWindow_toGuides(window, 2, 3, 0, 1);
}
In your screen states display() function, display the SimEnvironment
void DerivedScreenState::display()
{
//Optional: display the info bar at the bottom of the screen
objHandler->displayInfoBar(window);
//Displays all gui sections that were set up. Indavidual functions for each gui section can be called if not all should be displayed.
objHandler->displayGui();
}
Now in your main.cpp file, create and setup a window
int main()
{
JMwindow* window = new JMwindow(1920, 1080, "Your window title here");
//Sets the windows size to the work area size of the primary monitor.
window->window->size(window->window->workAreaWidth(), window->window->workAreaHeight());
//Optional: Define the min and max size the user can resize the window to.
window->heightMax = window->height();
window->widthMax = window->width();
window->heightMin = window->height() * 0.75f;
window->widthMin = window->width() * 0.75f;
//Optional: using the custom title bar will replace the default white one created by windows.
window->useCustomTitleBar();
window->maximize();
Then create your screen state manager and your screen states
ScreenStateManager* screenStateManager = new ScreenStateManager(window);
DerivedScreenState* myScreenState = new DerivedScreenState();
screenStateManager->addScreenState(myScreenState);
If you make multiple screen states, just call addScreenState for each one and tabs will appear at the top of the screen when the cursor is near it to navigate between them.
Now start the main rendering loop. This will run until the user closes the window.
while (window->window->windowOpen()) {
window->beginDraw();
screenStateManager->display();
window->endDraw();
}
After the window is closed, delete everything
delete(screenStateManager);
delete(window);
return 0;
}
This should result in a basic usable editor environment. For more info about creating your own SimObjects, creating gui elements/menus and rendering custom graphics, reference the documentation pages for JMEditorEnvironment, JMgui and JMGraphics.

