Panda3D
scissorEffect.cxx
1 // Filename: scissorEffect.cxx
2 // Created by: drose (30Jul08)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "scissorEffect.h"
16 #include "scissorAttrib.h"
17 #include "cullTraverser.h"
18 #include "cullTraverserData.h"
19 #include "nodePath.h"
20 #include "bamReader.h"
21 #include "bamWriter.h"
22 #include "datagram.h"
23 #include "datagramIterator.h"
24 #include "boundingHexahedron.h"
25 #include "lens.h"
26 
27 TypeHandle ScissorEffect::_type_handle;
28 
29 ////////////////////////////////////////////////////////////////////
30 // Function: ScissorEffect::Constructor
31 // Access: Private
32 // Description: Use ScissorEffect::make() to construct a new
33 // ScissorEffect object.
34 ////////////////////////////////////////////////////////////////////
35 ScissorEffect::
36 ScissorEffect(bool screen, const LVecBase4 &frame,
37  const PointDef *points, int num_points, bool clip) :
38  _screen(screen), _frame(frame), _clip(clip)
39 {
40  _points.reserve(num_points);
41  for (int i = 0; i < num_points; ++i) {
42  _points.push_back(points[i]);
43  }
44 }
45 
46 ////////////////////////////////////////////////////////////////////
47 // Function: ScissorEffect::Copy Constructor
48 // Access: Private
49 // Description: Use ScissorEffect::make() to construct a new
50 // ScissorEffect object.
51 ////////////////////////////////////////////////////////////////////
52 ScissorEffect::
53 ScissorEffect(const ScissorEffect &copy) :
54  _screen(copy._screen),
55  _frame(copy._frame),
56  _points(copy._points),
57  _clip(copy._clip)
58 {
59 }
60 
61 ////////////////////////////////////////////////////////////////////
62 // Function: ScissorEffect::make_screen
63 // Access: Published, Static
64 // Description: Constructs a new screen-relative ScissorEffect. The
65 // frame defines a left, right, bottom, top region,
66 // relative to the DisplayRegion. See ScissorAttrib.
67 ////////////////////////////////////////////////////////////////////
68 CPT(RenderEffect) ScissorEffect::
69 make_screen(const LVecBase4 &frame, bool clip) {
70  ScissorEffect *effect = new ScissorEffect(true, frame, NULL, 0, clip);
71  return return_new(effect);
72 }
73 
74 ////////////////////////////////////////////////////////////////////
75 // Function: ScissorEffect::make_node
76 // Access: Published, Static
77 // Description: Constructs a new node-relative ScissorEffect, with no
78 // points. This empty ScissorEffect does nothing. You
79 // must then call add_point a number of times to add the
80 // points you require.
81 ////////////////////////////////////////////////////////////////////
82 CPT(RenderEffect) ScissorEffect::
83 make_node(bool clip) {
84  ScissorEffect *effect = new ScissorEffect(false, LVecBase4::zero(), NULL, 0, clip);
85  return return_new(effect);
86 }
87 
88 ////////////////////////////////////////////////////////////////////
89 // Function: ScissorEffect::make_node
90 // Access: Published, Static
91 // Description: Constructs a new node-relative ScissorEffect. The
92 // two points are understood to be relative to the
93 // indicated node, or the current node if the NodePath
94 // is empty, and determine the diagonally opposite
95 // corners of the scissor region.
96 ////////////////////////////////////////////////////////////////////
97 CPT(RenderEffect) ScissorEffect::
98 make_node(const LPoint3 &a, const LPoint3 &b, const NodePath &node) {
99  PointDef points[2];
100  points[0]._p = a;
101  points[0]._node = node;
102  points[1]._p = b;
103  points[1]._node = node;
104  ScissorEffect *effect = new ScissorEffect(false, LVecBase4::zero(), points, 2, true);
105  return return_new(effect);
106 }
107 
108 ////////////////////////////////////////////////////////////////////
109 // Function: ScissorEffect::make_node
110 // Access: Published, Static
111 // Description: Constructs a new node-relative ScissorEffect. The
112 // four points are understood to be relative to the
113 // indicated node, or the current node if the indicated
114 // NodePath is empty, and determine four points
115 // surrounding the scissor region.
116 ////////////////////////////////////////////////////////////////////
117 CPT(RenderEffect) ScissorEffect::
118 make_node(const LPoint3 &a, const LPoint3 &b, const LPoint3 &c, const LPoint3 &d, const NodePath &node) {
119  PointDef points[4];
120  points[0]._p = a;
121  points[0]._node = node;
122  points[1]._p = b;
123  points[1]._node = node;
124  points[2]._p = c;
125  points[2]._node = node;
126  points[3]._p = d;
127  points[3]._node = node;
128  ScissorEffect *effect = new ScissorEffect(false, LVecBase4::zero(), points, 4, true);
129  return return_new(effect);
130 }
131 
132 ////////////////////////////////////////////////////////////////////
133 // Function: ScissorEffect::add_point
134 // Access: Published
135 // Description: Returns a new ScissorEffect with the indicated point
136 // added. It is only valid to call this on a "node"
137 // type ScissorEffect. The full set of points,
138 // projected into screen space, defines the bounding
139 // volume of the rectangular scissor region.
140 //
141 // Each point may be relative to a different node, if
142 // desired.
143 ////////////////////////////////////////////////////////////////////
144 CPT(RenderEffect) ScissorEffect::
145 add_point(const LPoint3 &p, const NodePath &node) const {
146  nassertr(!is_screen(), this);
147  ScissorEffect *effect = new ScissorEffect(*this);
148  PointDef point;
149  point._p = p;
150  point._node = node;
151  effect->_points.push_back(point);
152  return return_new(effect);
153 }
154 
155 ////////////////////////////////////////////////////////////////////
156 // Function: ScissorEffect::xform
157 // Access: Public, Virtual
158 // Description: Returns a new RenderEffect transformed by the
159 // indicated matrix.
160 ////////////////////////////////////////////////////////////////////
161 CPT(RenderEffect) ScissorEffect::
162 xform(const LMatrix4 &mat) const {
163  if (is_screen()) {
164  return this;
165  }
166  ScissorEffect *effect = new ScissorEffect(*this);
167  Points::iterator pi;
168  for (pi = effect->_points.begin();
169  pi != effect->_points.end();
170  ++pi) {
171  PointDef &point = (*pi);
172  if (point._node.is_empty()) {
173  point._p = point._p * mat;
174  }
175  }
176  return return_new(effect);
177 }
178 
179 ////////////////////////////////////////////////////////////////////
180 // Function: ScissorEffect::output
181 // Access: Public, Virtual
182 // Description:
183 ////////////////////////////////////////////////////////////////////
184 void ScissorEffect::
185 output(ostream &out) const {
186  out << get_type() << ":";
187  if (is_screen()) {
188  out << "screen [" << _frame << "]";
189  } else {
190  out << "node";
191  Points::const_iterator pi;
192  for (pi = _points.begin(); pi != _points.end(); ++pi) {
193  const PointDef &point = (*pi);
194  if (point._node.is_empty()) {
195  out << " (" << point._p << ")";
196  } else {
197  out << " (" << point._node << ":" << point._p << ")";
198  }
199  }
200  }
201  if (!get_clip()) {
202  out << " !clip";
203  }
204 }
205 
206 ////////////////////////////////////////////////////////////////////
207 // Function: ScissorEffect::has_cull_callback
208 // Access: Public, Virtual
209 // Description: Should be overridden by derived classes to return
210 // true if cull_callback() has been defined. Otherwise,
211 // returns false to indicate cull_callback() does not
212 // need to be called for this effect during the cull
213 // traversal.
214 ////////////////////////////////////////////////////////////////////
215 bool ScissorEffect::
216 has_cull_callback() const {
217  return true;
218 }
219 
220 ////////////////////////////////////////////////////////////////////
221 // Function: ScissorEffect::cull_callback
222 // Access: Public, Virtual
223 // Description: If has_cull_callback() returns true, this function
224 // will be called during the cull traversal to perform
225 // any additional operations that should be performed at
226 // cull time. This may include additional manipulation
227 // of render state or additional visible/invisible
228 // decisions, or any other arbitrary operation.
229 //
230 // At the time this function is called, the current
231 // node's transform and state have not yet been applied
232 // to the net_transform and net_state. This callback
233 // may modify the node_transform and node_state to apply
234 // an effective change to the render state at this
235 // level.
236 ////////////////////////////////////////////////////////////////////
237 void ScissorEffect::
238 cull_callback(CullTraverser *trav, CullTraverserData &data,
239  CPT(TransformState) &node_transform,
240  CPT(RenderState) &node_state) const {
241  LVecBase4 frame;
242  const Lens *lens = trav->get_scene()->get_lens();
243  CPT(TransformState) modelview_transform = data.get_modelview_transform(trav);
244  CPT(TransformState) net_transform = modelview_transform->compose(node_transform);
245  if (net_transform->is_singular()) {
246  // If we're under a singular transform, never mind.
247  return;
248  }
249 
250  if (is_screen()) {
251  frame = _frame;
252  } else {
253  const LMatrix4 &proj_mat = lens->get_projection_mat();
254  LMatrix4 net_mat = net_transform->get_mat() * proj_mat;
255 
256  bool any_points = false;
257 
258  Points::const_iterator pi;
259  for (pi = _points.begin(); pi != _points.end(); ++pi) {
260  const PointDef &point = (*pi);
261  LVecBase4 pv(point._p[0], point._p[1], point._p[2], 1.0f);
262  if (point._node.is_empty()) {
263  // Relative to this node.
264  pv = pv * net_mat;
265 
266  } else {
267  // Relative to some other node.
268  LMatrix4 other_mat = point._node.get_net_transform()->get_mat() * proj_mat;
269  pv = pv * other_mat;
270  }
271 
272  if (pv[3] == 0) {
273  continue;
274  }
275  LPoint3 pr(pv[0] / pv[3], pv[1] / pv[3], pv[2] / pv[3]);
276  if (!any_points) {
277  frame[0] = pr[0];
278  frame[1] = pr[0];
279  frame[2] = pr[1];
280  frame[3] = pr[1];
281  any_points = true;
282  } else {
283  frame[0] = min(frame[0], pr[0]);
284  frame[1] = max(frame[1], pr[0]);
285  frame[2] = min(frame[2], pr[1]);
286  frame[3] = max(frame[3], pr[1]);
287  }
288  }
289 
290  // Scale from -1..1 to 0..1.
291  frame[0] = (frame[0] + 1.0f) * 0.5f;
292  frame[1] = (frame[1] + 1.0f) * 0.5f;
293  frame[2] = (frame[2] + 1.0f) * 0.5f;
294  frame[3] = (frame[3] + 1.0f) * 0.5f;
295  }
296 
297  // Impose bounding volumes.
298  frame[0] = max(min(frame[0], (PN_stdfloat)1.0), (PN_stdfloat)0.0);
299  frame[1] = max(min(frame[1], (PN_stdfloat)1.0), frame[0]);
300  frame[2] = max(min(frame[2], (PN_stdfloat)1.0), (PN_stdfloat)0.0);
301  frame[3] = max(min(frame[3], (PN_stdfloat)1.0), frame[2]);
302 
303  if (_clip) {
304  CPT(RenderAttrib) scissor_attrib = ScissorAttrib::make(frame);
305  CPT(RenderState) state = RenderState::make(scissor_attrib);
306  node_state = node_state->compose(state);
307  }
308 
309  // Set up the culling. We do this by extruding the four corners of
310  // the frame into the eight corners of the bounding frustum.
311  PT(GeometricBoundingVolume) frustum = make_frustum(lens, frame);
312  if (frustum != (GeometricBoundingVolume *)NULL) {
313  frustum->xform(modelview_transform->get_inverse()->get_mat());
314  data._view_frustum = frustum;
315  }
316 }
317 
318 ////////////////////////////////////////////////////////////////////
319 // Function: ScissorEffect::compare_to_impl
320 // Access: Protected, Virtual
321 // Description: Intended to be overridden by derived ScissorEffect
322 // types to return a unique number indicating whether
323 // this ScissorEffect is equivalent to the other one.
324 //
325 // This should return 0 if the two ScissorEffect objects
326 // are equivalent, a number less than zero if this one
327 // should be sorted before the other one, and a number
328 // greater than zero otherwise.
329 //
330 // This will only be called with two ScissorEffect
331 // objects whose get_type() functions return the same.
332 ////////////////////////////////////////////////////////////////////
333 int ScissorEffect::
334 compare_to_impl(const RenderEffect *other) const {
335  const ScissorEffect *ta;
336  DCAST_INTO_R(ta, other, 0);
337 
338  if (_screen != ta->_screen) {
339  return (int)_screen - (int)ta->_screen;
340  }
341  if (_clip != ta->_clip) {
342  return (int)_clip - (int)ta->_clip;
343  }
344  if (_screen) {
345  int compare = _frame.compare_to(ta->_frame);
346  if (compare != 0) {
347  return compare;
348  }
349  } else {
350  int compare = (int)_points.size() - (int)ta->_points.size();
351  if (compare != 0) {
352  return compare;
353  }
354  for (size_t i = 0; i < _points.size(); ++i) {
355  compare = _points[i]._p.compare_to(ta->_points[i]._p);
356  if (compare != 0) {
357  return compare;
358  }
359  compare = _points[i]._node.compare_to(ta->_points[i]._node);
360  if (compare != 0) {
361  return compare;
362  }
363  }
364  }
365  return 0;
366 }
367 
368 ////////////////////////////////////////////////////////////////////
369 // Function: ScissorEffect::register_with_read_factory
370 // Access: Public, Static
371 // Description: Tells the BamReader how to create objects of type
372 // ScissorEffect.
373 ////////////////////////////////////////////////////////////////////
374 void ScissorEffect::
375 register_with_read_factory() {
376  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
377 }
378 
379 ////////////////////////////////////////////////////////////////////
380 // Function: ScissorEffect::write_datagram
381 // Access: Public, Virtual
382 // Description: Writes the contents of this object to the datagram
383 // for shipping out to a Bam file.
384 ////////////////////////////////////////////////////////////////////
385 void ScissorEffect::
387  RenderEffect::write_datagram(manager, dg);
388 
389  dg.add_bool(_screen);
390  if (_screen) {
391  _frame.write_datagram(dg);
392  } else {
393  dg.add_uint16(_points.size());
394  Points::const_iterator pi;
395  for (pi = _points.begin(); pi != _points.end(); ++pi) {
396  (*pi)._p.write_datagram(dg);
397  }
398  }
399  dg.add_bool(_clip);
400 }
401 
402 ////////////////////////////////////////////////////////////////////
403 // Function: ScissorEffect::make_from_bam
404 // Access: Protected, Static
405 // Description: This function is called by the BamReader's factory
406 // when a new object of type ScissorEffect is encountered
407 // in the Bam file. It should create the ScissorEffect
408 // and extract its information from the file.
409 ////////////////////////////////////////////////////////////////////
410 TypedWritable *ScissorEffect::
411 make_from_bam(const FactoryParams &params) {
412  ScissorEffect *effect = new ScissorEffect(true, LVecBase4::zero(), NULL, 0, false);
413  DatagramIterator scan;
414  BamReader *manager;
415 
416  parse_params(params, scan, manager);
417  effect->fillin(scan, manager);
418 
419  return effect;
420 }
421 
422 ////////////////////////////////////////////////////////////////////
423 // Function: ScissorEffect::fillin
424 // Access: Protected
425 // Description: This internal function is called by make_from_bam to
426 // read in all of the relevant data from the BamFile for
427 // the new ScissorEffect.
428 ////////////////////////////////////////////////////////////////////
429 void ScissorEffect::
430 fillin(DatagramIterator &scan, BamReader *manager) {
431  RenderEffect::fillin(scan, manager);
432 
433  _screen = scan.get_bool();
434  if (_screen) {
435  _frame.read_datagram(scan);
436  } else {
437  int num_points = scan.get_uint16();
438  _points.reserve(num_points);
439  for (int i = 0; i < num_points; ++i) {
440  PointDef point;
441  point._p.read_datagram(scan);
442  _points.push_back(point);
443  }
444  }
445  _clip = scan.get_bool();
446 }
447 
448 ////////////////////////////////////////////////////////////////////
449 // Function: ScissorEffect::make_frustum
450 // Access: Private
451 // Description: Constructs a new bounding frustum from the lens
452 // properties, given the indicated scissor frame.
453 ////////////////////////////////////////////////////////////////////
454 PT(GeometricBoundingVolume) ScissorEffect::
455 make_frustum(const Lens *lens, const LVecBase4 &frame) const{
456  // Scale the frame from 0 .. 1 into -1 .. 1.
457  LVecBase4 f2(frame[0] * 2.0f - 1.0f,
458  frame[1] * 2.0f - 1.0f,
459  frame[2] * 2.0f - 1.0f,
460  frame[3] * 2.0f - 1.0f);
461 
462  LPoint3 fll, flr, ful, fur;
463  LPoint3 nll, nlr, nul, nur;
464  LPoint2 corner;
465 
466  corner[0] = f2[0]; corner[1] = f2[3];
467 
468  // Upper left.
469  if (!lens->extrude(corner, nul, ful)) {
470  return (GeometricBoundingVolume *)NULL;
471  }
472 
473  corner[0] = f2[1]; corner[1] = f2[3];
474 
475  // Upper right.
476  if (!lens->extrude(corner, nur, fur)) {
477  return (GeometricBoundingVolume *)NULL;
478  }
479 
480  corner[0] = f2[1]; corner[1] = f2[2];
481 
482  // Lower right.
483  if (!lens->extrude(corner, nlr, flr)) {
484  return (GeometricBoundingVolume *)NULL;
485  }
486 
487  corner[0] = f2[0]; corner[1] = f2[2];
488 
489  // Lower left.
490  if (!lens->extrude(corner, nll, fll)) {
491  return (GeometricBoundingVolume *)NULL;
492  }
493 
494  return new BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul);
495 }
This provides a higher-level wrapper around ScissorAttrib.
Definition: scissorEffect.h:35
bool get_bool()
Extracts a boolean value.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:45
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
bool is_screen() const
Returns true if the ScissorEffect is a screen-based effect, meaning get_frame() has a meaningful valu...
Definition: scissorEffect.I:24
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
This collects together the pieces of data that are accumulated for each node while walking the scene ...
const Lens * get_lens() const
Returns the particular Lens used for rendering.
Definition: sceneSetup.I:164
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
This is the base class for a number of special render effects that may be set on scene graph nodes to...
Definition: renderEffect.h:56
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:118
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
int compare_to(const LVecBase4f &other) const
This flavor of compare_to uses a default threshold value based on the numeric type.
Definition: lvecBase4.h:988
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
void read_datagram(DatagramIterator &source)
Reads the vector from the Datagram using get_stdfloat().
Definition: lvecBase4.h:1458
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
const LMatrix4 & get_projection_mat(StereoChannel channel=SC_mono) const
Returns the complete transformation matrix from a 3-d point in space to a point on the film...
Definition: lens.I:717
void write_datagram(Datagram &destination) const
Writes the vector to the Datagram using add_stdfloat().
Definition: lvecBase4.h:1438
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:43
bool get_clip() const
Returns true if this ScissorEffect actually enables scissoring, or false if it culls only...
Definition: scissorEffect.I:88
bool extrude(const LPoint2 &point2d, LPoint3 &near_point, LPoint3 &far_point) const
Given a 2-d point in the range (-1,1) in both dimensions, where (0,0) is the center of the lens and (...
Definition: lens.I:32
A class to retrieve the individual data elements previously stored in a Datagram. ...
This is a two-component point in space.
Definition: lpoint2.h:92
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
This defines a bounding convex hexahedron.
static const LVecBase4f & zero()
Returns a zero-length vector.
Definition: lvecBase4.h:493