Panda3D
 All Classes Functions Variables Enumerations
openCVTexture.cxx
1 // Filename: openCVTexture.cxx
2 // Created by: zacpavlov (19Aug05)
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 "pandabase.h"
16 
17 #ifdef HAVE_OPENCV
18 #include "openCVTexture.h"
19 #include "clockObject.h"
20 #include "config_gobj.h"
21 #include "config_vision.h"
22 #include "bamReader.h"
23 #include "bamCacheRecord.h"
24 
25 TypeHandle OpenCVTexture::_type_handle;
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: OpenCVTexture::Constructor
29 // Access: Published
30 // Description: Sets up the texture to read frames from a camera
31 ////////////////////////////////////////////////////////////////////
32 OpenCVTexture::
33 OpenCVTexture(const string &name) :
34  VideoTexture(name)
35 {
36 }
37 
38 ////////////////////////////////////////////////////////////////////
39 // Function: OpenCVTexture::Copy Constructor
40 // Access: Protected
41 // Description: Use OpenCVTexture::make_copy() to make a duplicate copy of
42 // an existing OpenCVTexture.
43 ////////////////////////////////////////////////////////////////////
44 OpenCVTexture::
45 OpenCVTexture(const OpenCVTexture &copy) :
46  VideoTexture(copy),
47  _pages(copy._pages)
48 {
49  nassertv(false);
50 }
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: OpenCVTexture::Destructor
54 // Access: Published, Virtual
55 // Description:
56 ////////////////////////////////////////////////////////////////////
57 OpenCVTexture::
58 ~OpenCVTexture() {
59 }
60 
61 ////////////////////////////////////////////////////////////////////
62 // Function: OpenCVTexture::consider_update
63 // Access: Protected, Virtual
64 // Description: Calls update_frame() if the current frame has
65 // changed.
66 ////////////////////////////////////////////////////////////////////
67 void OpenCVTexture::
68 consider_update() {
69  int this_frame = ClockObject::get_global_clock()->get_frame_count();
70  if (this_frame != _last_frame_update) {
71  int frame = get_frame();
72  if (_current_frame != frame) {
73  Texture::CDWriter cdata(Texture::_cycler, false);
74  do_update_frame(cdata, frame);
75  _current_frame = frame;
76  } else {
77  // Loop through the pages to see if there's any camera stream to update.
78  Texture::CDWriter cdata(Texture::_cycler, false);
79  int max_z = max(cdata->_z_size, (int)_pages.size());
80  for (int z = 0; z < max_z; ++z) {
81  VideoPage &page = _pages[z];
82  if (!page._color.is_from_file() || !page._alpha.is_from_file()) {
83  do_update_frame(cdata, frame, z);
84  }
85  }
86  }
87  _last_frame_update = this_frame;
88  }
89 }
90 
91 ////////////////////////////////////////////////////////////////////
92 // Function: OpenCVTexture::make_copy_impl
93 // Access: Protected, Virtual
94 // Description: Returns a new copy of the same Texture. This copy,
95 // if applied to geometry, will be copied into texture
96 // as a separate texture from the original, so it will
97 // be duplicated in texture memory (and may be
98 // independently modified if desired).
99 //
100 // If the Texture is an OpenCVTexture, the resulting
101 // duplicate may be animated independently of the
102 // original.
103 ////////////////////////////////////////////////////////////////////
104 PT(Texture) OpenCVTexture::
105 make_copy_impl() {
106  Texture::CDReader cdata_tex(Texture::_cycler);
107  PT(OpenCVTexture) copy = new OpenCVTexture(get_name());
108  Texture::CDWriter cdata_copy_tex(copy->Texture::_cycler, true);
109  copy->do_assign(cdata_copy_tex, this, cdata_tex);
110 
111  return copy.p();
112 }
113 
114 ////////////////////////////////////////////////////////////////////
115 // Function: OpenCVTexture::do_assign
116 // Access: Protected
117 // Description: Implements make_copy().
118 ////////////////////////////////////////////////////////////////////
119 void OpenCVTexture::
120 do_assign(Texture::CData *cdata_tex, const OpenCVTexture *copy,
121  const Texture::CData *cdata_copy_tex) {
122  VideoTexture::do_assign(cdata_tex, copy, cdata_copy_tex);
123  _pages = copy->_pages;
124 }
125 
126 ////////////////////////////////////////////////////////////////////
127 // Function: OpenCVTexture::from_camera
128 // Access: Published
129 // Description: Sets up the OpenCVTexture (or the indicated page, if z
130 // is specified) to accept its input from the camera
131 // with the given index number, or the default camera if
132 // the index number is -1 or unspecified.
133 //
134 // If alpha_file_channel is 0, then the camera image
135 // becomes a normal RGB texture. If it is 1, 2, or 3,
136 // then the camera image becomes an alpha texture, using
137 // the indicated channel of the source.
138 ////////////////////////////////////////////////////////////////////
139 bool OpenCVTexture::
140 from_camera(int camera_index, int z, int alpha_file_channel,
141  const LoaderOptions &options) {
142  Texture::CDWriter cdata(Texture::_cycler, true);
143  if (!do_reconsider_z_size(cdata, z, options)) {
144  return false;
145  }
146  nassertr(z >= 0 && z < cdata->_z_size, false);
147 
148  cdata->_alpha_file_channel = alpha_file_channel;
149 
150  VideoPage &page = do_modify_page(cdata, z);
151  if (alpha_file_channel == 0) {
152  // A normal RGB texture.
153  page._alpha.clear();
154  if (!page._color.from_camera(camera_index)) {
155  return false;
156  }
157 
158  if (!do_reconsider_video_properties(cdata, page._color, 3, z, options)) {
159  page._color.clear();
160  return false;
161  }
162  } else {
163  // An alpha texture.
164  page._color.clear();
165  if (!page._alpha.from_camera(camera_index)) {
166  return false;
167  }
168 
169  if (!do_reconsider_video_properties(cdata, page._alpha, 1, z, options)) {
170  page._alpha.clear();
171  return false;
172  }
173  do_set_format(cdata, F_alpha);
174  }
175 
176  cdata->_loaded_from_image = true;
177  clear_current_frame();
178  do_update_frame(cdata, 0);
179  return true;
180 }
181 
182 ////////////////////////////////////////////////////////////////////
183 // Function: OpenCVTexture::do_modify_page
184 // Access: Private
185 // Description: Returns a reference to the zth VideoPage (level) of
186 // the texture. In the case of a 2-d texture, there is
187 // only one page, level 0; but cube maps and 3-d
188 // textures have more.
189 ////////////////////////////////////////////////////////////////////
190 OpenCVTexture::VideoPage &OpenCVTexture::
191 do_modify_page(const Texture::CData *cdata, int z) {
192  nassertr(z < cdata->_z_size, _pages[0]);
193  while (z >= (int)_pages.size()) {
194  _pages.push_back(VideoPage());
195  }
196  return _pages[z];
197 }
198 
199 ////////////////////////////////////////////////////////////////////
200 // Function: OpenCVTexture::do_reconsider_video_properties
201 // Access: Private
202 // Description: Resets the internal Texture properties when a new
203 // video file is loaded. Returns true if the new image
204 // is valid, false otherwise.
205 ////////////////////////////////////////////////////////////////////
206 bool OpenCVTexture::
207 do_reconsider_video_properties(Texture::CData *cdata,
208  const OpenCVTexture::VideoStream &stream,
209  int num_components, int z,
210  const LoaderOptions &options) {
211  double frame_rate = 0.0f;
212  int num_frames = 0;
213 
214  if (stream.is_from_file()) {
215  frame_rate = cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FPS);
216  num_frames = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_COUNT);
217  if (vision_cat.is_debug()) {
218  vision_cat.debug()
219  << "Loaded " << stream._filename << ", " << num_frames << " frames at "
220  << frame_rate << " fps\n";
221  }
222  } else {
223  // In this case, we don't have a specific frame rate or number of
224  // frames. Let both values remain at 0.
225  if (vision_cat.is_debug()) {
226  vision_cat.debug()
227  << "Loaded camera stream\n";
228  }
229  }
230 
231  int width = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_WIDTH);
232  int height = (int)cvGetCaptureProperty(stream._capture, CV_CAP_PROP_FRAME_HEIGHT);
233 
234  int x_size = width;
235  int y_size = height;
236  do_adjust_this_size(cdata, x_size, y_size, get_name(), true);
237 
238  if (vision_cat.is_debug()) {
239  vision_cat.debug()
240  << "Video stream is " << width << " by " << height
241  << " pixels; fitting in texture " << x_size << " by "
242  << y_size << " texels.\n";
243  }
244 
245  if (!do_reconsider_image_properties(cdata, x_size, y_size, num_components,
246  T_unsigned_byte, z, options)) {
247  return false;
248  }
249 
250  if (cdata->_loaded_from_image &&
251  (get_video_width() != width || get_video_height() != height ||
252  get_num_frames() != num_frames || get_frame_rate() != frame_rate)) {
253  vision_cat.error()
254  << "Video properties have changed for texture " << get_name()
255  << " level " << z << ".\n";
256  return false;
257  }
258 
259  set_frame_rate(frame_rate);
260  set_num_frames(num_frames);
261  set_video_size(width, height);
262 
263  // By default, the newly-loaded video stream will immediately start
264  // looping.
265  loop(true);
266 
267  return true;
268 }
269 
270 ////////////////////////////////////////////////////////////////////
271 // Function: OpenCVTexture::make_texture
272 // Access: Public, Static
273 // Description: A factory function to make a new OpenCVTexture, used
274 // to pass to the TexturePool.
275 ////////////////////////////////////////////////////////////////////
276 PT(Texture) OpenCVTexture::
277 make_texture() {
278  return new OpenCVTexture;
279 }
280 
281 ////////////////////////////////////////////////////////////////////
282 // Function: OpenCVTexture::do_update_frame
283 // Access: Protected, Virtual
284 // Description: Called once per frame, as needed, to load the new
285 // image contents.
286 ////////////////////////////////////////////////////////////////////
287 void OpenCVTexture::
288 do_update_frame(Texture::CData *cdata, int frame) {
289  int max_z = max(cdata->_z_size, (int)_pages.size());
290  for (int z = 0; z < max_z; ++z) {
291  do_update_frame(cdata, frame, z);
292  }
293 }
294 
295 ////////////////////////////////////////////////////////////////////
296 // Function: OpenCVTexture::do_update_frame
297 // Access: Protected, Virtual
298 // Description: This variant of update_frame updates the
299 // indicated page only.
300 ////////////////////////////////////////////////////////////////////
301 void OpenCVTexture::
302 do_update_frame(Texture::CData *cdata, int frame, int z) {
303  if (vision_cat.is_spam()) {
304  vision_cat.spam()
305  << "Updating OpenCVTexture page " << z << "\n";
306  }
307 
308  VideoPage &page = _pages[z];
309  if (page._color.is_valid() || page._alpha.is_valid()) {
310  do_modify_ram_image(cdata);
311  ++(cdata->_image_modified);
312  }
313  int dest_x_pitch = cdata->_num_components * cdata->_component_width;
314  int dest_y_pitch = cdata->_x_size * dest_x_pitch;
315 
316  if (page._color.is_valid()) {
317  nassertv(get_num_components() >= 3 && get_component_width() == 1);
318 
319  const unsigned char *r, *g, *b;
320  int x_pitch, y_pitch;
321  if (page._color.get_frame_data(frame, r, g, b, x_pitch, y_pitch)) {
322  nassertv(get_video_width() <= cdata->_x_size && get_video_height() <= cdata->_y_size);
323  nassertv(!cdata->_ram_images.empty())
324  unsigned char *dest = cdata->_ram_images[0]._image.p() + do_get_expected_ram_page_size(cdata) * z;
325 
326  if (cdata->_num_components == 3 && x_pitch == 3) {
327  // The easy case--copy the whole thing in, row by row.
328  int copy_bytes = get_video_width() * dest_x_pitch;
329  nassertv(copy_bytes <= dest_y_pitch && copy_bytes <= abs(y_pitch));
330 
331  for (int y = 0; y < get_video_height(); ++y) {
332  memcpy(dest, r, copy_bytes);
333  dest += dest_y_pitch;
334  r += y_pitch;
335  }
336 
337  } else {
338  // The harder case--interleave in the color channels, pixel by
339  // pixel, possibly leaving room for alpha.
340 
341  for (int y = 0; y < get_video_height(); ++y) {
342  int dx = 0;
343  int sx = 0;
344  for (int x = 0; x < get_video_width(); ++x) {
345  dest[dx] = r[sx];
346  dest[dx + 1] = g[sx];
347  dest[dx + 2] = b[sx];
348  dx += dest_x_pitch;
349  sx += x_pitch;
350  }
351  dest += dest_y_pitch;
352  r += y_pitch;
353  g += y_pitch;
354  b += y_pitch;
355  }
356  }
357  }
358  }
359  if (page._alpha.is_valid()) {
360  nassertv(get_component_width() == 1);
361 
362  const unsigned char *source[3];
363  int x_pitch, y_pitch;
364  if (page._alpha.get_frame_data(frame, source[0], source[1], source[2],
365  x_pitch, y_pitch)) {
366  nassertv(get_video_width() <= cdata->_x_size && get_video_height() <= cdata->_y_size);
367  nassertv(!cdata->_ram_images.empty())
368  unsigned char *dest = cdata->_ram_images[0]._image.p() + do_get_expected_ram_page_size(cdata) * z;
369 
370  // Interleave the alpha in with the color, pixel by pixel.
371  // Even though the alpha will probably be a grayscale video,
372  // the OpenCV library presents it as RGB.
373  const unsigned char *sch = source[0];
374  if (cdata->_alpha_file_channel >= 1 && cdata->_alpha_file_channel <= 3) {
375  sch = source[cdata->_alpha_file_channel - 1];
376  }
377 
378  for (int y = 0; y < get_video_height(); ++y) {
379  // Start dx at _num_components - 1, which writes to the last
380  // channel, i.e. the alpha channel.
381  int dx = (cdata->_num_components - 1) * cdata->_component_width;
382  int sx = 0;
383  for (int x = 0; x < get_video_width(); ++x) {
384  dest[dx] = sch[sx];
385  dx += dest_x_pitch;
386  sx += x_pitch;
387  }
388  dest += dest_y_pitch;
389  sch += y_pitch;
390  }
391  }
392  }
393 }
394 
395 ////////////////////////////////////////////////////////////////////
396 // Function: OpenCVTexture::do_read_one
397 // Access: Protected, Virtual
398 // Description: Combines a color and alpha video image from the two
399 // indicated filenames. Both must be the same kind of
400 // video with similar properties.
401 ////////////////////////////////////////////////////////////////////
402 bool OpenCVTexture::
403 do_read_one(Texture::CData *cdata,
404  const Filename &fullpath, const Filename &alpha_fullpath,
405  int z, int n, int primary_file_num_channels, int alpha_file_channel,
406  const LoaderOptions &options,
407  bool header_only, BamCacheRecord *record) {
408  if (record != (BamCacheRecord *)NULL) {
409  record->add_dependent_file(fullpath);
410  }
411 
412  nassertr(n == 0, false);
413  nassertr(z >= 0 && z < cdata->_z_size, false);
414 
415  VideoPage &page = do_modify_page(cdata, z);
416  if (!page._color.read(fullpath)) {
417  vision_cat.error()
418  << "OpenCV couldn't read " << fullpath << " as video.\n";
419  return false;
420  }
421  if (!alpha_fullpath.empty()) {
422  if (!page._alpha.read(alpha_fullpath)) {
423  vision_cat.error()
424  << "OpenCV couldn't read " << alpha_fullpath << " as video.\n";
425  page._color.clear();
426  return false;
427  }
428  }
429 
430  if (z == 0) {
431  if (!has_name()) {
432  set_name(fullpath.get_basename_wo_extension());
433  }
434  // Don't use has_filename() here, it will cause a deadlock
435  if (cdata->_filename.empty()) {
436  cdata->_filename = fullpath;
437  cdata->_alpha_filename = alpha_fullpath;
438  }
439 
440  cdata->_fullpath = fullpath;
441  cdata->_alpha_fullpath = alpha_fullpath;
442  }
443 
444  cdata->_primary_file_num_channels = 3;
445  cdata->_alpha_file_channel = 0;
446 
447  if (alpha_fullpath.empty()) {
448  // Only one RGB movie.
449  if (!do_reconsider_video_properties(cdata, page._color, 3, z, options)) {
450  page._color.clear();
451  return false;
452  }
453 
454  } else {
455  // An RGB movie combined with an alpha movie.
456  cdata->_alpha_file_channel = alpha_file_channel;
457 
458  if (!do_reconsider_video_properties(cdata, page._color, 4, z, options)) {
459  page._color.clear();
460  page._alpha.clear();
461  return false;
462  }
463 
464  if (!do_reconsider_video_properties(cdata, page._alpha, 4, z, options)) {
465  page._color.clear();
466  page._alpha.clear();
467  return false;
468  }
469  }
470 
471  set_loaded_from_image();
472  clear_current_frame();
473  do_update_frame(cdata, 0);
474  return true;
475 }
476 
477 ////////////////////////////////////////////////////////////////////
478 // Function: OpenCVTexture::do_load_one
479 // Access: Protected, Virtual
480 // Description: Resets the texture (or the particular level of the
481 // texture) to the indicated static image.
482 ////////////////////////////////////////////////////////////////////
483 bool OpenCVTexture::
484 do_load_one(Texture::CData *cdata,
485  const PNMImage &pnmimage, const string &name,
486  int z, int n, const LoaderOptions &options) {
487  if (z <= (int)_pages.size()) {
488  VideoPage &page = do_modify_page(cdata, z);
489  page._color.clear();
490  page._alpha.clear();
491  }
492 
493  return Texture::do_load_one(cdata, pnmimage, name, z, n, options);
494 }
495 
496 ////////////////////////////////////////////////////////////////////
497 // Function: OpenCVTexture::register_with_read_factory
498 // Access: Public, Static
499 // Description: Factory method to generate a Texture object
500 ////////////////////////////////////////////////////////////////////
501 void OpenCVTexture::
502 register_with_read_factory() {
503  // Since Texture is such a funny object that is reloaded from the
504  // TexturePool each time, instead of actually being read fully from
505  // the bam file, and since the VideoTexture and OpenCVTexture
506  // classes don't really add any useful data to the bam record, we
507  // don't need to define make_from_bam(), fillin(), or
508  // write_datagram() in this class--we just inherit the same
509  // functions from Texture.
510 
511  // We do, however, have to register this class with the BamReader,
512  // to avoid warnings about creating the wrong kind of object from
513  // the bam file.
514  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
515 }
516 
517 ////////////////////////////////////////////////////////////////////
518 // Function: OpenCVTexture::VideoStream::Constructor
519 // Access: Public
520 // Description:
521 ////////////////////////////////////////////////////////////////////
522 OpenCVTexture::VideoStream::
523 VideoStream() :
524  _capture(NULL),
525  _camera_index(-1),
526  _next_frame(0)
527 {
528 }
529 
530 ////////////////////////////////////////////////////////////////////
531 // Function: OpenCVTexture::VideoStream::Copy Constructor
532 // Access: Public
533 // Description:
534 ////////////////////////////////////////////////////////////////////
535 OpenCVTexture::VideoStream::
536 VideoStream(const OpenCVTexture::VideoStream &copy) :
537  _capture(NULL),
538  _camera_index(-1)
539 {
540  // Rather than copying the _capture pointer, we must open a new
541  // stream that references the same file.
542  if (copy.is_valid()) {
543  if (copy.is_from_file()) {
544  read(copy._filename);
545  } else {
546  from_camera(copy._camera_index);
547  }
548  }
549 }
550 
551 ////////////////////////////////////////////////////////////////////
552 // Function: OpenCVTexture::VideoStream::Copy Constructor
553 // Access: Public
554 // Description:
555 ////////////////////////////////////////////////////////////////////
556 OpenCVTexture::VideoStream::
557 ~VideoStream() {
558  clear();
559 }
560 
561 ////////////////////////////////////////////////////////////////////
562 // Function: OpenCVTexture::VideoStream::get_frame_data
563 // Access: Public
564 // Description: Gets the data needed to traverse through the
565 // decompressed buffer for the indicated frame number.
566 // It is most efficient to call this in increasing order
567 // of frame number. Returns true on success, false on
568 // failure.
569 //
570 // In the case of a success indication (true return
571 // value), the three pointers r, g, b are loaded with
572 // the addresses of the three components of the
573 // bottom-left pixel of the image. (They will be
574 // adjacent in memory in the case of an interleaved
575 // image, and separated in the case of a
576 // separate-channel image.) The x_pitch value is filled
577 // with the amount to add to each pointer to advance to
578 // the pixel to the right; and the y_pitch value is
579 // filled with the amount to add to each pointer to
580 // advance to the pixel above. Note that these values
581 // may be negative (particularly in the case of a
582 // top-down image).
583 ////////////////////////////////////////////////////////////////////
584 bool OpenCVTexture::VideoStream::
585 get_frame_data(int frame,
586  const unsigned char *&r,
587  const unsigned char *&g,
588  const unsigned char *&b,
589  int &x_pitch, int &y_pitch) {
590  nassertr(is_valid(), false);
591 
592  if (is_from_file() && _next_frame != frame) {
593  cvSetCaptureProperty(_capture, CV_CAP_PROP_POS_FRAMES, frame);
594  }
595 
596  _next_frame = frame + 1;
597  IplImage *image = cvQueryFrame(_capture);
598  if (image == NULL) {
599  return false;
600  }
601 
602  r = (const unsigned char *)image->imageData;
603  g = r + 1;
604  b = g + 1;
605  x_pitch = 3;
606  y_pitch = image->widthStep;
607 
608  if (image->dataOrder == 1) {
609  // Separate channel images. That means a block of r, followed by
610  // a block of g, followed by a block of b.
611  x_pitch = 1;
612  g = r + image->height * y_pitch;
613  b = g + image->height * y_pitch;
614  }
615 
616  if (image->origin == 0) {
617  // The image data starts with the top row and ends with the bottom
618  // row--the opposite of Texture::_ram_data's storage convention.
619  // Therefore, we must increment the initial pointers to the last
620  // row, and count backwards.
621  r += (image->height - 1) * y_pitch;
622  g += (image->height - 1) * y_pitch;
623  b += (image->height - 1) * y_pitch;
624  y_pitch = -y_pitch;
625  }
626 
627  return true;
628 }
629 
630 ////////////////////////////////////////////////////////////////////
631 // Function: OpenCVTexture::VideoStream::read
632 // Access: Public
633 // Description: Sets up the stream to read the indicated file.
634 // Returns true on success, false on failure.
635 ////////////////////////////////////////////////////////////////////
636 bool OpenCVTexture::VideoStream::
637 read(const Filename &filename) {
638  clear();
639 
640  string os_specific = filename.to_os_specific();
641  _capture = cvCaptureFromFile(os_specific.c_str());
642  if (_capture == NULL) {
643  return false;
644  }
645  _filename = filename;
646  return true;
647 }
648 
649 ////////////////////////////////////////////////////////////////////
650 // Function: OpenCVTexture::VideoStream::from_camera
651 // Access: Public
652 // Description: Sets up the stream to display the indicated camera.
653 // Returns true on success, false on failure.
654 ////////////////////////////////////////////////////////////////////
655 bool OpenCVTexture::VideoStream::
656 from_camera(int camera_index) {
657  clear();
658 
659  _capture = cvCaptureFromCAM(camera_index);
660  if (_capture == NULL) {
661  return false;
662  }
663  _camera_index = camera_index;
664  return true;
665 }
666 
667 ////////////////////////////////////////////////////////////////////
668 // Function: OpenCVTexture::VideoStream::clear
669 // Access: Public
670 // Description: Stops the video playback and frees the associated
671 // resources.
672 ////////////////////////////////////////////////////////////////////
673 void OpenCVTexture::VideoStream::
674 clear() {
675  if (_capture != NULL) {
676  cvReleaseCapture(&_capture);
677  _capture = NULL;
678  }
679  _filename = Filename();
680  _camera_index = -1;
681  _next_frame = 0;
682 }
683 
684 #endif // HAVE_OPENCV
685 
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:271
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:26
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
The base class for a family of animated Textures that take their input from a video source...
Definition: videoTexture.h:31
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
int get_frame_count(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of times tick() has been called since the ClockObject was created, or since it was last reset.
Definition: clockObject.I:113
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85