瀏覽代碼

prevent crash when sprite particle renderer is flattened accidentally

Jason Yeung 20 年之前
父節點
當前提交
168d499af6

+ 2 - 1
panda/src/display/graphicsStateGuardian.cxx

@@ -895,11 +895,12 @@ finish_decal() {
 //               are ok, false to abort this group of primitives.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
-begin_draw_primitives(const Geom *, const GeomMunger *munger,
+begin_draw_primitives(const Geom *geom, const GeomMunger *munger,
                       const GeomVertexData *data) {
   _munger = munger;
   _vertex_data = data;
 
+  nassertr(geom->check_valid(data), false);
   return _vertex_data->has_vertex();
 }
 

+ 25 - 1
panda/src/gobj/geom.cxx

@@ -593,7 +593,7 @@ get_num_bytes() const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::transform_vertices
-//       Access: Published, Virtual
+//       Access: Published
 //  Description: Applies the indicated transform to all of the
 //               vertices in the Geom.  If the Geom happens to share a
 //               vertex table with another Geom, this operation will
@@ -652,6 +652,30 @@ check_valid() const {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Geom::check_valid
+//       Access: Published
+//  Description: Verifies that the all of the primitives within the
+//               geom reference vertices that actually exist within
+//               the indicated GeomVertexData.  Returns true if the
+//               geom appears to be valid, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Geom::
+check_valid(const GeomVertexData *vertex_data) const {
+  CDReader cdata(_cycler);
+
+  Primitives::const_iterator pi;
+  for (pi = cdata->_primitives.begin(); 
+       pi != cdata->_primitives.end();
+       ++pi) {
+    if (!(*pi)->check_valid(vertex_data)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Geom::output
 //       Access: Published, Virtual

+ 1 - 3
panda/src/gobj/geom.h

@@ -96,11 +96,9 @@ PUBLISHED:
   int get_num_bytes() const;
   INLINE UpdateSeq get_modified() const;
 
-  // Temporarily virtual.
   virtual void transform_vertices(const LMatrix4f &mat);
-
-  // Temporarily virtual.
   virtual bool check_valid() const;
+  bool check_valid(const GeomVertexData *vertex_data) const;
 
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level = 0) const;

+ 16 - 0
panda/src/particlesystem/spriteParticleRenderer.cxx

@@ -530,6 +530,8 @@ init_geoms() {
       }
     }
   }
+
+  nassertv(render_node->check_valid());
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -726,9 +728,21 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
     }
   }
 
+  int n = 0;
+  GeomNode *render_node = get_render_node();
+
   for (i = 0; i < anim_count; ++i) {
     for (j = 0; j < _anim_size[i]; ++j) {
       _sprites[i][j]->clear_vertices();
+
+      // We have to reassign the GeomVertexData and GeomPrimitive to
+      // the Geom, and the Geom to the GeomNode, in case it got
+      // flattened away.
+      _sprite_primitive[i][j]->set_primitive(0, _sprites[i][j]);
+      _sprite_primitive[i][j]->set_vertex_data(_vdata[i][j]);
+
+      render_node->set_geom(n, _sprite_primitive[i][j]);
+      ++n;
     }
   }
 
@@ -750,11 +764,13 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
 
   for (i = 0; i < anim_count; ++i) {
     for (j = 0; j < _anim_size[i]; ++j) {
+      nassertv(_sprite_primitive[i][j]->check_valid());
       _sprite_primitive[i][j]->set_bound(BoundingSphere(aabb_center, radius));
     }
   }
 
   get_render_node()->mark_bound_stale();
+  nassertv(render_node->check_valid());
   _animation_removed = false;
 }
 

+ 143 - 140
panda/src/pgraph/cullableObject.cxx

@@ -54,6 +54,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
   if (_geom != (Geom *)NULL) {
     _munger = munger;
     _munged_data = _geom->get_vertex_data();
+    nassertv(_geom->check_valid(_munged_data));
 
     int geom_rendering = _geom->get_geom_rendering();
     geom_rendering = _state->get_geom_rendering(geom_rendering);
@@ -305,159 +306,161 @@ munge_points_to_quads(const CullTraverser *traverser) {
   int num_primitives = _geom->get_num_primitives();
   for (int pi = 0; pi < num_primitives; ++pi) {
     const GeomPrimitive *primitive = _geom->get_primitive(pi);
-
-    // We must first convert all of the points to eye space.
-    int num_points = primitive->get_max_vertex() + 1;
-    int num_vertices = primitive->get_num_vertices();
-    PointData *points = (PointData *)alloca(num_points * sizeof(PointData));
-    unsigned int *vertices = (unsigned int *)alloca(num_vertices * sizeof(unsigned int));
-    unsigned int *vertices_end = vertices + num_vertices;
-
-    if (primitive->is_indexed()) {
-      GeomVertexReader index(primitive->get_vertices(), 0);
-      for (unsigned int *vi = vertices; vi != vertices_end; ++vi) {
-        // Get the point in eye-space coordinates.
-        unsigned int v = index.get_data1i();
-        nassertv(v < (unsigned int)num_points);
-        (*vi) = v;
-        vertex.set_row(v);
-        points[v]._eye = modelview.xform_point(vertex.get_data3f());
-        points[v]._dist = gsg->compute_distance_to(points[v]._eye);
-      }
-    } else {
-      // Nonindexed case.
-      unsigned int first_vertex = primitive->get_first_vertex();
-      for (int i = 0; i < num_vertices; ++i) {
-        unsigned int v = i + first_vertex;
-        nassertv(v < (unsigned int)num_points);
-        vertices[i] = v;
-        vertex.set_row(i + first_vertex);
-        points[v]._eye = modelview.xform_point(vertex.get_data3f());
-        points[v]._dist = gsg->compute_distance_to(points[v]._eye);
+    if (primitive->get_num_vertices() != 0) {
+      // We must first convert all of the points to eye space.
+      int num_points = primitive->get_max_vertex() + 1;
+
+      int num_vertices = primitive->get_num_vertices();
+      PointData *points = (PointData *)alloca(num_points * sizeof(PointData));
+      unsigned int *vertices = (unsigned int *)alloca(num_vertices * sizeof(unsigned int));
+      unsigned int *vertices_end = vertices + num_vertices;
+
+      if (primitive->is_indexed()) {
+	GeomVertexReader index(primitive->get_vertices(), 0);
+	for (unsigned int *vi = vertices; vi != vertices_end; ++vi) {
+	  // Get the point in eye-space coordinates.
+	  unsigned int v = index.get_data1i();
+	  nassertv(v < (unsigned int)num_points);
+	  (*vi) = v;
+	  vertex.set_row(v);
+	  points[v]._eye = modelview.xform_point(vertex.get_data3f());
+	  points[v]._dist = gsg->compute_distance_to(points[v]._eye);
+	}
+      } else {
+	// Nonindexed case.
+	unsigned int first_vertex = primitive->get_first_vertex();
+	for (int i = 0; i < num_vertices; ++i) {
+	  unsigned int v = i + first_vertex;
+	  nassertv(v < (unsigned int)num_points);
+	  vertices[i] = v;
+	  vertex.set_row(v);
+	  points[v]._eye = modelview.xform_point(vertex.get_data3f());
+	  points[v]._dist = gsg->compute_distance_to(points[v]._eye);
+	}
       }
-    }
   
-    // Now sort the points in order from back-to-front so they will
-    // render properly with transparency, at least with each other.
-    sort(vertices, vertices_end, SortPoints(points));
+      // Now sort the points in order from back-to-front so they will
+      // render properly with transparency, at least with each other.
+      sort(vertices, vertices_end, SortPoints(points));
   
-    // Go through the points, now in sorted order, and generate a pair
-    // of triangles for each one.  We generate indexed triangles
-    // instead of two-triangle strips, since this seems to be
-    // generally faster on PC hardware (otherwise, we'd have to nearly
-    // double the vertices to stitch all the little triangle strips
-    // together).
-    PT(GeomPrimitive) new_primitive = new GeomTriangles(Geom::UH_client);
-
-    for (unsigned int *vi = vertices; vi != vertices_end; ++vi) {
-      // The point in eye coordinates.
-      const LPoint3f &eye = points[*vi]._eye;
+      // Go through the points, now in sorted order, and generate a pair
+      // of triangles for each one.  We generate indexed triangles
+      // instead of two-triangle strips, since this seems to be
+      // generally faster on PC hardware (otherwise, we'd have to nearly
+      // double the vertices to stitch all the little triangle strips
+      // together).
+      PT(GeomPrimitive) new_primitive = new GeomTriangles(Geom::UH_client);
+
+      for (unsigned int *vi = vertices; vi != vertices_end; ++vi) {
+	// The point in eye coordinates.
+	const LPoint3f &eye = points[*vi]._eye;
     
-      // The point in clip coordinates.
-      LPoint4f p4 = LPoint4f(eye[0], eye[1], eye[2], 1.0f) * projection;
+	// The point in clip coordinates.
+	LPoint4f p4 = LPoint4f(eye[0], eye[1], eye[2], 1.0f) * projection;
 
-      if (has_size) {
-        size.set_row(*vi);
-        point_size = size.get_data1f();
-      }
+	if (has_size) {
+	  size.set_row(*vi);
+	  point_size = size.get_data1f();
+	}
 
-      float scale_y = point_size;
-      if (perspective) {
-        // Perspective-sized points.  Here point_size is the point's
-        // height in 3-d units.  To arrange that, we need to figure
-        // out the appropriate scaling factor based on the current
-        // viewport and projection matrix.
-        float scale = _modelview_transform->get_scale()[1];
-        LVector3f height(0.0f, point_size * scale, scale);
-        height = height * height_projection;
-        scale_y = height[1] * viewport_height;
-
-        // We should then divide the radius by the distance from the
-        // camera plane, to emulate the glPointParameters() behavior.
-        if (!lens->is_orthographic()) {
-          scale_y /= gsg->compute_distance_to(eye);
-        }
-      }
+	float scale_y = point_size;
+	if (perspective) {
+	  // Perspective-sized points.  Here point_size is the point's
+	  // height in 3-d units.  To arrange that, we need to figure
+	  // out the appropriate scaling factor based on the current
+	  // viewport and projection matrix.
+	  float scale = _modelview_transform->get_scale()[1];
+	  LVector3f height(0.0f, point_size * scale, scale);
+	  height = height * height_projection;
+	  scale_y = height[1] * viewport_height;
+
+	  // We should then divide the radius by the distance from the
+	  // camera plane, to emulate the glPointParameters() behavior.
+	  if (!lens->is_orthographic()) {
+	    scale_y /= gsg->compute_distance_to(eye);
+	  }
+	}
       
-      // Also factor in the homogeneous scale for being in clip
-      // coordinates still.
-      scale_y *= p4[3];
-
-      float scale_x = scale_y;
-      if (has_aspect_ratio) {
-        aspect_ratio.set_row(*vi);
-        scale_x *= aspect_ratio.get_data1f();
-      }
+	// Also factor in the homogeneous scale for being in clip
+	// coordinates still.
+	scale_y *= p4[3];
+
+	float scale_x = scale_y;
+	if (has_aspect_ratio) {
+	  aspect_ratio.set_row(*vi);
+	  scale_x *= aspect_ratio.get_data1f();
+	}
 
-      // Define the first two corners based on the scales in X and Y.
-      LPoint2f c0(scale_x, scale_y);
-      LPoint2f c1(-scale_x, scale_y);
-
-      if (has_rotate) { 
-       // If we have a rotate factor, apply it to those two corners.
-        rotate.set_row(*vi);
-        float r = rotate.get_data1f();
-        LMatrix3f mat = LMatrix3f::rotate_mat(r);
-        c0 = c0 * mat;
-        c1 = c1 * mat;
-      }
+	// Define the first two corners based on the scales in X and Y.
+	LPoint2f c0(scale_x, scale_y);
+	LPoint2f c1(-scale_x, scale_y);
+
+	if (has_rotate) { 
+	  // If we have a rotate factor, apply it to those two corners.
+	  rotate.set_row(*vi);
+	  float r = rotate.get_data1f();
+	  LMatrix3f mat = LMatrix3f::rotate_mat(r);
+	  c0 = c0 * mat;
+	  c1 = c1 * mat;
+	}
 
-      // Finally, scale the corners in their newly-rotated position,
-      // to compensate for the aspect ratio of the viewport.
-      float rx = 1.0f / viewport_width;
-      float ry = 1.0f / viewport_height;
-      c0.set(c0[0] * rx, c0[1] * ry);
-      c1.set(c1[0] * rx, c1[1] * ry);
-
-      new_vertex.add_data4f(p4[0] + c0[0], p4[1] + c0[1], p4[2], p4[3]);
-      new_vertex.add_data4f(p4[0] + c1[0], p4[1] + c1[1], p4[2], p4[3]);
-      new_vertex.add_data4f(p4[0] - c1[0], p4[1] - c1[1], p4[2], p4[3]);
-      new_vertex.add_data4f(p4[0] - c0[0], p4[1] - c0[1], p4[2], p4[3]);
-
-      if (has_normal) {
-        normal.set_row(*vi);
-        Normalf c = render_transform.xform_vec(normal.get_data3f());
-        new_normal.add_data3f(c);
-        new_normal.add_data3f(c);
-        new_normal.add_data3f(c);
-        new_normal.add_data3f(c);
-      }
-      if (has_color) {
-        color.set_row(*vi);
-        const Colorf &c = color.get_data4f();
-        new_color.add_data4f(c);
-        new_color.add_data4f(c);
-        new_color.add_data4f(c);
-        new_color.add_data4f(c);
-      }
-      if (sprite_texcoord) {
-        new_texcoord.add_data2f(1.0f, 0.0f);
-        new_texcoord.add_data2f(0.0f, 0.0f);
-        new_texcoord.add_data2f(1.0f, 1.0f);
-        new_texcoord.add_data2f(0.0f, 1.0f);
-      } else if (has_texcoord) {
-        texcoord.set_row(*vi);
-        const LVecBase4f &c = texcoord.get_data4f();
-        new_texcoord.add_data4f(c);
-        new_texcoord.add_data4f(c);
-        new_texcoord.add_data4f(c);
-        new_texcoord.add_data4f(c);
-      }
+	// Finally, scale the corners in their newly-rotated position,
+	// to compensate for the aspect ratio of the viewport.
+	float rx = 1.0f / viewport_width;
+	float ry = 1.0f / viewport_height;
+	c0.set(c0[0] * rx, c0[1] * ry);
+	c1.set(c1[0] * rx, c1[1] * ry);
+
+	new_vertex.add_data4f(p4[0] + c0[0], p4[1] + c0[1], p4[2], p4[3]);
+	new_vertex.add_data4f(p4[0] + c1[0], p4[1] + c1[1], p4[2], p4[3]);
+	new_vertex.add_data4f(p4[0] - c1[0], p4[1] - c1[1], p4[2], p4[3]);
+	new_vertex.add_data4f(p4[0] - c0[0], p4[1] - c0[1], p4[2], p4[3]);
+
+	if (has_normal) {
+	  normal.set_row(*vi);
+	  Normalf c = render_transform.xform_vec(normal.get_data3f());
+	  new_normal.add_data3f(c);
+	  new_normal.add_data3f(c);
+	  new_normal.add_data3f(c);
+	  new_normal.add_data3f(c);
+	}
+	if (has_color) {
+	  color.set_row(*vi);
+	  const Colorf &c = color.get_data4f();
+	  new_color.add_data4f(c);
+	  new_color.add_data4f(c);
+	  new_color.add_data4f(c);
+	  new_color.add_data4f(c);
+	}
+	if (sprite_texcoord) {
+	  new_texcoord.add_data2f(1.0f, 0.0f);
+	  new_texcoord.add_data2f(0.0f, 0.0f);
+	  new_texcoord.add_data2f(1.0f, 1.0f);
+	  new_texcoord.add_data2f(0.0f, 1.0f);
+	} else if (has_texcoord) {
+	  texcoord.set_row(*vi);
+	  const LVecBase4f &c = texcoord.get_data4f();
+	  new_texcoord.add_data4f(c);
+	  new_texcoord.add_data4f(c);
+	  new_texcoord.add_data4f(c);
+	  new_texcoord.add_data4f(c);
+	}
 
-      new_primitive->add_vertex(new_vi);
-      new_primitive->add_vertex(new_vi + 1);
-      new_primitive->add_vertex(new_vi + 2);
-      new_primitive->close_primitive();
+	new_primitive->add_vertex(new_vi);
+	new_primitive->add_vertex(new_vi + 1);
+	new_primitive->add_vertex(new_vi + 2);
+	new_primitive->close_primitive();
 
-      new_primitive->add_vertex(new_vi + 2);
-      new_primitive->add_vertex(new_vi + 1);
-      new_primitive->add_vertex(new_vi + 3);
-      new_primitive->close_primitive();
+	new_primitive->add_vertex(new_vi + 2);
+	new_primitive->add_vertex(new_vi + 1);
+	new_primitive->add_vertex(new_vi + 3);
+	new_primitive->close_primitive();
 
-      new_vi += 4;
-    }
+	new_vi += 4;
+      }
 
-    new_geom->add_primitive(new_primitive);
+      new_geom->add_primitive(new_primitive);
+    }
   }
 
   _geom = new_geom.p();

+ 39 - 0
panda/src/pgraph/geomNode.cxx

@@ -420,6 +420,45 @@ add_geoms_from(const GeomNode *other) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::set_geom
+//       Access: Public
+//  Description: Replaces the nth Geom of the node with a new pointer.
+//               There must already be a Geom in this slot.
+////////////////////////////////////////////////////////////////////
+void GeomNode::
+set_geom(int n, Geom *geom) {
+  nassertv(geom != (Geom *)NULL);
+  nassertv(geom->check_valid());
+
+  CDWriter cdata(_cycler);
+  nassertv(n >= 0 && n < (int)cdata->_geoms.size());
+  cdata->_geoms[n]._geom = geom;
+
+  mark_bound_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::check_valid
+//       Access: Published
+//  Description: Verifies that the each Geom within the GeomNode
+//               reference vertices that actually exist within its
+//               GeomVertexData.  Returns true if the GeomNode appears
+//               to be valid, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool GeomNode::
+check_valid() const {
+  int num_geoms = get_num_geoms();
+  for (int i = 0; i < num_geoms; i++) {
+    const Geom *geom = get_geom(i);
+    if (!geom->check_valid()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::unify
 //       Access: Published

+ 2 - 0
panda/src/pgraph/geomNode.h

@@ -65,8 +65,10 @@ PUBLISHED:
 
   int add_geom(Geom *geom, const RenderState *state = RenderState::make_empty());
   void add_geoms_from(const GeomNode *other);
+  void set_geom(int n, Geom *geom);
   INLINE void remove_geom(int n);
   INLINE void remove_all_geoms();
+  bool check_valid() const;
 
   void unify();