Greetings,
I need to use the cartoon inking shader from the Cartoon Painter that was developed a year ago by Cla (see this topic: [Cartoon Painter)).
Since I’ve never used shaders before, I’m trying to port it to C++ first, and see what I can do with it then.
I have something that actually do stuff. I’m just not exactly sure what or why: though I managed to port pretty much everything, some of the code in Python isn’t directly portable to C++.
And I think the two lines I didn’t manage to reproduce correctly using the C++ API might be responsible.
There are at least two things that I didn’t manage to port, and a few other things might be wrong.
First, this line:
self._normals_camera = base.makeCamera(self._normals_buf,
camName='normals_camera',
lens=base.cam.node().getLens())
There is no such make_camera method in C++ Panda3D, and I can’t find what the first parameter might be (it’s a variable created by makeTextureBuffer, which in C++ returns a GraphicsOutput).
And then, there’s this line:
base.cam2d.node().getLens()
What is this cam2d ? And where does it come from ?
Also, please tell me if I was wrong to do the next things (where framework was declared as ‘PandaFramework framwork’):
-> Replacing base.win with framework.get_window(0)
-> Replacing base.cam with framework.get_window(0).get_camera(0)
If you want a look at the whole thing, here it is:
#include <panda3d/pandaFramework.h>
#include <panda3d/pandaSystem.h>
#include <panda3d/stereoDisplayRegion.h>
#define DEFAULT_STEPFUNC_MIN 0.8
#define DEFAULT_STEPFUNC_MAX 1.0
#define DEFAULT_STEPFUNC_STEPS 1.0
#define DEFAULT_SEPARATION 0.001
#define DEFAULT_CUTOFF 0.3
#define DEFAULT_LIGHT_POS LVector3f(30, -50, 0)
#define CARTOON_SHADING_SHADER "cartoonpainter/shading.sha"
#define CARTOON_NORMALS_SHADER "cartoonpainter/normalGen.sha"
#define CARTOON_INKING_SHADER "cartoonpainter/inkGen.sha"
#define CARTOON_SHADING_TAG "CartoonPainter.CartoonShading"
#define CARTOON_INKING_TAG "CartoonPainter.CartoonInking"
PandaFramework framework;
class CartoonPainter : public AsyncTask
{
public:
CartoonPainter(int sort = -1)
{
NodePath tmp;
PT(Shader) shader = Shader::load(CARTOON_SHADING_SHADER, Shader::SL_Cg);
PT(Shader) normal_shader = Shader::load(CARTOON_NORMALS_SHADER, Shader::SL_Cg);
PT(Shader) inking_shader = Shader::load(CARTOON_INKING_SHADER, Shader::SL_Cg);
enabled = true;
camera_spot_light = false;
// paintings = {}
stepf_min = DEFAULT_STEPFUNC_MIN;
stepf_max = DEFAULT_STEPFUNC_MAX;
stepf_steps = DEFAULT_STEPFUNC_STEPS;
separation = DEFAULT_SEPARATION;
cutoff = DEFAULT_CUTOFF;
// Check if the videocard support shaders. If it doesn't, then the CartoonPainter will be disabled
shaders_supported = framework.get_window(0)->get_graphics_window()->get_gsg()->get_supports_basic_shaders();
if (!shaders_supported)
{
enabled = false;
cerr << "CartoonPainter disabled. Video driver reports that shaders are not supported" << endl;
return ;
}
// Create two new scenes, one 3d and the other 2d. The 3d scene will have attached all those nodepaths
// we want to cartoon paint. The 2d scene will have a card where the black outlines of the cartoon inking
// are drawn.
toon_render = NodePath("toon_render");
inking_render_2d = NodePath("inking_render2d");
inking_render_2d.set_depth_test(false);
inking_render_2d.set_depth_write(false);
// Make a new display region where to render objects in cartoon shading.
// toon_render will have a light node used by the shader as input.
toon_dr = framework.get_window(0)->get_graphics_window()->make_stereo_display_region();
toon_dr->set_sort(sort - 1);
toon_camera_ptr = new Camera("toon_camera");
toon_camera = toon_render.attach_new_node(toon_camera_ptr);
toon_camera_ptr->set_lens(framework.get_window(0)->get_camera(0)->get_lens());
toon_dr->set_camera(toon_camera);
light = toon_render.attach_new_node("light");
light.set_pos(DEFAULT_LIGHT_POS);
toon_render.set_shader_input("light", light);
toon_render.set_shader_input("min", LVector4f(stepf_min));
toon_render.set_shader_input("max", LVector4f(stepf_max));
toon_render.set_shader_input("steps", LVector4f(stepf_steps));
toon_camera_ptr->set_tag_state_key(CARTOON_SHADING_TAG);
tmp = NodePath("tmp");
tmp.set_shader(shader);
toon_camera_ptr->set_tag_state("True", tmp.get_state());
// Make a 'normals buffer' user later by the cartoon inker to get the
// black outlines around all theo bjects of toon render. The normals
// buffer will contain a picture of the model colorized so that the
// color of the model is a representation of the model's normal at that point
normals_buf = framework.get_window(0)->get_graphics_window()->make_texture_buffer("normals_buf", 0, 0);
normals_buf->set_clear_color(LVector4f(0.5f, 0.5f, 0.5f, 1));
normals_camera = framework.get_window(0)->make_camera();
normals_camera_ptr = reinterpret_cast<Camera*>(normals_camera.node());
normals_camera_ptr->set_name("normals_camera");
normals_camera_ptr->set_lens(framework.get_window(0)->get_camera(0)->get_lens());
// WARNING The python make_camera call also did something with the GraphicsOutput. No idea what. No idea how to reproduce.
tmp = NodePath("tmp");
tmp.set_shader(normal_shader);
normals_camera_ptr->set_initial_state(tmp.get_state());
// Make a new display region for a 2d scene where we are going to draw a texture card
// with the black outlines of every objects in toon_render
inking_dr = framework.get_window(0)->get_graphics_window()->make_stereo_display_region();
inking_dr->set_sort(sort);
inking_camera_ptr = new Camera("inking_camera");
inking_camera = inking_render_2d.attach_new_node(inking_camera_ptr);
// WARNING The next line is bullshit, python said base.cam2d.node. The fuck is cam2d ??
inking_camera_ptr->set_lens(framework.get_window(0)->get_camera(0)->get_lens());
inking_dr->set_camera(inking_camera);
// Extract a texture card from the normal buffer and process it using the cartoon inker. The
// final result will be a transparent texture containing the black outlines for every objects in toon_render
ink_outlines = normals_buf->get_texture_card();
ink_outlines.set_transparency(TransparencyAttrib::M_alpha);
ink_outlines.set_color(1, 1, 1, 0);
ink_outlines.reparent_to(inking_render_2d);
ink_outlines.set_shader(inking_shader);
ink_outlines.set_shader_input("separation", LVector4f(separation, 0, separation, 0));
ink_outlines.set_shader_input("cutoff", LVector4f(cutoff));
// Start a task to update the internal cameras and every nodepath in toon_render
AsyncTaskManager::get_global_ptr()->add(this);
}
AsyncTask::DoneStatus do_task(void)
{
if (enabled)
{
Camera* base_cam = framework.get_window(0)->get_camera(0);
NodePath np_base_cam(base_cam);
LQuaternion cam_quat = np_base_cam.get_quat(np_base_cam.get_top());
LPoint3 cam_pos = np_base_cam.get_pos(np_base_cam.get_top());
Lens* cam_lens = base_cam->get_lens();
toon_camera.set_quat (toon_camera.get_top(), cam_quat);
toon_camera.set_pos (toon_camera.get_top(), cam_pos);
normals_camera.set_quat(normals_camera.get_top(), cam_quat);
normals_camera.set_pos (normals_camera.get_top(), cam_pos);
toon_camera_ptr->set_lens(cam_lens);
normals_camera_ptr->set_lens(cam_lens);
}
return (AsyncTask::DS_cont);
}
void paint(NodePath nodepath)
{
if (enabled)
{
inp = nodepath.instance_under_node(toon_render, nodepath.get_name());
inp.set_tag(CARTOON_SHADING_TAG, "True");
inp.set_tag(CARTOON_INKING_TAG, "True");
nodepath.stash();
}
}
private:
bool enabled, shaders_supported;
bool camera_spot_light;
float stepf_min, stepf_max, stepf_steps, separation, cutoff;
NodePath toon_render, inking_render_2d;
StereoDisplayRegion* toon_dr;
NodePath toon_camera;
Camera* toon_camera_ptr;
NodePath light;
GraphicsOutput* normals_buf;
NodePath normals_camera;
Camera* normals_camera_ptr;
StereoDisplayRegion* inking_dr;
NodePath inking_camera;
Camera* inking_camera_ptr;
NodePath ink_outlines;
NodePath inp;
};
class ToonMaker
{
public:
ToonMaker() : cartoon_painter()
{
WindowFramework* win = framework.get_window(0);
NodePath big_nik = win->load_model(framework.get_models(), "nik-dragon");
big_nik.reparent_to(win->get_render());
big_nik.set_pos(0, 70, 0);
cartoon_painter.paint(big_nik);
}
private:
CartoonPainter cartoon_painter;
};
int main(int argc, char** argv)
{
WindowFramework* window;
framework.open_framework(argc, argv);
framework.set_window_title("Fallout Equestria");
window = framework.open_window();
{
ToonMaker tm;
framework.main_loop();
framework.close_framework();
}
return (0);
}