john-chapman / im3d Goto Github PK
View Code? Open in Web Editor NEWImmediate mode rendering and 3d gizmos.
License: MIT License
Immediate mode rendering and 3d gizmos.
License: MIT License
Please consider adding a snapping option to the gizmo.
Hello @john-chapman, thank you for creating and sharing this awesome library.
One important feature that the library is missing are C-bindings. These would not only allow C programmers to benefit from this library, but make it easier to port to languages such as Rust or zig.
pixelsToWorldSize()
is broken for ortho projectionsOtherwise they are not visible outside, IMHO defining them first in a custom im3d_config.h
is not elegant ๐
Use case: im3d as a static-lib
LookAt
is used internally, need to set a world up vector on AppDatag_CurrentContext
thread local). Don't want to pay the cost for thread local all the time so could be a config option.Questions to review:
Such as: https://github.com/ocornut/imgui/blob/6b2e03c5b1bc4f99dbd86d10e5fd12af9e3fe1c2/imgui.h#L2005-L2009
Premise for that:
im3d_impl_win32
internals goes to (as-is): AppData::m_appData
im3d_impl_dx12
goes to: AppData::m_rendererData
<-- TODONow, we have a nice AppData
for different renderers.... ๐
Couple of things are broken if the projection is ortho:
Snapping is also broken - currently it's relative to the starting transformation, but perhaps it should be absolute in world space.
Special primitive type; store null-terminated string + position, color, size + some flags (e.g. for alignment, etc.).
Hi,
Running premake4
, for one of the examples, with following command:
premake4 --file=premake.lua
gives the following error:
im3d/examples/OpenGL33/premake.lua:4: attempt to call global 'filter' (a nil value)
On the other hand, running make
directly gives following error:
==== Building im3d_opengl33 (debug_win32) ==== Creating obj/Win32/Debug glew.c im3d_example.cpp In file included from ../../common/im3d_example.cpp:1:0: ../../common/im3d_example.h:33:3: error: #error im3d: Platform not defined #error im3d: Platform not defined ^ In file included from ../../common/im3d_example.h:102:0, from ../../common/im3d_example.cpp:1: /usr/include/c++/5/cstdlib:41:28: fatal error: bits/c++config.h: No such file or directory compilation terminated. im3d_opengl33.make:191: recipe for target 'obj/Win32/Debug/im3d_example.o' failed make[1]: *** [obj/Win32/Debug/im3d_example.o] Error 1 Makefile:34: recipe for target 'im3d_opengl33' failed make: *** [im3d_opengl33] Error 2
I am running above on Ubuntu 16.04 (Linux)
Could you please help resolve the issue with premake4
or include an alternate easy way to build on Linux, e.g. using CMake ?
Hi,I am having errors during VS2012 compilation:
for example :
error C2864: 'Im3d::Vector::m_size' : only static const integral data members can be initialized within a class
::m_capacity' : only static const integral data members can be initialized within a class
): error C2864: 'Im3d::Vector::m_data' : only static const integral data members can be initialized within a class
and many others
How to fix?
I modified the code in the sample program (main.cpp).
#include "im3d_example.h"
int main(int, char**)
{
#ifdef _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
.
.
.
A memory leak message was found in the result window.
Detected memory leaks!
Dumping objects ->
{4183} normal block at 0x00753568, 167 bytes long.
Data: 68 35 75 00 00 00 00 00 00 00 00 00 00 00 00 00
{4182} normal block at 0x0BCB4FA8, 727 bytes long.
Data: < O >; > > A8 4F CB 0B 3E 3B 17 3E 00 00 00 00 00 00 00 00
{4181} normal block at 0x0BC82148, 367 bytes long.
Data: <H! &= &= > 48 21 C8 0B 94 26 5C 3D 94 26 5C 3D 00 00 00 00
{1376} normal block at 0x0BCCB108, 1807 bytes long.
Data: < > 08 B1 CC 0B 00 00 20 C1 00 00 00 00 00 00 20 C1
Object dump complete.
BeginCache(_id);
, EndCache()
, DrawCache(_id)
(but with better names).You should handle a case where the viewport does not start at {0, 0}.
internal::g_CurrentContext
thread local by default (disable from config).Im3d::Draw()
- this alsonaturally supports sorted primitives between contexts).Question: is it acceptable to rely on application code to avoid modifying any of the thread-local contexts during the merge operation? This means application threads cannot make Im3d calls while the main thread is merging contexts.
Answer yes, there's basically no other way. The application must be responsible for locking modifications to a context, and the only tools which Im3d can provide are per-thread context ptrs and context merging.
That said, applications have a few choices for how to go about making Im3d thread safe:
LockIm3d();
// Im3d calls...
UnlockIm3d();
Note that the application must fill AppData
for each context.
Mat4*
to Gizmo as an output, need instead to pass a float*
(and cast to Mat4*
internally).gizmoAxis_Behavior
and gizmoAxis_Draw
are separate internal methods.Hi!
The default Im3d camera model does not support "height" (aspect ratio) influence onto its projection matrix.
If you change view width the pixel size (as a square) is kept, when height changes projection matrix "scales" the output.
Here is the snippet for camera projection matrix that handles that:
float a = viewport_res_x / viewport_res_y;
fov_rad_ = Im3d::Radians(fov_deg_);
const float n = 0.1f;
const float f = 500.0f;
float scale = std::tanf(fov_rad_ * 0.5f) * n;
float r = scale; //no aspect correction here
float l = -scale; //no aspect correction here
float t = scale;
float b = -scale;
//-----
if (a > 1.0f)
{
b /= a;
t /= a;
}
else
{
l *= a;
r *= a;
}
//-----
const float viewZ = -1.0f;
proj_ = Im3d::Mat4
(
2.0f * n / (r - l),
0.0f,
-viewZ * (r + l) / (r - l), //unnecessary for infinite cam (lim -> 0.0f)
0.0f,
0.0f,
2.0f * n / (t - b),
-viewZ * (t + b) / (t - b), //unnecessary for infinite cam (lim -> 0.0f)
0.0f,
0.0f,
0.0f,
viewZ,
-n,
0.0f,
0.0f,
viewZ,
0.0f
);
I have an existing opengl / CUDA app doing volumetric rendering, now need to add grids, rulers and text. Since I am not a opendgl fundi you lid seems quite ideal for my development.
May I please get a little guidance here on how to plug im3D into existing systems and drawing to already defined lg screen textures
Thanks
hello,
thanks for your work.
I embeded your library within a native/android application
(Im3d on PC Version / my application runs correcly)
but on the android version, the axis selection does not work
due to an issue with Intersect (see t0 & t1 )
bool Context::gizmoAxisTranslation_Behavior(Id _id, const Vec3& _origin, const Vec3& _axis, float _snap, float _worldHeight, float _worldSize, Vec3* _out_)
{
......
float t0, t1; <===== here
bool intersects = Intersect(ray, axisCapsule, t0, t1); <===== here
makeHot(_id, t0, intersects); <===== here
......
}
bool Im3d::Intersect(const Ray& _ray, const Capsule& _capsule, float& t0_, float& t1_ <==== here)
{
//IM3D_ASSERT(false); // \todo implement
return Intersects(_ray, _capsule);
}
t0 & t1 are not initialized & not filled by Intersect but used by makeHot
workarround : makeHot => /_depth < m_hotDepth &&/ or Intersect => /* t0_ = 0.0f; t1_ = 1.0f;*/
bool Context::makeHot(Id _id, float _depth, bool _intersects)
{
if (m_activeId == Id_Invalid && /*_depth < m_hotDepth &&*/ <==== here _intersects && !isKeyDown(Action_Select)) {
m_hotId = _id;
m_appHotId = m_appId;
m_hotDepth = _depth;
return true;
}
return false;
}
or
bool Im3d::Intersect(const Ray& _ray, const Capsule& _capsule, float& t0_, float& t1_)
{
//IM3D_ASSERT(false); // \todo implement
t0_ = 0.0f;
t1_ = 1.0f;
return Intersects(_ray, _capsule);
}
regards
Hi
Some planes in local "translation mode" are not drawn
I have fixed that by a workaround (see below); but i guess there is a better solution
Regards
workaround: color.setA(1.0 /* color.getA() * aligned */);
void Context::gizmoPlaneTranslation_Draw(Id _id, const Vec3& _origin, const Vec3& _normal, float _worldSize, Color _color)
{
Vec3 viewDir = m_appData.m_projOrtho
? m_appData.m_viewDirection
: Normalize(m_appData.m_viewOrigin - _origin)
;
float aligned = fabs(Dot(_normal, viewDir));
aligned = Remap(aligned, 0.1f, 0.2f);
Color color = _color;
color.setA(1.0 /*color.getA() * aligned*/); <=== HERE
pushColor(color);
pushAlpha(_id == m_hotId ? 0.7f : 0.1f * getAlpha());
DrawQuadFilled(_origin, _normal, Vec2(_worldSize));
popAlpha();
DrawQuad(_origin, _normal, Vec2(_worldSize));
popColor();
}
Hey John!
How about adding something like IMGUI_USER_CONFIG
, which allows including a different file in case you don't want to modify im3d_config.h? (If you use im3d as a submodule for example.)
// Configuration file with compile-time options (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system')
#ifdef IMGUI_USER_CONFIG
#include IMGUI_USER_CONFIG
#endif
#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H)
#include "imconfig.h"
#endif
I can make a PR if you're interested.
Cheers
Each example should be 1 cpp of the form im3d_, with some Im3d_ functions in it. Everything else goes in im3d_example.h/im3d_example.cpp with a shit tonne of #ifdefs (simplified, centralized albeit a bit messy).
Each example should have its own premake build script. Use defines to control what's compiled in the example 'common' code.
This results in gizmo controls become unresponsive.
Somewhat general steps to reproduce:
Related issue in our project citizenfx/fivem#928 fixed with workaround by resetting ids manually if ID gets changed.
Sometimes when the snapping value is set above 0.0f any translation on (XY, YZ, XZ, or View) results in the position to be set to NAN.
i am using qt5 for ui interface, and wander how to make this effective tool available for qt
Hello, I've been enjoying this library immensely and recommended it to multiple people for use in their own projects a little while back. It is a really useful self-contained library. During my tweet, I also pointed out in a reply on how I would go about getting the project working with newer deferred APIs. I feel like I did a pretty lousy job at explaining myself and why I thought the limited lifespan of draw data presented issues, so I thought I would elaborate on my points here.
The current examples suggest to use this process as a callback:
Callback
This works well if Draw executes immediately but in a more modern API like Vulkan, it basically adds it to a TODO list. We would end up overwriting the vertex data needed by all of the earlier draw commands. You would have to manually execute all of the commands leading up to it which would incur a massive performance penalty.
So you would probably want to make sure all of the vertex data lasts until the end of all render commands,
to do this you could load all of the data into a global vertex buffer. So here would be your' callback would do: (this is what my program deals with Im3d draw data at the moment)
Callback
Now this works but is hardly efficient since it requires us to reallocate the vertex buffer if there is not enough room and since this is performed every time the callback is issued, having to resize the buffer is incredibly wasteful. Keep in mind you don't need to resize all of the time or even every frame if you offset what is the END of the vertex buffer back to the beginning and simply overwrite the last frame's data. But if one of your earlier vertex arrays demands more space that can have a ripple effect on the rest of them, resulting in a TON of unnecessary allocations and copies of the entire global vertex buffer.
Here is where the lack of persistent/accessible draw data outside of the callback is an issue.
This is a way we could cut down on all of those allocations and copies (how my application deals with ImGui draw data):
Do Once
For Each Draw List
This should perform a whole lot better when the amount of vertices in the scene is changing since we would only need to do it once up front (and since we don't even care about the contents before the resize we wouldn't need to perform a copy of the data to the new larger buffer.)
Currently, however, we can't do this since we don't know the contents or even the size of any of the vertex data outside of a one-time callback. By the time the callback is over, that data would be gone and before the render function is called that data probably isn't there.
So I hope this scenario demonstrates the user's possible need to go around the callback function. Thank you for the amazing work!
I'm confused about how to draw primitives in different locations. If I run the following code:
Im3d::PushMatrix(/* a valid non-identity matrix here*/);
Im3d::DrawCylinder(Im3d::Vec3(0,0,0), Im3d::Vec3(0,height,0), radius);
Im3d::PopMatrix();
Im3d::PushMatrix(/* a different valid non-identity matrix here*/);
Im3d::DrawSphere(Im3d::Vec3(0,0,0), radius);
Im3d::PopMatrix();
The resulting primitive's vertices in the draw callback are always exactly the same, regardless of the matrix I pass in. What is the correct way to transform a primitive so that its vertices are transformed? I can of course pass a different matrix directly into the shader, but this translates all of the shapes I draw, and not each one individually.
The library is pretty cool but is there a way for it to work without the geometry shaders for lower end devices?
Hey, I was wondering if you were considering adding MacOS support to the example? I need a library that supports both Mac and Windows but I don't think I currently have the skill to implement it myself
I use im3d to draw some 3d overlays over a 3d scene in an entirely separate render pass, after the scene.
Sometimes, logically, the overlays are meant to traverse some entities in the scene, but visually, the overlays are always rendered on top.
In the example image above, the blue quad (drawn using DrawQuadFilled
) is supposed to traverse the cube, but it's hard to see without moving the camera around to understand where the quad is located.
I was wondering if anyone has experimented with depth testing for im3d, at least for the triangle primitives. I was able to get a ID3D11DepthStencilView*
from my 3d scene, and provide it to my D3D11 im3d implementation, and bind it using OMSetRenderTargets
, while binding a depth-stencil state with depth testing enabled.
However, I could not get results that made sense for the im3d primitives. The overlays would have parts that randomly appeared and disappeared as the camera moved in closer or farther.
I kind of expected point and line primitives to have issues, but even the blue quad in the screenshot above would have nonsensical results.
To improve gizmo robustness, detect NaNs during gizmo operations and don't modify the output position/rotation/scale.
Currently, my scene has already been translated, scaled, and rotated and I want to place a gizmo in such a scene.
For example, if my scene is a bounding box of size 2000, I already translate (1000, 1000, 1000) and scale (0.001, 0.001, 0.001) to the scene (get the model
matrix), and view the scene from (0, 0, 5) to (0, 0, 0) (get the view
matrix) with a perspective camera (get the 'proj' matrix).
How do I fill the AppData
correctly? I can already calculate the camera position and direction and cursor origin and direction in world space.
How can I place a gizmo at the location of the original (1100, 1100, 1100) coordinate?
Thanks!
Hi John, Im3d cannot be exported inside its own dll for better modularization, it would be nice if you could add some IM3D_API macro in front of your API functions and in front of public classes such as Context or AppData.
Thanks!
Use-case: multiple views of the same data are trivially supported, however gizmos rely on some projection information for sizing and to handle interactions. Applications can hack around this by swapping out the AppData
prior to making a Gizmo*
call, but this isn't ideal since the context derives some state from AppData
during NewFrame
.
AppData
stack: could use a stack however there's potentially a lot of data to push/pop, plus some processing overhead.AppData
per-layer: much cleaner from the application point of view: SetAppData
takes a layer ID and the context stores 1 AppData
per layer (by default a copy of the default layer's data). PushLayerId
is therefore still cheap to call.Small UX improvement: when gizmo is in use, store it's flip state and prevent flipping as the gizmo moves.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.