1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "movie_shooter.h"
25 #include "time_client.h"
26 #include "nel/misc/bitmap.h"
27 #include "nel/misc/path.h"
28 #include "nel/misc/fast_mem.h"
29 #include "nel/misc/common.h"
30 #include "nel/misc/file.h"
31 #include "nel/misc/debug.h"
32 #include "nel/misc/system_info.h"
41 using namespace NLMISC
;
45 // ***************************************************************************
46 CMovieShooter MovieShooter
;
49 // ***************************************************************************
50 CMovieShooter::CMovieShooter()
58 _FrameSkip
= _CurrentFrameSkip
= 0;
61 // ***************************************************************************
62 CMovieShooter::~CMovieShooter()
67 // ***************************************************************************
68 bool CMovieShooter::init(uint maxMemory
)
75 _MemoryBlock
= new uint8
[maxMemory
];
78 nlwarning("Failed to Allocate %d bytes for MovieShooter", maxMemory
);
81 // Must fill memory with 0, just to ensure the system allocate the virtual pages.
82 memset(_MemoryBlock
, 0, maxMemory
);
83 _MemorySize
= maxMemory
;
92 // ***************************************************************************
93 void CMovieShooter::clearMemory()
96 delete [] (_MemoryBlock
);
105 // ***************************************************************************
106 bool CMovieShooter::addFrame(double time
, UDriver
*pDriver
)
111 // Get the buffer from driver. static to avoid reallocation
112 static CBitmap bitmap
;
113 pDriver
->getBuffer(bitmap
);
114 nlassert(bitmap
.getPixelFormat()==CBitmap::RGBA
);
117 if(bitmap
.getPixels().empty())
119 return addFrame(time
, (CRGBA
*)(&bitmap
.getPixels()[0]), bitmap
.getWidth(), bitmap
.getHeight());
122 // ***************************************************************************
123 bool CMovieShooter::addFrame(double time
, CRGBA
*pImage
, uint w
, uint h
)
125 if(!enabled() || w
==0 || h
==0)
129 if (_CurrentFrameSkip
<= _FrameSkip
)
131 _CurrentFrameSkip
= 0;
133 // _MemorySize must contain at least ONE frame.
134 uint dataSize
= w
*h
*sizeof(uint16
);
135 uint totalSize
= dataSize
+sizeof(CFrameHeader
);
136 nlassert(totalSize
<=_MemorySize
);
138 // If too big to fit in memory malloc, loop
139 if( totalSize
+ _CurrentIndex
> _MemorySize
)
144 // prepare a new frame.
145 CFrameHeader
*newFrame
= (CFrameHeader
*)(_MemoryBlock
+_CurrentIndex
);
147 // while this frame erase first one.
148 while( _FirstFrame
!=NULL
&& (uint8
*)newFrame
<=(uint8
*)_FirstFrame
&& ((uint8
*)newFrame
+totalSize
)>(uint8
*)_FirstFrame
)
150 // skip to the next frame.
151 _FirstFrame
= _FirstFrame
->Next
;
153 // if empty, clean all.
154 if(_FirstFrame
==NULL
)
156 nlassert(_NumFrames
==0);
159 newFrame
= (CFrameHeader
*)_MemoryBlock
;
164 newFrame
->Time
= time
;
167 newFrame
->Data
= _MemoryBlock
+_CurrentIndex
+sizeof(CFrameHeader
);
168 newFrame
->Next
= NULL
;
170 // Compress and Fill Data. As fast as possible
171 uint16
*dst
= (uint16
*)newFrame
->Data
;
173 for(uint y
=h
;y
>0;y
--)
175 // Precache all the line. NB: correct for 800, since 800*4==3200, ie under the 4K cache size.
176 CFastMem::precache(src
, w
*sizeof(CRGBA
));
177 // For all pixels; compress, and store.
178 #if defined(NL_OS_WINDOWS) && !defined(NL_NO_ASM)
213 for(uint x
=w
;x
>0;x
--, src
++, dst
++)
221 _CurrentIndex
+= totalSize
;
224 if(_FirstFrame
==NULL
)
226 _FirstFrame
= _LastFrame
= newFrame
;
230 _LastFrame
->Next
= newFrame
;
231 _LastFrame
= newFrame
;
239 // ***************************************************************************
240 uint
CMovieShooter::getNumFrames()
245 // ***************************************************************************
246 void CMovieShooter::saveMovie(UDriver
*drv
, UTextContext
*textContext
, const char *path
, float framePeriod
, bool allowLinearInterp
, const char *savePrefix
/*= "shot_"*/)
248 if(!CFile::isDirectory(path
))
249 throw Exception("SaveMovie: %s is not a directory", path
);
252 throw Exception("SaveMovie: no Frames to save");
255 throw Exception("SaveMovie: bad Frame Period");
259 textContext
->setFontSize(10);
260 textContext
->setColor(CRGBA(255,255,255));
261 textContext
->setHotSpot(UTextContext::TopLeft
);
265 string fileName
= string(path
) + "/" + string(savePrefix
);
268 CFrameHeader
*precFrame
= _FirstFrame
;
269 double time
= precFrame
->Time
;
273 sint n
= getNumFrames();
274 // Write interpolation of frames => must have 2 frames.
278 drv
->EventServer
.pump(true);
280 if(drv
->AsyncListener
.isKeyDown(KeyESCAPE
))
286 CFrameHeader
*nextFrame
= precFrame
->Next
;
288 // Skip any frame. Get first end frame with Time>time
289 while(n
>=2 && nextFrame
->Time
<=time
)
292 precFrame
= nextFrame
;
293 nextFrame
= nextFrame
->Next
;
297 // If a frame exist, get it.
300 // get the interpolation factor
301 double interpValue
= (time
-precFrame
->Time
)/(nextFrame
->Time
-precFrame
->Time
);
303 // interpolation is possible only if 2 frames are of same size, and if wanted
307 h
= precFrame
->Height
;
308 interpOk
= allowLinearInterp
&& (w
==nextFrame
->Width
&& h
==nextFrame
->Height
);
310 // get the first frame
312 getFrameData(precFrame
, bmp0
);
318 getFrameData(nextFrame
, bmp1
);
321 uint coef
= (uint
)floor(256*interpValue
+0.5f
);
322 coef
= min(256U, coef
);
325 CRGBA
*dst
= (CRGBA
*)&bmp0
.getPixels()[0];
326 CRGBA
*src
= (CRGBA
*)&bmp1
.getPixels()[0];
327 for(uint nPix
= w
*h
;nPix
>0;nPix
--, src
++, dst
++)
329 dst
->blendFromuiRGBOnly(*dst
, *src
, coef
);
335 smprintf(fname
, 500, "%s%05d.tga", fileName
.c_str(), fileIndex
);
337 bmp0
.writeTGA(file
,24,false);
339 // Copy frame to buffer, and swap
340 drv
->fillBuffer(bmp0
);
341 textContext
->printfAt(0.05f
,0.80f
, "Movie Saving: %d%%", 100-n
*100/getNumFrames());
345 // next frame to write.
351 // ***************************************************************************
352 void CMovieShooter::replayMovie(UDriver
*drv
, UTextContext
*textContext
)
357 throw Exception("ReplayMovie: no Frames to save");
360 textContext
->setFontSize(10);
361 textContext
->setColor(CRGBA(255,255,255));
362 textContext
->setHotSpot(UTextContext::TopLeft
);
365 CFrameHeader
*frame
= _FirstFrame
;
366 uint n
= getNumFrames();
369 double tPrec
= ryzomGetLocalTime ()*0.001;
370 double lastFrameTime
= frame
->Time
;
374 drv
->EventServer
.pump(true);
376 if(drv
->AsyncListener
.isKeyDown(KeyESCAPE
))
383 getFrameData(frame
, bmp0
);
385 // Copy frame to buffer
386 drv
->fillBuffer(bmp0
);
387 textContext
->printfAt(0.05f
,0.80f
, "Movie Replay: %d%%", 100-n
*100/getNumFrames());
393 tNext
= ryzomGetLocalTime ()*0.001;
395 while( tNext
-tPrec
< frame
->Time
-lastFrameTime
);
397 lastFrameTime
= frame
->Time
;
410 // ***************************************************************************
411 void CMovieShooter::getFrameData(CFrameHeader
*frame
, NLMISC::CBitmap
&bmp
)
413 uint w
= frame
->Width
;
414 uint h
= frame
->Height
;
416 if(bmp
.getWidth()!=w
|| bmp
.getHeight()!=h
)
419 uint16
*src
= (uint16
*)frame
->Data
;
420 CRGBA
*dst
= (CRGBA
*)&bmp
.getPixels()[0];
421 for(uint npix
= w
*h
; npix
>0; npix
--, src
++, dst
++)
427 // ***************************************************************************
428 void CMovieShooter::resetMovie()
436 // ***************************************************************************
437 void CMovieShooter::setFrameSkip (uint32 nNbFrameToSkip
)
439 _FrameSkip
= nNbFrameToSkip
;
440 _CurrentFrameSkip
= 0;