BINKMovie.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "binkmovie.h"
  19. #include "dx8wrapper.h"
  20. #include "formconv.h"
  21. #include "render2d.h"
  22. #include "Bink.h"
  23. #include "rect.h"
  24. #include "subtitlemanager.h"
  25. #include "dx8caps.h"
  26. class BINKMovieClass
  27. {
  28. private:
  29. StringClass Filename;
  30. HBINK Bink;
  31. bool FrameChanged;
  32. unsigned TextureCount;
  33. unsigned long TicksPerFrame;
  34. struct TextureInfoStruct {
  35. TextureClass* Texture;
  36. int TextureWidth;
  37. int TextureHeight;
  38. int TextureLocX;
  39. int TextureLocY;
  40. RectClass UV;
  41. RectClass Rect;
  42. };
  43. TextureInfoStruct* TextureInfos;
  44. unsigned char* TempBuffer;
  45. Render2DClass Renderer;
  46. SubTitleManagerClass* SubTitleManager;
  47. public:
  48. BINKMovieClass(const char* filename,const char* subtitlename,FontCharsClass* font);
  49. ~BINKMovieClass();
  50. void Update();
  51. void Render();
  52. bool Is_Complete();
  53. };
  54. static BINKMovieClass* CurrentMovie;
  55. void BINKMovie::Play(const char* filename,const char* subtitlename, FontCharsClass* font)
  56. {
  57. if (CurrentMovie) {
  58. delete CurrentMovie;
  59. CurrentMovie = NULL;
  60. }
  61. CurrentMovie = new BINKMovieClass(filename,subtitlename,font);
  62. }
  63. void BINKMovie::Stop()
  64. {
  65. if (CurrentMovie) {
  66. delete CurrentMovie;
  67. CurrentMovie = NULL;
  68. }
  69. }
  70. void BINKMovie::Update()
  71. {
  72. if (CurrentMovie) {
  73. CurrentMovie->Update();
  74. }
  75. }
  76. void BINKMovie::Render()
  77. {
  78. if (CurrentMovie) {
  79. CurrentMovie->Render();
  80. }
  81. }
  82. void BINKMovie::Init()
  83. {
  84. BinkSoundUseDirectSound(0);
  85. }
  86. void BINKMovie::Shutdown()
  87. {
  88. Stop();
  89. }
  90. bool BINKMovie::Is_Complete()
  91. {
  92. if (CurrentMovie) {
  93. return CurrentMovie->Is_Complete();
  94. }
  95. return true;
  96. }
  97. // ----------------------------------------------------------------------------
  98. BINKMovieClass::BINKMovieClass(const char* filename, const char* subtitlename, FontCharsClass* font)
  99. :
  100. Filename(filename),
  101. Bink(0),
  102. FrameChanged(true),
  103. TicksPerFrame(0),
  104. SubTitleManager(NULL)
  105. {
  106. Bink = BinkOpen(Filename, 0);
  107. if (Bink == NULL) {
  108. return;
  109. }
  110. TempBuffer = new unsigned char[Bink->Width * Bink->Height*2];
  111. const D3DCAPS8& dx8caps = DX8Wrapper::Get_Current_Caps()->Get_DX8_Caps();
  112. unsigned poweroftwowidth = 1;
  113. while (poweroftwowidth < Bink->Width) {
  114. poweroftwowidth <<= 1;
  115. }
  116. unsigned poweroftwoheight = 1;
  117. while (poweroftwoheight < Bink->Height) {
  118. poweroftwoheight <<= 1;
  119. }
  120. if (poweroftwowidth > dx8caps.MaxTextureWidth) {
  121. poweroftwowidth = dx8caps.MaxTextureWidth;
  122. }
  123. if (poweroftwoheight > dx8caps.MaxTextureHeight) {
  124. poweroftwoheight = dx8caps.MaxTextureHeight;
  125. }
  126. TextureCount = 0;
  127. unsigned max_width = poweroftwowidth;
  128. unsigned max_height = poweroftwoheight;
  129. unsigned x, y;
  130. for (y = 0; y < Bink->Height; y += max_height-2) { // Two pixels are lost due to duplicated edges to prevent bilinear artifacts
  131. for (x = 0; x < Bink->Width; x += max_width-2) {
  132. ++TextureCount;
  133. }
  134. }
  135. TextureInfos = new TextureInfoStruct[TextureCount];
  136. unsigned cnt = 0;
  137. for (y = 0; y < Bink->Height; y += max_height-1) {
  138. for (x = 0; x < Bink->Width; x += max_width-1) {
  139. TextureInfos[cnt].Texture = new TextureClass(
  140. max_width, max_height, D3DFormat_To_WW3DFormat(D3DFMT_R5G6B5),
  141. TextureClass::MIP_LEVELS_1, TextureClass::POOL_MANAGED, false);
  142. TextureInfos[cnt].TextureLocX = x;
  143. TextureInfos[cnt].TextureLocY = y;
  144. TextureInfos[cnt].TextureWidth = max_width;
  145. TextureInfos[cnt].UV.Right = float(max_width) / float(max_width);
  146. if ((TextureInfos[cnt].TextureWidth + x) > Bink->Width) {
  147. TextureInfos[cnt].TextureWidth = Bink->Width - x;
  148. TextureInfos[cnt].UV.Right = float(TextureInfos[cnt].TextureWidth - 1) / float(max_width);
  149. }
  150. TextureInfos[cnt].TextureHeight = max_height;
  151. TextureInfos[cnt].UV.Bottom = float(max_height) / float(max_height);
  152. if ((TextureInfos[cnt].TextureHeight + y) > Bink->Height) {
  153. TextureInfos[cnt].TextureHeight = Bink->Height - y;
  154. TextureInfos[cnt].UV.Bottom = float(TextureInfos[cnt].TextureHeight + 1) / float(max_height);
  155. }
  156. TextureInfos[cnt].UV.Left = 1.0f / float(max_width);
  157. TextureInfos[cnt].UV.Top = 1.0f / float(max_height);
  158. TextureInfos[cnt].Rect.Left = float(TextureInfos[cnt].TextureLocX) / float(Bink->Width);
  159. TextureInfos[cnt].Rect.Top = float(TextureInfos[cnt].TextureLocY) / float(Bink->Height);
  160. TextureInfos[cnt].Rect.Right = float(TextureInfos[cnt].TextureLocX + TextureInfos[cnt].TextureWidth) / float(Bink->Width);
  161. TextureInfos[cnt].Rect.Bottom = float(TextureInfos[cnt].TextureLocY + TextureInfos[cnt].TextureHeight) / float(Bink->Height);
  162. ++cnt;
  163. }
  164. }
  165. Renderer.Reset();
  166. // Calculate the time per frame of video
  167. unsigned int rate = (Bink->FrameRate / Bink->FrameRateDiv);
  168. TicksPerFrame = (60 / rate);
  169. if (subtitlename && font) {
  170. SubTitleManager = SubTitleManagerClass::Create(filename, subtitlename, font);
  171. }
  172. }
  173. BINKMovieClass::~BINKMovieClass()
  174. {
  175. if (Bink == NULL) {
  176. return;
  177. }
  178. if (Bink) {
  179. BinkClose(Bink);
  180. }
  181. delete[] TempBuffer;
  182. if (TextureInfos) {
  183. for (unsigned t = 0; t < TextureCount; ++t) {
  184. REF_PTR_RELEASE(TextureInfos[t].Texture);
  185. }
  186. delete[] TextureInfos;
  187. }
  188. if (SubTitleManager) {
  189. delete SubTitleManager;
  190. }
  191. }
  192. void BINKMovieClass::Update()
  193. {
  194. if (!Bink) {
  195. return;
  196. }
  197. FrameChanged |= !BinkWait(Bink);
  198. }
  199. static unsigned char* Get_Tex_Address(unsigned char* buffer, int x, int y, int w, int h)
  200. {
  201. if (x < 0) {
  202. x = 0;
  203. } else if (x >= w) {
  204. x = w - 1;
  205. }
  206. if (y < 0) {
  207. y = 0;
  208. } else if (y >= h) {
  209. y = h - 1;
  210. }
  211. return buffer + x * 2 + y * 2 * w;
  212. }
  213. void BINKMovieClass::Render()
  214. {
  215. if (!Bink) {
  216. return;
  217. }
  218. // decompress a frame
  219. if (FrameChanged) {
  220. BinkDoFrame(Bink);
  221. FrameChanged = false;
  222. BinkCopyToBuffer(Bink, TempBuffer, Bink->Width * 2, Bink->Height, 0, 0, BINKSURFACE565|BINKCOPYNOSCALING);
  223. for (unsigned t = 0; t < TextureCount; ++t) {
  224. IDirect3DTexture8* d3d_texture = TextureInfos[t].Texture->Peek_DX8_Texture();
  225. if (d3d_texture) {
  226. unsigned char* cur_tex_ptr = Get_Tex_Address(TempBuffer, TextureInfos[t].TextureLocX,
  227. TextureInfos[t].TextureLocY, Bink->Width, Bink->Height);
  228. unsigned w = TextureInfos[t].TextureWidth;
  229. unsigned h = TextureInfos[t].TextureHeight;
  230. if (w > Bink->Width-TextureInfos[t].TextureLocX) {
  231. w = Bink->Width-TextureInfos[t].TextureLocX;
  232. }
  233. if (h > Bink->Height-TextureInfos[t].TextureLocY) {
  234. h = Bink->Height-TextureInfos[t].TextureLocY;
  235. }
  236. D3DSURFACE_DESC d3d_surf_desc;
  237. D3DLOCKED_RECT locked_rect;
  238. DX8_ErrorCode(d3d_texture->GetLevelDesc(0, &d3d_surf_desc));
  239. RECT rect;
  240. rect.left = 0;
  241. rect.top = 0;
  242. rect.right = w;
  243. rect.bottom = h;
  244. DX8_ErrorCode(d3d_texture->LockRect(0,&locked_rect,&rect,0));
  245. for (unsigned y = 0; y < h; ++y) {
  246. unsigned char* dest = (unsigned char*)locked_rect.pBits + y * locked_rect.Pitch;
  247. memcpy(dest, cur_tex_ptr, w * 2);
  248. cur_tex_ptr += Bink->Width * 2;
  249. }
  250. DX8_ErrorCode(d3d_texture->UnlockRect(0));
  251. }
  252. }
  253. if (Bink->FrameNum < Bink->Frames) // goto the next if not on the last
  254. BinkNextFrame(Bink);
  255. }
  256. for (unsigned t = 0; t < TextureCount; ++t) {
  257. Renderer.Reset();
  258. Renderer.Set_Texture(TextureInfos[t].Texture);
  259. Renderer.Set_Coordinate_Range(RectClass(0.0f, 0.0f, 1.0f, 1.0f));//Bink->Width,Bink->Height));
  260. RectClass rect(TextureInfos[t].TextureLocX, TextureInfos[t].TextureLocY, TextureInfos[t].TextureWidth, TextureInfos[t].TextureHeight);
  261. Renderer.Add_Quad(TextureInfos[t].Rect, TextureInfos[t].UV, 0xffffffff);
  262. Renderer.Render();
  263. }
  264. if (SubTitleManager) {
  265. unsigned long movieTime = (Bink->FrameNum * TicksPerFrame);
  266. SubTitleManager->Process(movieTime);
  267. SubTitleManager->Render();
  268. }
  269. }
  270. bool BINKMovieClass::Is_Complete()
  271. {
  272. if (!Bink) return true;
  273. return (Bink->FrameNum>=Bink->Frames);
  274. }