This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/BinkMovie/BINKMovie.cpp

358 lines
8.2 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "binkmovie.h"
#include "dx8wrapper.h"
#include "formconv.h"
#include "render2d.h"
#include "Bink.h"
#include "rect.h"
#include "subtitlemanager.h"
#include "dx8caps.h"
class BINKMovieClass
{
private:
StringClass Filename;
HBINK Bink;
bool FrameChanged;
unsigned TextureCount;
unsigned long TicksPerFrame;
struct TextureInfoStruct {
TextureClass* Texture;
int TextureWidth;
int TextureHeight;
int TextureLocX;
int TextureLocY;
RectClass UV;
RectClass Rect;
};
TextureInfoStruct* TextureInfos;
unsigned char* TempBuffer;
Render2DClass Renderer;
SubTitleManagerClass* SubTitleManager;
public:
BINKMovieClass(const char* filename,const char* subtitlename,FontCharsClass* font);
~BINKMovieClass();
void Update();
void Render();
bool Is_Complete();
};
static BINKMovieClass* CurrentMovie;
void BINKMovie::Play(const char* filename,const char* subtitlename, FontCharsClass* font)
{
if (CurrentMovie) {
delete CurrentMovie;
CurrentMovie = NULL;
}
CurrentMovie = new BINKMovieClass(filename,subtitlename,font);
}
void BINKMovie::Stop()
{
if (CurrentMovie) {
delete CurrentMovie;
CurrentMovie = NULL;
}
}
void BINKMovie::Update()
{
if (CurrentMovie) {
CurrentMovie->Update();
}
}
void BINKMovie::Render()
{
if (CurrentMovie) {
CurrentMovie->Render();
}
}
void BINKMovie::Init()
{
BinkSoundUseDirectSound(0);
}
void BINKMovie::Shutdown()
{
Stop();
}
bool BINKMovie::Is_Complete()
{
if (CurrentMovie) {
return CurrentMovie->Is_Complete();
}
return true;
}
// ----------------------------------------------------------------------------
BINKMovieClass::BINKMovieClass(const char* filename, const char* subtitlename, FontCharsClass* font)
:
Filename(filename),
Bink(0),
FrameChanged(true),
TicksPerFrame(0),
SubTitleManager(NULL)
{
Bink = BinkOpen(Filename, 0);
if (Bink == NULL) {
return;
}
TempBuffer = new unsigned char[Bink->Width * Bink->Height*2];
const D3DCAPS8& dx8caps = DX8Wrapper::Get_Current_Caps()->Get_DX8_Caps();
unsigned poweroftwowidth = 1;
while (poweroftwowidth < Bink->Width) {
poweroftwowidth <<= 1;
}
unsigned poweroftwoheight = 1;
while (poweroftwoheight < Bink->Height) {
poweroftwoheight <<= 1;
}
if (poweroftwowidth > dx8caps.MaxTextureWidth) {
poweroftwowidth = dx8caps.MaxTextureWidth;
}
if (poweroftwoheight > dx8caps.MaxTextureHeight) {
poweroftwoheight = dx8caps.MaxTextureHeight;
}
TextureCount = 0;
unsigned max_width = poweroftwowidth;
unsigned max_height = poweroftwoheight;
unsigned x, y;
for (y = 0; y < Bink->Height; y += max_height-2) { // Two pixels are lost due to duplicated edges to prevent bilinear artifacts
for (x = 0; x < Bink->Width; x += max_width-2) {
++TextureCount;
}
}
TextureInfos = new TextureInfoStruct[TextureCount];
unsigned cnt = 0;
for (y = 0; y < Bink->Height; y += max_height-1) {
for (x = 0; x < Bink->Width; x += max_width-1) {
TextureInfos[cnt].Texture = new TextureClass(
max_width, max_height, D3DFormat_To_WW3DFormat(D3DFMT_R5G6B5),
TextureClass::MIP_LEVELS_1, TextureClass::POOL_MANAGED, false);
TextureInfos[cnt].TextureLocX = x;
TextureInfos[cnt].TextureLocY = y;
TextureInfos[cnt].TextureWidth = max_width;
TextureInfos[cnt].UV.Right = float(max_width) / float(max_width);
if ((TextureInfos[cnt].TextureWidth + x) > Bink->Width) {
TextureInfos[cnt].TextureWidth = Bink->Width - x;
TextureInfos[cnt].UV.Right = float(TextureInfos[cnt].TextureWidth - 1) / float(max_width);
}
TextureInfos[cnt].TextureHeight = max_height;
TextureInfos[cnt].UV.Bottom = float(max_height) / float(max_height);
if ((TextureInfos[cnt].TextureHeight + y) > Bink->Height) {
TextureInfos[cnt].TextureHeight = Bink->Height - y;
TextureInfos[cnt].UV.Bottom = float(TextureInfos[cnt].TextureHeight + 1) / float(max_height);
}
TextureInfos[cnt].UV.Left = 1.0f / float(max_width);
TextureInfos[cnt].UV.Top = 1.0f / float(max_height);
TextureInfos[cnt].Rect.Left = float(TextureInfos[cnt].TextureLocX) / float(Bink->Width);
TextureInfos[cnt].Rect.Top = float(TextureInfos[cnt].TextureLocY) / float(Bink->Height);
TextureInfos[cnt].Rect.Right = float(TextureInfos[cnt].TextureLocX + TextureInfos[cnt].TextureWidth) / float(Bink->Width);
TextureInfos[cnt].Rect.Bottom = float(TextureInfos[cnt].TextureLocY + TextureInfos[cnt].TextureHeight) / float(Bink->Height);
++cnt;
}
}
Renderer.Reset();
// Calculate the time per frame of video
unsigned int rate = (Bink->FrameRate / Bink->FrameRateDiv);
TicksPerFrame = (60 / rate);
if (subtitlename && font) {
SubTitleManager = SubTitleManagerClass::Create(filename, subtitlename, font);
}
}
BINKMovieClass::~BINKMovieClass()
{
if (Bink == NULL) {
return;
}
if (Bink) {
BinkClose(Bink);
}
delete[] TempBuffer;
if (TextureInfos) {
for (unsigned t = 0; t < TextureCount; ++t) {
REF_PTR_RELEASE(TextureInfos[t].Texture);
}
delete[] TextureInfos;
}
if (SubTitleManager) {
delete SubTitleManager;
}
}
void BINKMovieClass::Update()
{
if (!Bink) {
return;
}
FrameChanged |= !BinkWait(Bink);
}
static unsigned char* Get_Tex_Address(unsigned char* buffer, int x, int y, int w, int h)
{
if (x < 0) {
x = 0;
} else if (x >= w) {
x = w - 1;
}
if (y < 0) {
y = 0;
} else if (y >= h) {
y = h - 1;
}
return buffer + x * 2 + y * 2 * w;
}
void BINKMovieClass::Render()
{
if (!Bink) {
return;
}
// decompress a frame
if (FrameChanged) {
BinkDoFrame(Bink);
FrameChanged = false;
BinkCopyToBuffer(Bink, TempBuffer, Bink->Width * 2, Bink->Height, 0, 0, BINKSURFACE565|BINKCOPYNOSCALING);
for (unsigned t = 0; t < TextureCount; ++t) {
IDirect3DTexture8* d3d_texture = TextureInfos[t].Texture->Peek_DX8_Texture();
if (d3d_texture) {
unsigned char* cur_tex_ptr = Get_Tex_Address(TempBuffer, TextureInfos[t].TextureLocX,
TextureInfos[t].TextureLocY, Bink->Width, Bink->Height);
unsigned w = TextureInfos[t].TextureWidth;
unsigned h = TextureInfos[t].TextureHeight;
if (w > Bink->Width-TextureInfos[t].TextureLocX) {
w = Bink->Width-TextureInfos[t].TextureLocX;
}
if (h > Bink->Height-TextureInfos[t].TextureLocY) {
h = Bink->Height-TextureInfos[t].TextureLocY;
}
D3DSURFACE_DESC d3d_surf_desc;
D3DLOCKED_RECT locked_rect;
DX8_ErrorCode(d3d_texture->GetLevelDesc(0, &d3d_surf_desc));
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = w;
rect.bottom = h;
DX8_ErrorCode(d3d_texture->LockRect(0,&locked_rect,&rect,0));
for (unsigned y = 0; y < h; ++y) {
unsigned char* dest = (unsigned char*)locked_rect.pBits + y * locked_rect.Pitch;
memcpy(dest, cur_tex_ptr, w * 2);
cur_tex_ptr += Bink->Width * 2;
}
DX8_ErrorCode(d3d_texture->UnlockRect(0));
}
}
if (Bink->FrameNum < Bink->Frames) // goto the next if not on the last
BinkNextFrame(Bink);
}
for (unsigned t = 0; t < TextureCount; ++t) {
Renderer.Reset();
Renderer.Set_Texture(TextureInfos[t].Texture);
Renderer.Set_Coordinate_Range(RectClass(0.0f, 0.0f, 1.0f, 1.0f));//Bink->Width,Bink->Height));
RectClass rect(TextureInfos[t].TextureLocX, TextureInfos[t].TextureLocY, TextureInfos[t].TextureWidth, TextureInfos[t].TextureHeight);
Renderer.Add_Quad(TextureInfos[t].Rect, TextureInfos[t].UV, 0xffffffff);
Renderer.Render();
}
if (SubTitleManager) {
unsigned long movieTime = (Bink->FrameNum * TicksPerFrame);
SubTitleManager->Process(movieTime);
SubTitleManager->Render();
}
}
bool BINKMovieClass::Is_Complete()
{
if (!Bink) return true;
return (Bink->FrameNum>=Bink->Frames);
}