support real-time rendered cube maps

This commit is contained in:
David Rose 2005-03-04 22:28:19 +00:00
parent eacf0ccd13
commit c045c5894d
30 changed files with 854 additions and 240 deletions

View File

@ -50,6 +50,34 @@ get_sort() const {
return _sort;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::set_cube_map_index
// Access: Published
// Description: This is a special parameter that is only used when
// rendering the faces of a cube map. Normally you
// should not need to set it directly. This sets up the
// DisplayRegion to render to the nth cube map face; the
// value must be between 0 and 5, inclusive. A normal
// DisplayRegion that is not associated with any
// particular cube map should be set to -1.
////////////////////////////////////////////////////////////////////
INLINE void DisplayRegion::
set_cube_map_index(int cube_map_index) {
_cube_map_index = cube_map_index;
}
////////////////////////////////////////////////////////////////////
// Function: DisplayRegion::get_cube_map_index
// Access: Published
// Description: Returns the cube map face index associated with this
// particular DisplayRegion, or -1 if it is not
// associated with a cube map. See
// set_cube_map_index().
////////////////////////////////////////////////////////////////////
INLINE int DisplayRegion::
get_cube_map_index() const {
return _cube_map_index;
}
INLINE ostream &operator << (ostream &out, const DisplayRegion &dr) {
dr.output(out);

View File

@ -37,7 +37,8 @@ DisplayRegion(GraphicsOutput *window) :
_window(window),
_camera_node((Camera *)NULL),
_active(true),
_sort(0)
_sort(0),
_cube_map_index(-1)
{
_draw_buffer_type = window->get_draw_buffer_type();
compute_pixels();
@ -55,7 +56,8 @@ DisplayRegion(GraphicsOutput *window, const float l,
_window(window),
_camera_node((Camera *)NULL),
_active(true),
_sort(0)
_sort(0),
_cube_map_index(-1)
{
_draw_buffer_type = window->get_draw_buffer_type();
compute_pixels();
@ -535,7 +537,7 @@ get_screenshot(PNMImage &image) {
PT(Texture) tex = new Texture;
RenderBuffer buffer = gsg->get_render_buffer(get_screenshot_buffer_type());
if (!gsg->framebuffer_copy_to_ram(tex, this, buffer)) {
if (!gsg->framebuffer_copy_to_ram(tex, -1, this, buffer)) {
return false;
}

View File

@ -82,6 +82,9 @@ PUBLISHED:
void set_sort(int sort);
INLINE int get_sort() const;
INLINE void set_cube_map_index(int cube_map_index);
INLINE int get_cube_map_index() const;
void compute_pixels();
void compute_pixels(int x_size, int y_size);
void get_pixels(int &pl, int &pr, int &pb, int &pt) const;
@ -123,6 +126,7 @@ private:
bool _active;
int _sort;
int _cube_map_index;
// This is used to cache the culling result from last frame's
// drawing into this display region. It should only be accessed or

View File

@ -240,7 +240,7 @@ make_window(GraphicsStateGuardian *gsg, const string &name, int sort) {
////////////////////////////////////////////////////////////////////
GraphicsOutput *GraphicsEngine::
make_buffer(GraphicsStateGuardian *gsg, const string &name,
int sort, int x_size, int y_size, bool want_texture) {
int sort, int x_size, int y_size) {
if (show_buffers) {
GraphicsWindow *window = make_window(gsg, name, sort);
if (window != (GraphicsWindow *)NULL) {
@ -249,11 +249,6 @@ make_buffer(GraphicsStateGuardian *gsg, const string &name,
props.set_fixed_size(true);
props.set_title(name);
window->request_properties(props);
if (want_texture) {
window->setup_render_texture();
}
return window;
}
}
@ -269,9 +264,6 @@ make_buffer(GraphicsStateGuardian *gsg, const string &name,
gsg->get_pipe()->make_buffer(gsg, name, x_size, y_size);
if (buffer != (GraphicsBuffer *)NULL) {
buffer->_sort = sort;
if (want_texture) {
buffer->setup_render_texture();
}
do_add_window(buffer, gsg, threading_model);
}
return buffer;
@ -303,7 +295,6 @@ make_parasite(GraphicsOutput *host, const string &name,
props.set_fixed_size(true);
props.set_title(name);
window->request_properties(props);
window->setup_render_texture();
return window;
}
@ -317,7 +308,6 @@ make_parasite(GraphicsOutput *host, const string &name,
ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size);
buffer->_sort = sort;
buffer->setup_render_texture();
do_add_window(buffer, gsg, threading_model);
return buffer;
@ -643,12 +633,12 @@ flip_frame() {
// whichever thread that may be.
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
render_subframe(GraphicsStateGuardian *gsg, DisplayRegion *dr,
render_subframe(GraphicsOutput *win, DisplayRegion *dr,
bool cull_sorting) {
if (cull_sorting) {
cull_bin_draw(gsg, dr);
cull_bin_draw(win, dr);
} else {
cull_and_draw_together(gsg, dr);
cull_and_draw_together(win, dr);
}
}
@ -736,7 +726,7 @@ cull_and_draw_together(const GraphicsEngine::Windows &wlist) {
for (int i = 0; i < num_display_regions; i++) {
DisplayRegion *dr = win->get_active_display_region(i);
if (dr != (DisplayRegion *)NULL) {
cull_and_draw_together(win->get_gsg(), dr);
cull_and_draw_together(win, dr);
}
}
win->end_frame();
@ -765,11 +755,13 @@ cull_and_draw_together(const GraphicsEngine::Windows &wlist) {
// only by render_subframe().
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
cull_and_draw_together(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr) {
GraphicsStateGuardian *gsg = win->get_gsg();
nassertv(gsg != (GraphicsStateGuardian *)NULL);
PT(SceneSetup) scene_setup = setup_scene(gsg, dr);
if (setup_gsg(gsg, scene_setup)) {
win->change_scenes(dr);
DisplayRegionStack old_dr = gsg->push_display_region(dr);
gsg->prepare_display_region();
if (dr->is_any_clear_active()) {
@ -808,7 +800,7 @@ cull_bin_draw(const GraphicsEngine::Windows &wlist) {
for (int i = 0; i < num_display_regions; i++) {
DisplayRegion *dr = win->get_active_display_region(i);
if (dr != (DisplayRegion *)NULL) {
cull_bin_draw(win->get_gsg(), dr);
cull_bin_draw(win, dr);
}
}
win->end_frame();
@ -838,7 +830,8 @@ cull_bin_draw(const GraphicsEngine::Windows &wlist) {
// implementation of cull_bin_draw(), above.
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
cull_bin_draw(GraphicsOutput *win, DisplayRegion *dr) {
GraphicsStateGuardian *gsg = win->get_gsg();
nassertv(gsg != (GraphicsStateGuardian *)NULL);
PT(CullResult) cull_result = dr->_cull_result;
@ -860,7 +853,7 @@ cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr) {
// Now draw.
// This should get deferred into the next pipeline stage.
do_draw(cull_result, scene_setup, gsg, dr);
do_draw(cull_result, scene_setup, win, dr);
}
}
@ -1121,11 +1114,13 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
////////////////////////////////////////////////////////////////////
void GraphicsEngine::
do_draw(CullResult *cull_result, SceneSetup *scene_setup,
GraphicsStateGuardian *gsg, DisplayRegion *dr) {
GraphicsOutput *win, DisplayRegion *dr) {
// Statistics
PStatTimer timer(_draw_pcollector);
GraphicsStateGuardian *gsg = win->get_gsg();
if (setup_gsg(gsg, scene_setup)) {
win->change_scenes(dr);
DisplayRegionStack old_dr = gsg->push_display_region(dr);
gsg->prepare_display_region();
if (dr->is_any_clear_active()) {

View File

@ -80,8 +80,7 @@ PUBLISHED:
GraphicsWindow *make_window(GraphicsStateGuardian *gsg, const string &name,
int sort);
GraphicsOutput *make_buffer(GraphicsStateGuardian *gsg, const string &name,
int sort,
int x_size, int y_size, bool want_texture);
int sort, int x_size, int y_size);
GraphicsOutput *make_parasite(GraphicsOutput *host, const string &name,
int sort, int x_size, int y_size);
@ -98,7 +97,7 @@ PUBLISHED:
void sync_frame();
void flip_frame();
void render_subframe(GraphicsStateGuardian *gsg, DisplayRegion *dr,
void render_subframe(GraphicsOutput *win, DisplayRegion *dr,
bool cull_sorting);
public:
@ -143,10 +142,10 @@ private:
void set_window_sort(GraphicsOutput *window, int sort);
void cull_and_draw_together(const Windows &wlist);
void cull_and_draw_together(GraphicsStateGuardian *gsg, DisplayRegion *dr);
void cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr);
void cull_bin_draw(const Windows &wlist);
void cull_bin_draw(GraphicsStateGuardian *gsg, DisplayRegion *dr);
void cull_bin_draw(GraphicsOutput *win, DisplayRegion *dr);
void make_contexts(const Windows &wlist);
void process_events(const Windows &wlist);
@ -159,7 +158,7 @@ private:
void do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
GraphicsStateGuardian *gsg);
void do_draw(CullResult *cull_result, SceneSetup *scene_setup,
GraphicsStateGuardian *gsg, DisplayRegion *dr);
GraphicsOutput *win, DisplayRegion *dr);
bool setup_gsg(GraphicsStateGuardian *gsg, SceneSetup *scene_setup);

View File

@ -26,12 +26,32 @@
#include "indirectLess.h"
#include "pStatTimer.h"
#include "configVariableBool.h"
#include "camera.h"
#include "displayRegion.h"
#include "lens.h"
#include "perspectiveLens.h"
#include "pointerTo.h"
TypeHandle GraphicsOutput::_type_handle;
PStatCollector GraphicsOutput::_make_current_pcollector("Draw:Make current");
PStatCollector GraphicsOutput::_copy_texture_pcollector("Draw:Copy texture");
struct CubeFaceDef {
const char *_name;
LPoint3f _look_at;
LVector3f _up;
};
static CubeFaceDef cube_faces[6] = {
{ "positive_x", LPoint3f(1, 0, 0), LVector3f(0, -1, 0) },
{ "negative_x", LPoint3f(-1, 0, 0), LVector3f(0, -1, 0) },
{ "positive_y", LPoint3f(0, 1, 0), LVector3f(0, 0, 1) },
{ "negative_y", LPoint3f(0, -1, 0), LVector3f(0, 0, -1) },
{ "positive_z", LPoint3f(0, 0, 1), LVector3f(0, -1, 0) },
{ "negative_z", LPoint3f(0, 0, -1), LVector3f(0, -1, 0) }
};
////////////////////////////////////////////////////////////////////
// Function: GraphicsOutput::Constructor
// Access: Protected
@ -52,10 +72,10 @@ GraphicsOutput(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
_y_size = 0;
_has_size = false;
_is_valid = false;
_copy_texture = false;
_render_texture = false;
_rtm_mode = RTM_none;
_flip_ready = false;
_needs_context = true;
_cube_map_index = -1;
_sort = 0;
_internal_sort_index = 0;
_active = true;
@ -168,42 +188,67 @@ void GraphicsOutput::
detach_texture() {
MutexHolder holder(_lock);
if (_render_texture && _gsg != (GraphicsStateGuardian *)NULL) {
if (_rtm_mode == RTM_bind_texture && _gsg != (GraphicsStateGuardian *)NULL) {
_gsg->framebuffer_release_texture(this, get_texture());
}
_texture = NULL;
_copy_texture = false;
_render_texture = false;
_rtm_mode = RTM_none;
set_inverted(window_inverted);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsOutput::setup_render_texture
// Access: Published, Virtual
// Access: Published
// Description: Creates a new Texture object, suitable for rendering
// the contents of this buffer into, and stores it in
// _texture. This also disassociates the previous
// texture (if any).
//
// If the backend supports it, this will actually set up
// the framebuffer to render directly into texture
// memory; otherwise, the framebuffer will be copied
// into the texture after each frame.
// If tex is not NULL, it is the texture that will be
// set up for rendering into; otherwise, a new Texture
// object will be created (in which case you may call
// get_texture() to retrieve the new texture pointer
// later).
//
// If allow_bind is true, and this GraphicsOutput is an
// offscreen graphics buffer that has not yet been
// rendered into, it will attempt to set up the buffer
// for rendering directly into the texture, avoiding the
// cost of the copy-to-texture-memory each frame. This
// is not supported by all graphics hardware, but if it
// is not supported, this option is quietly ignored.
//
// If to_ram is true, the texture image will be
// downloaded from the framebuffer memory into system
// RAM each frame, which is more expensive but allows
// the texture to subsequently be applied to any GSG.
// Otherwise, the texture image remains in texture
// memory only.
//
// Also see make_texture_buffer(), which is a
// higher-level interface for preparing
// render-to-a-texture mode.
////////////////////////////////////////////////////////////////////
void GraphicsOutput::
setup_render_texture() {
setup_render_texture(Texture *tex, bool allow_bind, bool to_ram) {
MutexHolder holder(_lock);
if (_render_texture && _gsg != (GraphicsStateGuardian *)NULL) {
if (_rtm_mode == RTM_bind_texture && _gsg != (GraphicsStateGuardian *)NULL) {
_gsg->framebuffer_release_texture(this, get_texture());
}
_texture = new Texture(get_name());
if (tex == (Texture *)NULL) {
_texture = new Texture(get_name());
_texture->set_wrap_u(Texture::WM_clamp);
_texture->set_wrap_v(Texture::WM_clamp);
} else {
_texture = tex;
_texture->clear_ram_image();
}
_texture->set_match_framebuffer_format(true);
_texture->set_wrap_u(Texture::WM_clamp);
_texture->set_wrap_v(Texture::WM_clamp);
// Go ahead and tell the texture our anticipated size, even if it
// might be inaccurate (particularly if this is a GraphicsWindow,
@ -211,8 +256,13 @@ setup_render_texture() {
_texture->set_x_size(get_x_size());
_texture->set_y_size(get_y_size());
_copy_texture = true;
_render_texture = false;
if (to_ram) {
_rtm_mode = RTM_copy_ram;
} else if (allow_bind) {
_rtm_mode = RTM_bind_if_possible;
} else {
_rtm_mode = RTM_copy_texture;
}
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
set_inverted(_gsg->get_copy_texture_inverted());
@ -412,6 +462,20 @@ get_active_display_region(int n) const {
// for applying to geometry within the scene rendered
// into this window.
//
// If tex is not NULL, it is the texture that will be
// set up for rendering into; otherwise, a new Texture
// object will be created. In either case, the target
// texture can be retrieved from the return value with
// buffer->get_texture() (assuming the return value is
// not NULL).
//
// If to_ram is true, the buffer will be set up to
// download its contents to the system RAM memory
// associated with the Texture object, instead of
// keeping it strictly within texture memory; this is
// much slower, but it allows using the texture with any
// GSG.
//
// This will attempt to be smart about maximizing render
// performance while minimizing framebuffer waste. It
// might return a GraphicsBuffer set to render directly
@ -420,14 +484,14 @@ get_active_display_region(int n) const {
// return value is NULL if the buffer could not be
// created for some reason.
//
// Assuming the return value is not NULL, the texture
// that is represents the scene rendered to the new
// buffer can be accessed by buffer->get_texture().
// When you are done using the buffer, you should remove
// it with a call to GraphicsEngine::remove_window().
// it with a call to GraphicsEngine::remove_window() (or
// set the one_shot flag so it removes itself after one
// frame).
////////////////////////////////////////////////////////////////////
GraphicsOutput *GraphicsOutput::
make_texture_buffer(const string &name, int x_size, int y_size) {
make_texture_buffer(const string &name, int x_size, int y_size,
Texture *tex, bool to_ram) {
GraphicsStateGuardian *gsg = get_gsg();
GraphicsEngine *engine = gsg->get_engine();
GraphicsOutput *host = get_host();
@ -437,26 +501,30 @@ make_texture_buffer(const string &name, int x_size, int y_size) {
// value himself.
int sort = get_sort() - 1;
GraphicsOutput *buffer = NULL;
if (show_buffers) {
// If show_buffers is true, just go ahead and call make_buffer(),
// since it all amounts to the same thing anyway--this will
// actually create a new GraphicsWindow.
return engine->make_buffer(gsg, name, sort, x_size, y_size, true);
buffer = engine->make_buffer(gsg, name, sort, x_size, y_size);
buffer->setup_render_texture(tex, false, to_ram);
return buffer;
}
GraphicsOutput *buffer = NULL;
bool allow_bind =
(prefer_texture_buffer && gsg->get_supports_render_texture() && !to_ram);
// If the user so indicated in the Config.prc file, try to create a
// parasite buffer first. We can only do this if the requested size
// fits within the available framebuffer size. Also, don't do this
// if the GSG supports render-to-a-texture and prefer_texture_buffer
// is true, since using a ParasiteButter precludes
// render-to-a-texture.
if (prefer_parasite_buffer &&
!(prefer_texture_buffer && gsg->get_supports_render_texture()) &&
// if we want to try using render-to-a-texture mode, since using a
// ParasiteButter will preclude that.
if (prefer_parasite_buffer && !allow_bind &&
(x_size <= host->get_x_size() && y_size <= host->get_y_size())) {
buffer = engine->make_parasite(host, name, sort, x_size, y_size);
if (buffer != (GraphicsOutput *)NULL) {
buffer->setup_render_texture(tex, false, to_ram);
return buffer;
}
}
@ -472,9 +540,10 @@ make_texture_buffer(const string &name, int x_size, int y_size) {
PT(GraphicsStateGuardian) sb_gsg =
engine->make_gsg(gsg->get_pipe(), sb_props, gsg);
if (sb_gsg != (GraphicsStateGuardian *)NULL) {
buffer = engine->make_buffer(sb_gsg, name, sort, x_size, y_size, true);
buffer = engine->make_buffer(sb_gsg, name, sort, x_size, y_size);
if (buffer != (GraphicsOutput *)NULL) {
// Check the buffer for goodness.
buffer->setup_render_texture(tex, allow_bind, to_ram);
engine->open_windows();
if (buffer->is_valid()) {
return buffer;
@ -492,8 +561,9 @@ make_texture_buffer(const string &name, int x_size, int y_size) {
// All right, attempt to create an offscreen buffer, using the same
// GSG. This will be a double-buffered offscreen buffer, if the
// source window is double-buffered.
buffer = engine->make_buffer(gsg, name, sort, x_size, y_size, true);
buffer = engine->make_buffer(gsg, name, sort, x_size, y_size);
if (buffer != (GraphicsOutput *)NULL) {
buffer->setup_render_texture(tex, allow_bind, to_ram);
engine->open_windows();
if (buffer->is_valid()) {
return buffer;
@ -506,12 +576,82 @@ make_texture_buffer(const string &name, int x_size, int y_size) {
// Looks like we have to settle for a parasite buffer.
if (x_size <= host->get_x_size() && y_size <= host->get_y_size()) {
return engine->make_parasite(host, name, sort, x_size, y_size);
buffer = engine->make_parasite(host, name, sort, x_size, y_size);
buffer->setup_render_texture(tex, false, to_ram);
return buffer;
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsOutput::make_cube_map
// Access: Published
// Description: This is similar to make_texture_buffer() in that it
// allocates a separate buffer suitable for rendering to
// a texture that can be assigned to geometry in this
// window, but in this case, the buffer is set up to
// render the six faces of a cube map.
//
// The buffer is automatically set up with six display
// regions and six cameras, each of which are assigned
// the indicated draw_mask and parented to the given
// camera_rig node (which you should then put in your
// scene to render the cube map from the appropriate
// point of view).
//
// You may take the texture associated with the buffer
// and apply it to geometry, particularly with
// TexGenAttrib::M_world_cube_map also in effect, to
// apply a reflection of everything seen by the camera
// rig.
////////////////////////////////////////////////////////////////////
GraphicsOutput *GraphicsOutput::
make_cube_map(const string &name, int size, bool to_ram,
NodePath &camera_rig, DrawMask camera_mask) {
if (!to_ram) {
// Check the limits imposed by the GSG. (However, if we're
// rendering the texture to RAM only, these limits may be
// irrelevant.)
GraphicsStateGuardian *gsg = get_gsg();
int max_dimension = gsg->get_max_cube_map_dimension();
if (max_dimension == 0) {
// The GSG doesn't support cube mapping; too bad for you.
return NULL;
}
if (max_dimension > 0) {
size = min(max_dimension, size);
}
}
PT(Texture) tex = new Texture(name);
tex->setup_cube_map();
GraphicsOutput *buffer = make_texture_buffer(name, size, size, tex, to_ram);
// We don't need to clear the overall buffer; instead, we'll clear
// each display region.
buffer->set_clear_color_active(false);
buffer->set_clear_depth_active(false);
PT(Lens) lens = new PerspectiveLens;
lens->set_fov(90.0f);
for (int i = 0; i < 6; i++) {
PT(Camera) camera = new Camera(cube_faces[i]._name);
camera->set_lens(lens);
camera->set_camera_mask(camera_mask);
NodePath camera_np = camera_rig.attach_new_node(camera);
camera_np.look_at(cube_faces[i]._look_at, cube_faces[i]._up);
DisplayRegion *dr = buffer->make_display_region();
dr->set_cube_map_index(i);
dr->copy_clear_settings(*this);
dr->set_camera(camera_np);
}
return buffer;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsOutput::get_host
// Access: Public, Virtual
@ -604,11 +744,13 @@ begin_frame() {
// Okay, we already have a GSG, so activate it.
make_current();
if (_render_texture) {
if (_rtm_mode == RTM_bind_texture) {
// Release the texture so we can render into the frame buffer.
_gsg->framebuffer_release_texture(this, get_texture());
}
_cube_map_index = -1;
return _gsg->begin_frame();
}
@ -657,17 +799,17 @@ end_frame() {
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
_gsg->end_frame();
// If _copy_texture is true, it means we should copy or lock the
// If _rtm_mode isn't RTM_none, it means we should copy or lock the
// framebuffer to the GraphicsOutput's associated texture after the
// frame has rendered.
if (_copy_texture) {
if (_rtm_mode != RTM_none) {
PStatTimer timer(_copy_texture_pcollector);
nassertv(has_texture());
// If _render_texture is true, it means we should attempt to lock
// the framebuffer directly to the texture memory, avoiding the
// copy.
if (_render_texture) {
// If _rtm_mode is RTM_bind_texture, it means we should attempt to
// lock the framebuffer directly to the texture memory, avoiding
// the copy.
if (_rtm_mode == RTM_bind_texture) {
if (display_cat.is_debug()) {
display_cat.debug()
<< "Locking texture for " << get_name() << " at frame end.\n";
@ -675,17 +817,25 @@ end_frame() {
if (!_gsg->framebuffer_bind_to_texture(this, get_texture())) {
display_cat.warning()
<< "Lock-to-texture failed, resorting to copy.\n";
_render_texture = false;
_rtm_mode = RTM_copy_texture;
}
}
if (!_render_texture) {
if (_rtm_mode != RTM_bind_texture) {
if (display_cat.is_debug()) {
display_cat.debug()
<< "Copying texture for " << get_name() << " at frame end.\n";
display_cat.debug()
<< "cube_map_index = " << _cube_map_index << "\n";
}
RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
_gsg->framebuffer_copy_to_texture(get_texture(), _default_display_region, buffer);
if (_rtm_mode == RTM_copy_ram) {
_gsg->framebuffer_copy_to_ram(get_texture(), _cube_map_index,
_default_display_region, buffer);
} else {
_gsg->framebuffer_copy_to_texture(get_texture(), _cube_map_index,
_default_display_region, buffer);
}
}
}
@ -702,6 +852,68 @@ end_frame() {
_active = false;
_delete_flag = true;
}
_cube_map_index = -1;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsOutput::change_scenes
// Access: Public
// Description: Called by the GraphicsEngine when the window is about
// to change to another DisplayRegion. This exists
// mainly to provide a callback for switching the cube
// map face, if we are rendering to the different faces
// of a cube map.
////////////////////////////////////////////////////////////////////
void GraphicsOutput::
change_scenes(DisplayRegion *new_dr) {
int new_cube_map_index = new_dr->get_cube_map_index();
if (new_cube_map_index != -1 &&
new_cube_map_index != _cube_map_index) {
int old_cube_map_index = _cube_map_index;
_cube_map_index = new_cube_map_index;
if (_rtm_mode != RTM_none) {
if (_rtm_mode == RTM_bind_texture) {
// In render-to-texture mode, switch the rendering backend to
// the new cube map face, so that the subsequent frame will be
// rendered to the new face.
select_cube_map(new_cube_map_index);
} else if (old_cube_map_index != -1) {
// In copy-to-texture mode, copy the just-rendered framebuffer
// to the old cube map face.
if (display_cat.is_debug()) {
display_cat.debug()
<< "Copying texture for " << get_name() << " at scene change.\n";
display_cat.debug()
<< "cube_map_index = " << old_cube_map_index << "\n";
}
RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());
if (_rtm_mode == RTM_copy_ram) {
_gsg->framebuffer_copy_to_ram(get_texture(), old_cube_map_index,
_default_display_region, buffer);
} else {
_gsg->framebuffer_copy_to_texture(get_texture(), old_cube_map_index,
_default_display_region, buffer);
}
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsOutput::select_cube_map
// Access: Public, Virtual
// Description: Called internally when the window is in
// render-to-a-texture mode and we are in the process of
// rendering the six faces of a cube map. This should
// do whatever needs to be done to switch the buffer to
// the indicated face.
////////////////////////////////////////////////////////////////////
void GraphicsOutput::
select_cube_map(int) {
}
////////////////////////////////////////////////////////////////////

View File

@ -32,6 +32,7 @@
#include "notify.h"
#include "pmutex.h"
#include "filename.h"
#include "drawMask.h"
#include "pvector.h"
class PNMImage;
@ -75,7 +76,7 @@ PUBLISHED:
INLINE bool has_texture() const;
INLINE Texture *get_texture() const;
void detach_texture();
virtual void setup_render_texture();
void setup_render_texture(Texture *tex, bool allow_bind, bool to_ram);
INLINE int get_x_size() const;
INLINE int get_y_size() const;
@ -108,7 +109,10 @@ PUBLISHED:
int get_num_active_display_regions() const;
PT(DisplayRegion) get_active_display_region(int n) const;
GraphicsOutput *make_texture_buffer(const string &name, int x_size, int y_size);
GraphicsOutput *make_texture_buffer(const string &name, int x_size, int y_size,
Texture *tex = NULL, bool to_ram = false);
GraphicsOutput *make_cube_map(const string &name, int size, bool to_ram,
NodePath &camera_rig, DrawMask camera_mask);
INLINE Filename save_screenshot_default(const string &prefix = "screenshot");
INLINE bool save_screenshot(const Filename &filename);
@ -136,6 +140,9 @@ public:
void clear();
virtual void end_frame();
void change_scenes(DisplayRegion *new_dr);
virtual void select_cube_map(int cube_map_index);
// This method is called in the draw thread prior to issuing any
// drawing commands for the window.
virtual bool make_context();
@ -152,14 +159,22 @@ public:
virtual void process_events();
protected:
enum RenderTextureMode {
RTM_none,
RTM_bind_texture,
RTM_bind_if_possible,
RTM_copy_texture,
RTM_copy_ram,
};
PT(GraphicsStateGuardian) _gsg;
PT(GraphicsPipe) _pipe;
string _name;
PT(Texture) _texture;
bool _copy_texture;
bool _render_texture;
RenderTextureMode _rtm_mode;
bool _flip_ready;
bool _needs_context;
int _cube_map_index;
private:
DisplayRegion *add_display_region(DisplayRegion *display_region);

View File

@ -151,6 +151,56 @@ get_max_texture_stages() const {
return _max_texture_stages;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_max_texture_dimension
// Access: Published
// Description: Returns the largest possible texture size in any one
// dimension supported by the GSG, or -1 if there is no
// particular limit.
//
// The value returned may not be meaningful until after
// the graphics context has been fully created (e.g. the
// window has been opened).
////////////////////////////////////////////////////////////////////
INLINE int GraphicsStateGuardian::
get_max_texture_dimension() const {
return _max_texture_dimension;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_max_3d_texture_dimension
// Access: Published
// Description: Returns the largest possible texture size in any one
// dimension for a 3-d texture, or -1 if there is no
// particular limit. Returns 0 if 3-d textures are not
// supported.
//
// The value returned may not be meaningful until after
// the graphics context has been fully created (e.g. the
// window has been opened).
////////////////////////////////////////////////////////////////////
INLINE int GraphicsStateGuardian::
get_max_3d_texture_dimension() const {
return _max_3d_texture_dimension;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_max_cube_map_dimension
// Access: Published
// Description: Returns the largest possible texture size in any one
// dimension for a cube map texture, or -1 if there is
// no particular limit. Returns 0 if cube map textures
// are not supported.
//
// The value returned may not be meaningful until after
// the graphics context has been fully created (e.g. the
// window has been opened).
////////////////////////////////////////////////////////////////////
INLINE int GraphicsStateGuardian::
get_max_cube_map_dimension() const {
return _max_cube_map_dimension;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_copy_texture_inverted
// Access: Published

View File

@ -90,6 +90,12 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
// supports multitexturing.
_max_texture_stages = 1;
// Also initially, we assume there are no limits on texture sizes,
// and that 3-d and cube-map textures are not supported.
_max_texture_dimension = -1;
_max_3d_texture_dimension = 0;
_max_cube_map_dimension = 0;
// Initially, we set this to false; a GSG that knows it has this
// property should set it to true.
_copy_texture_inverted = false;

View File

@ -83,6 +83,10 @@ PUBLISHED:
INLINE const GraphicsThreadingModel &get_threading_model() const;
INLINE int get_max_texture_stages() const;
INLINE int get_max_texture_dimension() const;
INLINE int get_max_3d_texture_dimension() const;
INLINE int get_max_cube_map_dimension() const;
INLINE bool get_copy_texture_inverted() const;
virtual bool get_supports_multisample() const;
INLINE bool get_supports_generate_mipmap() const;
@ -278,6 +282,10 @@ protected:
PT(PreparedGraphicsObjects) _prepared_objects;
int _max_texture_stages;
int _max_texture_dimension;
int _max_3d_texture_dimension;
int _max_cube_map_dimension;
bool _copy_texture_inverted;
bool _supports_multisample;
bool _supports_generate_mipmap;

View File

@ -680,7 +680,8 @@ recompute_screen(NonlinearImager::Screen &screen, size_t vi) {
if (screen._buffer == (GraphicsOutput *)NULL) {
GraphicsOutput *win = viewer._dr->get_window();
GraphicsOutput *buffer = win->make_texture_buffer(screen._name, screen._tex_width, screen._tex_height);
GraphicsOutput *buffer = win->make_texture_buffer
(screen._name, screen._tex_width, screen._tex_height, NULL, false);
if (buffer != (GraphicsOutput *)NULL) {
screen._buffer = buffer;

View File

@ -279,12 +279,14 @@ appear before they are referenced.
for instance to apply a reflection map or some other effect. The
valid values for mode are:
SPHERE_MAP
CUBE_MAP
EYE_SPHERE_MAP (or SPHERE_MAP)
WORLD_CUBE_MAP
EYE_CUBE_MAP (or CUBE_MAP)
WORLD_NORMAL
EYE_NORMAL
WORLD_POSITION
OBJECT_POSITION
EYE_POSITION
OBJECT_NORMAL
<Scalar> stage-name { name }

View File

@ -3488,7 +3488,7 @@ release_texture(TextureContext *tc) {
// Description:
////////////////////////////////////////////////////////////////////
void DXGraphicsStateGuardian7::
framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb) {
framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb) {
dxgsg7_cat.error() << "DX copy_texture unimplemented!!!";
}
@ -3498,7 +3498,7 @@ framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr, const RenderB
// Description:
////////////////////////////////////////////////////////////////////
bool DXGraphicsStateGuardian7::
framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr,
framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb) {
set_read_buffer(rb);

View File

@ -91,10 +91,10 @@ public:
virtual void apply_texture(TextureContext *tc);
virtual void release_texture(TextureContext *tc);
virtual void framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr,
virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram(Texture *pb, const DisplayRegion *dr,
virtual bool framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb);
virtual void apply_material(const Material *material);

View File

@ -2783,7 +2783,7 @@ release_texture(TextureContext *tc) {
// copies current display region in framebuffer to the texture
// usually its more efficient to do SetRenderTgt
void DXGraphicsStateGuardian8::
framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb) {
framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb) {
set_read_buffer(rb);
HRESULT hr;
@ -2838,7 +2838,7 @@ framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr, const RenderB
// Description:
////////////////////////////////////////////////////////////////////
bool DXGraphicsStateGuardian8::
framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb) {
framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb) {
set_read_buffer(rb);
RECT SrcCopyRect;
@ -3145,7 +3145,7 @@ issue_tex_gen(const TexGenAttrib *attrib) {
_pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 0);
_pD3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0);
} else if (attrib->get_mode(TextureStage::get_default()) == TexGenAttrib::M_sphere_map) {
} else if (attrib->get_mode(TextureStage::get_default()) == TexGenAttrib::M_eye_sphere_map) {
#if 0
// best reflection on a sphere is achieved by camera space normals in directx

View File

@ -93,9 +93,9 @@ public:
virtual void apply_texture(TextureContext *tc);
virtual void release_texture(TextureContext *tc);
virtual void framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr,
virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr,
virtual bool framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb);
virtual void apply_material(const Material *material);

View File

@ -2766,7 +2766,7 @@ release_texture(TextureContext *tc) {
// copies current display region in framebuffer to the texture
// usually its more efficient to do SetRenderTgt
void DXGraphicsStateGuardian9::
framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb) {
framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb) {
set_read_buffer(rb);
HRESULT hr;
@ -2825,7 +2825,7 @@ framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr, const RenderB
// Description:
////////////////////////////////////////////////////////////////////
bool DXGraphicsStateGuardian9::
framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb) {
framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb) {
set_read_buffer(rb);
RECT SrcCopyRect;
@ -3132,7 +3132,7 @@ issue_tex_gen(const TexGenAttrib *attrib) {
_pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 0);
_pD3DDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0);
} else if (attrib->get_mode(TextureStage::get_default()) == TexGenAttrib::M_sphere_map) {
} else if (attrib->get_mode(TextureStage::get_default()) == TexGenAttrib::M_eye_sphere_map) {
#if 0
// best reflection on a sphere is achieved by camera space normals in directx

View File

@ -94,9 +94,9 @@ public:
virtual void apply_texture(TextureContext *tc);
virtual void release_texture(TextureContext *tc);
virtual void framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr,
virtual void framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr,
virtual bool framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb);
virtual void apply_material(const Material *material);

View File

@ -801,11 +801,22 @@ string_tex_gen(const string &string) {
if (cmp_nocase_uh(string, "unspecified") == 0) {
return TG_unspecified;
} else if (cmp_nocase_uh(string, "sphere_map") == 0) {
return TG_sphere_map;
} else if (cmp_nocase_uh(string, "sphere_map") == 0 ||
cmp_nocase_uh(string, "eye_sphere_map") == 0) {
return TG_eye_sphere_map;
} else if (cmp_nocase_uh(string, "cube_map") == 0) {
return TG_cube_map;
} else if (cmp_nocase_uh(string, "world_cube_map") == 0) {
return TG_world_cube_map;
} else if (cmp_nocase_uh(string, "cube_map") == 0 ||
cmp_nocase_uh(string, "eye_cube_map") == 0) {
return TG_eye_cube_map;
} else if (cmp_nocase_uh(string, "world_normal") == 0) {
return TG_world_normal;
} else if (cmp_nocase_uh(string, "eye_normal") == 0) {
return TG_eye_normal;
} else if (cmp_nocase_uh(string, "world_position") == 0) {
return TG_world_position;
@ -816,9 +827,6 @@ string_tex_gen(const string &string) {
} else if (cmp_nocase_uh(string, "eye_position") == 0) {
return TG_eye_position;
} else if (cmp_nocase_uh(string, "object_normal") == 0) {
return TG_object_normal;
} else {
return TG_unspecified;
}
@ -1114,11 +1122,20 @@ operator << (ostream &out, EggTexture::TexGen tex_gen) {
case EggTexture::TG_unspecified:
return out << "unspecified";
case EggTexture::TG_sphere_map:
return out << "sphere_map";
case EggTexture::TG_eye_sphere_map:
return out << "eye_sphere_map";
case EggTexture::TG_cube_map:
return out << "cube_map";
case EggTexture::TG_world_cube_map:
return out << "world_cube_map";
case EggTexture::TG_eye_cube_map:
return out << "eye_cube_map";
case EggTexture::TG_world_normal:
return out << "world_normal";
case EggTexture::TG_eye_normal:
return out << "eye_normal";
case EggTexture::TG_world_position:
return out << "world_position";
@ -1128,9 +1145,6 @@ operator << (ostream &out, EggTexture::TexGen tex_gen) {
case EggTexture::TG_eye_position:
return out << "eye_position";
case EggTexture::TG_object_normal:
return out << "object_normal";
}
return out << "**invalid TexGen(" << (int)tex_gen << ")**";

View File

@ -129,12 +129,18 @@ PUBLISHED:
};
enum TexGen {
TG_unspecified,
TG_sphere_map,
TG_cube_map,
TG_eye_sphere_map,
TG_world_cube_map,
TG_eye_cube_map,
TG_world_normal,
TG_eye_normal,
TG_world_position,
TG_object_position,
TG_eye_position,
TG_object_normal,
};
INLINE void set_format(Format format);

View File

@ -3115,11 +3115,20 @@ get_tex_gen(const EggTexture *egg_tex) {
case EggTexture::TG_unspecified:
return TexGenAttrib::M_off;
case EggTexture::TG_sphere_map:
return TexGenAttrib::M_sphere_map;
case EggTexture::TG_eye_sphere_map:
return TexGenAttrib::M_eye_sphere_map;
case EggTexture::TG_cube_map:
return TexGenAttrib::M_cube_map;
case EggTexture::TG_world_cube_map:
return TexGenAttrib::M_world_cube_map;
case EggTexture::TG_eye_cube_map:
return TexGenAttrib::M_eye_cube_map;
case EggTexture::TG_world_normal:
return TexGenAttrib::M_world_normal;
case EggTexture::TG_eye_normal:
return TexGenAttrib::M_eye_normal;
case EggTexture::TG_world_position:
return TexGenAttrib::M_world_position;
@ -3129,9 +3138,6 @@ get_tex_gen(const EggTexture *egg_tex) {
case EggTexture::TG_eye_position:
return TexGenAttrib::M_eye_position;
case EggTexture::TG_object_normal:
return TexGenAttrib::M_object_normal;
};
return TexGenAttrib::M_off;

View File

@ -487,25 +487,33 @@ reset() {
_supports_multisample = false;
}
}
GLint max_texture_size;
GLint max_3d_texture_size;
GLint max_cube_map_size;
GLP(GetIntegerv)(GL_MAX_TEXTURE_SIZE, &_max_texture_size);
GLP(GetIntegerv)(GL_MAX_TEXTURE_SIZE, &max_texture_size);
_max_texture_dimension = max_texture_size;
if (_supports_3d_texture) {
GLP(GetIntegerv)(GL_MAX_3D_TEXTURE_SIZE, &_max_3d_texture_size);
GLP(GetIntegerv)(GL_MAX_3D_TEXTURE_SIZE, &max_3d_texture_size);
_max_3d_texture_dimension = max_3d_texture_size;
} else {
_max_3d_texture_size = 0;
_max_3d_texture_dimension = 0;
}
if (_supports_cube_map) {
GLP(GetIntegerv)(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &_max_cube_map_size);
GLP(GetIntegerv)(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &max_cube_map_size);
_max_cube_map_dimension = max_cube_map_size;
} else {
_max_cube_map_size = 0;
_max_cube_map_dimension = 0;
}
if (GLCAT.is_debug()) {
GLCAT.debug()
<< "max texture size = " << _max_texture_size
<< ", max 3d texture = " << _max_3d_texture_size
<< ", max cube map = " << _max_cube_map_size << "\n";
<< "max texture dimension = " << _max_texture_dimension
<< ", max 3d texture = " << _max_3d_texture_dimension
<< ", max cube map = " << _max_cube_map_dimension << "\n";
}
report_my_gl_errors();
@ -723,7 +731,7 @@ do_clear(const RenderBuffer &buffer) {
// Function: CLP(GraphicsStateGuardian)::prepare_display_region
// Access: Public, Virtual
// Description: Prepare a display region for rendering (set up
// scissor region and viewport)
// scissor region and viewport)
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
prepare_display_region() {
@ -2162,13 +2170,15 @@ static int binary_log_cap(const int x) {
// Function: CLP(GraphicsStateGuardian)::framebuffer_copy_to_texture
// Access: Public, Virtual
// Description: Copy the pixels within the indicated display
// region from the framebuffer into texture memory
// region from the framebuffer into texture memory.
//
// If z > -1, it is the cube map index into which to
// copy.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr,
framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb) {
nassertv(tex != NULL && dr != NULL &&
tex->get_texture_type() == Texture::TT_2d_texture);
nassertv(tex != NULL && dr != NULL);
set_read_buffer(rb);
int xo, yo, w, h;
@ -2181,9 +2191,27 @@ framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr,
nassertv(tc != (TextureContext *)NULL);
bind_texture(tc);
GLP(CopyTexImage2D)(GL_TEXTURE_2D, 0,
get_internal_image_format(tex->get_format()),
xo, yo, w, h, 0);
if (z >= 0) {
// Copy to a cube map face.
nassertv(z < 6);
nassertv(tex->get_texture_type() == Texture::TT_cube_map);
if (_supports_cube_map) {
// We cleverly defined the cube map faces to fall in the same
// order as the GL constants are defined, so we can just make this
// simple addition to get to the right GL constant.
GLP(CopyTexImage2D)(GL_TEXTURE_CUBE_MAP_POSITIVE_X + z, 0,
get_internal_image_format(tex->get_format()),
xo, yo, w, h, 0);
}
} else {
// Copy to a regular texture.
nassertv(tex->get_texture_type() == Texture::TT_2d_texture);
GLP(CopyTexImage2D)(GL_TEXTURE_2D, 0,
get_internal_image_format(tex->get_format()),
xo, yo, w, h, 0);
}
// Clear the internal texture state, since we've just monkeyed with it.
modify_state(get_untextured_state());
@ -2195,9 +2223,12 @@ framebuffer_copy_to_texture(Texture *tex, const DisplayRegion *dr,
// Description: Copy the pixels within the indicated display region
// from the framebuffer into system memory, not texture
// memory. Returns true on success, false on failure.
//
// This completely redefines the ram image of the
// indicated texture.
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr,
framebuffer_copy_to_ram(Texture *tex, int z, const DisplayRegion *dr,
const RenderBuffer &rb) {
nassertr(tex != NULL && dr != NULL, false);
set_read_buffer(rb);
@ -2228,7 +2259,21 @@ framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr,
format = Texture::F_rgb;
}
tex->setup_2d_texture(w, h, component_type, format);
Texture::TextureType texture_type;
if (z >= 0) {
texture_type = Texture::TT_cube_map;
} else {
texture_type = Texture::TT_2d_texture;
}
if (tex->get_x_size() != w || tex->get_y_size() != h ||
tex->get_component_type() != component_type ||
tex->get_format() != format ||
tex->get_texture_type() != texture_type) {
// Re-setup the texture; its properties have changed.
tex->setup_texture(texture_type, w, h, 1, component_type, format);
}
GLenum external_format = get_external_image_format(format);
#ifdef GSG_VERBOSE
@ -2272,13 +2317,21 @@ framebuffer_copy_to_ram(Texture *tex, const DisplayRegion *dr,
<< ")" << endl;
#endif
unsigned char *image = tex->modify_ram_image();
if (z >= 0) {
nassertr(z < tex->get_z_size(), false);
image += z * tex->get_expected_ram_page_size();
}
GLP(ReadPixels)(xo, yo, w, h,
external_format, get_component_type(component_type),
tex->make_ram_image());
image);
// We may have to reverse the byte ordering of the image if GL
// didn't do it for us.
if (!_supports_bgr) {
// didn't do it for us. This assumes we render out the six faces of
// a cube map in ascending order, since we can't do this until we
// have rendered the last face.
if (!_supports_bgr && (z == -1 || z == 5)) {
tex->set_ram_image(fix_component_ordering(tex->get_ram_image(),
external_format, tex));
}
@ -3399,15 +3452,15 @@ apply_texture_immediate(CLP(TextureContext) *gtc, Texture *tex) {
int max_dimension;
switch (tex->get_texture_type()) {
case Texture::TT_3d_texture:
max_dimension = _max_3d_texture_size;
max_dimension = _max_3d_texture_dimension;
break;
case Texture::TT_cube_map:
max_dimension = _max_cube_map_size;
max_dimension = _max_cube_map_dimension;
break;
default:
max_dimension = _max_texture_size;
max_dimension = _max_texture_dimension;
}
if (max_dimension == 0) {
@ -3421,48 +3474,51 @@ apply_texture_immediate(CLP(TextureContext) *gtc, Texture *tex) {
// If it doesn't fit, we have to reduce it on-the-fly. This is kind
// of expensive and it doesn't look great; it would have been better
// if the user had specified max-texture-dimension to reduce the
// texture at load time instead.
if (width > max_dimension) {
int byte_chunk = texel_size;
int stride = 1;
int new_width = width;
while (new_width > max_dimension) {
stride <<= 1;
new_width >>= 1;
// texture at load time instead. Of course, the user doesn't always
// know ahead of time what the hardware limits are.
if (max_dimension > 0) {
if (width > max_dimension) {
int byte_chunk = texel_size;
int stride = 1;
int new_width = width;
while (new_width > max_dimension) {
stride <<= 1;
new_width >>= 1;
}
GLCAT.info()
<< "Reducing width of " << tex->get_name()
<< " from " << width << " to " << new_width << "\n";
image = reduce_image(image, byte_chunk, stride);
width = new_width;
}
GLCAT.info()
<< "Reducing width of " << tex->get_name()
<< " from " << width << " to " << new_width << "\n";
image = reduce_image(image, byte_chunk, stride);
width = new_width;
}
if (height > max_dimension) {
int byte_chunk = width * texel_size;
int stride = 1;
int new_height = height;
while (new_height > max_dimension) {
stride <<= 1;
new_height >>= 1;
if (height > max_dimension) {
int byte_chunk = width * texel_size;
int stride = 1;
int new_height = height;
while (new_height > max_dimension) {
stride <<= 1;
new_height >>= 1;
}
GLCAT.info()
<< "Reducing height of " << tex->get_name()
<< " from " << height << " to " << new_height << "\n";
image = reduce_image(image, byte_chunk, stride);
height = new_height;
}
GLCAT.info()
<< "Reducing height of " << tex->get_name()
<< " from " << height << " to " << new_height << "\n";
image = reduce_image(image, byte_chunk, stride);
height = new_height;
}
if (depth > max_dimension) {
int byte_chunk = height * width * texel_size;
int stride = 1;
int new_depth = depth;
while (new_depth > max_dimension) {
stride <<= 1;
new_depth >>= 1;
if (depth > max_dimension) {
int byte_chunk = height * width * texel_size;
int stride = 1;
int new_depth = depth;
while (new_depth > max_dimension) {
stride <<= 1;
new_depth >>= 1;
}
GLCAT.info()
<< "Reducing depth of " << tex->get_name()
<< " from " << depth << " to " << new_depth << "\n";
image = reduce_image(image, byte_chunk, stride);
depth = new_depth;
}
GLCAT.info()
<< "Reducing depth of " << tex->get_name()
<< " from " << depth << " to " << new_depth << "\n";
image = reduce_image(image, byte_chunk, stride);
depth = new_depth;
}
if (!_supports_bgr) {
@ -4542,7 +4598,7 @@ finish_modify_state() {
}
// Apply the texture matrix, if needed.
if (_needs_tex_mat) {
if (_needs_tex_mat || _needs_tex_gen) {
_needs_tex_mat = false;
int num_stages = _current_texture->get_num_on_stages();
@ -4590,8 +4646,9 @@ finish_modify_state() {
for (int i = 0; i < num_stages; i++) {
TextureStage *stage = _current_texture->get_on_stage(i);
_glActiveTexture(GL_TEXTURE0 + i);
switch (_current_tex_gen->get_mode(stage)) {
TexGenAttrib::Mode mode = _current_tex_gen->get_mode(stage);
switch (mode) {
case TexGenAttrib::M_off:
GLP(Disable)(GL_TEXTURE_GEN_S);
GLP(Disable)(GL_TEXTURE_GEN_T);
@ -4599,7 +4656,7 @@ finish_modify_state() {
GLP(Disable)(GL_TEXTURE_GEN_Q);
break;
case TexGenAttrib::M_sphere_map:
case TexGenAttrib::M_eye_sphere_map:
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
GLP(Enable)(GL_TEXTURE_GEN_S);
@ -4609,8 +4666,28 @@ finish_modify_state() {
force_normal = true;
break;
case TexGenAttrib::M_cube_map:
case TexGenAttrib::M_eye_cube_map:
case TexGenAttrib::M_world_cube_map:
if (_supports_cube_map) {
if (mode != TexGenAttrib::M_eye_cube_map) {
// We dynamically transform normals from eye space to
// world space by applying the appropriate rotation
// transform to the current texture matrix. Although it's
// tempting to try, we can't safely convert to object
// space, since this method doesn't get called with each
// different object.
CPT(TransformState) transform = _scene_setup->get_render_transform();
transform = transform->invert_compose(TransformState::make_identity());
LMatrix4f mat = transform->get_mat();
mat.set_row(3, LVecBase3f(0.0f, 0.0f, 0.0f));
GLP(MatrixMode)(GL_TEXTURE);
GLP(MultMatrixf)(mat.get_data());
// Now we need to reset the texture matrix next time
// around to undo this.
_needs_tex_mat = true;
}
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
GLP(TexGeni)(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
@ -4626,6 +4703,44 @@ finish_modify_state() {
GLP(Disable)(GL_TEXTURE_GEN_Q);
}
break;
case TexGenAttrib::M_eye_normal:
case TexGenAttrib::M_world_normal:
if (_supports_cube_map) {
if (mode != TexGenAttrib::M_eye_normal) {
// We dynamically transform normals from eye space to
// world space by applying the appropriate rotation
// transform to the current texture matrix. Although it's
// tempting to try, we can't safely convert to object
// space, since this method doesn't get called with each
// different object.
CPT(TransformState) transform = _scene_setup->get_render_transform();
transform = transform->invert_compose(TransformState::make_identity());
LMatrix4f mat = transform->get_mat();
mat.set_row(3, LVecBase3f(0.0f, 0.0f, 0.0f));
GLP(MatrixMode)(GL_TEXTURE);
GLP(MultMatrixf)(mat.get_data());
// Now we need to reset the texture matrix next time
// around to undo this.
_needs_tex_mat = true;
}
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
GLP(TexGeni)(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
GLP(Enable)(GL_TEXTURE_GEN_S);
GLP(Enable)(GL_TEXTURE_GEN_T);
GLP(Enable)(GL_TEXTURE_GEN_R);
GLP(Disable)(GL_TEXTURE_GEN_Q);
force_normal = true;
} else {
GLP(Disable)(GL_TEXTURE_GEN_S);
GLP(Disable)(GL_TEXTURE_GEN_T);
GLP(Disable)(GL_TEXTURE_GEN_R);
GLP(Disable)(GL_TEXTURE_GEN_Q);
}
break;
case TexGenAttrib::M_object_position:
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
@ -4698,24 +4813,6 @@ finish_modify_state() {
GLP(PopMatrix)();
}
break;
case TexGenAttrib::M_object_normal:
if (_supports_cube_map) {
GLP(TexGeni)(GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
GLP(TexGeni)(GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
GLP(TexGeni)(GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
GLP(Enable)(GL_TEXTURE_GEN_S);
GLP(Enable)(GL_TEXTURE_GEN_T);
GLP(Enable)(GL_TEXTURE_GEN_R);
GLP(Disable)(GL_TEXTURE_GEN_Q);
force_normal = true;
} else {
GLP(Disable)(GL_TEXTURE_GEN_S);
GLP(Disable)(GL_TEXTURE_GEN_T);
GLP(Disable)(GL_TEXTURE_GEN_R);
GLP(Disable)(GL_TEXTURE_GEN_Q);
}
break;
}
}
@ -4829,16 +4926,20 @@ do_issue_texture() {
_glActiveTexture(GL_TEXTURE0 + i);
GLenum target = get_texture_target(texture->get_texture_type());
// First, turn off the previous texture mode.
GLP(Disable)(GL_TEXTURE_1D);
GLP(Disable)(GL_TEXTURE_2D);
if (_supports_3d_texture) {
GLP(Disable)(GL_TEXTURE_3D);
}
if (_supports_cube_map) {
GLP(Disable)(GL_TEXTURE_CUBE_MAP);
}
// Then, turn on the current texture mode.
if (target == GL_NONE) {
// Unsupported texture mode.
GLP(Disable)(GL_TEXTURE_1D);
GLP(Disable)(GL_TEXTURE_2D);
if (_supports_3d_texture) {
GLP(Disable)(GL_TEXTURE_3D);
}
if (_supports_cube_map) {
GLP(Disable)(GL_TEXTURE_CUBE_MAP);
}
break;
}
GLP(Enable)(target);

View File

@ -97,9 +97,9 @@ public:
virtual void release_geom(GeomContext *gc);
virtual void framebuffer_copy_to_texture
(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb);
(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb);
virtual bool framebuffer_copy_to_ram
(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb);
(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb);
virtual void apply_material(const Material *material);
void apply_fog(Fog *fog);
@ -301,10 +301,6 @@ public:
bool _supports_cube_map;
GLint _max_texture_size;
GLint _max_3d_texture_size;
GLint _max_cube_map_size;
bool _supports_bgr;
bool _supports_rescale_normal;

View File

@ -28,6 +28,7 @@
#include "string_utils.h"
#include "preparedGraphicsObjects.h"
#include "pnmImage.h"
#include "virtualFileSystem.h"
#include <stddef.h>
@ -263,6 +264,118 @@ write(const Filename &name, int z) const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read_pages
// Access: Published
// Description: Automatically reads in a sequence of pages, for the
// purpose of reading in a 3-d texture or a cube map
// texture. The filename should contain a sequence of
// one or more hash marks ("#") which will be filled in
// with the z value of each page, zero-based. If z_size
// is specified, the reading will stop there; otherwise,
// all found textures will be loaded, until a gap in the
// sequence is encountered.
//
// If more than one hash mark is used, the numbers will
// be padded with zeroes if necessary to the
// corresponding number of digits.
////////////////////////////////////////////////////////////////////
bool Texture::
read_pages(const Filename &fullpath_template, int z_size) {
string fp = fullpath_template.get_fullpath();
size_t hash = fp.rfind('#');
if (hash == string::npos) {
gobj_cat.error()
<< "Template " << fullpath_template << " contains no hash marks.\n";
return false;
}
// Count the number of hash marks.
size_t num_hash = 1;
while (hash >= num_hash && fp[hash - num_hash] == '#') {
num_hash++;
}
string prefix = fp.substr(0, hash - num_hash + 1);
string suffix = fp.substr(hash + 1);
clear_ram_image();
if (z_size != 0) {
set_z_size(z_size);
for (int z = 0; z < z_size; z++) {
ostringstream strm;
strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
if (!read(strm.str(), z)) {
return false;
}
}
} else {
set_z_size(0);
int z = 0;
ostringstream strm;
strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
Filename file(strm.str());
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
while (vfs->exists(file)) {
if (!read(file, z)) {
return false;
}
++z;
ostringstream strm;
strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
file = Filename(strm.str());
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::write_pages
// Access: Published
// Description: Automatically writes out a sequence of pages, for the
// purpose of writing out a 3-d texture or a cube map
// texture. The filename should contain a sequence of
// one or more hash marks ("#") which will be filled in
// with the z value of each page, zero-based.
//
// If more than one hash mark is used, the numbers will
// be padded with zeroes if necessary to the
// corresponding number of digits.
////////////////////////////////////////////////////////////////////
bool Texture::
write_pages(const Filename &fullpath_template) {
string fp = fullpath_template.get_fullpath();
size_t hash = fp.rfind('#');
if (hash == string::npos) {
gobj_cat.error()
<< "Template " << fullpath_template << " contains no hash marks.\n";
return false;
}
// Count the number of hash marks.
size_t num_hash = 1;
while (hash >= num_hash && fp[hash - num_hash] == '#') {
num_hash++;
}
string prefix = fp.substr(0, hash - num_hash + 1);
string suffix = fp.substr(hash + 1);
for (int z = 0; z < _z_size; z++) {
ostringstream strm;
strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
if (!write(strm.str(), z)) {
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::load
// Access: Published
@ -500,7 +613,7 @@ store(PNMImage &pnmimage, int z) const {
}
}
nassertr(idx == (int)get_expected_ram_image_size(), false);
nassertr((size_t)idx == get_expected_ram_page_size() * (z + 1), false);
return true;
@ -526,7 +639,7 @@ store(PNMImage &pnmimage, int z) const {
}
}
nassertr(idx == (int)get_expected_ram_image_size(), false);
nassertr((size_t)idx == get_expected_ram_page_size() * (z + 1), false);
return true;
}

View File

@ -157,6 +157,9 @@ PUBLISHED:
int primary_file_num_channels = 0, int alpha_file_channel = 0);
bool write(const Filename &fullpath, int z = 0) const;
bool read_pages(const Filename &fullpath_template, int z_size = 0);
bool write_pages(const Filename &fullpath_template);
bool load(const PNMImage &pnmimage, int z = 0);
bool store(PNMImage &pnmimage, int z = 0) const;

View File

@ -263,8 +263,8 @@ flatten(GraphicsOutput *window) {
multitex_name_strm << "multitex" << multitex_id;
multitex_id++;
GraphicsOutput *buffer =
window->make_texture_buffer(multitex_name_strm.str(), x_size, y_size);
GraphicsOutput *buffer = window->make_texture_buffer
(multitex_name_strm.str(), x_size, y_size, NULL, false);
buffer->set_one_shot(true);
Texture *tex = buffer->get_texture();
tex->set_anisotropic_degree(aniso_degree);

View File

@ -166,9 +166,9 @@ public:
virtual void draw_sphere(GeomSphere *geom, GeomContext *gc)=0;
virtual void framebuffer_copy_to_texture
(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb)=0;
(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0;
virtual bool framebuffer_copy_to_ram
(Texture *tex, const DisplayRegion *dr, const RenderBuffer &rb)=0;
(Texture *tex, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0;
virtual bool framebuffer_bind_to_texture(GraphicsOutput *win, Texture *tex)=0;
virtual void framebuffer_release_texture(GraphicsOutput *win, Texture *tex)=0;

View File

@ -157,12 +157,25 @@ output(ostream &out) const {
case M_off:
out << "off";
break;
case M_sphere_map:
out << "sphere_map";
case M_eye_sphere_map:
out << "eye_sphere_map";
break;
case M_cube_map:
out << "cube_map";
case M_world_cube_map:
out << "world_cube_map";
break;
case M_eye_cube_map:
out << "eye_cube_map";
break;
case M_world_normal:
out << "world_normal";
break;
case M_eye_normal:
out << "eye_normal";
break;
case M_world_position:
out << "world_position";
break;
@ -172,9 +185,6 @@ output(ostream &out) const {
case M_eye_position:
out << "eye_position";
break;
case M_object_normal:
out << "object_normal";
break;
}
out << ")";
}

View File

@ -40,12 +40,42 @@ class EXPCL_PANDA TexGenAttrib : public RenderAttrib {
PUBLISHED:
enum Mode {
M_off,
M_sphere_map,
M_cube_map,
// In the types below, "eye" means the coordinate space of the
// observing camera, "object" means the local coordinate space of
// the object, and "world" means world coordinates, e.g. the
// coordinate space of the root of the graph.
// Sphere maps are classic static reflection maps. They are
// supported on just about any hardware, and require a precomputed
// 180-degree fisheye image. Sphere maps only make sense in eye
// coordinate space.
M_eye_sphere_map,
// Cube maps are a modern improvement on the sphere map; they
// don't suffer from any polar singularities, but they require six
// texture images. They can also be generated dynamically for
// real-time reflections (see GraphicsOutput::make_cube_map()).
// Typically, a statically-generated cube map will be in eye
// space, while a dynamically-generated map will be in world space
// or object space (depending on where the camera rig that
// generates the map is parented).
// Cube mapping is not supported on all hardware.
M_world_cube_map,
M_eye_cube_map,
// Normal maps are most useful for applying diffuse lighting
// effects via a pregenerated cube map.
M_world_normal,
M_eye_normal,
// Position maps convert XYZ coordinates directly to texture
// coordinates. This is particularly useful for implementing
// projective texturing (see NodePath::project_texture()).
M_world_position,
M_object_position,
M_eye_position,
M_object_normal,
};
protected:

View File

@ -43,7 +43,6 @@ wglGraphicsBuffer(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
// Since the pbuffer never gets flipped, we get screenshots from the
// same buffer we draw into.
_screenshot_buffer_type = _draw_buffer_type;
_render_texture = false;
}
////////////////////////////////////////////////////////////////////
@ -204,12 +203,25 @@ open_buffer() {
if (wgldisplay_cat.is_debug()) {
wgldisplay_cat.debug()
<< "Created PBuffer " << _pbuffer << ", DC " << _pbuffer_dc << "\n";
if (_render_texture) {
switch (_rtm_mode) {
case RTM_bind_texture:
wgldisplay_cat.debug()
<< "pbuffer renders directly to texture.\n";
} else if (_copy_texture) {
break;
case RTM_copy_texture:
case RTM_bind_if_possible:
wgldisplay_cat.debug()
<< "pbuffer copies indirectly into texture.\n";
break;
case RTM_copy_ram:
wgldisplay_cat.debug()
<< "pbuffer copies indirectly into system RAM.\n";
break;
default:
break;
}
}
@ -245,14 +257,15 @@ make_pbuffer(HDC twindow_dc) {
if (wglgsg->_supports_pixel_format) {
bool got_pbuffer_format = false;
if (_copy_texture && wglgsg->_supports_render_texture) {
if (_rtm_mode == RTM_bind_if_possible &&
wglgsg->_supports_render_texture) {
// First, try to get a pbuffer format that supports
// render-to-texture.
int new_pbformat = choose_pbuffer_format(twindow_dc, true);
if (new_pbformat != 0) {
pbformat = new_pbformat;
got_pbuffer_format = true;
_render_texture = true;
_rtm_mode = RTM_bind_texture;
}
}
@ -279,7 +292,7 @@ make_pbuffer(HDC twindow_dc) {
int iattrib_list[max_attrib_list];
int ni = 0;
if (_render_texture) {
if (_rtm_mode == RTM_bind_texture) {
if (_gsg->get_properties().get_frame_buffer_mode() & FrameBufferProperties::FM_alpha) {
iattrib_list[ni++] = WGL_TEXTURE_FORMAT_ARB;
iattrib_list[ni++] = WGL_TEXTURE_RGBA_ARB;