diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 5cc7d8d0f6..f71e01d298 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -3822,6 +3822,13 @@ do_issue_blending() { set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); return; + case TransparencyAttrib::M_premultiplied_alpha: + set_render_state(D3DRS_ALPHABLENDENABLE, TRUE); + set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD); + set_render_state(D3DRS_SRCBLEND, D3DBLEND_ONE); + set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + return; + default: dxgsg9_cat.error() << "invalid transparency mode " << (int)transparency_mode << endl; diff --git a/panda/src/egg/eggRenderMode.cxx b/panda/src/egg/eggRenderMode.cxx index 0be5d41edf..7c7e478a6d 100644 --- a/panda/src/egg/eggRenderMode.cxx +++ b/panda/src/egg/eggRenderMode.cxx @@ -183,6 +183,8 @@ string_alpha_mode(const string &string) { return AM_binary; } else if (cmp_nocase_uh(string, "dual") == 0) { return AM_dual; + } else if (cmp_nocase_uh(string, "premultiplied") == 0) { + return AM_premultiplied; } else { return AM_unspecified; } @@ -260,6 +262,8 @@ ostream &operator << (ostream &out, EggRenderMode::AlphaMode mode) { return out << "binary"; case EggRenderMode::AM_dual: return out << "dual"; + case EggRenderMode::AM_premultiplied: + return out << "premultiplied"; } nassertr(false, out); diff --git a/panda/src/egg/eggRenderMode.h b/panda/src/egg/eggRenderMode.h index b4664db736..66d9a6c645 100644 --- a/panda/src/egg/eggRenderMode.h +++ b/panda/src/egg/eggRenderMode.h @@ -45,7 +45,8 @@ PUBLISHED: AM_ms, // TransparencyAttrib::M_multisample AM_ms_mask, // TransparencyAttrib::M_multisample_mask AM_binary, // TransparencyAttrib::M_binary - AM_dual // TransparencyAttrib::M_dual + AM_dual, // TransparencyAttrib::M_dual + AM_premultiplied // TransparencyAttrib::M_premultiplied_alpha }; enum DepthWriteMode { diff --git a/panda/src/egg2pg/eggRenderState.cxx b/panda/src/egg2pg/eggRenderState.cxx index 1f068c4655..cb8a9681e3 100644 --- a/panda/src/egg2pg/eggRenderState.cxx +++ b/panda/src/egg2pg/eggRenderState.cxx @@ -333,6 +333,10 @@ fill_state(EggPrimitive *egg_prim) { add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual)); break; + case EggRenderMode::AM_premultiplied: + add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_premultiplied_alpha)); + break; + default: break; } diff --git a/panda/src/egg2pg/eggSaver.cxx b/panda/src/egg2pg/eggSaver.cxx index 4ca97a9aeb..103ba24fe2 100644 --- a/panda/src/egg2pg/eggSaver.cxx +++ b/panda/src/egg2pg/eggSaver.cxx @@ -686,12 +686,15 @@ convert_primitive(const GeomVertexData *vertex_data, break; case TransparencyAttrib::M_alpha: if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) { - tex_trans = EggRenderMode::AM_blend_no_occlude; - has_depthwrite = false; + tex_trans = EggRenderMode::AM_blend_no_occlude; + has_depthwrite = false; } else { tex_trans = EggRenderMode::AM_blend; } break; + case TransparencyAttrib::M_premultiplied_alpha: + tex_trans = EggRenderMode::AM_premultiplied; + break; case TransparencyAttrib::M_multisample: tex_trans = EggRenderMode::AM_ms; break; @@ -705,7 +708,6 @@ convert_primitive(const GeomVertexData *vertex_data, tex_trans = EggRenderMode::AM_dual; break; default: // intentional fall-through - case TransparencyAttrib::M_notused: break; } if (tex_trans != EggRenderMode::AM_unspecified) { diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 62a0f43b50..dc5d3e31e8 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -6722,6 +6722,19 @@ do_issue_blending() { } return; + case TransparencyAttrib::M_premultiplied_alpha: + enable_multisample_alpha_one(false); + enable_multisample_alpha_mask(false); + enable_blend(true); + _glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + if (GLCAT.is_spam()) { + GLCAT.spam() << "glBlendEquation(GL_FUNC_ADD)\n"; + GLCAT.spam() << "glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)\n"; + } + return; + case TransparencyAttrib::M_multisample: // We need to enable *both* of these in M_multisample case. enable_multisample_alpha_one(true); diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 3cc5380ab2..53751f07ca 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -131,6 +131,7 @@ add_object(CullableObject *object, const CullTraverser *traverser) { if (object->_state->get_attrib(trans)) { switch (trans->get_mode()) { case TransparencyAttrib::M_alpha: + case TransparencyAttrib::M_premultiplied_alpha: // M_alpha implies an alpha-write test, so we don't waste time writing // 0-valued pixels. object->_state = object->_state->compose(get_alpha_state()); diff --git a/panda/src/pgraph/cullTraverserData.cxx b/panda/src/pgraph/cullTraverserData.cxx index 186869cde6..d5ad3cba4f 100644 --- a/panda/src/pgraph/cullTraverserData.cxx +++ b/panda/src/pgraph/cullTraverserData.cxx @@ -47,7 +47,7 @@ apply_transform_and_state(CullTraverser *trav) { _node_reader.compose_draw_mask(_draw_mask); apply_transform_and_state(trav, _node_reader.get_transform(), - node_state, _node_reader.get_effects(), + MOVE(node_state), _node_reader.get_effects(), _node_reader.get_off_clip_planes()); } diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 86437c3122..0555d300a3 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -1853,8 +1853,8 @@ determine_bin_index() { string bin_name; _draw_order = 0; - const CullBinAttrib *bin = DCAST(CullBinAttrib, get_attrib(CullBinAttrib::get_class_slot())); - if (bin != (const CullBinAttrib *)NULL) { + const CullBinAttrib *bin; + if (get_attrib(bin)) { bin_name = bin->get_bin_name(); _draw_order = bin->get_draw_order(); } @@ -1864,10 +1864,11 @@ determine_bin_index() { // opaque or transparent, based on the transparency setting. bin_name = "opaque"; - const TransparencyAttrib *transparency = DCAST(TransparencyAttrib, get_attrib(TransparencyAttrib::get_class_slot())); - if (transparency != (const TransparencyAttrib *)NULL) { + const TransparencyAttrib *transparency; + if (get_attrib(transparency)) { switch (transparency->get_mode()) { case TransparencyAttrib::M_alpha: + case TransparencyAttrib::M_premultiplied_alpha: case TransparencyAttrib::M_dual: // These transparency modes require special back-to-front sorting. bin_name = "transparent"; diff --git a/panda/src/pgraph/transparencyAttrib.cxx b/panda/src/pgraph/transparencyAttrib.cxx index 358008ac1f..4dd7d0a1c2 100644 --- a/panda/src/pgraph/transparencyAttrib.cxx +++ b/panda/src/pgraph/transparencyAttrib.cxx @@ -55,6 +55,10 @@ output(ostream &out) const { out << "alpha"; break; + case M_premultiplied_alpha: + out << "premultiplied alpha"; + break; + case M_multisample: out << "multisample"; break; @@ -70,9 +74,6 @@ output(ostream &out) const { case M_dual: out << "dual"; break; - - case M_notused: - break; } } diff --git a/panda/src/pgraph/transparencyAttrib.h b/panda/src/pgraph/transparencyAttrib.h index fe49dc938e..2baf3710a1 100644 --- a/panda/src/pgraph/transparencyAttrib.h +++ b/panda/src/pgraph/transparencyAttrib.h @@ -36,7 +36,7 @@ PUBLISHED: // corresponded to M_none or M_alpha). M_none = 0, // No transparency. M_alpha = 1, // Normal transparency, panda will sort back-to-front. - M_notused, // Unused placeholder. Do not use this. + M_premultiplied_alpha, // Assume textures use premultiplied alpha. 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. diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 794c3123a9..2c840e46ee 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -209,6 +209,7 @@ analyze_renderstate(const RenderState *rs) { const TransparencyAttrib *transparency; rs->get_attrib_def(transparency); if ((transparency->get_mode() == TransparencyAttrib::M_alpha)|| + (transparency->get_mode() == TransparencyAttrib::M_premultiplied_alpha)|| (transparency->get_mode() == TransparencyAttrib::M_dual)) { _have_alpha_blend = true; } diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index ba33377546..dacb90c88e 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -804,6 +804,21 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader, } break; + case TransparencyAttrib::M_premultiplied_alpha: + { + // Implement a color mask, with pre-multiplied alpha blending. + int op_a = get_color_blend_op(ColorBlendAttrib::O_one); + int op_b = get_color_blend_op(ColorBlendAttrib::O_one_minus_incoming_alpha); + + if (srgb_blend) { + _c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels]; + } else { + _c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels]; + } + color_write_state = 2; // cgeneral + } + break; + default: break; }