GeomParticleRenderer always uses white color for geometry

While i was testing Panda’s particle system i came a cross the following issue:
1- started (Particle Panel)
2- Selected (GeomParticleRenderer)
3- Selected an egg file which has colors assigned to it’s vertices.
4- the vertex colors where replaced with WHITE!!.

To verify if the issue is a bug in the (Particle Panel) or Panda3D i wrote a code in c++. The result was the same ( so the issue is not with the Particle Panel).

After much debugging i found the following:

file: GeomParticleRenderer.cxx
cur_node->set_attrib(ColorAttrib::make_flat(c), 0);

if no color interpolation is used, the above line assigns the interpolation start color to the geometry which defaults to WHITE!!!

After more debugging i found that there is another issue with the line:

file: GeomParticleRenderer.cxx
cur_node->set_state(_render_state);

when Alpha is enabled the value of:

_render_state = RenderState::make(TransparencyAttrib::make (TransparencyAttrib::M_alpha), ColorAttrib::make_vertex());

ColorAttrib::make_vertex() results in the following:

ColorAttrib *attrib = new ColorAttrib(T_vertex, Colorf::zero());

then i tested the following:
1- loaded a geom from an egg file which vertices has colors assigned
2- Printed the RenderAttributes: cube.get_node(0)->get_state()->output(cout);
3- no ColorAttributes are assigned to the loaded geom!!! though based on the code it should be assigned the color type: T_vertex. so that panda will use the vertex colors to draw the geom.

a temporary solution is to modify the class GeomParticleRenderer such that it doesn’t use make_vertex() with _render_state.

any idea why make_vertex() causes the model vertex colors to be replaced with white?

I’m not entirely following the situation you’re describing. So both make_flat() and make_vertex() are applied, and either of them makes the vertices white?

The first one is not surprising, because ColorAttrib::make_flat() will, of course, replace the vertex color with the specified flat color; that’s what it’s supposed to do. ColorAttrib::make_vertex(), however, means that you want the color stored on the vertices to become visible. If ColorAttrib::make_vertex() is indeed applied to the geometry, and the color you observe is not the expected color, then it might mean that the expected color is not actually stored on the vertices.

Note that the egg loader may not load the color indicated on the vertices in the egg file into the vertices in Panda. If all the vertices are the same color, the egg loader may conclude that it is more efficient to use a ColorAttrib::make_flat() to apply the color, rather than bloat the vertex table with the identical color for each vertex. In that case, ColorAttrib::make_vertex() would lose the requested color.

You can get a clue what you’ve got with model.analyze(). If it lists the same number of colors as vertices, then you’ve got per-vertex color. You can also get the RenderState with something like:

print model.find('**/+GeomNode').node().getGeomState(0)

Also, note that the GeomParticleRenderer probably wouldn’t be actually modifying the state of the geom that you give it; it would be apply the required state change to the render stream. So you can’t analyze the state of your model afterwards to see what it has done to the model.

David

Thank you David for your answer. i will do a few more tests and then i will report my findings.

Ah, and I accidentally gave you Python syntax above, my apologies. The C++ equivalent is:

model.find("**/+GeomNode").node()->get_geom_state(0)->write(cerr, 0);

David

once again, thank you David for your help. I have finished doing more tests on this issue and my decision is that this is a bug in Panda3D since using any geometry with vertex or polygon color in the particle panel results in white geometry as is the case with c++ code. i don’t believe this was the intention of GeomParticleRenderer creator.

After some tests i came up with a solution which allows GeomParticleRenderer to use vertex color, flat color or whatever color attribute applied to the geomnode. The solution also works with geomnodes having mixed attributes ( some children have a flat color and others have vertex colors ). the results are very nice and are much better than using textures since no matter what scaling is applied quality is preserved.

I beleive the modifications should be added to Panda3D since they match more what is expected of the way GeomParticleRenderer should handle colors.

first modifications are in the file: baseParticleRenderer.cxx

void BaseParticleRenderer::
enable_alpha() {
  _render_state = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
  //=== original ===
  //_render_state = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
  //                                  ColorAttrib::make_vertex());
}
void BaseParticleRenderer::
disable_alpha() {
  _render_state = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_none));
  //_render_state = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_none),
  //                                  ColorAttrib::make_vertex());
}

part of the function: void GeomParticleRenderer::render(…) should be modified too. we could depend on number of color interpolation segments to decide if color interpolation should be used or geomnode original color attribute (vertex, flat, …)
– OR –
add a boolean (_interpolate_color) to the constructor

here are the modifications:

{
  //bool _interpolate_colors = false;

  PStatTimer t1(_render_collector);

  BaseParticle *cur_particle;
  int i, remaining_particles = ttl_particles;

  pvector< PT(PandaNode) >::iterator cur_node_iter = _node_vector.begin();

  // run through the particle vector

  for (i = 0; i < (int)po_vector.size(); i++) {
    PandaNode *cur_node;

    cur_particle = (BaseParticle *) po_vector[i].p();
    cur_node = *cur_node_iter;

    if (cur_particle->get_alive())
	{
      // living particle
      if (cur_node == (PandaNode *)NULL)
	  {
        birth_particle(i);
        cur_node = *cur_node_iter;
      }
      nassertv(cur_node != (PandaNode *)NULL);

      cur_node->set_state(_render_state);
	  
      float t = cur_particle->get_parameterized_age();
      Colorf c = _color_interpolation_manager->generateColor(t);

      if ((_alpha_mode != PR_ALPHA_NONE)) 
	  {
        float alpha_scalar;

        if(_alpha_mode == PR_ALPHA_USER) {
          alpha_scalar = get_user_alpha();
        } else {
          alpha_scalar = t;
          if (_alpha_mode == PR_ALPHA_OUT)
            alpha_scalar = 1.0f - alpha_scalar;
          else if (_alpha_mode == PR_ALPHA_IN_OUT)
            alpha_scalar = 2.0f * min(alpha_scalar, 1.0f - alpha_scalar);
          alpha_scalar *= get_user_alpha();
        }
        
        c[3] *= alpha_scalar;

		if(_interpolate_colors)
		{
			cur_node->set_attrib(ColorScaleAttrib::make(Colorf(1.0f, 1.0f, 1.0f, c[3])));
		}
		else
		{
			//=============================
			// scale alpha without changing scale of the other colors
			float scale = c[3];
			int priority = 0;
			  const RenderAttrib *attrib =
				cur_node->get_attrib(ColorScaleAttrib::get_class_slot());
			  if (attrib != (const RenderAttrib *)NULL) {
				priority = max(priority,
							   cur_node->get_state()->get_override(ColorScaleAttrib::get_class_slot()));
				const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);

				// Modify the existing ColorScaleAttrib to add the indicated colorScale.
				const LVecBase4f &sc = csa->get_scale();
				cur_node->set_attrib(csa->set_scale(LVecBase4f(sc[0], sc[1], sc[2], scale)), priority);

			  } else {
				// Create a new ColorScaleAttrib for this node.
				cur_node->set_attrib(ColorScaleAttrib::make(LVecBase4f(1.0f, 1.0f, 1.0f, scale)), priority);
			  }
			//========================
		}
      }

      if(_interpolate_colors)
	 cur_node->set_attrib(ColorAttrib::make_flat(c), 0);

      // animate scale
      float current_x_scale = _initial_x_scale;
      float current_y_scale = _initial_y_scale;
      float current_z_scale = _initial_z_scale;

      if (_animate_x_ratio || _animate_y_ratio || _animate_z_ratio) {
        if (_animate_x_ratio) {
          current_x_scale = (_initial_x_scale + 
                             (t * (_final_x_scale - _initial_x_scale)));
        }
        if (_animate_y_ratio) {
          current_y_scale = (_initial_y_scale + 
                             (t * (_final_y_scale - _initial_y_scale)));
        }
        if (_animate_z_ratio) {
          current_z_scale = (_initial_z_scale + 
                             (t * (_final_z_scale - _initial_z_scale)));
        }
      }

      cur_node->set_transform(TransformState::make_pos_quat_scale
                              (cur_particle->get_position(),
                               cur_particle->get_orientation(),
                               LVecBase3f(current_x_scale, current_y_scale, current_z_scale)));

      // maybe get out early if possible.

      remaining_particles--;

      if (remaining_particles == 0)
        break;
    }

    cur_node_iter++;
  }
}