mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
shadows: fix shadow buffer creation deadlock in multithreaded pipeline
Fixes #162
This commit is contained in:
parent
39abc66025
commit
7ee9467f8d
@ -84,22 +84,30 @@ DisplayRegion::
|
|||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
cleanup() {
|
cleanup() {
|
||||||
set_camera(NodePath());
|
CDStageWriter cdata(_cycler, 0);
|
||||||
|
if (cdata->_camera_node != nullptr) {
|
||||||
|
// We need to tell the old camera we're not using it anymore.
|
||||||
|
cdata->_camera_node->remove_display_region(this);
|
||||||
|
}
|
||||||
|
cdata->_camera_node = nullptr;
|
||||||
|
cdata->_camera = NodePath();
|
||||||
|
|
||||||
CDCullWriter cdata(_cycler_cull, true);
|
CDCullWriter cdata_cull(_cycler_cull, true);
|
||||||
cdata->_cull_result = NULL;
|
cdata_cull->_cull_result = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the lens index, allows for multiple lenses to be attached to a camera.
|
* Sets the lens index, allows for multiple lenses to be attached to a camera.
|
||||||
* This is useful for a variety of setups, such as fish eye rendering. The
|
* This is useful for a variety of setups, such as fish eye rendering. The
|
||||||
* default is 0.
|
* default is 0.
|
||||||
|
*
|
||||||
|
* Don't call this in a downstream thread unless you don't mind it blowing
|
||||||
|
* away other changes you might have recently made in an upstream thread.
|
||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
set_lens_index(int index) {
|
set_lens_index(int index) {
|
||||||
int pipeline_stage = Thread::get_current_pipeline_stage();
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
nassertv(pipeline_stage == 0);
|
CDWriter cdata(_cycler, true, current_thread);
|
||||||
CDWriter cdata(_cycler);
|
|
||||||
cdata->_lens_index = index;
|
cdata->_lens_index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,12 +115,14 @@ set_lens_index(int index) {
|
|||||||
* Changes the portion of the framebuffer this DisplayRegion corresponds to.
|
* Changes the portion of the framebuffer this DisplayRegion corresponds to.
|
||||||
* The parameters range from 0 to 1, where 0,0 is the lower left corner and
|
* The parameters range from 0 to 1, where 0,0 is the lower left corner and
|
||||||
* 1,1 is the upper right; (0, 1, 0, 1) represents the whole screen.
|
* 1,1 is the upper right; (0, 1, 0, 1) represents the whole screen.
|
||||||
|
*
|
||||||
|
* Don't call this in a downstream thread unless you don't mind it blowing
|
||||||
|
* away other changes you might have recently made in an upstream thread.
|
||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
set_dimensions(int i, const LVecBase4 &dimensions) {
|
set_dimensions(int i, const LVecBase4 &dimensions) {
|
||||||
int pipeline_stage = Thread::get_current_pipeline_stage();
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
nassertv(pipeline_stage == 0);
|
CDWriter cdata(_cycler, true, current_thread);
|
||||||
CDWriter cdata(_cycler);
|
|
||||||
|
|
||||||
cdata->_regions[i]._dimensions = dimensions;
|
cdata->_regions[i]._dimensions = dimensions;
|
||||||
|
|
||||||
@ -145,15 +155,13 @@ is_stereo() const {
|
|||||||
*
|
*
|
||||||
* The camera is actually set via a NodePath, which clarifies which instance
|
* The camera is actually set via a NodePath, which clarifies which instance
|
||||||
* of the camera (if there happen to be multiple instances) we should use.
|
* of the camera (if there happen to be multiple instances) we should use.
|
||||||
|
*
|
||||||
|
* Don't call this in a downstream thread unless you don't mind it blowing
|
||||||
|
* away other changes you might have recently made in an upstream thread.
|
||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
set_camera(const NodePath &camera) {
|
set_camera(const NodePath &camera) {
|
||||||
int pipeline_stage = Thread::get_current_pipeline_stage();
|
CDWriter cdata(_cycler, true);
|
||||||
|
|
||||||
// We allow set_camera(NodePath()) to happen in cleanup(), which can be
|
|
||||||
// called from any pipeline stage.
|
|
||||||
nassertv(pipeline_stage == 0 || camera.is_empty());
|
|
||||||
CDStageWriter cdata(_cycler, 0);
|
|
||||||
|
|
||||||
Camera *camera_node = (Camera *)NULL;
|
Camera *camera_node = (Camera *)NULL;
|
||||||
if (!camera.is_empty()) {
|
if (!camera.is_empty()) {
|
||||||
@ -181,16 +189,17 @@ set_camera(const NodePath &camera) {
|
|||||||
/**
|
/**
|
||||||
* Sets the active flag associated with the DisplayRegion. If the
|
* Sets the active flag associated with the DisplayRegion. If the
|
||||||
* DisplayRegion is marked inactive, nothing is rendered.
|
* DisplayRegion is marked inactive, nothing is rendered.
|
||||||
|
*
|
||||||
|
* Don't call this in a downstream thread unless you don't mind it blowing
|
||||||
|
* away other changes you might have recently made in an upstream thread.
|
||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
set_active(bool active) {
|
set_active(bool active) {
|
||||||
int pipeline_stage = Thread::get_current_pipeline_stage();
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
nassertv(pipeline_stage == 0);
|
CDWriter cdata(_cycler, true, current_thread);
|
||||||
CDLockedReader cdata(_cycler);
|
|
||||||
|
|
||||||
if (active != cdata->_active) {
|
if (active != cdata->_active) {
|
||||||
CDWriter cdataw(_cycler, cdata);
|
cdata->_active = active;
|
||||||
cdataw->_active = active;
|
|
||||||
win_display_regions_changed();
|
win_display_regions_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,15 +208,17 @@ set_active(bool active) {
|
|||||||
* Sets the sort value associated with the DisplayRegion. Within a window,
|
* Sets the sort value associated with the DisplayRegion. Within a window,
|
||||||
* DisplayRegions will be rendered in order from the lowest sort value to the
|
* DisplayRegions will be rendered in order from the lowest sort value to the
|
||||||
* highest.
|
* highest.
|
||||||
|
*
|
||||||
|
* Don't call this in a downstream thread unless you don't mind it blowing
|
||||||
|
* away other changes you might have recently made in an upstream thread.
|
||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
set_sort(int sort) {
|
set_sort(int sort) {
|
||||||
nassertv(Thread::get_current_pipeline_stage() == 0);
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
CDLockedReader cdata(_cycler);
|
CDWriter cdata(_cycler, true, current_thread);
|
||||||
|
|
||||||
if (sort != cdata->_sort) {
|
if (sort != cdata->_sort) {
|
||||||
CDWriter cdataw(_cycler, cdata);
|
cdata->_sort = sort;
|
||||||
cdataw->_sort = sort;
|
|
||||||
win_display_regions_changed();
|
win_display_regions_changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -332,12 +343,14 @@ get_cull_traverser() {
|
|||||||
*
|
*
|
||||||
* This is particularly useful when rendering cube maps and/or stereo
|
* This is particularly useful when rendering cube maps and/or stereo
|
||||||
* textures.
|
* textures.
|
||||||
|
*
|
||||||
|
* Don't call this in a downstream thread unless you don't mind it blowing
|
||||||
|
* away other changes you might have recently made in an upstream thread.
|
||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
set_target_tex_page(int page) {
|
set_target_tex_page(int page) {
|
||||||
int pipeline_stage = Thread::get_current_pipeline_stage();
|
Thread *current_thread = Thread::get_current_thread();
|
||||||
nassertv(pipeline_stage == 0);
|
CDWriter cdata(_cycler, true, current_thread);
|
||||||
CDWriter cdata(_cycler);
|
|
||||||
cdata->_target_tex_page = page;
|
cdata->_target_tex_page = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,9 +568,6 @@ compute_pixels() {
|
|||||||
*/
|
*/
|
||||||
void DisplayRegion::
|
void DisplayRegion::
|
||||||
compute_pixels_all_stages() {
|
compute_pixels_all_stages() {
|
||||||
int pipeline_stage = Thread::get_current_pipeline_stage();
|
|
||||||
nassertv(pipeline_stage == 0);
|
|
||||||
|
|
||||||
if (_window != (GraphicsOutput *)NULL) {
|
if (_window != (GraphicsOutput *)NULL) {
|
||||||
OPEN_ITERATE_ALL_STAGES(_cycler) {
|
OPEN_ITERATE_ALL_STAGES(_cycler) {
|
||||||
CDStageWriter cdata(_cycler, pipeline_stage);
|
CDStageWriter cdata(_cycler, pipeline_stage);
|
||||||
|
@ -153,7 +153,6 @@ GraphicsEngine(Pipeline *pipeline) :
|
|||||||
|
|
||||||
_windows_sorted = true;
|
_windows_sorted = true;
|
||||||
_window_sort_index = 0;
|
_window_sort_index = 0;
|
||||||
_needs_open_windows = false;
|
|
||||||
|
|
||||||
set_threading_model(GraphicsThreadingModel(threading_model));
|
set_threading_model(GraphicsThreadingModel(threading_model));
|
||||||
if (!_threading_model.is_default()) {
|
if (!_threading_model.is_default()) {
|
||||||
@ -326,13 +325,10 @@ make_output(GraphicsPipe *pipe,
|
|||||||
|
|
||||||
// Sanity check everything.
|
// Sanity check everything.
|
||||||
|
|
||||||
GraphicsThreadingModel threading_model = get_threading_model();
|
|
||||||
nassertr(pipe != (GraphicsPipe *)NULL, NULL);
|
nassertr(pipe != (GraphicsPipe *)NULL, NULL);
|
||||||
if (gsg != (GraphicsStateGuardian *)NULL) {
|
if (gsg != (GraphicsStateGuardian *)NULL) {
|
||||||
nassertr(pipe == gsg->get_pipe(), NULL);
|
nassertr(pipe == gsg->get_pipe(), NULL);
|
||||||
nassertr(this == gsg->get_engine(), NULL);
|
nassertr(this == gsg->get_engine(), NULL);
|
||||||
nassertr(threading_model.get_draw_name() ==
|
|
||||||
gsg->get_threading_model().get_draw_name(), NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we really asking for a callback window?
|
// Are we really asking for a callback window?
|
||||||
@ -346,8 +342,8 @@ make_output(GraphicsPipe *pipe,
|
|||||||
if (this_gsg != (GraphicsStateGuardian *)NULL) {
|
if (this_gsg != (GraphicsStateGuardian *)NULL) {
|
||||||
CallbackGraphicsWindow *window = new CallbackGraphicsWindow(this, pipe, name, fb_prop, win_prop, flags, this_gsg);
|
CallbackGraphicsWindow *window = new CallbackGraphicsWindow(this, pipe, name, fb_prop, win_prop, flags, this_gsg);
|
||||||
window->_sort = sort;
|
window->_sort = sort;
|
||||||
do_add_window(window, threading_model);
|
do_add_window(window);
|
||||||
do_add_gsg(window->get_gsg(), pipe, threading_model);
|
do_add_gsg(window->get_gsg(), pipe);
|
||||||
display_cat.info() << "Created output of type CallbackGraphicsWindow\n";
|
display_cat.info() << "Created output of type CallbackGraphicsWindow\n";
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
@ -386,8 +382,8 @@ make_output(GraphicsPipe *pipe,
|
|||||||
(host->get_fb_properties().subsumes(fb_prop))) {
|
(host->get_fb_properties().subsumes(fb_prop))) {
|
||||||
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
|
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
|
||||||
buffer->_sort = sort;
|
buffer->_sort = sort;
|
||||||
do_add_window(buffer, threading_model);
|
do_add_window(buffer);
|
||||||
do_add_gsg(host->get_gsg(), pipe, threading_model);
|
do_add_gsg(host->get_gsg(), pipe);
|
||||||
display_cat.info() << "Created output of type ParasiteBuffer\n";
|
display_cat.info() << "Created output of type ParasiteBuffer\n";
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
@ -398,8 +394,8 @@ make_output(GraphicsPipe *pipe,
|
|||||||
if (force_parasite_buffer && can_use_parasite) {
|
if (force_parasite_buffer && can_use_parasite) {
|
||||||
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
|
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
|
||||||
buffer->_sort = sort;
|
buffer->_sort = sort;
|
||||||
do_add_window(buffer, threading_model);
|
do_add_window(buffer);
|
||||||
do_add_gsg(host->get_gsg(), pipe, threading_model);
|
do_add_gsg(host->get_gsg(), pipe);
|
||||||
display_cat.info() << "Created output of type ParasiteBuffer\n";
|
display_cat.info() << "Created output of type ParasiteBuffer\n";
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
@ -412,17 +408,15 @@ make_output(GraphicsPipe *pipe,
|
|||||||
pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify);
|
pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify);
|
||||||
if (window != (GraphicsOutput *)NULL) {
|
if (window != (GraphicsOutput *)NULL) {
|
||||||
window->_sort = sort;
|
window->_sort = sort;
|
||||||
if ((precertify) && (gsg != 0) && (window->get_gsg()==gsg)) {
|
if (precertify && gsg != nullptr && window->get_gsg() == gsg) {
|
||||||
do_add_window(window, threading_model);
|
do_add_window(window);
|
||||||
do_add_gsg(window->get_gsg(), pipe, threading_model);
|
|
||||||
display_cat.info()
|
display_cat.info()
|
||||||
<< "Created output of type " << window->get_type() << "\n";
|
<< "Created output of type " << window->get_type() << "\n";
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
do_add_window(window, threading_model);
|
do_add_window(window);
|
||||||
open_windows();
|
open_windows();
|
||||||
if (window->is_valid()) {
|
if (window->is_valid()) {
|
||||||
do_add_gsg(window->get_gsg(), pipe, threading_model);
|
|
||||||
display_cat.info()
|
display_cat.info()
|
||||||
<< "Created output of type " << window->get_type() << "\n";
|
<< "Created output of type " << window->get_type() << "\n";
|
||||||
|
|
||||||
@ -462,8 +456,8 @@ make_output(GraphicsPipe *pipe,
|
|||||||
if (can_use_parasite) {
|
if (can_use_parasite) {
|
||||||
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
|
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
|
||||||
buffer->_sort = sort;
|
buffer->_sort = sort;
|
||||||
do_add_window(buffer, threading_model);
|
do_add_window(buffer);
|
||||||
do_add_gsg(host->get_gsg(), pipe, threading_model);
|
do_add_gsg(host->get_gsg(), pipe);
|
||||||
display_cat.info() << "Created output of type ParasiteBuffer\n";
|
display_cat.info() << "Created output of type ParasiteBuffer\n";
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
@ -479,30 +473,24 @@ make_output(GraphicsPipe *pipe,
|
|||||||
* shouldn't be called by user code as make_output normally does this under
|
* shouldn't be called by user code as make_output normally does this under
|
||||||
* the hood; it may be useful in esoteric cases in which a custom window
|
* the hood; it may be useful in esoteric cases in which a custom window
|
||||||
* object is used.
|
* object is used.
|
||||||
|
*
|
||||||
|
* This can be called during the rendering loop, unlike make_output(); the
|
||||||
|
* window will be opened before the next frame begins rendering. Because it
|
||||||
|
* doesn't call open_windows(), however, it's not guaranteed that the window
|
||||||
|
* will succeed opening even if it returns true.
|
||||||
*/
|
*/
|
||||||
bool GraphicsEngine::
|
bool GraphicsEngine::
|
||||||
add_window(GraphicsOutput *window, int sort) {
|
add_window(GraphicsOutput *window, int sort) {
|
||||||
nassertr(window != NULL, false);
|
nassertr(window != nullptr, false);
|
||||||
|
|
||||||
GraphicsThreadingModel threading_model = get_threading_model();
|
|
||||||
nassertr(this == window->get_engine(), false);
|
nassertr(this == window->get_engine(), false);
|
||||||
|
|
||||||
window->_sort = sort;
|
window->_sort = sort;
|
||||||
do_add_window(window, threading_model);
|
do_add_window(window);
|
||||||
|
|
||||||
open_windows();
|
display_cat.info()
|
||||||
if (window->is_valid()) {
|
<< "Added output of type " << window->get_type() << "\n";
|
||||||
do_add_gsg(window->get_gsg(), window->get_pipe(), threading_model);
|
|
||||||
|
|
||||||
display_cat.info()
|
return true;
|
||||||
<< "Added output of type " << window->get_type() << "\n";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
remove_window(window);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -537,6 +525,13 @@ remove_window(GraphicsOutput *window) {
|
|||||||
}
|
}
|
||||||
count = _windows.erase(ptwin);
|
count = _windows.erase(ptwin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also check whether it is in _new_windows.
|
||||||
|
{
|
||||||
|
MutexHolder new_windows_holder(_new_windows_lock, current_thread);
|
||||||
|
_new_windows.erase(std::remove(_new_windows.begin(), _new_windows.end(), ptwin));
|
||||||
|
}
|
||||||
|
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
// Never heard of this window. Do nothing.
|
// Never heard of this window. Do nothing.
|
||||||
return false;
|
return false;
|
||||||
@ -601,6 +596,11 @@ remove_all_windows() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexHolder new_windows_holder(_new_windows_lock, current_thread);
|
||||||
|
_new_windows.clear();
|
||||||
|
}
|
||||||
|
|
||||||
_app.do_close(this, current_thread);
|
_app.do_close(this, current_thread);
|
||||||
_app.do_pending(this, current_thread);
|
_app.do_pending(this, current_thread);
|
||||||
terminate_threads(current_thread);
|
terminate_threads(current_thread);
|
||||||
@ -694,14 +694,12 @@ render_frame() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (_needs_open_windows) {
|
// Make sure our buffers and windows are fully realized before we render a
|
||||||
// Make sure our buffers and windows are fully realized before we render a
|
// frame. We do this particularly to realize our offscreen buffers, so
|
||||||
// frame. We do this particularly to realize our offscreen buffers, so
|
// that we don't render a frame before the offscreen buffers are ready
|
||||||
// that we don't render a frame before the offscreen buffers are ready
|
// (which might result in a frame going by without some textures having
|
||||||
// (which might result in a frame going by without some textures having
|
// been rendered).
|
||||||
// been rendered).
|
open_windows();
|
||||||
open_windows();
|
|
||||||
}
|
|
||||||
|
|
||||||
ClockObject *global_clock = ClockObject::get_global_clock();
|
ClockObject *global_clock = ClockObject::get_global_clock();
|
||||||
|
|
||||||
@ -945,10 +943,58 @@ open_windows() {
|
|||||||
|
|
||||||
ReMutexHolder holder(_lock, current_thread);
|
ReMutexHolder holder(_lock, current_thread);
|
||||||
|
|
||||||
if (!_windows_sorted) {
|
pvector<PT(GraphicsOutput)> new_windows;
|
||||||
do_resort_windows();
|
{
|
||||||
|
MutexHolder new_windows_holder(_new_windows_lock, current_thread);
|
||||||
|
if (_new_windows.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = _new_windows.begin(); it != _new_windows.end(); ++it) {
|
||||||
|
GraphicsOutput *window = *it;
|
||||||
|
|
||||||
|
WindowRenderer *cull =
|
||||||
|
get_window_renderer(_threading_model.get_cull_name(),
|
||||||
|
_threading_model.get_cull_stage());
|
||||||
|
WindowRenderer *draw =
|
||||||
|
get_window_renderer(_threading_model.get_draw_name(),
|
||||||
|
_threading_model.get_draw_stage());
|
||||||
|
|
||||||
|
if (_threading_model.get_cull_sorting()) {
|
||||||
|
cull->add_window(cull->_cull, window);
|
||||||
|
draw->add_window(draw->_draw, window);
|
||||||
|
} else {
|
||||||
|
cull->add_window(cull->_cdraw, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask the pipe which thread it prefers to run its windowing commands in
|
||||||
|
// (the "window thread"). This is the thread that handles the commands
|
||||||
|
// to open, resize, etc. the window. X requires this to be done in the
|
||||||
|
// app thread (along with all the other windows, since X is strictly
|
||||||
|
// single-threaded), but Windows requires this to be done in draw
|
||||||
|
// (because once an OpenGL context has been bound in a given thread, it
|
||||||
|
// cannot subsequently be bound in any other thread, and we have to bind
|
||||||
|
// a context in open_window()).
|
||||||
|
|
||||||
|
switch (window->get_pipe()->get_preferred_window_thread()) {
|
||||||
|
case GraphicsPipe::PWT_app:
|
||||||
|
_app.add_window(_app._window, window);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GraphicsPipe::PWT_draw:
|
||||||
|
draw->add_window(draw->_window, window);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_windows.push_back(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steal the list, since remove_window() may remove from _new_windows.
|
||||||
|
new_windows.swap(_new_windows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do_resort_windows();
|
||||||
|
|
||||||
// We do it twice, to allow both cull and draw to process the window.
|
// We do it twice, to allow both cull and draw to process the window.
|
||||||
for (int i = 0; i < 2; ++i) {
|
for (int i = 0; i < 2; ++i) {
|
||||||
_app.do_windows(this, current_thread);
|
_app.do_windows(this, current_thread);
|
||||||
@ -970,7 +1016,15 @@ open_windows() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_needs_open_windows = false;
|
// Now go through the list again to check whether they opened successfully.
|
||||||
|
for (auto it = new_windows.begin(); it != new_windows.end(); ++it) {
|
||||||
|
GraphicsOutput *window = *it;
|
||||||
|
if (window->is_valid()) {
|
||||||
|
do_add_gsg(window->get_gsg(), window->get_pipe());
|
||||||
|
} else {
|
||||||
|
remove_window(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1927,10 +1981,10 @@ do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg, DisplayRegion *dr, Thre
|
|||||||
* list of windows, and to request that the window be opened.
|
* list of windows, and to request that the window be opened.
|
||||||
*/
|
*/
|
||||||
void GraphicsEngine::
|
void GraphicsEngine::
|
||||||
do_add_window(GraphicsOutput *window,
|
do_add_window(GraphicsOutput *window) {
|
||||||
const GraphicsThreadingModel &threading_model) {
|
nassertv(window != nullptr);
|
||||||
nassertv(window != NULL);
|
|
||||||
ReMutexHolder holder(_lock);
|
MutexHolder holder(_new_windows_lock);
|
||||||
nassertv(window->get_engine() == this);
|
nassertv(window->get_engine() == this);
|
||||||
|
|
||||||
// We have a special counter that is unique per window that allows us to
|
// We have a special counter that is unique per window that allows us to
|
||||||
@ -1938,50 +1992,13 @@ do_add_window(GraphicsOutput *window,
|
|||||||
window->_internal_sort_index = _window_sort_index;
|
window->_internal_sort_index = _window_sort_index;
|
||||||
++_window_sort_index;
|
++_window_sort_index;
|
||||||
|
|
||||||
_windows_sorted = false;
|
|
||||||
_windows.push_back(window);
|
|
||||||
|
|
||||||
WindowRenderer *cull =
|
|
||||||
get_window_renderer(threading_model.get_cull_name(),
|
|
||||||
threading_model.get_cull_stage());
|
|
||||||
WindowRenderer *draw =
|
|
||||||
get_window_renderer(threading_model.get_draw_name(),
|
|
||||||
threading_model.get_draw_stage());
|
|
||||||
|
|
||||||
if (threading_model.get_cull_sorting()) {
|
|
||||||
cull->add_window(cull->_cull, window);
|
|
||||||
draw->add_window(draw->_draw, window);
|
|
||||||
} else {
|
|
||||||
cull->add_window(cull->_cdraw, window);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ask the pipe which thread it prefers to run its windowing commands in (the
|
|
||||||
* "window thread"). This is the thread that handles the commands to open,
|
|
||||||
* resize, etc. the window. X requires this to be done in the app thread
|
|
||||||
* (along with all the other windows, since X is strictly single-threaded),
|
|
||||||
* but Windows requires this to be done in draw (because once an OpenGL
|
|
||||||
* context has been bound in a given thread, it cannot subsequently be bound
|
|
||||||
* in any other thread, and we have to bind a context in open_window()).
|
|
||||||
*/
|
|
||||||
|
|
||||||
switch (window->get_pipe()->get_preferred_window_thread()) {
|
|
||||||
case GraphicsPipe::PWT_app:
|
|
||||||
_app.add_window(_app._window, window);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GraphicsPipe::PWT_draw:
|
|
||||||
draw->add_window(draw->_window, window);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (display_cat.is_debug()) {
|
if (display_cat.is_debug()) {
|
||||||
display_cat.debug()
|
display_cat.debug()
|
||||||
<< "Created " << window->get_type() << " " << (void *)window << "\n";
|
<< "Created " << window->get_type() << " " << (void *)window << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
window->request_open();
|
window->request_open();
|
||||||
_needs_open_windows = true;
|
_new_windows.push_back(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1990,13 +2007,12 @@ do_add_window(GraphicsOutput *window,
|
|||||||
* variables based on the gsg's capabilities.
|
* variables based on the gsg's capabilities.
|
||||||
*/
|
*/
|
||||||
void GraphicsEngine::
|
void GraphicsEngine::
|
||||||
do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
|
do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe) {
|
||||||
const GraphicsThreadingModel &threading_model) {
|
|
||||||
nassertv(gsg != NULL);
|
nassertv(gsg != NULL);
|
||||||
|
|
||||||
ReMutexHolder holder(_lock);
|
ReMutexHolder holder(_lock);
|
||||||
nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this);
|
nassertv(gsg->get_pipe() == pipe && gsg->get_engine() == this);
|
||||||
gsg->_threading_model = threading_model;
|
gsg->_threading_model = _threading_model;
|
||||||
if (!_default_loader.is_null()) {
|
if (!_default_loader.is_null()) {
|
||||||
gsg->set_loader(_default_loader);
|
gsg->set_loader(_default_loader);
|
||||||
}
|
}
|
||||||
@ -2004,8 +2020,8 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
|
|||||||
auto_adjust_capabilities(gsg);
|
auto_adjust_capabilities(gsg);
|
||||||
|
|
||||||
WindowRenderer *draw =
|
WindowRenderer *draw =
|
||||||
get_window_renderer(threading_model.get_draw_name(),
|
get_window_renderer(_threading_model.get_draw_name(),
|
||||||
threading_model.get_draw_stage());
|
_threading_model.get_draw_stage());
|
||||||
|
|
||||||
draw->add_gsg(gsg);
|
draw->add_gsg(gsg);
|
||||||
}
|
}
|
||||||
|
@ -166,10 +166,8 @@ private:
|
|||||||
void do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg,
|
void do_draw(GraphicsOutput *win, GraphicsStateGuardian *gsg,
|
||||||
DisplayRegion *dr, Thread *current_thread);
|
DisplayRegion *dr, Thread *current_thread);
|
||||||
|
|
||||||
void do_add_window(GraphicsOutput *window,
|
void do_add_window(GraphicsOutput *window);
|
||||||
const GraphicsThreadingModel &threading_model);
|
void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe);
|
||||||
void do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
|
|
||||||
const GraphicsThreadingModel &threading_model);
|
|
||||||
void do_remove_window(GraphicsOutput *window, Thread *current_thread);
|
void do_remove_window(GraphicsOutput *window, Thread *current_thread);
|
||||||
void do_resort_windows();
|
void do_resort_windows();
|
||||||
void terminate_threads(Thread *current_thread);
|
void terminate_threads(Thread *current_thread);
|
||||||
@ -308,8 +306,11 @@ private:
|
|||||||
Pipeline *_pipeline;
|
Pipeline *_pipeline;
|
||||||
Windows _windows;
|
Windows _windows;
|
||||||
bool _windows_sorted;
|
bool _windows_sorted;
|
||||||
|
|
||||||
|
// This lock protects the next two fields.
|
||||||
|
Mutex _new_windows_lock;
|
||||||
unsigned int _window_sort_index;
|
unsigned int _window_sort_index;
|
||||||
bool _needs_open_windows;
|
pvector<PT(GraphicsOutput)> _new_windows;
|
||||||
|
|
||||||
WindowRenderer _app;
|
WindowRenderer _app;
|
||||||
typedef pmap<string, PT(RenderThread) > Threads;
|
typedef pmap<string, PT(RenderThread) > Threads;
|
||||||
|
@ -3231,9 +3231,10 @@ async_reload_texture(TextureContext *tc) {
|
|||||||
PT(Texture) GraphicsStateGuardian::
|
PT(Texture) GraphicsStateGuardian::
|
||||||
get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) {
|
get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) {
|
||||||
PandaNode *node = light_np.node();
|
PandaNode *node = light_np.node();
|
||||||
|
bool is_point = node->is_of_type(PointLight::get_class_type());
|
||||||
nassertr(node->is_of_type(DirectionalLight::get_class_type()) ||
|
nassertr(node->is_of_type(DirectionalLight::get_class_type()) ||
|
||||||
node->is_of_type(PointLight::get_class_type()) ||
|
node->is_of_type(Spotlight::get_class_type()) ||
|
||||||
node->is_of_type(Spotlight::get_class_type()), NULL);
|
is_point, nullptr);
|
||||||
|
|
||||||
LightLensNode *light = (LightLensNode *)node;
|
LightLensNode *light = (LightLensNode *)node;
|
||||||
if (light == nullptr || !light->_shadow_caster) {
|
if (light == nullptr || !light->_shadow_caster) {
|
||||||
@ -3246,20 +3247,49 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The light's shadow map should have been created by set_shadow_caster().
|
||||||
|
nassertr(light->_shadow_map != nullptr, nullptr);
|
||||||
|
|
||||||
// See if we already have a buffer. If not, create one.
|
// See if we already have a buffer. If not, create one.
|
||||||
if (light->_sbuffers.count(this) == 0) {
|
if (light->_sbuffers.count(this) != 0) {
|
||||||
if (host == (GraphicsOutputBase *)NULL) {
|
|
||||||
host = _current_display_region->get_window();
|
|
||||||
}
|
|
||||||
nassertr(host != NULL, NULL);
|
|
||||||
|
|
||||||
// Nope, the light doesn't have a buffer for our GSG. Make one.
|
|
||||||
return make_shadow_buffer(light_np, host);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// There's already a buffer - use that.
|
// There's already a buffer - use that.
|
||||||
return light->_sbuffers[this]->get_texture();
|
return light->_shadow_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (display_cat.is_debug()) {
|
||||||
|
display_cat.debug()
|
||||||
|
<< "Constructing shadow buffer for light '" << light->get_name()
|
||||||
|
<< "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1]
|
||||||
|
<< ", sort=" << light->_sb_sort << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host == nullptr) {
|
||||||
|
nassertr(_current_display_region != nullptr, nullptr);
|
||||||
|
host = _current_display_region->get_window();
|
||||||
|
}
|
||||||
|
nassertr(host != nullptr, nullptr);
|
||||||
|
|
||||||
|
// Nope, the light doesn't have a buffer for our GSG. Make one.
|
||||||
|
GraphicsOutput *sbuffer = make_shadow_buffer(light, light->_shadow_map,
|
||||||
|
DCAST(GraphicsOutput, host));
|
||||||
|
|
||||||
|
// Assign display region(s) to the buffer and camera
|
||||||
|
if (is_point) {
|
||||||
|
for (int i = 0; i < 6; ++i) {
|
||||||
|
PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1);
|
||||||
|
dr->set_lens_index(i);
|
||||||
|
dr->set_target_tex_page(i);
|
||||||
|
dr->set_camera(light_np);
|
||||||
|
dr->set_clear_depth_active(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1);
|
||||||
|
dr->set_camera(light_np);
|
||||||
|
dr->set_clear_depth_active(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
light->_sbuffers[this] = sbuffer;
|
||||||
|
return light->_shadow_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3299,101 +3329,33 @@ get_dummy_shadow_map(Texture::TextureType texture_type) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a depth buffer for shadow mapping. This is a convenience function
|
* Creates a depth buffer for shadow mapping. A derived GSG can override this
|
||||||
* for the ShaderGenerator; putting this directly in the ShaderGenerator would
|
* if it knows that a particular buffer type works best for shadow rendering.
|
||||||
* cause circular dependency issues. Returns the depth texture.
|
|
||||||
*/
|
*/
|
||||||
PT(Texture) GraphicsStateGuardian::
|
GraphicsOutput *GraphicsStateGuardian::
|
||||||
make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
|
make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) {
|
||||||
// Make sure everything is valid.
|
|
||||||
PandaNode *node = light_np.node();
|
|
||||||
nassertr(node->is_of_type(DirectionalLight::get_class_type()) ||
|
|
||||||
node->is_of_type(PointLight::get_class_type()) ||
|
|
||||||
node->is_of_type(Spotlight::get_class_type()), NULL);
|
|
||||||
|
|
||||||
LightLensNode *light = (LightLensNode *)node;
|
|
||||||
if (light == NULL || !light->_shadow_caster) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_point = light->is_of_type(PointLight::get_class_type());
|
bool is_point = light->is_of_type(PointLight::get_class_type());
|
||||||
|
|
||||||
nassertr(light->_sbuffers.count(this) == 0, NULL);
|
|
||||||
|
|
||||||
if (display_cat.is_debug()) {
|
|
||||||
display_cat.debug()
|
|
||||||
<< "Constructing shadow buffer for light '" << light->get_name()
|
|
||||||
<< "', size=" << light->_sb_size[0] << "x" << light->_sb_size[1]
|
|
||||||
<< ", sort=" << light->_sb_sort << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the properties for creating the depth buffer.
|
// Determine the properties for creating the depth buffer.
|
||||||
FrameBufferProperties fbp;
|
FrameBufferProperties fbp;
|
||||||
fbp.set_depth_bits(shadow_depth_bits);
|
fbp.set_depth_bits(shadow_depth_bits);
|
||||||
|
|
||||||
WindowProperties props = WindowProperties::size(light->_sb_size[0], light->_sb_size[1]);
|
WindowProperties props = WindowProperties::size(light->_sb_size);
|
||||||
int flags = GraphicsPipe::BF_refuse_window;
|
int flags = GraphicsPipe::BF_refuse_window;
|
||||||
if (is_point) {
|
if (is_point) {
|
||||||
flags |= GraphicsPipe::BF_size_square;
|
flags |= GraphicsPipe::BF_size_square;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the buffer
|
// Create the buffer. This is a bit tricky because make_output() can only
|
||||||
PT(GraphicsOutput) sbuffer = get_engine()->make_output(get_pipe(), light->get_name(),
|
// be called from the app thread, but it won't cause issues as long as the
|
||||||
light->_sb_sort, fbp, props, flags, this, DCAST(GraphicsOutput, host));
|
// pipe can precertify the buffer, which it can in most cases.
|
||||||
nassertr(sbuffer != NULL, NULL);
|
GraphicsOutput *sbuffer = get_engine()->make_output(get_pipe(),
|
||||||
|
light->get_name(), light->_sb_sort, fbp, props, flags, this, host);
|
||||||
|
|
||||||
// Create a texture and fill it in with some data to workaround an OpenGL
|
if (sbuffer != nullptr) {
|
||||||
// error
|
sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);
|
||||||
PT(Texture) tex = new Texture(light->get_name());
|
|
||||||
if (is_point) {
|
|
||||||
if (light->_sb_size[0] != light->_sb_size[1]) {
|
|
||||||
display_cat.error()
|
|
||||||
<< "PointLight shadow buffers must have an equal width and height!\n";
|
|
||||||
}
|
|
||||||
tex->setup_cube_map(light->_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component);
|
|
||||||
} else {
|
|
||||||
tex->setup_2d_texture(light->_sb_size[0], light->_sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component);
|
|
||||||
}
|
}
|
||||||
tex->make_ram_image();
|
return sbuffer;
|
||||||
sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);
|
|
||||||
|
|
||||||
// Set the wrap mode
|
|
||||||
if (is_point) {
|
|
||||||
tex->set_wrap_u(SamplerState::WM_clamp);
|
|
||||||
tex->set_wrap_v(SamplerState::WM_clamp);
|
|
||||||
} else {
|
|
||||||
tex->set_wrap_u(SamplerState::WM_border_color);
|
|
||||||
tex->set_wrap_v(SamplerState::WM_border_color);
|
|
||||||
tex->set_border_color(LVecBase4(1, 1, 1, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: cube map shadow filtering doesn't seem to work in Cg.
|
|
||||||
if (get_supports_shadow_filter() && !is_point) {
|
|
||||||
// If we have the ARB_shadow extension, enable shadow filtering.
|
|
||||||
tex->set_minfilter(SamplerState::FT_shadow);
|
|
||||||
tex->set_magfilter(SamplerState::FT_shadow);
|
|
||||||
} else {
|
|
||||||
tex->set_minfilter(SamplerState::FT_linear);
|
|
||||||
tex->set_magfilter(SamplerState::FT_linear);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign display region(s) to the buffer and camera
|
|
||||||
if (is_point) {
|
|
||||||
for (int i = 0; i < 6; ++i) {
|
|
||||||
PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1);
|
|
||||||
dr->set_lens_index(i);
|
|
||||||
dr->set_target_tex_page(i);
|
|
||||||
dr->set_camera(light_np);
|
|
||||||
dr->set_clear_depth_active(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PT(DisplayRegion) dr = sbuffer->make_mono_display_region(0, 1, 0, 1);
|
|
||||||
dr->set_camera(light_np);
|
|
||||||
dr->set_clear_depth_active(true);
|
|
||||||
}
|
|
||||||
light->_sbuffers[this] = sbuffer;
|
|
||||||
|
|
||||||
return tex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -424,7 +424,7 @@ public:
|
|||||||
|
|
||||||
PT(Texture) get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host=NULL);
|
PT(Texture) get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host=NULL);
|
||||||
PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const;
|
PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const;
|
||||||
PT(Texture) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host);
|
virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host);
|
||||||
|
|
||||||
virtual void ensure_generated_shader(const RenderState *state);
|
virtual void ensure_generated_shader(const RenderState *state);
|
||||||
|
|
||||||
|
@ -133,6 +133,12 @@ clear_default() {
|
|||||||
* size is the only property that matters to buffers.
|
* size is the only property that matters to buffers.
|
||||||
*/
|
*/
|
||||||
WindowProperties WindowProperties::
|
WindowProperties WindowProperties::
|
||||||
|
size(const LVecBase2i &size) {
|
||||||
|
WindowProperties props;
|
||||||
|
props.set_size(size);
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
WindowProperties WindowProperties::
|
||||||
size(int x_size, int y_size) {
|
size(int x_size, int y_size) {
|
||||||
WindowProperties props;
|
WindowProperties props;
|
||||||
props.set_size(x_size, y_size);
|
props.set_size(x_size, y_size);
|
||||||
|
@ -52,6 +52,7 @@ PUBLISHED:
|
|||||||
MAKE_PROPERTY(config_properties, get_config_properties);
|
MAKE_PROPERTY(config_properties, get_config_properties);
|
||||||
MAKE_PROPERTY(default, get_default, set_default);
|
MAKE_PROPERTY(default, get_default, set_default);
|
||||||
|
|
||||||
|
static WindowProperties size(const LVecBase2i &size);
|
||||||
static WindowProperties size(int x_size, int y_size);
|
static WindowProperties size(int x_size, int y_size);
|
||||||
|
|
||||||
bool operator == (const WindowProperties &other) const;
|
bool operator == (const WindowProperties &other) const;
|
||||||
|
@ -7561,6 +7561,36 @@ bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
|
|||||||
}
|
}
|
||||||
#endif // SUPPORT_FIXED_FUNCTION
|
#endif // SUPPORT_FIXED_FUNCTION
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a depth buffer for shadow mapping. A derived GSG can override this
|
||||||
|
* if it knows that a particular buffer type works best for shadow rendering.
|
||||||
|
*/
|
||||||
|
GraphicsOutput *CLP(GraphicsStateGuardian)::
|
||||||
|
make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host) {
|
||||||
|
// We override this to circumvent the fact that GraphicsEngine::make_output
|
||||||
|
// can only be called from the app thread.
|
||||||
|
if (!_supports_framebuffer_object) {
|
||||||
|
return GraphicsStateGuardian::make_shadow_buffer(light, tex, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_point = light->is_of_type(PointLight::get_class_type());
|
||||||
|
|
||||||
|
// Determine the properties for creating the depth buffer.
|
||||||
|
FrameBufferProperties fbp;
|
||||||
|
fbp.set_depth_bits(shadow_depth_bits);
|
||||||
|
|
||||||
|
WindowProperties props = WindowProperties::size(light->get_shadow_buffer_size());
|
||||||
|
int flags = GraphicsPipe::BF_refuse_window;
|
||||||
|
if (is_point) {
|
||||||
|
flags |= GraphicsPipe::BF_size_square;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLP(GraphicsBuffer) *sbuffer = new GLGraphicsBuffer(get_engine(), get_pipe(), light->get_name(), fbp, props, flags, this, host);
|
||||||
|
sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, GraphicsOutput::RTP_depth);
|
||||||
|
get_engine()->add_window(sbuffer, light->get_shadow_buffer_sort());
|
||||||
|
return sbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SUPPORT_IMMEDIATE_MODE
|
#ifdef SUPPORT_IMMEDIATE_MODE
|
||||||
/**
|
/**
|
||||||
* Uses the ImmediateModeSender to draw a series of primitives of the
|
* Uses the ImmediateModeSender to draw a series of primitives of the
|
||||||
|
@ -381,6 +381,8 @@ public:
|
|||||||
int light_id);
|
int light_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
virtual GraphicsOutput *make_shadow_buffer(LightLensNode *light, Texture *tex, GraphicsOutput *host);
|
||||||
|
|
||||||
LVecBase4 get_light_color(Light *light) const;
|
LVecBase4 get_light_color(Light *light) const;
|
||||||
|
|
||||||
#ifdef SUPPORT_IMMEDIATE_MODE
|
#ifdef SUPPORT_IMMEDIATE_MODE
|
||||||
|
@ -41,6 +41,9 @@ set_shadow_caster(bool caster) {
|
|||||||
}
|
}
|
||||||
_shadow_caster = caster;
|
_shadow_caster = caster;
|
||||||
set_active(caster);
|
set_active(caster);
|
||||||
|
if (caster) {
|
||||||
|
setup_shadow_map();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,6 +68,17 @@ set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int buffer_so
|
|||||||
_sb_sort = buffer_sort;
|
_sb_sort = buffer_sort;
|
||||||
}
|
}
|
||||||
set_active(caster);
|
set_active(caster);
|
||||||
|
if (caster) {
|
||||||
|
setup_shadow_map();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the sort of the shadow buffer to be created for this light source.
|
||||||
|
*/
|
||||||
|
INLINE int LightLensNode::
|
||||||
|
get_shadow_buffer_sort() const {
|
||||||
|
return _sb_sort;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,8 +96,9 @@ INLINE void LightLensNode::
|
|||||||
set_shadow_buffer_size(const LVecBase2i &size) {
|
set_shadow_buffer_size(const LVecBase2i &size) {
|
||||||
if (size != _sb_size) {
|
if (size != _sb_size) {
|
||||||
clear_shadow_buffers();
|
clear_shadow_buffers();
|
||||||
|
_sb_size = size;
|
||||||
|
setup_shadow_map();
|
||||||
}
|
}
|
||||||
_sb_size = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,6 +67,9 @@ LightLensNode(const LightLensNode ©) :
|
|||||||
_has_specular_color(copy._has_specular_color),
|
_has_specular_color(copy._has_specular_color),
|
||||||
_attrib_count(0)
|
_attrib_count(0)
|
||||||
{
|
{
|
||||||
|
if (_shadow_caster) {
|
||||||
|
setup_shadow_map();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,20 +78,43 @@ LightLensNode(const LightLensNode ©) :
|
|||||||
*/
|
*/
|
||||||
void LightLensNode::
|
void LightLensNode::
|
||||||
clear_shadow_buffers() {
|
clear_shadow_buffers() {
|
||||||
|
if (_shadow_map) {
|
||||||
|
// Clear it to all ones, so that any shaders that might still be using
|
||||||
|
// it will see the shadows being disabled.
|
||||||
|
_shadow_map->clear_image();
|
||||||
|
}
|
||||||
|
|
||||||
ShadowBuffers::iterator it;
|
ShadowBuffers::iterator it;
|
||||||
for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) {
|
for(it = _sbuffers.begin(); it != _sbuffers.end(); ++it) {
|
||||||
PT(Texture) tex = (*it).second->get_texture();
|
|
||||||
if (tex) {
|
|
||||||
// Clear it to all ones, so that any shaders that might still be using
|
|
||||||
// it will see the shadows being disabled.
|
|
||||||
tex->set_clear_color(LColor(1));
|
|
||||||
tex->clear_image();
|
|
||||||
}
|
|
||||||
(*it).first->remove_window((*it).second);
|
(*it).first->remove_window((*it).second);
|
||||||
}
|
}
|
||||||
_sbuffers.clear();
|
_sbuffers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the shadow map texture. Can be overridden.
|
||||||
|
*/
|
||||||
|
void LightLensNode::
|
||||||
|
setup_shadow_map() {
|
||||||
|
if (_shadow_map != nullptr &&
|
||||||
|
_shadow_map->get_x_size() == _sb_size[0] &&
|
||||||
|
_shadow_map->get_y_size() == _sb_size[1]) {
|
||||||
|
// Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_shadow_map == nullptr) {
|
||||||
|
_shadow_map = new Texture(get_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
_shadow_map->setup_2d_texture(_sb_size[0], _sb_size[1], Texture::T_unsigned_byte, Texture::F_depth_component);
|
||||||
|
_shadow_map->set_clear_color(LColor(1));
|
||||||
|
_shadow_map->set_wrap_u(SamplerState::WM_border_color);
|
||||||
|
_shadow_map->set_wrap_v(SamplerState::WM_border_color);
|
||||||
|
_shadow_map->set_border_color(LColor(1));
|
||||||
|
_shadow_map->set_minfilter(SamplerState::FT_shadow);
|
||||||
|
_shadow_map->set_magfilter(SamplerState::FT_shadow);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is called when the light is added to a LightAttrib.
|
* This is called when the light is added to a LightAttrib.
|
||||||
|
@ -41,6 +41,8 @@ PUBLISHED:
|
|||||||
INLINE void set_shadow_caster(bool caster);
|
INLINE void set_shadow_caster(bool caster);
|
||||||
INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
|
INLINE void set_shadow_caster(bool caster, int buffer_xsize, int buffer_ysize, int sort = -10);
|
||||||
|
|
||||||
|
INLINE int get_shadow_buffer_sort() const;
|
||||||
|
|
||||||
INLINE LVecBase2i get_shadow_buffer_size() const;
|
INLINE LVecBase2i get_shadow_buffer_size() const;
|
||||||
INLINE void set_shadow_buffer_size(const LVecBase2i &size);
|
INLINE void set_shadow_buffer_size(const LVecBase2i &size);
|
||||||
|
|
||||||
@ -53,12 +55,15 @@ PUBLISHED:
|
|||||||
protected:
|
protected:
|
||||||
LightLensNode(const LightLensNode ©);
|
LightLensNode(const LightLensNode ©);
|
||||||
void clear_shadow_buffers();
|
void clear_shadow_buffers();
|
||||||
|
virtual void setup_shadow_map();
|
||||||
|
|
||||||
LVecBase2i _sb_size;
|
LVecBase2i _sb_size;
|
||||||
bool _shadow_caster;
|
bool _shadow_caster;
|
||||||
bool _has_specular_color;
|
bool _has_specular_color;
|
||||||
int _sb_sort;
|
int _sb_sort;
|
||||||
|
|
||||||
|
PT(Texture) _shadow_map;
|
||||||
|
|
||||||
// This is really a map of GSG -> GraphicsOutput.
|
// This is really a map of GSG -> GraphicsOutput.
|
||||||
typedef pmap<PT(GraphicsStateGuardianBase), PT(GraphicsOutputBase) > ShadowBuffers;
|
typedef pmap<PT(GraphicsStateGuardianBase), PT(GraphicsOutputBase) > ShadowBuffers;
|
||||||
ShadowBuffers _sbuffers;
|
ShadowBuffers _sbuffers;
|
||||||
@ -106,7 +111,6 @@ private:
|
|||||||
static TypeHandle _type_handle;
|
static TypeHandle _type_handle;
|
||||||
|
|
||||||
friend class GraphicsStateGuardian;
|
friend class GraphicsStateGuardian;
|
||||||
friend class ShaderGenerator;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
INLINE ostream &operator << (ostream &out, const LightLensNode &light) {
|
INLINE ostream &operator << (ostream &out, const LightLensNode &light) {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "bamReader.h"
|
#include "bamReader.h"
|
||||||
#include "datagram.h"
|
#include "datagram.h"
|
||||||
#include "datagramIterator.h"
|
#include "datagramIterator.h"
|
||||||
|
#include "config_pgraphnodes.h"
|
||||||
|
|
||||||
TypeHandle PointLight::_type_handle;
|
TypeHandle PointLight::_type_handle;
|
||||||
|
|
||||||
@ -184,6 +185,35 @@ bind(GraphicsStateGuardianBase *gsg, const NodePath &light, int light_id) {
|
|||||||
gsg->bind_light(this, light, light_id);
|
gsg->bind_light(this, light, light_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the shadow map texture. Can be overridden.
|
||||||
|
*/
|
||||||
|
void PointLight::
|
||||||
|
setup_shadow_map() {
|
||||||
|
if (_shadow_map != nullptr && _shadow_map->get_x_size() == _sb_size[0]) {
|
||||||
|
// Nothing to do.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_sb_size[0] != _sb_size[1]) {
|
||||||
|
pgraphnodes_cat.error()
|
||||||
|
<< "PointLight shadow buffers must have an equal width and height!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_shadow_map == nullptr) {
|
||||||
|
_shadow_map = new Texture(get_name());
|
||||||
|
}
|
||||||
|
|
||||||
|
_shadow_map->setup_cube_map(_sb_size[0], Texture::T_unsigned_byte, Texture::F_depth_component);
|
||||||
|
_shadow_map->set_clear_color(LColor(1));
|
||||||
|
_shadow_map->set_wrap_u(SamplerState::WM_clamp);
|
||||||
|
_shadow_map->set_wrap_v(SamplerState::WM_clamp);
|
||||||
|
|
||||||
|
// Note: cube map shadow filtering doesn't seem to work in Cg.
|
||||||
|
_shadow_map->set_minfilter(SamplerState::FT_linear);
|
||||||
|
_shadow_map->set_magfilter(SamplerState::FT_linear);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells the BamReader how to create objects of type PointLight.
|
* Tells the BamReader how to create objects of type PointLight.
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +63,8 @@ public:
|
|||||||
int light_id);
|
int light_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual void setup_shadow_map();
|
||||||
|
|
||||||
// This is the data that must be cycled between pipeline stages.
|
// This is the data that must be cycled between pipeline stages.
|
||||||
class EXPCL_PANDA_PGRAPHNODES CData : public CycleData {
|
class EXPCL_PANDA_PGRAPHNODES CData : public CycleData {
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user