nDrawExts2: Do more with Mojo- without hacking it!

Monkey Forums/User Modules/nDrawExts2: Do more with Mojo- without hacking it!

Nobuyuki(Posted 2013) [#1]
Here we go again! After learning some tricks with opengl.gles11 in Mojo, I decided to revisit my old nDrawExts module and give it a makeover with less external dependencies. The result is nDrawExts2.



Along with the previous version's features, the new version can compile to Android and supports a feature many people have been wanting: Full alpha pixel data support. You can take a whole image and load it into an array ready for WritePixels, or you can take slices of an image for faster processing. Writing this was a necessary first step for another future library, so this should be a nice start for those of you who liked the earlier version but felt it was lacking in features/portability. In the demo you can see it being used to shift the hue of the flame sprite - but you could use it for palette shifts, character compositing, or anything you can think of really.

In addition to this new feature, the previous features are still available -- extra blending modes, special Logic Operator blend modes (potentially faster!), and, of course, full access to the Stencil Buffer. Thanks in part to some changes to Monkey in the past few months, it's possible to re-initialize the surface with stencil buffer support without having to hack Mojo's natives. Of course, it would be nice for Mark to simply initialize the surface with 8 stencil bits instead of 0, since 0 doesn't guarantee there will be a stencil buffer, but at least we can make it work on all systems without a problem now :)

Special thanks to the people who tested earlier code this was based on, and helped optimize some internal channel shifting stuff so that it runs faster on mobile. I look forward to further testing, ideas, and suggestions for improvement!

Download/clone it here: https://github.com/nobuyukinyuu/nDrawExts2


Shinkiro1(Posted 2014) [#2]
I totally overlooked this. Thanks, and this time everything works :)
Actually a few days ago I needed the alpha thresold for some metaballs and had to design around it. Now I can use it easily, great!


CopperCircle(Posted 2014) [#3]
This is great, thanks.


lom(Posted 2015) [#4]
Hi Nobuyuki,

Is it possible to draw stuff into Image Buffer in a realtime (without using ReadPixels) with your module?


ImmutableOctet(SKNG)(Posted 2015) [#5]
@lom: There's a problem with having nice setups like that in Mojo; the 'surface' field in the 'Image' class is private. Because of this, anything lower-level which directly deals with normal objects is nearly impossible without modification. However, if you're willing to add a "GetSurface" command (Or similar) to the 'Image' class, then you can basically do anything you want. Alternatively, you could utilize Mojo's existing external surface functionality, and skip the 'Image' class.

The first method I brought up was used by SKN3 in his 'skn3.imagebuffer' module. That works generally how you want it to. The thing is, it's GLFW only. This means we really just need to overhaul it to work on the other OpenGL targets (Possibly using the 'opengl' module). The problem is, that's not the easiest thing in the world. I personally want to do this at some point, but it's not high enough priority for me yet. If I do end up making a similar module, it's not going to be compatible with SKN3's code, and it would likely be better abstracted.

Also, if you end up using SKN3's module, beware of the shader code. It works great... If you're willing to rewrite a small portion of it. You see, Mark's made the 'String' conversion functionality a lot nicer since SKN3 wrote that module, so when he tried to patch it, he didn't do a good job. Basically, the strings we get for the source (Most notably) have already been deallocated a majority of the time. So, you end up with gibberish. The answer to this is to use 'String::CString' class directly, then let it be deallocated when the stack pointer gets moved.

TL;DR: No, not really. Not unless you want to deal with image-data (Read from the file). If that's the case, use the 'PixelArray' class in his module.


lom(Posted 2015) [#6]
ImmutableOctet(SKNG),

Thanks for the tip! That's exactly what I was looking for.
I've tried to run an examples of this module but monkey throws out an error like this:
../main.cpp: In member function 'GLint ShaderProgramNative::_GetUniformLocation(String)':
../main.cpp:5069:43: error: invalid user-defined conversion from 'String::CString<char>' to 'GLchar* {aka char*}' [-fpermissive]
  GLchar *theName = name.ToCString<GLchar>();
                                           ^
../main.cpp:1100:3: note: candidate is: String::CString<C>::operator const C*() const [with C = char] <near match>
   operator const C*()const{ 
   ^
../main.cpp:1100:3: note:   no known conversion for implicit 'this' parameter from 'const char*' to 'GLchar* {aka char*}'
<builtin>: recipe for target '../main.o' failed
mingw32-make: *** [../main.o] Error 1

It seems like the problem is in 'String::CString' class as you told me. But I have no idea how to edit shader code. I guess I should find another way.


ImmutableOctet(SKNG)(Posted 2015) [#7]
@lom: Here's a list of things you'll need to do in order to get it running (Line numbers based on the current GitHub version):
* Edit the "imagebuffer.monkey" file, so the 'SKN3_GRAPHICS_IMPLEMENTED' check isn't checking against a string. (Just remove the ' ="1" ' on line 93)

* The next changes are in "imagebuffer.glfw.cpp".

* The '_GetUniformLocation' command's use of 'glGetUniformLocation' should be like this, basically (Memory management will already be done for you):
GLint ShaderProgramNative::_GetUniformLocation(String name)
{
	// --- get location of a uniform ---
	// This location won't change again until the program is re-linked, so it can be reused.
	GLint location = glGetUniformLocation(program, name.ToCString<GLchar>());
	
	// Check for errors:
	error = glGetError();
	if (error == GL_INVALID_VALUE || error == GL_INVALID_OPERATION)
	{
		// An error has occurred.
		return -1;
	}
	
	// Everything worked as expected.
	return location;
}

Just copy and paste that over the existing version.

* This next one is a bit more work, basically, you'll just remove the code on this line ('_SetSource'), and replace it with:
String::CString<GLchar> convertedSource = source.ToCString<GLchar>(); // ToUtf8();
const GLchar* theSource = convertedSource;


That's about it. If you get any other problems, you'll need to use a static cast with 'ToUtf8', or something. That should be it, though. Just make sure that his use of 'delete' on 'theSource' is no longer there (Delete it, or comment it out).

Other:
* Make sure the uses of 'delete' on lines 395 and 742 are no longer present. If you just used what I posted, then they shouldn't be.

* Those should be the only changes you need to make. I specifically made those changes to a new clone myself, and it's working.

That's about it. The problem right now is that SKN3 didn't really set this module up very well, so it's not the most portable. Not to mention that he kind of hacked this in. If you're interested, though, Harmony finally supports shaders via Mojo, but it's not really in a stable form yet. SKN3's module should work for the GLFW targets, though.

There's also the 'GetSurface' method you'll need to add to the 'Image' class (In 'mojo.graphics'), but that's a simple "getter" method (Just have a method called 'GetSurface' return the 'surface' field). I assume you've done that already. Example:


You should probably put it here (As long as it isn't private).


lom(Posted 2015) [#8]
ImmutableOctet(SKNG),
Thank you very much for explanation! Now everything works:D
However I have some issues with implementing this into my game.
1. In file 'example_imagebuffer' (https://github.com/skn3/imagebuffer/blob/8aac2b3c2e7dc56de697a4662b09ff15ab51f189/example_imagebuffer.monkey#L72) there are drawing operations in OnUpdate method, that's strange. I'm doing all drawing stuff in my game within OnRender
2. It seems like it stops working after changing display mode. I tried SetDeviceWindow 640,480,0 and GlfwGame.GetGlfwGame().SetGlfwWindow(640,480,8,8,8,0,0,0,True ), the result is an empty screen.


ImmutableOctet(SKNG)(Posted 2015) [#9]
@lom: Other than the input stuff, that should be in 'OnRender', so you can blame SKN3's bad habits for that (Apparently he added support for 'OnUpdate' explicitly). To my knowledge, there's nothing wrong with using the module via 'OnRender', and I personally think that's the best option. Just keep in mind that attributes like the color will still be kept after you use 'Finish' (Like anything else). As for the display-resize problem, you'll simply have to recreate the FBO and image. Also, you should probably be using Mojo's wrappers for display-management ('SetDeviceWindow'). When destructing the FBO, please make sure to call 'Free' before doing anything else. If you don't, you may have garbage lingering. I recommend manually discarding the 'Image' object it's tied to, as well.

Considering how problematic SKN3's module is, you should probably just refactor it to your liking. I'll take a crack at this at some point, but I haven't dealt with OpenGL in a while.


Skn3(Posted 2015) [#10]
Yo, yeah I wouldn't rely on the imagebuffer module for much clean code really. It was just a little test into shaders and buffers and didn't go through much refactoring and definitely wasn't finished. I ran out of spare time in life!

The decision to add support for onupdate was to eventually allow creation of graphics outside of monkeys render loop, eg to prerender content at runtime. If you have to wait for onrender to create a image from buffer, but you need details of it in onupdate, it could be useful in this scenario.

But really I wouldn't rely on imagebuffer module for anything major. I only really made it public for people to harvest functionality from.


lom(Posted 2015) [#11]
ImmutableOctet(SKNG),
Thanks, now it finally works in my game as I was expected, what a relief :)

Skn3,
Thank you for your module, I was looking for something like this for a while! Without it I just couldn't continue with my game development, because I need this to scale my game to fit the display window. Scaling all graphics with matrix doesn't work well for me, because it causes artefacts and sprites bleeding.
I think you should definitely finish your module, it will be helpful in a lot of ways, especially with shader support. Keep it up!