initial experiments with keystone correction, revisit M_multisample transparency, robustify MultitexReducer

This commit is contained in:
David Rose 2004-12-15 05:53:23 +00:00
parent 12821e21f5
commit b1375f32a4
25 changed files with 246 additions and 90 deletions

View File

@ -42,15 +42,15 @@ GraphicsBuffer(GraphicsPipe *pipe, GraphicsStateGuardian *gsg,
<< " using GSG " << (void *)gsg << "\n";
}
if (want_texture) {
setup_copy_texture(_name);
}
_x_size = x_size;
_y_size = y_size;
_has_size = true;
_default_display_region->compute_pixels(_x_size, _y_size);
_open_request = OR_none;
if (want_texture) {
setup_copy_texture(_name);
}
}
////////////////////////////////////////////////////////////////////

View File

@ -1196,7 +1196,7 @@ do_add_window(GraphicsOutput *window, GraphicsStateGuardian *gsg,
_app.add_window(_app._window, window);
display_cat.info()
<< "Created " << window->get_type() << "\n";
<< "Created " << window->get_type() << " " << (void *)window << "\n";
// By default, try to open each window as it is added.
window->request_open();
@ -1230,6 +1230,11 @@ do_remove_window(GraphicsOutput *window) {
// If the window happened to be controlled by the app thread, we
// might as well close it now rather than waiting for next frame.
_app.do_pending(this);
if (display_cat.is_debug()) {
display_cat.debug()
<< "Removed " << window->get_type() << " " << (void *)window << "\n";
}
}
////////////////////////////////////////////////////////////////////
@ -1450,6 +1455,19 @@ resort_windows() {
_cdraw.sort();
_draw.sort();
_window.sort();
if (display_cat.is_debug()) {
display_cat.debug()
<< "Windows resorted:";
Windows::const_iterator wi;
for (wi = _window.begin(); wi != _window.end(); ++wi) {
GraphicsOutput *win = (*wi);
display_cat.debug(false)
<< " " << (void *)win;
}
display_cat.debug(false)
<< "\n";
}
}
////////////////////////////////////////////////////////////////////

View File

@ -164,6 +164,13 @@ setup_copy_texture(const string &name) {
_texture->set_wrapu(Texture::WM_clamp);
_texture->set_wrapv(Texture::WM_clamp);
if (has_size()) {
// If we know our size now, go ahead and tell the texture.
PixelBuffer *pb = _texture->_pbuffer;
pb->set_xsize(get_x_size());
pb->set_ysize(get_y_size());
}
_copy_texture = true;
nassertv(_gsg != (GraphicsStateGuardian *)NULL);
@ -587,6 +594,10 @@ end_frame() {
// directly into texture memory don't need to do this; they will set
// _copy_texture to false.
if (_copy_texture) {
if (display_cat.is_debug()) {
display_cat.debug()
<< "Copying texture for " << (void *)this << " at frame end.\n";
}
PStatTimer timer(_copy_texture_pcollector);
nassertv(has_texture());
RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type());

View File

@ -168,6 +168,20 @@ get_copy_texture_inverted() const {
return _copy_texture_inverted;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_supports_generate_mipmap
// Access: Published
// Description: Returns true if this particular GSG can generate
// mipmaps for a texture automatically, or if they must
// be generated in software. If this is true, then
// mipmaps can safely be enabled for rendered textures
// (e.g. using the MultitexReducer).
////////////////////////////////////////////////////////////////////
INLINE bool GraphicsStateGuardian::
get_supports_generate_mipmap() const {
return _supports_generate_mipmap;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::set_scene

View File

@ -92,6 +92,10 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
// Initially, we set this to false; a GSG that knows it has this
// property should set it to true.
_copy_texture_inverted = false;
// Similarly with these capabilities flags.
_supports_multisample = false;
_supports_generate_mipmap = false;
}
////////////////////////////////////////////////////////////////////
@ -103,6 +107,24 @@ GraphicsStateGuardian::
~GraphicsStateGuardian() {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_supports_multisample
// Access: Published, Virtual
// Description: Returns true if this particular GSG supports using
// the multisample bits to provide antialiasing, and
// also supports M_multisample and M_multisample_mask
// transparency modes. If this is not true for a
// particular GSG, Panda will map the M_multisample
// modes to M_binary.
//
// This method is declared virtual solely so that it can
// be queried from cullResult.cxx.
////////////////////////////////////////////////////////////////////
bool GraphicsStateGuardian::
get_supports_multisample() const {
return _supports_multisample;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::reset
// Access: Public, Virtual

View File

@ -84,6 +84,8 @@ PUBLISHED:
INLINE int get_max_texture_stages() const;
INLINE bool get_copy_texture_inverted() const;
virtual bool get_supports_multisample() const;
INLINE bool get_supports_generate_mipmap() const;
public:
INLINE bool set_scene(SceneSetup *scene_setup);
@ -280,6 +282,8 @@ protected:
PT(PreparedGraphicsObjects) _prepared_objects;
int _max_texture_stages;
bool _copy_texture_inverted;
bool _supports_multisample;
bool _supports_generate_mipmap;
public:
// Statistics

View File

@ -44,8 +44,6 @@ ParasiteBuffer(GraphicsOutput *host, const string &name,
<< " on " << _host->get_name() << "\n";
}
setup_copy_texture(_name);
_x_size = x_size;
_y_size = y_size;
_has_size = true;
@ -53,6 +51,8 @@ ParasiteBuffer(GraphicsOutput *host, const string &name,
_is_valid = true;
setup_copy_texture(_name);
nassertv(_x_size <= host->get_x_size() && _y_size <= host->get_y_size());
}

View File

@ -4590,7 +4590,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
break;
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_alpha_sorted:
case TransparencyAttrib::M_multisample:
case TransparencyAttrib::M_multisample_mask:
case TransparencyAttrib::M_dual:

View File

@ -4316,7 +4316,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
break;
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_alpha_sorted:
case TransparencyAttrib::M_multisample:
case TransparencyAttrib::M_multisample_mask:
case TransparencyAttrib::M_dual:

View File

@ -4319,7 +4319,6 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
break;
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_alpha_sorted:
case TransparencyAttrib::M_multisample:
case TransparencyAttrib::M_multisample_mask:
case TransparencyAttrib::M_dual:

View File

@ -616,7 +616,7 @@ sort_unique() {
template<class Key, class Compare>
INLINE void ordered_vector<Key, Compare>::
sort_nonunique() {
sort(begin(), end(), _compare);
stable_sort(begin(), end(), _compare);
}
////////////////////////////////////////////////////////////////////

View File

@ -191,23 +191,15 @@ enable_scissor(bool val)
////////////////////////////////////////////////////////////////////
INLINE void CLP(GraphicsStateGuardian)::
enable_multisample_alpha_one(bool val) {
if (_multisample_alpha_one_enabled != val) {
_multisample_alpha_one_enabled = val;
#ifdef GL_SAMPLE_ALPHA_TO_ONE_SGIS
if (val) {
#ifdef GSG_VERBOSE
GLCAT.spam()
<< "glEnable(GL_SAMPLE_ALPHA_TO_ONE_SGIS)" << endl;
#endif
GLP(Enable)(GL_SAMPLE_ALPHA_TO_ONE_SGIS);
} else {
#ifdef GSG_VERBOSE
GLCAT.spam()
<< "glDisable(GL_SAMPLE_ALPHA_TO_ONE_SGIS)" << endl;
#endif
GLP(Disable)(GL_SAMPLE_ALPHA_TO_ONE_SGIS);
if (_supports_multisample) {
if (_multisample_alpha_one_enabled != val) {
_multisample_alpha_one_enabled = val;
if (val) {
GLP(Enable)(GL_SAMPLE_ALPHA_TO_ONE);
} else {
GLP(Disable)(GL_SAMPLE_ALPHA_TO_ONE);
}
}
#endif // GL_SAMPLE_ALPHA_TO_ONE_SGIS
}
}
@ -220,21 +212,11 @@ INLINE void CLP(GraphicsStateGuardian)::
enable_multisample_alpha_mask(bool val) {
if (_multisample_alpha_mask_enabled != val) {
_multisample_alpha_mask_enabled = val;
#ifdef GL_SAMPLE_ALPHA_TO_MASK_SGIS
if (val) {
#ifdef GSG_VERBOSE
GLCAT.spam()
<< "glEnable(GL_SAMPLE_ALPHA_TO_MASK_SGIS)" << endl;
#endif
GLP(Enable)(GL_SAMPLE_ALPHA_TO_MASK_SGIS);
GLP(Enable)(GL_SAMPLE_ALPHA_TO_COVERAGE);
} else {
#ifdef GSG_VERBOSE
GLCAT.spam()
<< "glDisable(GL_SAMPLE_ALPHA_TO_MASK_SGIS)" << endl;
#endif
GLP(Disable)(GL_SAMPLE_ALPHA_TO_MASK_SGIS);
GLP(Disable)(GL_SAMPLE_ALPHA_TO_COVERAGE);
}
#endif // GL_SAMPLE_ALPHA_TO_MASK_SGIS
}
}

View File

@ -4345,15 +4345,7 @@ set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
break;
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_alpha_sorted:
case TransparencyAttrib::M_dual:
// Should we really have an "alpha" and an "alpha_sorted"
// mode, like Performer does? (The difference is that
// "alpha_sorted" is with the write to the depth buffer
// disabled.) Or should we just use the separate depth write
// transition to control this? Doing it implicitly requires a
// bit more logic here and in the state management; for now we
// require the user to explicitly turn off the depth write.
enable_multisample_alpha_one(false);
enable_multisample_alpha_mask(false);
enable_blend(true);

View File

@ -281,8 +281,6 @@ protected:
public:
bool _supports_bgr;
bool _supports_multisample;
bool _supports_generate_mipmap;
bool _supports_multitexture;
PFNGLACTIVETEXTUREPROC _glActiveTexture;

View File

@ -105,6 +105,7 @@ clear() {
_view_vector.set(0.0f, 1.0f, 0.0f);
_up_vector.set(0.0f, 0.0f, 1.0f);
_iod_offset = 0.0f;
_keystone.set(0.0f, 0.0f, 0.0f);
_user_flags = 0;
_comp_flags = CF_fov;
@ -574,6 +575,66 @@ get_view_mat() const {
return _lens_mat;
}
////////////////////////////////////////////////////////////////////
// Function: Lens::clear_view_mat
// Access: Published
// Description: Resets the lens transform to identity.
////////////////////////////////////////////////////////////////////
void Lens::
clear_view_mat() {
_lens_mat = LMatrix4f::ident_mat();
adjust_user_flags(0, UF_view_vector | UF_view_hpr | UF_iod_offset | UF_view_mat);
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset,
CF_lens_mat);
throw_change_event();
}
////////////////////////////////////////////////////////////////////
// Function: Lens::set_keystone
// Access: Published
// Description: Indicates the ratio of keystone correction to perform
// on the lens, in each of three axes. This will build
// a special non-affine scale factor into the projection
// matrix that will compensate for keystoning of a
// projected image; this can be used to compensate for a
// projector that for physical reasons cannot be aimed
// directly at it screen. The default value of 0, 0, 0
// indicates no keystone correction; specify a small
// value (usually in the range -1 .. 1) in one of the
// three axes to generate a keystone correction in that
// axis.
////////////////////////////////////////////////////////////////////
void Lens::
set_keystone(const LVecBase3f &keystone) {
_keystone = keystone;
adjust_user_flags(0, UF_keystone);
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_film_mat | CF_film_mat_inv, 0);
throw_change_event();
}
////////////////////////////////////////////////////////////////////
// Function: Lens::get_keystone
// Access: Published
// Description: Returns the direction in which the lens is facing.
////////////////////////////////////////////////////////////////////
const LVecBase3f &Lens::
get_keystone() const {
return _keystone;
}
////////////////////////////////////////////////////////////////////
// Function: Lens::clear_keystone
// Access: Published
// Description: Resets the lens transform to identity.
////////////////////////////////////////////////////////////////////
void Lens::
clear_keystone() {
_keystone = LVecBase3f(0.0f, 0.0f, 0.0f);
adjust_user_flags(UF_keystone, 0);
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | CF_film_mat | CF_film_mat_inv, 0);
throw_change_event();
}
////////////////////////////////////////////////////////////////////
// Function: Lens::set_frustum_from_corners
// Access: Published
@ -1098,21 +1159,25 @@ extrude_impl(const LPoint3f &point2d, LPoint3f &near_point, LPoint3f &far_point)
{
LVecBase4f full(point2d[0], point2d[1], -1.0f, 1.0f);
full = projection_mat_inv.xform(full);
if (full[3] == 0.0f) {
return false;
}
float recip_full3 = 1.0f/full[3];
near_point.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
float recip_full3 = 1.0f / max(full[3], 0.001f);
near_point.set(full[0] * recip_full3,
full[1] * recip_full3,
full[2] * recip_full3);
}
{
LVecBase4f full(point2d[0], point2d[1], 1.0f, 1.0f);
full = projection_mat_inv.xform(full);
if (full[3] == 0.0f) {
return false;
}
float recip_full3 = 1.0f/full[3];
far_point.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
// We can truncate the weight factor at near 0. If it goes too
// close to zero, or becomes negative, the far plane moves out
// past infinity and comes back in behind the lens, which is just
// crazy. Truncating it to zero keeps the far plane from moving
// too far out.
float recip_full3 = 1.0f / max(full[3], 0.001f);
far_point.set(full[0] * recip_full3,
full[1] * recip_full3,
full[2] * recip_full3);
}
return true;
}
@ -1376,12 +1441,6 @@ compute_film_mat() {
LVecBase2f film_size = get_film_size();
LVector2f film_offset = get_film_offset();
/* this line triggers a VC7 opt bug, so explicitly set matrix below instead
_film_mat =
LMatrix4f::translate_mat(-film_offset[0], -film_offset[1], 0.0f) *
LMatrix4f::scale_mat(2.0f / film_size[0], 2.0f / film_size[1], 1.0f);
*/
float scale_x = 2.0f / film_size[0];
float scale_y = 2.0f / film_size[1];
_film_mat.set(scale_x, 0.0f, 0.0f, 0.0f,
@ -1389,6 +1448,13 @@ compute_film_mat() {
0.0f, 0.0f, 1.0f, 0.0f,
-film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f, 1.0f);
if ((_user_flags & UF_keystone) != 0) {
_film_mat = LMatrix4f(csqrt(1.0f - _keystone[0] * _keystone[0]), 0.0f, 0.0f, _keystone[0],
0.0f, csqrt(1.0f - _keystone[1] * _keystone[1]), 0.0f, _keystone[1],
0.0f, 0.0f, csqrt(1.0f - _keystone[2] * _keystone[2]), _keystone[2],
0.0f, 0.0f, 0.0f, 1.0f) * _film_mat;
}
adjust_comp_flags(CF_film_mat_inv,
CF_film_mat);
}

View File

@ -110,6 +110,11 @@ PUBLISHED:
void set_view_mat(const LMatrix4f &view_mat);
const LMatrix4f &get_view_mat() const;
void clear_view_mat();
void set_keystone(const LVecBase3f &keystone);
const LVecBase3f &get_keystone() const;
void clear_keystone();
// These flags are passed in as the last parameter to control the
// behavior of set_frustum_from_corners(). See the documentation
@ -197,6 +202,7 @@ protected:
LVecBase3f _view_hpr;
LVector3f _view_vector, _up_vector;
float _iod_offset;
LVecBase3f _keystone;
LMatrix4f _film_mat, _film_mat_inv;
LMatrix4f _lens_mat, _lens_mat_inv;
@ -214,6 +220,7 @@ protected:
UF_view_vector = 0x0080,
UF_iod_offset = 0x0100,
UF_view_mat = 0x0200,
UF_keystone = 0x0400,
};
enum CompFlags {

View File

@ -24,7 +24,8 @@
#define INSTALL_HEADERS \
cardMaker.I cardMaker.h \
frameRateMeter.I frameRateMeter.h \
lineSegs.I lineSegs.h
lineSegs.I lineSegs.h \
multitexReducer.I multitexReducer.h
#define IGATESCAN all

View File

@ -418,11 +418,22 @@ scan_geom_node(GeomNode *node, const RenderState *state,
int num_stages = ta->get_num_on_stages();
for (int si = 0; si < num_stages; si++) {
TextureStage *stage = ta->get_on_stage(si);
Texture *tex = ta->get_on_texture(stage);
PixelBuffer *tex_pbuffer = tex->_pbuffer;
if (tex_pbuffer != (PixelBuffer *)NULL &&
tex_pbuffer->get_xsize() != 0 &&
tex_pbuffer->get_ysize() != 0) {
stage_list.push_back(StageInfo(stage, ta, tma));
stage_list.push_back(StageInfo(stage, ta, tma));
} else {
grutil_cat.info()
<< "Ignoring invalid texture stage " << stage->get_name() << "\n";
}
}
record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
if (stage_list.size() >= 2) {
record_stage_list(stage_list, GeomInfo(state, geom_net_state, node, gi));
}
}
}
}

View File

@ -103,6 +103,8 @@ class Lens;
////////////////////////////////////////////////////////////////////
class EXPCL_PANDA GraphicsStateGuardianBase : public TypedWritableReferenceCount {
public:
virtual bool get_supports_multisample() const=0;
// These functions will be queried by the GeomIssuer to determine if
// it should issue normals, texcoords, and/or colors, based on the
// GSG's current state.

View File

@ -97,6 +97,15 @@ add_object(CullableObject *object) {
object->_state = state->compose(get_binary_state());
break;
case TransparencyAttrib::M_multisample:
case TransparencyAttrib::M_multisample_mask:
// The multisample modes are implemented using M_binary if the
// GSG in use doesn't support multisample.
if (!_gsg->get_supports_multisample()) {
object->_state = state->compose(get_binary_state());
}
break;
case TransparencyAttrib::M_dual:
if (m_dual) {
// M_dual is implemented by drawing the opaque parts first,

View File

@ -1371,7 +1371,6 @@ determine_bin_index() {
if (trans != (const TransparencyAttrib *)NULL) {
switch (trans->get_mode()) {
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_alpha_sorted:
case TransparencyAttrib::M_dual:
// These transparency modes require special back-to-front sorting.
bin_name = "transparent";

View File

@ -257,7 +257,7 @@ compare_to_impl(const RenderAttrib *other) const {
}
}
if (bi != _stages.end()) {
if (bi != ta->_stages.end()) {
// a ran out first; b was longer.
return -1;
}

View File

@ -68,10 +68,6 @@ output(ostream &out) const {
out << "alpha";
break;
case M_alpha_sorted:
out << "alpha sorted";
break;
case M_multisample:
out << "multisample";
break;

View File

@ -31,8 +31,8 @@ class FactoryParams;
// setting an alpha component to non-1 does not in
// itself make an object transparent; you must also
// enable transparency mode with a suitable
// TransparencyTransition. Similarly, it is wasteful to
// render an object with a TransparencyTransition in
// TransparencyAttrib. Similarly, it is wasteful to
// render an object with a TransparencyAttrib in
// effect unless you actually want it to be at least
// partially transparent (and it has alpha components
// less than 1).
@ -40,13 +40,13 @@ class FactoryParams;
class EXPCL_PANDA TransparencyAttrib : public RenderAttrib {
PUBLISHED:
enum Mode {
M_none, // No transparency in effect.
M_alpha, // Writes to depth buffer of transp objects disabled
M_alpha_sorted, // Assumes transp objects are depth sorted
M_multisample, // Source alpha values modified to 1.0 before writing
M_multisample_mask, // Source alpha values not modified
M_binary, // Only writes pixels with alpha = 1.0
M_dual, // 2-pass: draws opaque, then draws transparent
M_none, // No transparency.
M_alpha, // Normal transparency, panda will sort back-to-front.
M_notused, // Unused placeholder. Do not use this.
M_multisample, // Uses ms buffer, alpha values modified to 1.0.
M_multisample_mask, // Uses ms buffer, alpha values not modified.
M_binary, // Only writes pixels with alpha >= 0.5.
M_dual, // opaque parts first, then sorted transparent parts.
};
private:

View File

@ -20,6 +20,7 @@
#include "textNode.h"
#include "multitexReducer.h"
#include "configVariableBool.h"
#include "texturePool.h"
#ifndef HAVE_GETOPT
#include "gnu_getopt.h"
@ -108,24 +109,50 @@ event_2(CPT_Event event, void *) {
void
event_0(CPT_Event event, void *) {
// 0: run hacky test.
static bool first = true;
static int count = 0;
if (first) {
static PT(TextureStage) ts;
if (ts == (TextureStage *)NULL) {
ts = new TextureStage("ts");
}
NodePath models = framework.get_models();
if (count == 0) {
cerr << "applying scale\n";
framework.get_models().set_color_scale(0.7, 0.7, 0.1, 1.0);
first = false;
models.set_color_scale(0.7, 0.7, 0.1, 1.0);
} else {
cerr << "flattening\n";
MultitexReducer mr;
mr.set_use_geom(true);
mr.scan(framework.get_models());
mr.scan(models);
WindowFramework *wf = framework.get_window(0);
GraphicsWindow *win = wf->get_graphics_window();
mr.flatten(win);
if (count > 1) {
PT(Texture) tex = TexturePool::load_texture("maps/cmss12.rgb");
if (tex != (Texture *)NULL) {
cerr << "Reapplying\n";
ts->set_mode(TextureStage::M_decal);
models.set_texture(ts, tex);
if (count > 3 && (count % 2) == 1) {
cerr << "Reflattening\n";
MultitexReducer mr;
mr.scan(models);
WindowFramework *wf = framework.get_window(0);
GraphicsWindow *win = wf->get_graphics_window();
mr.flatten(win);
}
}
}
}
count++;
}
void