From ce24849adf2a0a94db1e073abb3e6bb67507158f Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Mon, 19 Aug 2024 15:00:27 +0600 Subject: [PATCH] Add support for fire textures in ANIMDEFS --- src/CMakeLists.txt | 1 + src/common/textures/firetexture.cpp | 139 +++++++++++++++++++++++++++ src/common/textures/firetexture.h | 17 ++++ src/gamedata/textures/animations.cpp | 110 +++++++++++++++++++++ src/gamedata/textures/animations.h | 10 ++ 5 files changed, 277 insertions(+) create mode 100644 src/common/textures/firetexture.cpp create mode 100644 src/common/textures/firetexture.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37d5d4839..789bea954 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1022,6 +1022,7 @@ set (PCH_SOURCES common/textures/multipatchtexturebuilder.cpp common/textures/skyboxtexture.cpp common/textures/animtexture.cpp + common/textures/firetexture.cpp common/textures/v_collection.cpp common/textures/formats/automaptexture.cpp common/textures/formats/brightmaptexture.cpp diff --git a/src/common/textures/firetexture.cpp b/src/common/textures/firetexture.cpp new file mode 100644 index 000000000..4a89622f7 --- /dev/null +++ b/src/common/textures/firetexture.cpp @@ -0,0 +1,139 @@ +/* +** firetexture.cpp +** PSX/N64-style fire texture implementation +** +**--------------------------------------------------------------------------- +** Copyright Cacodemon345 2024 +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +/* Algorithm based on https://github.com/fabiensanglard/DoomFirePSX */ + +#include "bitmap.h" +#include "firetexture.h" +#include "m_random.h" +#include "imagehelpers.h" + +#include "engineerrors.h" + +static constexpr int32_t FIRESKY_W = 64; +static constexpr int32_t FIRESKY_H = 128; + +FireTexture::FireTexture() +{ + SetSize(FIRESKY_W, FIRESKY_H); + Image.Clear(); + Image.AppendFill(0, Width * Height); +} + +void FireTexture::SetPalette(TArray& colors) +{ + Palette.Clear(); + Palette.Append(colors); + + /* This shouldn't happen in any circumstances. */ + if (Palette.Size() > 256) + { + I_FatalError("Fire palette too big!"); + } + + for (unsigned int i = 0; i < Width; i++) { + Image[(Height - 1) * Width + i] = (uint8_t)(Palette.Size() - 1); + } +} + +void FireTexture::Update() +{ + for (unsigned int y = 1; y < Height; y++) + { + for (unsigned int x = 0; x < Width; x++) + { + uint8_t srcPixel = Image[y * Width + x]; + + if (srcPixel == 0) + { + Image[(y - 1) * Width + x] = 0; + } + else + { + int XRand = M_Random() & 3; + int destRand = M_Random() & 1; + + Image[(y - 1) * Width + ((x + 1 - XRand) % Width)] = srcPixel - destRand; + } + } + } +} + +FBitmap FireTexture::GetBgraBitmap(const PalEntry* remap, int* trans) +{ + FBitmap bitmap; + bitmap.Create(Width, Height); + uint32_t* pixels = (uint32_t*)bitmap.GetPixels(); + + for (unsigned int y = 0; y < Height; y++) + { + for (unsigned int x = 0; x < Width; x++) + { + pixels[y * Width + x] = Palette[Image[y * Width + x]]; + } + } + + return bitmap; +} + +TArray FireTexture::Get8BitPixels(bool alphatex) +{ + FBitmap bitmap = GetBgraBitmap(nullptr, nullptr); + const uint8_t* data = bitmap.GetPixels(); + + uint8_t* dest_p; + int dest_adv = Height; + int dest_rew = Width * Height - 1; + + TArray Pixels(Width * Height); + dest_p = Pixels.Data(); + + bool doalpha = alphatex; + // Convert the source image from row-major to column-major format and remap it + for (int y = Height; y != 0; --y) + { + for (int x = Width; x != 0; --x) + { + int b = *data++; + int g = *data++; + int r = *data++; + int a = *data++; + if (a < 128) *dest_p = 0; + else *dest_p = ImageHelpers::RGBToPalette(doalpha, r, g, b); + dest_p += dest_adv; + } + dest_p -= dest_rew; + } + return Pixels; +} diff --git a/src/common/textures/firetexture.h b/src/common/textures/firetexture.h new file mode 100644 index 000000000..3ea6d967d --- /dev/null +++ b/src/common/textures/firetexture.h @@ -0,0 +1,17 @@ +#pragma once + +#include "textures.h" + + +class FireTexture : public FTexture +{ + TArray Image; + TArray Palette; + +public: + FireTexture(); + void SetPalette(TArray& colors); + void Update(); + virtual FBitmap GetBgraBitmap(const PalEntry* remap, int* trans) override; + virtual TArray Get8BitPixels(bool alphatex) override; +}; \ No newline at end of file diff --git a/src/gamedata/textures/animations.cpp b/src/gamedata/textures/animations.cpp index d40f2c7c5..6df649789 100644 --- a/src/gamedata/textures/animations.cpp +++ b/src/gamedata/textures/animations.cpp @@ -62,7 +62,12 @@ static FRandom pr_animatepictures ("AnimatePics"); void FTextureAnimator::DeleteAll() { + for (unsigned int i = 0; i < mFireTextures.Size(); i++) + { + mFireTextures[i].texture->CleanHardwareData(true); + } mAnimations.Clear(); + mFireTextures.Clear(); for (unsigned i = 0; i < mSwitchDefs.Size(); i++) { @@ -348,6 +353,10 @@ void FTextureAnimator::InitAnimDefs () TexMan.GameTexture(picnum)->SetSkyOffset(sc.Number); } } + else if (sc.Compare("firetexture")) + { + ParseFireTexture (sc); + } else { sc.ScriptError (NULL); @@ -782,6 +791,84 @@ void FTextureAnimator::ParseCameraTexture(FScanner &sc) viewer->SetDisplaySize((float)fitwidth, (float)fitheight); } +void FTextureAnimator::ParseFireTexture(FScanner& sc) +{ + FString picname; + FFireTexture fireTexture; + TArray palette; + uint32_t duration; + + sc.MustGetString(); + picname = sc.String; + sc.MustGetStringName("tics"); + sc.MustGetValue(true); + duration = uint32_t(sc.Float * 1000 / TICRATE); + + FGameTexture* gametex = MakeGameTexture(new FireTexture(), picname.GetChars(), ETextureType::Wall); + // No decals here. + gametex->SetNoDecals(true); + if (sc.GetString()) + { + if (sc.Compare("allowdecals")) + { + gametex->SetNoDecals(false); + } + else + { + sc.UnGet(); + } + } + while (sc.GetString()) + { + if (sc.Compare("color")) + { + uint8_t r, g, b, a; + sc.MustGetValue(false); + r = sc.Number; + sc.MustGetValue(false); + g = sc.Number; + sc.MustGetValue(false);; + b = sc.Number; + sc.MustGetValue(false); + a = sc.Number; + palette.Push(PalEntry(a, r, g, b)); + if (a != 255 && a != 0); + gametex->SetTranslucent(true); + + if (palette.Size() > 256) + { + sc.ScriptError("Too many colors specified!"); + } + } + else if (sc.Compare("palette")) + { + uint8_t index = 0; + sc.MustGetValue(false); + index = sc.Number; + PalEntry pal = GPalette.BaseColors[index]; + pal.a = pal.isBlack() ? 0 : 255; + palette.Push(pal); + + if (palette.Size() > 256) + { + sc.ScriptError("Too many colors specified!"); + } + } + else + { + sc.UnGet(); + break; + } + } + + fireTexture.texture = gametex; + fireTexture.Duration = duration; + fireTexture.SwitchTime = 0; + static_cast(gametex->GetTexture())->SetPalette(palette); + mFireTextures.Push(fireTexture); + TexMan.AddGameTexture(gametex); +} + //========================================================================== // // FTextureAnimator :: FixAnimations @@ -1040,6 +1127,29 @@ FTextureID FTextureAnimator::UpdateStandaloneAnimation(FStandaloneAnimation &ani void FTextureAnimator::UpdateAnimations (uint64_t mstime) { + for (unsigned int i = 0; i < mFireTextures.Size(); i++) + { + FFireTexture* fire = &mFireTextures[i]; + bool updated = false; + + if (fire->SwitchTime == 0) + { + fire->SwitchTime = mstime + fire->Duration; + } + else while (fire->SwitchTime <= mstime) + { + static_cast(fire->texture->GetTexture())->Update(); + fire->SwitchTime = mstime + fire->Duration; + updated = true; + } + + if (updated) + { + fire->texture->CleanHardwareData(); + delete fire->texture->GetSoftwareTexture(); + fire->texture->SetSoftwareTexture(nullptr); + } + } for (unsigned int j = 0; j < mAnimations.Size(); ++j) { FAnimDef *anim = &mAnimations[j]; diff --git a/src/gamedata/textures/animations.h b/src/gamedata/textures/animations.h index aa6b00e4e..1b3a11dbc 100644 --- a/src/gamedata/textures/animations.h +++ b/src/gamedata/textures/animations.h @@ -5,6 +5,7 @@ #include "textureid.h" #include "tarray.h" #include "s_soundinternal.h" +#include "firetexture.h" struct FStandaloneAnimation { @@ -17,6 +18,13 @@ struct FStandaloneAnimation static_assert(sizeof(FStandaloneAnimation) == sizeof(uint64_t)*2); +struct FFireTexture +{ + uint32_t Duration; // Duration before updates. + uint64_t SwitchTime; // Absolute time before update. + FGameTexture* texture; +}; + struct FAnimDef { struct FAnimFrame @@ -77,6 +85,7 @@ class FTextureAnimator TArray mAnimations; TArray mSwitchDefs; TArray mAnimatedDoors; + TArray mFireTextures; void ParseAnim(FScanner& sc, ETextureType usetype); FAnimDef* ParseRangeAnim(FScanner& sc, FTextureID picnum, ETextureType usetype, bool missing); @@ -84,6 +93,7 @@ class FTextureAnimator void ParseWarp(FScanner& sc); void ParseCanvasTexture(FScanner& sc); void ParseCameraTexture(FScanner& sc); + void ParseFireTexture(FScanner& sc); FTextureID ParseFramenum(FScanner& sc, FTextureID basepicnum, ETextureType usetype, bool allowMissing); void ParseTime(FScanner& sc, uint32_t& min, uint32_t& max);