Giter VIP home page Giter VIP logo

im3d's People

Contributors

airguanz avatar dwarfcrank avatar jiannanya avatar jlaumon avatar john-chapman avatar melix99 avatar mikkosivulainen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

im3d's Issues

Deal with transformed world coordinates.

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!

TODO

  • PushID(void*)
  • Copy changes from GfxSampleFramework

Question: Weird plane orientation

Hi, I setupped the gizmo in osg drawImplementation but got a problem, the orientation of planes are not correct and I didnt know why and what caused that, appreciating for any help. The wird orientation planes are showed like below:
planes

Ids don't get reset if some control is still active and no subsequent Im3d::Gizmo call

This results in gizmo controls become unresponsive.

Somewhat general steps to reproduce:

  1. Have gizmo drawn for some ID
  2. Select some gizmo control with mouse so it become highlighted
  3. Somehow stop drawing gizmo for this ID with control still being active
  4. Draw gizmo again (mouse state doesn't matter), probably with different ID
  5. All controls become unresponsive

Related issue in our project citizenfx/fivem#928 fixed with workaround by resetting ids manually if ID gets changed.

Color constants static init

  • Color constants are static init inside im3d.cpp, this causes issues e.g. when declaring a static array of Im3d::Color in another translation unit.
  • Solution is to mark these as constexpr, requires Color ctors to also be marked as constexpr.

Detect NaNs

To improve gizmo robustness, detect NaNs during gizmo operations and don't modify the output position/rotation/scale.

Questions

  • Should Im3d::Push* variants take an argument? By default they push what's currently on the top of the stack (OpenGL does this).

AppData stack/per-layer

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.

Add IM3D_API macro for dll export

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!

Custom include for im3d_config.h

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

Text Primitive

Special primitive type; store null-terminated string + position, color, size + some flags (e.g. for alignment, etc.).

Depth testing for Triangle primitives

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.

image

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.

Plug into existing application

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

Question: How to draw multiple shapes in different locations?

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.

OSX support

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

EXAMPLES

  • Demonstrate how to correctly fill AppData (including the cursor ray). Lots of comments. Use GetAsyncKeyState() for the keydown array, but note it's not the best way.
  • Example draw function, notes about how to use a depth buffer, etc.
  • Example init + shaders.

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.

Misc gizmo issues

Couple of things are broken if the projection is ortho:

  • View axis rotation gizmo.
  • Aligned alpha.
  • Flipping gizmo axes based on view direction.

Snapping is also broken - currently it's relative to the starting transformation, but perhaps it should be absolute in world space.

Errors compiling with VS2012

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?

Direct access to draw data outside of callback.

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

  1. SetupViewportScissors
  2. Upload array to Vertex Buffer
  3. Set Shaders/Push Constants
  4. Draw

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

  1. Setup Viewport/Scissors
  2. Reallocate Vertex Buffer to fit additional vertex data if necessary (Executes Immediately)
  3. Upload array to the END of the Vertex Buffer (Executes Immediately)
  4. Set Shaders/Push Constants
  5. Issue Draw Command with END as an offset into the buffer
  6. Offset the END of the Vertex buffer by the number of verts to draw.

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

  1. Get a sum of the number of vertices in all of the vertex arrays in the draw lists.
  2. Recreate the global vertex buffer only if necessary.

For Each Draw List

  1. Upload array to the END of the Vertex Buffer (Executes Immediately)
  2. Setup Viewport/Scissors
  3. Set Shaders/Push Constants
  4. Issue Draw Command with END as an offset into the buffer
  5. Offset the END of the Vertex buffer by the number of verts to draw.

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!

C Bindings

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.

wrong behavior when scale the model

i am testing this great tool, but it behaves strange when scale the model . as the image display

gizmoscaleerror

(mouse pos in the image(there is an offset) is not the real position due to the recording software)
using the lastest codebase

im3d default Camera model does not suppot aspect ratio on height

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
	); 

Leaves behind draws.

Both on the Demo and in my Application Im3d leaves behind draws when changing gizmo modes and adding new shapes sometimes.
leftbehind gizmo bug
I'm only drawing a wire cube with a gizmo, everything else is left behind (moved for info)
image
Same thing with the demo when I change gizmo mode.

VertexData Cache

  • Reduce draw cost for high order primitives by caching vertex data (+ primitive type).
  • Each cache maps to an ID.
  • Expose this system to the user e.g. BeginCache(_id);, EndCache(), DrawCache(_id) (but with better names).
  • Cached vertex positions are transformed by the current draw state as they are copied into the final buffer.
  • Transform other properties of the cached data (size/color).

plane drawing issue in local mode

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

world mode
image
local mode
image

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();
}

result in local mode
image

Thread Safety/Multiple Contexts

  • Make internal::g_CurrentContext thread local by default (disable from config).
  • Context merging API (simplest approach to MT will be to merge multiple contexts before making a single call to Im3d::Draw() - this alsonaturally supports sorted primitives between contexts).
  • MT example.

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:

  • Per-thread context with merge, ensuring that no jobs/threads can modify a context during the merge.
  • Per-thread context with merge, allocate 2 contexts per thread and atomically swap them prior to the merge.
  • Single context but wrap the whole API. Pretty error prone but simple and avoids the merge cost - you basically do:
LockIm3d();
// Im3d calls...
UnlockIm3d();

Note that the application must fill AppData for each context.

TESTING

  • Locally modify gizmo scale/thickness.
  • Error: Pop the default matrix.
  • Error: Forget to call NewFrame().
  • Error: Mismatched calls to Begin()/End().

TODO

  • Support row and column major matrices in im3d_math.
  • Draw list sorting. Investigate sorting performance (very slow in debug). Measure qsort, Reorder, etc. in both debug and release. -- Looks like the draw list construction is the most expensive (~20ms in debug and release).
  • Sphere/capsule intersections (for gizmos) + 'high order' shape rendering.
  • Mat3 type, helpers to inject/extract rotation. Move all math types which aren't in the public interface into im3d_math?
  • Better gizmo test (rotate a cuboid). Better still: hard-code a Utah teapot into the demo.
  • Position/rotation/scale gizmos.
  • User-level gizmo interface (for writing custom gizmos) = ID helpers, etc.
  • LookAt is used internally, need to set a world up vector on AppData
  • Note somewhere that you can check if active Id != Id_Invalid to see if the mouse is captured.
  • Note about transformation spaces: translation/rotation are global, scale is local. World space scale is complex and generally not useful (you don't want to skew a rotated object).
  • Thread safety? Context-per-thread could be an option (e.g. make g_CurrentContext thread local). Don't want to pay the cost for thread local all the time so could be a config option.
  • Set version to 1.0 before making public.

Questions to review:

  • FAQ ideas?

memory leak

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.

Ortho projection + gizmos

  • pixelsToWorldSize() is broken for ortho projections
  • Add example cursor ray generation with ortho projection (+ add ortho mode to example).

Build Error on Ubuntu 16.04

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 ?

Intersect(Ray, Capsule)

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

Layers

  • Simple way to group vertex data without creating separate contexts (e.g. to draw all gizmos into a separate layer).
  • Push/pop layer ID; internally map layer ID -> vertex data list index. Clear the map per frame?
  • Pass layer ID to the draw function via the draw list.
  • Gizmos in a separate layer by default?

Gizmos

  • Can't pass a Mat4* to Gizmo as an output, need instead to pass a float* (and cast to Mat4* internally).
  • Separate behaviour from visual representation; e.g. gizmoAxis_Behavior and gizmoAxis_Draw are separate internal methods.
  • Use matrix stack to make gizmos 'relative' (i.e. draw/intersect the gizmo as transformed by the matrix stack top, then apply the inverse matrix to the resulting world space position to make it 'local' again).
  • Local transformation also works via the matrix stack - push the transform, enable 'local' mode and then call the relevant Gizmo* function. The sub-gizmo functions should also use the matrix (makes implementing planar gizmos much easier).

Gizmo Optim/Enhancement

  • Check the world space bounding sphere of the gizmo for early-out.
  • Store/expose the hot/active app-provided gizmo ID (sub gizmo IDs aren't useful externally).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.