diff --git a/.gitignore b/.gitignore index cd2946a..6afae90 100644 --- a/.gitignore +++ b/.gitignore @@ -1,47 +1,2 @@ -# Windows image file caches -Thumbs.db -ehthumbs.db - -# Folder config file -Desktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# ========================= -# Operating System Files -# ========================= - -# OSX -# ========================= - -.DS_Store -.AppleDouble -.LSOverride - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk +release/* +libgui.cbp \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..535c311 --- /dev/null +++ b/Makefile @@ -0,0 +1,140 @@ +DO_LOGGING := 1 + +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC) +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +# DATA is a list of directories containing binary files +# LIBDIR is where the built library will be placed +# all directories are relative to this makefile +#--------------------------------------------------------------------------------- +BUILD ?= release +SOURCES := source \ + source/gui \ + source/resources \ + source/sounds \ + source/video \ + source/video/shaders +INCLUDES := source \ + include +DATA := +LIB := lib + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +CFLAGS = -g -Os -Wall -D__wiiu__ -D_GNU_SOURCE $(MACHDEP) $(INCLUDE) +CXXFLAGS = $(CFLAGS) + +ifeq ($(DO_LOGGING), 1) + CFLAGS += -D__LOGGING__ + CXXFLAGS += -D__LOGGING__ +endif + +ASFLAGS := -mregnames + +export WIIUBIN := $(LIB)/libgui.a + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lutils -ldynamiclibs -lfreetype -lgd -lpng -ljpeg -lz -lmad -lvorbisidec + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export TOPDIR ?= $(CURDIR)/.. +export DEPSDIR := $(CURDIR)/$(BUILD) + +export INCLUDEDIR := $(PORTLIBS)/include/libgui + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) $(sFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) -I$(LIBOGC_INC) \ + -I$(CURDIR)/$(BUILD) -I$(PORTLIBS)/include \ + -I$(PORTLIBS)/include/freetype2 -I$(PORTLIBS)/include/libutils + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(PORTLIBS)/lib + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr debug release $(LIB) include + +all: $(WIIUBIN) + +install: + @cp $(BUILD)/lib/libgui.a $(PORTLIBS)/lib + @mkdir -p $(INCLUDEDIR)/gui/ + @mkdir -p $(INCLUDEDIR)/resources/ + @mkdir -p $(INCLUDEDIR)/sounds/ + @mkdir -p $(INCLUDEDIR)/video/shaders + @cp source/gui/*.h $(INCLUDEDIR)/gui/ + @cp source/resources/*.h $(INCLUDEDIR)/resources/ + @cp source/sounds/*.h $(INCLUDEDIR)/sounds/ + @cp source/sounds/*.hpp $(INCLUDEDIR)/sounds/ + @cp source/video/*.h $(INCLUDEDIR)/video/ + @cp source/video/shaders/*.h $(INCLUDEDIR)/video/shaders/ + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(WIIUBIN) : $(OFILES) $(LIB) + @rm -f "$(WIIUBIN)" + @$(AR) rcs "$(WIIUBIN)" $(OFILES) + @echo built ... $(notdir $@) + + +$(LIB): + mkdir $(LIB) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- + diff --git a/README.md b/README.md new file mode 100644 index 0000000..fef847f --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# libgui + + +## Usage +Following steps are required for initialization: +``` +InitOSFunctionPointers(); // Load OS functions +InitPadScoreFunctionPointers(); +InitVPadFunctionPointers(); // Input functions for GUI control +InitFSFunctionPointers(); // To load file from the SD Card +InitGX2FunctionPointers(); // For rendering +InitAXFunctionPointers(); // For sound + +mount_sd_fat("sd"); // Mounting the SD Card +memoryInitialize(); // Initialize memory management + +//DO GUI STUFF HERE! + +memoryRelease(); +unmount_sd_fat("sd"); +``` + +Link the application with: +``` +-lgui -lutils -ldynamiclibs -lfreetype -lgd -lpng -ljpeg -lz -lmad -lvorbisidec +``` + +TODO: provide more information + +## Dependencies +To be able to use libgui, you need to install the following dependencies: + +- Application needs to be loaded from the [homebrew_launcher](https://github.com/dimok789/homebrew_launcher) +- [libutils](https://github.com/Maschell/libutils) for common functions. +- [dynamic_libs](https://github.com/Maschell/dynamic_libs/tree/lib) for access to the functions. + +And other portable libraries that can be found in the "libs" folder of this repository. Extract the "portlibs.zip" into your devkitPro directory. +This package includes: + +- freetype2 +- libgd +- libpng +- libjpeg +- libz +- libmad +- vorbisidec + +# Credits +- Orignally based on https://github.com/dborth/libwiigui +- Wii U port / modification / new functions / sound / much more by dimok. +- Minor changes by Maschell \ No newline at end of file diff --git a/libs/portlibs.zip b/libs/portlibs.zip new file mode 100644 index 0000000..baf263c Binary files /dev/null and b/libs/portlibs.zip differ diff --git a/source/gui/FreeTypeGX.cpp b/source/gui/FreeTypeGX.cpp new file mode 100644 index 0000000..c554a4e --- /dev/null +++ b/source/gui/FreeTypeGX.cpp @@ -0,0 +1,607 @@ +/* + * FreeTypeGX is a wrapper class for libFreeType which renders a compiled + * FreeType parsable font so a GX texture for Wii homebrew development. + * Copyright (C) 2008 Armin Tamzarian + * Modified by Dimok, 2015 for WiiU GX2 + * + * This file is part of FreeTypeGX. + * + * FreeTypeGX is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FreeTypeGX 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FreeTypeGX. If not, see . + */ + +#include "FreeTypeGX.h" +#include "video/CVideo.h" +#include "video/shaders/Texture2DShader.h" + +using namespace std; + +#define ALIGN4(x) (((x) + 3) & ~3) + +/** + * Default constructor for the FreeTypeGX class for WiiXplorer. + */ +FreeTypeGX::FreeTypeGX(const uint8_t* fontBuffer, FT_Long bufferSize, bool lastFace) +{ + int32_t faceIndex = 0; + ftPointSize = 0; + GX2InitSampler(&ftSampler, GX2_TEX_CLAMP_CLAMP_BORDER, GX2_TEX_XY_FILTER_BILINEAR); + + FT_Init_FreeType(&ftLibrary); + if(lastFace) + { + FT_New_Memory_Face(ftLibrary, (FT_Byte *)fontBuffer, bufferSize, -1, &ftFace); + faceIndex = ftFace->num_faces - 1; // Use the last face + FT_Done_Face(ftFace); + ftFace = NULL; + } + FT_New_Memory_Face(ftLibrary, (FT_Byte *) fontBuffer, bufferSize, faceIndex, &ftFace); + + ftKerningEnabled = FT_HAS_KERNING(ftFace); +} + +/** + * Default destructor for the FreeTypeGX class. + */ +FreeTypeGX::~FreeTypeGX() +{ + unloadFont(); + FT_Done_Face(ftFace); + FT_Done_FreeType(ftLibrary); +} + +/** + * Convert a short char string to a wide char string. + * + * This routine converts a supplied short character string into a wide character string. + * Note that it is the user's responsibility to clear the returned buffer once it is no longer needed. + * + * @param strChar Character string to be converted. + * @return Wide character representation of supplied character string. + */ + +wchar_t* FreeTypeGX::charToWideChar(const char* strChar) +{ + if (!strChar) return NULL; + + wchar_t *strWChar = new (std::nothrow) wchar_t[strlen(strChar) + 1]; + if (!strWChar) return NULL; + + int32_t bt = mbstowcs(strWChar, strChar, strlen(strChar)); + if (bt > 0) + { + strWChar[bt] = 0; + return strWChar; + } + + wchar_t *tempDest = strWChar; + while ((*tempDest++ = *strChar++)) + ; + + return strWChar; +} + +char *FreeTypeGX::wideCharToUTF8(const wchar_t* strChar) +{ + if(!strChar) { + return NULL; + } + + size_t len = 0; + wchar_t wc; + + for (size_t i = 0; strChar[i]; ++i) + { + wc = strChar[i]; + if (wc < 0x80) + ++len; + else if (wc < 0x800) + len += 2; + else if (wc < 0x10000) + len += 3; + else + len += 4; + } + + char *pOut = new (std::nothrow) char[len]; + if(!pOut) + return NULL; + + size_t n = 0; + + for (size_t i = 0; strChar[i]; ++i) + { + wc = strChar[i]; + if (wc < 0x80) + pOut[n++] = (char)wc; + else if (wc < 0x800) + { + pOut[n++] = (char)((wc >> 6) | 0xC0); + pOut[n++] = (char)((wc & 0x3F) | 0x80); + } + else if (wc < 0x10000) + { + pOut[n++] = (char)((wc >> 12) | 0xE0); + pOut[n++] = (char)(((wc >> 6) & 0x3F) | 0x80); + pOut[n++] = (char)((wc & 0x3F) | 0x80); + } + else + { + pOut[n++] = (char)(((wc >> 18) & 0x07) | 0xF0); + pOut[n++] = (char)(((wc >> 12) & 0x3F) | 0x80); + pOut[n++] = (char)(((wc >> 6) & 0x3F) | 0x80); + pOut[n++] = (char)((wc & 0x3F) | 0x80); + } + } + return pOut; +} + +/** + * Clears all loaded font glyph data. + * + * This routine clears all members of the font map structure and frees all allocated memory back to the system. + */ +void FreeTypeGX::unloadFont() +{ + map::iterator itr; + map::iterator itr2; + + for (itr = fontData.begin(); itr != fontData.end(); itr++) + { + for (itr2 = itr->second.ftgxCharMap.begin(); itr2 != itr->second.ftgxCharMap.end(); itr2++) + { + if(itr2->second.texture) + { + if(itr2->second.texture->surface.image_data) + free(itr2->second.texture->surface.image_data); + + delete itr2->second.texture; + itr2->second.texture = NULL; + } + } + } + + fontData.clear(); +} + +/** + * Caches the given font glyph in the instance font texture buffer. + * + * This routine renders and stores the requested glyph's bitmap and relevant information into its own quickly addressible + * structure within an instance-specific map. + * + * @param charCode The requested glyph's character code. + * @return A pointer to the allocated font structure. + */ +ftgxCharData * FreeTypeGX::cacheGlyphData(wchar_t charCode, int16_t pixelSize) +{ + map::iterator itr = fontData.find(pixelSize); + if (itr != fontData.end()) + { + map::iterator itr2 = itr->second.ftgxCharMap.find(charCode); + if (itr2 != itr->second.ftgxCharMap.end()) + { + return &itr2->second; + } + } + //!Cache ascender and decender as well + ftGX2Data *ftData = &fontData[pixelSize]; + + FT_UInt gIndex; + uint16_t textureWidth = 0, textureHeight = 0; + if (ftPointSize != pixelSize) + { + ftPointSize = pixelSize; + FT_Set_Pixel_Sizes(ftFace, 0, ftPointSize); + ftData->ftgxAlign.ascender = (int16_t) ftFace->size->metrics.ascender >> 6; + ftData->ftgxAlign.descender = (int16_t) ftFace->size->metrics.descender >> 6; + ftData->ftgxAlign.max = 0; + ftData->ftgxAlign.min = 0; + } + + gIndex = FT_Get_Char_Index(ftFace, (FT_ULong) charCode); + if (gIndex != 0 && FT_Load_Glyph(ftFace, gIndex, FT_LOAD_DEFAULT | FT_LOAD_RENDER) == 0) + { + if (ftFace->glyph->format == FT_GLYPH_FORMAT_BITMAP) + { + FT_Bitmap *glyphBitmap = &ftFace->glyph->bitmap; + + textureWidth = ALIGN4(glyphBitmap->width); + textureHeight = ALIGN4(glyphBitmap->rows); + if(textureWidth == 0) + textureWidth = 4; + if(textureHeight == 0) + textureHeight = 4; + + ftgxCharData *charData = &ftData->ftgxCharMap[charCode]; + charData->renderOffsetX = (int16_t) ftFace->glyph->bitmap_left; + charData->glyphAdvanceX = (uint16_t) (ftFace->glyph->advance.x >> 6); + charData->glyphAdvanceY = (uint16_t) (ftFace->glyph->advance.y >> 6); + charData->glyphIndex = (uint32_t) gIndex; + charData->renderOffsetY = (int16_t) ftFace->glyph->bitmap_top; + charData->renderOffsetMax = (int16_t) ftFace->glyph->bitmap_top; + charData->renderOffsetMin = (int16_t) glyphBitmap->rows - ftFace->glyph->bitmap_top; + + //! Initialize texture + charData->texture = new GX2Texture; + GX2InitTexture(charData->texture, textureWidth, textureHeight, 1, 0, GX2_SURFACE_FORMAT_TC_R5_G5_B5_A1_UNORM, GX2_SURFACE_DIM_2D, GX2_TILE_MODE_LINEAR_ALIGNED); + + loadGlyphData(glyphBitmap, charData); + + return charData; + } + } + return NULL; +} + +/** + * Locates each character in this wrapper's configured font face and proccess them. + * + * This routine locates each character in the configured font face and renders the glyph's bitmap. + * Each bitmap and relevant information is loaded into its own quickly addressible structure within an instance-specific map. + */ +uint16_t FreeTypeGX::cacheGlyphDataComplete(int16_t pixelSize) +{ + uint32_t i = 0; + FT_UInt gIndex; + + FT_ULong charCode = FT_Get_First_Char(ftFace, &gIndex); + while (gIndex != 0) + { + if (cacheGlyphData(charCode, pixelSize) != NULL) ++i; + charCode = FT_Get_Next_Char(ftFace, charCode, &gIndex); + } + return (uint16_t) (i); +} + +/** + * Loads the rendered bitmap into the relevant structure's data buffer. + * + * This routine does a simple byte-wise copy of the glyph's rendered 8-bit grayscale bitmap into the structure's buffer. + * Each byte is converted from the bitmap's intensity value into the a uint32_t RGBA value. + * + * @param bmp A pointer to the most recently rendered glyph's bitmap. + * @param charData A pointer to an allocated ftgxCharData structure whose data represent that of the last rendered glyph. + */ + +void FreeTypeGX::loadGlyphData(FT_Bitmap *bmp, ftgxCharData *charData) +{ + charData->texture->surface.image_data = (uint8_t *) memalign(charData->texture->surface.align, charData->texture->surface.image_size); + if(!charData->texture->surface.image_data) + return; + + memset(charData->texture->surface.image_data, 0x00, charData->texture->surface.image_size); + + uint8_t *src = (uint8_t *)bmp->buffer; + uint16_t *dst = (uint16_t *)charData->texture->surface.image_data; + int32_t x, y; + + for(y = 0; y < bmp->rows; y++) + { + for(x = 0; x < bmp->width; x++) + { + uint8_t intensity = src[y * bmp->width + x] >> 3; + dst[y * charData->texture->surface.pitch + x] = intensity ? ((intensity << 11) | (intensity << 6) | (intensity << 1) | 1) : 0; + } + } + GX2Invalidate(GX2_INVALIDATE_CPU_TEXTURE, charData->texture->surface.image_data, charData->texture->surface.image_size); +} + +/** + * Determines the x offset of the rendered string. + * + * This routine calculates the x offset of the rendered string based off of a supplied positional format parameter. + * + * @param width Current pixel width of the string. + * @param format Positional format of the string. + */ +int16_t FreeTypeGX::getStyleOffsetWidth(uint16_t width, uint16_t format) +{ + if (format & FTGX_JUSTIFY_LEFT) + return 0; + else if (format & FTGX_JUSTIFY_CENTER) + return -(width >> 1); + else if (format & FTGX_JUSTIFY_RIGHT) return -width; + return 0; +} + +/** + * Determines the y offset of the rendered string. + * + * This routine calculates the y offset of the rendered string based off of a supplied positional format parameter. + * + * @param offset Current pixel offset data of the string. + * @param format Positional format of the string. + */ +int16_t FreeTypeGX::getStyleOffsetHeight(int16_t format, uint16_t pixelSize) +{ + std::map::iterator itr = fontData.find(pixelSize); + if (itr == fontData.end()) return 0; + + switch (format & FTGX_ALIGN_MASK) + { + case FTGX_ALIGN_TOP: + return itr->second.ftgxAlign.descender; + + case FTGX_ALIGN_MIDDLE: + default: + return (itr->second.ftgxAlign.ascender + itr->second.ftgxAlign.descender + 1) >> 1; + + case FTGX_ALIGN_BOTTOM: + return itr->second.ftgxAlign.ascender; + + case FTGX_ALIGN_BASELINE: + return 0; + + case FTGX_ALIGN_GLYPH_TOP: + return itr->second.ftgxAlign.max; + + case FTGX_ALIGN_GLYPH_MIDDLE: + return (itr->second.ftgxAlign.max + itr->second.ftgxAlign.min + 1) >> 1; + + case FTGX_ALIGN_GLYPH_BOTTOM: + return itr->second.ftgxAlign.min; + } + return 0; +} + +/** + * Processes the supplied text string and prints the results at the specified coordinates. + * + * This routine processes each character of the supplied text string, loads the relevant preprocessed bitmap buffer, + * a texture from said buffer, and loads the resultant texture into the EFB. + * + * @param x Screen X coordinate at which to output the text. + * @param y Screen Y coordinate at which to output the text. Note that this value corresponds to the text string origin and not the top or bottom of the glyphs. + * @param text NULL terminated string to output. + * @param color Optional color to apply to the text characters. If not specified default value is ftgxWhite: (GXColor){0xff, 0xff, 0xff, 0xff} + * @param textStyle Flags which specify any styling which should be applied to the rendered string. + * @return The number of characters printed. + */ + +uint16_t FreeTypeGX::drawText(CVideo *video, int16_t x, int16_t y, int16_t z, const wchar_t *text, int16_t pixelSize, const glm::vec4 & color, uint16_t textStyle, uint16_t textWidth, const float &textBlur, const float & colorBlurIntensity, const glm::vec4 & blurColor, const float & internalRenderingScale) +{ + if (!text) + return 0; + + uint16_t fullTextWidth = (textWidth > 0) ? textWidth : getWidth(text, pixelSize); + uint16_t x_pos = x, printed = 0; + uint16_t x_offset = 0, y_offset = 0; + FT_Vector pairDelta; + + if (textStyle & FTGX_JUSTIFY_MASK) + { + x_offset = getStyleOffsetWidth(fullTextWidth, textStyle); + } + if (textStyle & FTGX_ALIGN_MASK) + { + y_offset = getStyleOffsetHeight(textStyle, pixelSize); + } + + int32_t i = 0; + while (text[i]) + { + ftgxCharData* glyphData = cacheGlyphData(text[i], pixelSize); + + if (glyphData != NULL) + { + if (ftKerningEnabled && i > 0) + { + FT_Get_Kerning(ftFace, fontData[pixelSize].ftgxCharMap[text[i - 1]].glyphIndex, glyphData->glyphIndex, FT_KERNING_DEFAULT, &pairDelta); + x_pos += (pairDelta.x >> 6); + + } + copyTextureToFramebuffer(video, glyphData->texture,x_pos + glyphData->renderOffsetX + x_offset, y + glyphData->renderOffsetY - y_offset, z, color, textBlur, colorBlurIntensity, blurColor,internalRenderingScale); + + x_pos += glyphData->glyphAdvanceX; + ++printed; + } + ++i; + } + + return printed; +} + + +/** + * Processes the supplied string and return the width of the string in pixels. + * + * This routine processes each character of the supplied text string and calculates the width of the entire string. + * Note that if precaching of the entire font set is not enabled any uncached glyph will be cached after the call to this function. + * + * @param text NULL terminated string to calculate. + * @return The width of the text string in pixels. + */ +uint16_t FreeTypeGX::getWidth(const wchar_t *text, int16_t pixelSize) +{ + if (!text) return 0; + + uint16_t strWidth = 0; + FT_Vector pairDelta; + int32_t i = 0; + + while (text[i]) + { + ftgxCharData* glyphData = cacheGlyphData(text[i], pixelSize); + + if (glyphData != NULL) + { + if (ftKerningEnabled && (i > 0)) + { + FT_Get_Kerning(ftFace, fontData[pixelSize].ftgxCharMap[text[i - 1]].glyphIndex, glyphData->glyphIndex, FT_KERNING_DEFAULT, &pairDelta); + strWidth += pairDelta.x >> 6; + } + + strWidth += glyphData->glyphAdvanceX; + } + ++i; + } + return strWidth; +} + +/** + * Single char width + */ +uint16_t FreeTypeGX::getCharWidth(const wchar_t wChar, int16_t pixelSize, const wchar_t prevChar) +{ + uint16_t strWidth = 0; + ftgxCharData * glyphData = cacheGlyphData(wChar, pixelSize); + + if (glyphData != NULL) + { + if (ftKerningEnabled && prevChar != 0x0000) + { + FT_Vector pairDelta; + FT_Get_Kerning(ftFace, fontData[pixelSize].ftgxCharMap[prevChar].glyphIndex, glyphData->glyphIndex, FT_KERNING_DEFAULT, &pairDelta); + strWidth += pairDelta.x >> 6; + } + strWidth += glyphData->glyphAdvanceX; + } + + return strWidth; +} + +/** + * Processes the supplied string and return the height of the string in pixels. + * + * This routine processes each character of the supplied text string and calculates the height of the entire string. + * Note that if precaching of the entire font set is not enabled any uncached glyph will be cached after the call to this function. + * + * @param text NULL terminated string to calculate. + * @return The height of the text string in pixels. + */ +uint16_t FreeTypeGX::getHeight(const wchar_t *text, int16_t pixelSize) +{ + getOffset(text, pixelSize); + return fontData[pixelSize].ftgxAlign.max - fontData[pixelSize].ftgxAlign.min; +} + +/** + * Get the maximum offset above and minimum offset below the font origin line. + * + * This function calculates the maximum pixel height above the font origin line and the minimum + * pixel height below the font origin line and returns the values in an addressible structure. + * + * @param text NULL terminated string to calculate. + * @param offset returns the max and min values above and below the font origin line + * + */ +void FreeTypeGX::getOffset(const wchar_t *text, int16_t pixelSize, uint16_t widthLimit) +{ + if (fontData.find(pixelSize) != fontData.end()) + return; + + int16_t strMax = 0, strMin = 9999; + uint16_t currWidth = 0; + + int32_t i = 0; + + while (text[i]) + { + if (widthLimit > 0 && currWidth >= widthLimit) break; + + ftgxCharData* glyphData = cacheGlyphData(text[i], pixelSize); + + if (glyphData != NULL) + { + strMax = glyphData->renderOffsetMax > strMax ? glyphData->renderOffsetMax : strMax; + strMin = glyphData->renderOffsetMin < strMin ? glyphData->renderOffsetMin : strMin; + currWidth += glyphData->glyphAdvanceX; + } + + ++i; + } + + if (ftPointSize != pixelSize) + { + ftPointSize = pixelSize; + FT_Set_Pixel_Sizes(ftFace, 0, ftPointSize); + } + + fontData[pixelSize].ftgxAlign.ascender = ftFace->size->metrics.ascender >> 6; + fontData[pixelSize].ftgxAlign.descender = ftFace->size->metrics.descender >> 6; + fontData[pixelSize].ftgxAlign.max = strMax; + fontData[pixelSize].ftgxAlign.min = strMin; +} + +/** + * Copies the supplied texture quad to the EFB. + * + * This routine uses the in-built GX quad builder functions to define the texture bounds and location on the EFB target. + * + * @param texObj A pointer to the glyph's initialized texture object. + * @param texWidth The pixel width of the texture object. + * @param texHeight The pixel height of the texture object. + * @param screenX The screen X coordinate at which to output the rendered texture. + * @param screenY The screen Y coordinate at which to output the rendered texture. + * @param color Color to apply to the texture. + */ +void FreeTypeGX::copyTextureToFramebuffer(CVideo *pVideo, GX2Texture *texture, int16_t x, int16_t y, int16_t z, const glm::vec4 & color, const float & defaultBlur, const float & blurIntensity, const glm::vec4 & blurColor, const float & internalRenderingScale) +{ + static const f32 imageAngle = 0.0f; + static const f32 blurScale = (2.0f/ (internalRenderingScale)); + + f32 offsetLeft = blurScale * ((f32)x + 0.5f * (f32)texture->surface.width) * (f32)pVideo->getWidthScaleFactor(); + f32 offsetTop = blurScale * ((f32)y - 0.5f * (f32)texture->surface.height) * (f32)pVideo->getHeightScaleFactor(); + + f32 widthScale = blurScale * (f32)texture->surface.width * pVideo->getWidthScaleFactor(); + f32 heightScale = blurScale * (f32)texture->surface.height * pVideo->getHeightScaleFactor(); + + glm::vec3 positionOffsets( offsetLeft, offsetTop, (f32)z ); + + //! blur doubles due to blur we have to scale the texture + glm::vec3 scaleFactor( widthScale, heightScale, 1.0f ); + + glm::vec3 blurDirection; + blurDirection[2] = 1.0f; + + Texture2DShader::instance()->setShaders(); + Texture2DShader::instance()->setAttributeBuffer(); + Texture2DShader::instance()->setAngle(imageAngle); + Texture2DShader::instance()->setOffset(positionOffsets); + Texture2DShader::instance()->setScale(scaleFactor); + Texture2DShader::instance()->setTextureAndSampler(texture, &ftSampler); + + if(blurIntensity > 0.0f) + { + //! glow blur color + Texture2DShader::instance()->setColorIntensity(blurColor); + + //! glow blur horizontal + blurDirection[0] = blurIntensity; + blurDirection[1] = 0.0f; + Texture2DShader::instance()->setBlurring(blurDirection); + Texture2DShader::instance()->draw(); + + //! glow blur vertical + blurDirection[0] = 0.0f; + blurDirection[1] = blurIntensity; + Texture2DShader::instance()->setBlurring(blurDirection); + Texture2DShader::instance()->draw(); + } + + //! set text color + Texture2DShader::instance()->setColorIntensity(color); + + //! blur horizontal + blurDirection[0] = defaultBlur; + blurDirection[1] = 0.0f; + Texture2DShader::instance()->setBlurring(blurDirection); + Texture2DShader::instance()->draw(); + + //! blur vertical + blurDirection[0] = 0.0f; + blurDirection[1] = defaultBlur; + Texture2DShader::instance()->setBlurring(blurDirection); + Texture2DShader::instance()->draw(); +} diff --git a/source/gui/FreeTypeGX.h b/source/gui/FreeTypeGX.h new file mode 100644 index 0000000..b93341d --- /dev/null +++ b/source/gui/FreeTypeGX.h @@ -0,0 +1,155 @@ +/* + * FreeTypeGX is a wrapper class for libFreeType which renders a compiled + * FreeType parsable font into a GX texture for Wii homebrew development. + * Copyright (C) 2008 Armin Tamzarian + * Modified by Dimok, 2015 for WiiU GX2 + * + * This file is part of FreeTypeGX. + * + * FreeTypeGX is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FreeTypeGX 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FreeTypeGX. If not, see . + */ + +#ifndef FREETYPEGX_H_ +#define FREETYPEGX_H_ + + +#include +#include +#include FT_FREETYPE_H +#include FT_BITMAP_H + +#include +#include +#include +#include + +#include +#include + +#include + +/*! \struct ftgxCharData_ + * + * Font face character glyph relevant data structure. + */ +typedef struct ftgxCharData_ +{ + int16_t renderOffsetX; /**< Texture X axis bearing offset. */ + uint16_t glyphAdvanceX; /**< Character glyph X coordinate advance in pixels. */ + uint16_t glyphAdvanceY; /**< Character glyph Y coordinate advance in pixels. */ + uint32_t glyphIndex; /**< Charachter glyph index in the font face. */ + + int16_t renderOffsetY; /**< Texture Y axis bearing offset. */ + int16_t renderOffsetMax; /**< Texture Y axis bearing maximum value. */ + int16_t renderOffsetMin; /**< Texture Y axis bearing minimum value. */ + + GX2Texture * texture; +} ftgxCharData; + +/*! \struct ftgxDataOffset_ + * + * Offset structure which hold both a maximum and minimum value. + */ +typedef struct ftgxDataOffset_ +{ + int16_t ascender; /**< Maximum data offset. */ + int16_t descender; /**< Minimum data offset. */ + int16_t max; /**< Maximum data offset. */ + int16_t min; /**< Minimum data offset. */ +} ftgxDataOffset; + +typedef struct ftgxCharData_ ftgxCharData; +typedef struct ftgxDataOffset_ ftgxDataOffset; +#define _TEXT(t) L ## t /**< Unicode helper macro. */ + +#define FTGX_NULL 0x0000 +#define FTGX_JUSTIFY_LEFT 0x0001 +#define FTGX_JUSTIFY_CENTER 0x0002 +#define FTGX_JUSTIFY_RIGHT 0x0004 +#define FTGX_JUSTIFY_MASK 0x000f + +#define FTGX_ALIGN_TOP 0x0010 +#define FTGX_ALIGN_MIDDLE 0x0020 +#define FTGX_ALIGN_BOTTOM 0x0040 +#define FTGX_ALIGN_BASELINE 0x0080 +#define FTGX_ALIGN_GLYPH_TOP 0x0100 +#define FTGX_ALIGN_GLYPH_MIDDLE 0x0200 +#define FTGX_ALIGN_GLYPH_BOTTOM 0x0400 +#define FTGX_ALIGN_MASK 0x0ff0 + +#define FTGX_STYLE_UNDERLINE 0x1000 +#define FTGX_STYLE_STRIKE 0x2000 +#define FTGX_STYLE_MASK 0xf000 + +/**< Constant color value used only to sanitize Doxygen documentation. */ +static const GX2ColorF32 ftgxWhite = (GX2ColorF32){ 1.0f, 1.0f, 1.0f, 1.0f }; + + +//! forward declaration +class CVideo; + +/*! \class FreeTypeGX + * \brief Wrapper class for the libFreeType library with GX rendering. + * \author Armin Tamzarian + * \version 0.2.4 + * + * FreeTypeGX acts as a wrapper class for the libFreeType library. It supports precaching of transformed glyph data into + * a specified texture format. Rendering of the data to the EFB is accomplished through the application of high performance + * GX texture functions resulting in high throughput of string rendering. + */ +class FreeTypeGX +{ + private: + FT_Library ftLibrary; /**< FreeType FT_Library instance. */ + FT_Face ftFace; /**< FreeType reusable FT_Face typographic object. */ + int16_t ftPointSize; /**< Current set size of the rendered font. */ + bool ftKerningEnabled; /**< Flag indicating the availability of font kerning data. */ + uint8_t vertexIndex; /**< Vertex format descriptor index. */ + GX2Sampler ftSampler; + + typedef struct _ftGX2Data + { + ftgxDataOffset ftgxAlign; + std::map ftgxCharMap; + } ftGX2Data; + + std::map fontData; /**< Map which holds the glyph data structures for the corresponding characters in one size. */ + + int16_t getStyleOffsetWidth(uint16_t width, uint16_t format); + int16_t getStyleOffsetHeight(int16_t format, uint16_t pixelSize); + + void unloadFont(); + ftgxCharData *cacheGlyphData(wchar_t charCode, int16_t pixelSize); + uint16_t cacheGlyphDataComplete(int16_t pixelSize); + void loadGlyphData(FT_Bitmap *bmp, ftgxCharData *charData); + + void copyTextureToFramebuffer(CVideo * pVideo, GX2Texture *tex, int16_t screenX, int16_t screenY, int16_t screenZ, const glm::vec4 & color, const float &textBlur, const float &colorBlurIntensity, const glm::vec4 & blurColor, const float & internalRenderingScale); + + public: + FreeTypeGX(const uint8_t* fontBuffer, FT_Long bufferSize, bool lastFace = false); + ~FreeTypeGX(); + + uint16_t drawText(CVideo * pVideo, int16_t x, int16_t y, int16_t z, const wchar_t *text, int16_t pixelSize, const glm::vec4 & color, + uint16_t textStyling, uint16_t textWidth, const float &textBlur, const float &colorBlurIntensity, const glm::vec4 & blurColor, const float & internalRenderingScale); + + uint16_t getWidth(const wchar_t *text, int16_t pixelSize); + uint16_t getCharWidth(const wchar_t wChar, int16_t pixelSize, const wchar_t prevChar = 0x0000); + uint16_t getHeight(const wchar_t *text, int16_t pixelSize); + void getOffset(const wchar_t *text, int16_t pixelSize, uint16_t widthLimit = 0); + + static wchar_t* charToWideChar(const char* p); + static char* wideCharToUTF8(const wchar_t* strChar); +}; + +#endif /* FREETYPEGX_H_ */ diff --git a/source/gui/GameBgImage.cpp b/source/gui/GameBgImage.cpp new file mode 100644 index 0000000..6497f4f --- /dev/null +++ b/source/gui/GameBgImage.cpp @@ -0,0 +1,42 @@ +#include "GameBgImage.h" +#include "video/CVideo.h" +#include "video/shaders/Shader3D.h" + +GameBgImage::GameBgImage(const std::string & filename, GuiImageData *preloadImage) + : GuiImageAsync(filename, preloadImage) +{ + identity = glm::mat4(1.0f); + alphaFadeOut = glm::vec4(1.0f, 0.075f, 5.305f, 2.0f); +} + +GameBgImage::~GameBgImage() +{ +} + +void GameBgImage::draw(CVideo *pVideo) +{ + if(!getImageData() || !getImageData()->getTexture()) + return; + + //! first setup 2D GUI positions + f32 currPosX = getCenterX(); + f32 currPosY = getCenterY(); + f32 currPosZ = getDepth(); + f32 currScaleX = getScaleX() * (f32)getWidth() * pVideo->getWidthScaleFactor(); + f32 currScaleY = getScaleY() * (f32)getHeight() * pVideo->getHeightScaleFactor(); + f32 currScaleZ = getScaleZ() * (f32)getWidth() * pVideo->getDepthScaleFactor(); + + glm::mat4 m_modelView = glm::translate(identity, glm::vec3(currPosX,currPosY, currPosZ)); + m_modelView = glm::scale(m_modelView, glm::vec3(currScaleX, currScaleY, currScaleZ)); + + Shader3D::instance()->setShaders(); + Shader3D::instance()->setProjectionMtx(identity); + Shader3D::instance()->setViewMtx(identity); + Shader3D::instance()->setModelViewMtx(m_modelView); + Shader3D::instance()->setTextureAndSampler(getImageData()->getTexture(), getImageData()->getSampler()); + Shader3D::instance()->setAlphaFadeOut(alphaFadeOut); + Shader3D::instance()->setDistanceFadeOut(0.0f); + Shader3D::instance()->setColorIntensity(glm::vec4(1.0f, 1.0f, 1.0f, getAlpha())); + Shader3D::instance()->setAttributeBuffer(); + Shader3D::instance()->draw(); +} diff --git a/source/gui/GameBgImage.h b/source/gui/GameBgImage.h new file mode 100644 index 0000000..a78b715 --- /dev/null +++ b/source/gui/GameBgImage.h @@ -0,0 +1,23 @@ +#ifndef _GAME_BG_IMAGE_H_ +#define _GAME_BG_IMAGE_H_ + +#include "GuiImageAsync.h" +#include "video/shaders/Shader3D.h" + +class GameBgImage : public GuiImageAsync +{ +public: + GameBgImage(const std::string & filename, GuiImageData *preloadImage); + virtual ~GameBgImage(); + + void setAlphaFadeOut(const glm::vec4 & a) { + alphaFadeOut = a; + } + + void draw(CVideo *pVideo); +private: + glm::mat4 identity; + glm::vec4 alphaFadeOut; +}; + +#endif // _GAME_BG_IMAGE_H_ diff --git a/source/gui/GridBackground.cpp b/source/gui/GridBackground.cpp new file mode 100644 index 0000000..026c7c9 --- /dev/null +++ b/source/gui/GridBackground.cpp @@ -0,0 +1,100 @@ +#include "GridBackground.h" +#include "video/CVideo.h" +#include "video/shaders/Shader3D.h" + +static const float bgRepeat = 1000.0f; +static const float bgTexRotate = 39.0f; + +GridBackground::GridBackground(GuiImageData *img) + : GuiImage(img) +{ + colorIntensity = glm::vec4(1.0f, 1.0f, 1.0f, 0.9f); + alphaFadeOut = glm::vec4(0.0f); + distanceFadeOut = 0.15f; + + vtxCount = 4; + + //! texture and vertex coordinates + f32 *m_posVtxs = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, vtxCount * Shader3D::cuVertexAttrSize); + f32 *m_texCoords = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, vtxCount * Shader3D::cuTexCoordAttrSize); + + if(m_posVtxs) + { + s32 i = 0; + m_posVtxs[i++] = -1.0f; m_posVtxs[i++] = 0.0f; m_posVtxs[i++] = 1.0f; + m_posVtxs[i++] = 1.0f; m_posVtxs[i++] = 0.0f; m_posVtxs[i++] = 1.0f; + m_posVtxs[i++] = 1.0f; m_posVtxs[i++] = 0.0f; m_posVtxs[i++] = -1.0f; + m_posVtxs[i++] = -1.0f; m_posVtxs[i++] = 0.0f; m_posVtxs[i++] = -1.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, m_posVtxs, vtxCount * Shader3D::cuVertexAttrSize); + } + + if(m_texCoords) + { + glm::vec2 texCoordVec[4]; + texCoordVec[0][0] = -0.5f * bgRepeat; texCoordVec[0][1] = 0.5f * bgRepeat; + texCoordVec[1][0] = 0.5f * bgRepeat; texCoordVec[1][1] = 0.5f * bgRepeat; + texCoordVec[2][0] = 0.5f * bgRepeat; texCoordVec[2][1] = -0.5f * bgRepeat; + texCoordVec[3][0] = -0.5f * bgRepeat; texCoordVec[3][1] = -0.5f * bgRepeat; + + const float cosRot = cosf(DegToRad(bgTexRotate)); + const float sinRot = sinf(DegToRad(bgTexRotate)); + + glm::mat2 texRotateMtx({ + cosRot, -sinRot, + sinRot, cosRot + }); + + for(s32 i = 0; i < 4; i++) { + texCoordVec[i] = texRotateMtx * texCoordVec[i]; + m_texCoords[i*2 + 0] = texCoordVec[i][0]; + m_texCoords[i*2 + 1] = texCoordVec[i][1]; + } + + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, m_texCoords, vtxCount * Shader3D::cuTexCoordAttrSize); + } + + //! assign to internal variables which are const but oh well + posVtxs = m_posVtxs; + texCoords = m_texCoords; +} + +GridBackground::~GridBackground() +{ + //! remove image so it can not be drawn anymore from this point on + imageData = NULL; + + //! main image vertexes + if(posVtxs) + { + free((void*)posVtxs); + posVtxs = NULL; + } + if(texCoords) + { + free((void*)texCoords); + texCoords = NULL; + } +} + +void GridBackground::draw(CVideo *pVideo, const glm::mat4 & modelView) +{ + //! first setup 2D GUI positions + f32 currScaleX = bgRepeat * scaleX * (f32)getWidth() * pVideo->getWidthScaleFactor(); + f32 currScaleY = 1.0f; + f32 currScaleZ = bgRepeat * scaleZ * (f32)getHeight() * pVideo->getDepthScaleFactor(); + + m_modelView = glm::scale(modelView, glm::vec3(currScaleX, currScaleY, currScaleZ)); + + colorIntensity[3] = getAlpha(); + + Shader3D::instance()->setShaders(); + Shader3D::instance()->setTextureAndSampler(imageData->getTexture(), imageData->getSampler()); + Shader3D::instance()->setProjectionMtx(pVideo->getProjectionMtx()); + Shader3D::instance()->setViewMtx(pVideo->getViewMtx()); + Shader3D::instance()->setModelViewMtx(m_modelView); + Shader3D::instance()->setDistanceFadeOut(distanceFadeOut); + Shader3D::instance()->setAlphaFadeOut(alphaFadeOut); + Shader3D::instance()->setColorIntensity(colorIntensity); + Shader3D::instance()->setAttributeBuffer(vtxCount, posVtxs, texCoords); + Shader3D::instance()->draw(GX2_PRIMITIVE_QUADS, vtxCount); +} diff --git a/source/gui/GridBackground.h b/source/gui/GridBackground.h new file mode 100644 index 0000000..198506a --- /dev/null +++ b/source/gui/GridBackground.h @@ -0,0 +1,30 @@ +#ifndef _GRID_BACKGROUND_H_ +#define _GRID_BACKGROUND_H_ + +#include "GuiImage.h" +#include "video/shaders/Shader.h" + +class GridBackground : public GuiImage +{ +public: + GridBackground(GuiImageData *imgData); + virtual ~GridBackground(); + + void setColorIntensity(const glm::vec4 & color) { + colorIntensity = color; + } + const glm::vec4 & getColorIntensity() const { + return colorIntensity; + } + void setDistanceFadeOut(const float & a) { + distanceFadeOut = a; + } + void draw(CVideo *pVideo, const glm::mat4 & modelView); +private: + glm::mat4 m_modelView; + glm::vec4 colorIntensity; + glm::vec4 alphaFadeOut; + float distanceFadeOut; +}; + +#endif // _GRID_BACKGROUND_H_ diff --git a/source/gui/Gui.h b/source/gui/Gui.h new file mode 100644 index 0000000..43087ef --- /dev/null +++ b/source/gui/Gui.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef __GUI_H +#define __GUI_H + +#include "FreeTypeGX.h" +#include "GameBgImage.h" +#include "GuiButton.h" +#include "GuiCheckBox.h" +#include "GuiController.h" +#include "GuiDragListener.h" +#include "GuiElement.h" +#include "GuiFrame.h" +#include "GuiImage.h" +#include "GuiImageAsync.h" +#include "GuiImageData.h" +#include "GuiParticleImage.h" +#include "GuiSelectBox.h" +#include "GuiSound.h" +#include "GuiSwitch.h" +#include "GuiText.h" +#include "GuiToggle.h" +#include "GuiTrigger.h" +#include "GuiScrollbar.h" +#include "VPadController.h" +#include "WPadController.h" + +#endif diff --git a/source/gui/GuiButton.cpp b/source/gui/GuiButton.cpp new file mode 100644 index 0000000..82820c9 --- /dev/null +++ b/source/gui/GuiButton.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiButton.h" +#include "GuiTrigger.h" +#include "GuiController.h" + +/** + * Constructor for the GuiButton class. + */ + +GuiButton::GuiButton(f32 w, f32 h) +{ + width = w; + height = h; + image = NULL; + imageOver = NULL; + imageHold = NULL; + imageClick = NULL; + icon = NULL; + iconOver = NULL; + + for(s32 i = 0; i < 4; i++) + { + label[i] = NULL; + labelOver[i] = NULL; + labelHold[i] = NULL; + labelClick[i] = NULL; + } + for(s32 i = 0; i < iMaxGuiTriggers; i++) + { + trigger[i] = NULL; + } + + soundOver = NULL; + soundHold = NULL; + soundClick = NULL; + clickedTrigger = NULL; + heldTrigger = NULL; + selectable = true; + holdable = false; + clickable = true; +} + +/** + * Destructor for the GuiButton class. + */ +GuiButton::~GuiButton() +{ +} + +void GuiButton::setImage(GuiImage* img) +{ + image = img; + if(img) img->setParent(this); +} +void GuiButton::setImageOver(GuiImage* img) +{ + imageOver = img; + if(img) img->setParent(this); +} +void GuiButton::setImageHold(GuiImage* img) +{ + imageHold = img; + if(img) img->setParent(this); +} +void GuiButton::setImageClick(GuiImage* img) +{ + imageClick = img; + if(img) img->setParent(this); +} +void GuiButton::setIcon(GuiImage* img) +{ + icon = img; + if(img) img->setParent(this); +} +void GuiButton::setIconOver(GuiImage* img) +{ + iconOver = img; + if(img) img->setParent(this); +} + +void GuiButton::setLabel(GuiText* txt, s32 n) +{ + label[n] = txt; + if(txt) txt->setParent(this); +} +void GuiButton::setLabelOver(GuiText* txt, s32 n) +{ + labelOver[n] = txt; + if(txt) txt->setParent(this); +} +void GuiButton::setLabelHold(GuiText* txt, s32 n) +{ + labelHold[n] = txt; + if(txt) txt->setParent(this); +} +void GuiButton::setLabelClick(GuiText* txt, s32 n) +{ + labelClick[n] = txt; + if(txt) txt->setParent(this); +} +void GuiButton::setSoundOver(GuiSound * snd) +{ + soundOver = snd; +} +void GuiButton::setSoundHold(GuiSound * snd) +{ + soundHold = snd; +} + +void GuiButton::setSoundClick(GuiSound * snd) +{ + soundClick = snd; +} + +void GuiButton::setTrigger(GuiTrigger * t, s32 idx) +{ + if(idx >= 0 && idx < iMaxGuiTriggers) + { + trigger[idx] = t; + } + else + { + for(s32 i = 0; i < iMaxGuiTriggers; i++) + { + if(!trigger[i]) + { + trigger[i] = t; + break; + } + } + } +} + +void GuiButton::resetState(void) +{ + clickedTrigger = NULL; + heldTrigger = NULL; + GuiElement::resetState(); +} + +/** + * Draw the button on screen + */ +void GuiButton::draw(CVideo *v) +{ + if(!this->isVisible()) + return; + + // draw image + if((isDrawOverOnlyWhenSelected() && (isStateSet(STATE_SELECTED) && imageOver)) || + (!isDrawOverOnlyWhenSelected() && (isStateSet(STATE_OVER | STATE_SELECTED | STATE_CLICKED | STATE_HELD) && imageOver))) + imageOver->draw(v); + else if(image) + image->draw(v); + + if((isDrawOverOnlyWhenSelected() && (isStateSet(STATE_SELECTED) && iconOver)) || + (!isDrawOverOnlyWhenSelected() && (isStateSet(STATE_OVER | STATE_SELECTED | STATE_CLICKED | STATE_HELD) && iconOver))) + iconOver->draw(v); + else if(icon) + icon->draw(v); + + // draw text + for(s32 i = 0; i < 4; i++) + { + if(isStateSet(STATE_OVER | STATE_SELECTED | STATE_CLICKED | STATE_HELD) && labelOver[i]) + labelOver[i]->draw(v); + else if(label[i]) + label[i]->draw(v); + } +} + +void GuiButton::update(GuiController * c) +{ + if(!c || isStateSet(STATE_DISABLED|STATE_HIDDEN|STATE_DISABLE_INPUT, c->chan)) + return; + else if(parentElement && (parentElement->isStateSet(STATE_DISABLED|STATE_HIDDEN|STATE_DISABLE_INPUT, c->chan))) + return; + + if(selectable) + { + if(c->data.validPointer && this->isInside(c->data.x, c->data.y)) + { + if(!isStateSet(STATE_OVER, c->chan)) + { + setState(STATE_OVER, c->chan); + + //if(this->isRumbleActive()) + // this->rumble(t->chan); + + if(soundOver) + soundOver->Play(); + + if(effectsOver && !effects) + { + // initiate effects + effects = effectsOver; + effectAmount = effectAmountOver; + effectTarget = effectTargetOver; + } + + pointedOn(this, c); + } + } + else if(isStateSet(STATE_OVER, c->chan)) + { + this->clearState(STATE_OVER, c->chan); + pointedOff(this, c); + + if(effectTarget == effectTargetOver && effectAmount == effectAmountOver) + { + // initiate effects (in reverse) + effects = effectsOver; + effectAmount = -effectAmountOver; + effectTarget = 100; + } + } + } + + for(s32 i = 0; i < iMaxGuiTriggers; i++) + { + if(!trigger[i]) + continue; + + // button triggers + if(clickable) + { + + s32 isClicked = trigger[i]->clicked(c); + + if( !clickedTrigger && (isClicked != GuiTrigger::CLICKED_NONE) + && (trigger[i]->isClickEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chan) && trigger[i]->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) + { + if(soundClick) + soundClick->Play(); + + clickedTrigger = trigger[i]; + + if(!isStateSet(STATE_CLICKED, c->chan)){ + if(isClicked == GuiTrigger::CLICKED_TOUCH){ + setState(STATE_CLICKED_TOUCH, c->chan); + }else{ + setState(STATE_CLICKED, c->chan); + } + } + + clicked(this, c, trigger[i]); + } + else if((isStateSet(STATE_CLICKED, c->chan) || isStateSet(STATE_CLICKED_TOUCH, c->chan)) && (clickedTrigger == trigger[i]) && !isStateSet(STATE_HELD, c->chan) && !trigger[i]->held(c) && ((isClicked == GuiTrigger::CLICKED_NONE) || trigger[i]->released(c))) + { + if((isStateSet(STATE_CLICKED_TOUCH, c->chan) && this->isInside(c->data.x, c->data.y)) || (isStateSet(STATE_CLICKED, c->chan))){ + clickedTrigger = NULL; + clearState(STATE_CLICKED, c->chan); + released(this, c, trigger[i]); + } + } + } + + if(holdable) + { + bool isHeld = trigger[i]->held(c); + + if( (!heldTrigger || heldTrigger == trigger[i]) && isHeld + && (trigger[i]->isHoldEverywhere() || (isStateSet(STATE_SELECTED | STATE_OVER, c->chan) && trigger[i]->isSelectionClickEverywhere()) || this->isInside(c->data.x, c->data.y))) + { + heldTrigger = trigger[i]; + + if(!isStateSet(STATE_HELD, c->chan)) + setState(STATE_HELD, c->chan); + + held(this, c, trigger[i]); + } + else if(isStateSet(STATE_HELD, c->chan) && (heldTrigger == trigger[i]) && (!isHeld || trigger[i]->released(c))) + { + //! click is removed at this point and converted to held + if(clickedTrigger == trigger[i]) + { + clickedTrigger = NULL; + clearState(STATE_CLICKED, c->chan); + } + heldTrigger = NULL; + clearState(STATE_HELD, c->chan); + released(this, c, trigger[i]); + } + } + } +} diff --git a/source/gui/GuiButton.h b/source/gui/GuiButton.h new file mode 100644 index 0000000..0336df1 --- /dev/null +++ b/source/gui/GuiButton.h @@ -0,0 +1,117 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_BUTTON_H_ +#define GUI_BUTTON_H_ + +#include "GuiElement.h" +#include "GuiText.h" +#include "GuiController.h" +#include "GuiImage.h" +#include "GuiSound.h" +#include "GuiTrigger.h" + +//!Display, manage, and manipulate buttons in the GUI. Buttons can have images, icons, text, and sound set (all of which are optional) +class GuiButton : public GuiElement +{ + public: + //!Constructor + //!\param w Width + //!\param h Height + GuiButton(f32 w, f32 h); + //!Destructor + virtual ~GuiButton(); + //!Sets the button's image + //!\param i Pointer to GuiImage object + void setImage(GuiImage* i); + //!Sets the button's image on over + //!\param i Pointer to GuiImage object + void setImageOver(GuiImage* i); + + void setIcon(GuiImage* i); + void setIconOver(GuiImage* i); + //!Sets the button's image on hold + //!\param i Pointer to GuiImage object + void setImageHold(GuiImage* i); + //!Sets the button's image on click + //!\param i Pointer to GuiImage object + void setImageClick(GuiImage* i); + //!Sets the button's label + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabel(GuiText* t, s32 n = 0); + //!Sets the button's label on over (eg: different colored text) + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabelOver(GuiText* t, s32 n = 0); + //!Sets the button's label on hold + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabelHold(GuiText* t, s32 n = 0); + //!Sets the button's label on click + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void setLabelClick(GuiText* t, s32 n = 0); + //!Sets the sound to play on over + //!\param s Pointer to GuiSound object + void setSoundOver(GuiSound * s); + //!Sets the sound to play on hold + //!\param s Pointer to GuiSound object + void setSoundHold(GuiSound * s); + //!Sets the sound to play on click + //!\param s Pointer to GuiSound object + void setSoundClick(GuiSound * s); + //!Set a new GuiTrigger for the element + //!\param i Index of trigger array to set + //!\param t Pointer to GuiTrigger + void setTrigger(GuiTrigger * t, s32 idx = -1); + //! + void resetState(void); + //!Constantly called to draw the GuiButton + void draw(CVideo *video); + //!Constantly called to allow the GuiButton to respond to updated input data + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void update(GuiController * c); + + sigslot::signal2 selected; + sigslot::signal2 deSelected; + sigslot::signal2 pointedOn; + sigslot::signal2 pointedOff; + sigslot::signal3 clicked; + sigslot::signal3 held; + sigslot::signal3 released; + protected: + static const s32 iMaxGuiTriggers = 10; + + GuiImage * image; //!< Button image (default) + GuiImage * imageOver; //!< Button image for STATE_SELECTED + GuiImage * imageHold; //!< Button image for STATE_HELD + GuiImage * imageClick; //!< Button image for STATE_CLICKED + GuiImage * icon; + GuiImage * iconOver; + GuiText * label[4]; //!< Label(s) to display (default) + GuiText * labelOver[4]; //!< Label(s) to display for STATE_SELECTED + GuiText * labelHold[4]; //!< Label(s) to display for STATE_HELD + GuiText * labelClick[4]; //!< Label(s) to display for STATE_CLICKED + GuiSound * soundOver; //!< Sound to play for STATE_SELECTED + GuiSound * soundHold; //!< Sound to play for STATE_HELD + GuiSound * soundClick; //!< Sound to play for STATE_CLICKED + GuiTrigger * trigger[iMaxGuiTriggers]; //!< GuiTriggers (input actions) that this element responds to + GuiTrigger * clickedTrigger; + GuiTrigger * heldTrigger; +}; + +#endif diff --git a/source/gui/GuiCheckBox.cpp b/source/gui/GuiCheckBox.cpp new file mode 100644 index 0000000..4131a09 --- /dev/null +++ b/source/gui/GuiCheckBox.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * 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 . + ****************************************************************************/ +#include "GuiCheckBox.h" +#include "GuiImage.h" +#include "GuiImageData.h" +/** + * Constructor for the GuiCheckBox class. + */ + +GuiCheckBox::GuiCheckBox(bool checked, f32 width,f32 height) + : GuiToggle(checked,width,height){ + +} + +/** + * Destructor for the GuiCheckBox class. + */ +GuiCheckBox::~GuiCheckBox(){ + +} + +void GuiCheckBox::setImageBackground(GuiImage* img){ + backgroundImg = img; + if(img){ img->setParent(this); } + setImage(img); +} + +void GuiCheckBox::setImageSelected(GuiImage* img){ + selectedImg = img; + if(img){ img->setParent(this); } +} + +void GuiCheckBox::setImageHighlighted(GuiImage* img){ + highlightedImg = img; + if(img){ img->setParent(this); } + setIconOver(img); +} + +void GuiCheckBox::update(GuiController * c){ + if(bChanged){ + if(selected){ + GuiButton::setImage(selectedImg); + }else{ + GuiButton::setImage(backgroundImg); + } + bChanged = false; + } + GuiToggle::update(c); +} diff --git a/source/gui/GuiCheckBox.h b/source/gui/GuiCheckBox.h new file mode 100644 index 0000000..005543c --- /dev/null +++ b/source/gui/GuiCheckBox.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * 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 . + ****************************************************************************/ +#ifndef GUI_CHECKBOX_H_ +#define GUI_CHECKBOX_H_ + +#include "GuiToggle.h" +#include "GuiImage.h" +#include "GuiImageData.h" + +//!A simple CheckBox +class GuiCheckBox : public GuiToggle{ + public: + //!Constructor + //!\param checked Checked + GuiCheckBox(bool checked, f32 width = 0.0f,f32 height= 0.0f); + + //!Destructor + virtual ~GuiCheckBox(); + + void setImageBackground(GuiImage* img); + + void setImageSelected(GuiImage* img); + + void setImageHighlighted(GuiImage* img); + + protected: + GuiImage * backgroundImg = NULL; + GuiImage * selectedImg = NULL; + GuiImage * highlightedImg = NULL; + + void update(GuiController * c); +}; + +#endif diff --git a/source/gui/GuiController.h b/source/gui/GuiController.h new file mode 100644 index 0000000..76aa13a --- /dev/null +++ b/source/gui/GuiController.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_CONTROLLER_H_ +#define GUI_CONTROLLER_H_ + +#include +#include "GuiTrigger.h" + +class GuiController +{ +public: + //!Constructor + GuiController(s32 channel) + : chan(channel) + { + memset(&lastData, 0, sizeof(lastData)); + memset(&data, 0, sizeof(data)); + + switch(chan) + { + default: + case GuiTrigger::CHANNEL_1: + chanIdx = 0; + break; + case GuiTrigger::CHANNEL_2: + chanIdx = 1; + break; + case GuiTrigger::CHANNEL_3: + chanIdx = 2; + break; + case GuiTrigger::CHANNEL_4: + chanIdx = 3; + break; + case GuiTrigger::CHANNEL_5: + chanIdx = 4; + break; + } + } + + //!Destructor + virtual ~GuiController() {} + + virtual bool update(s32 width, s32 height) = 0; + + typedef struct + { + u32 buttons_h; + u32 buttons_d; + u32 buttons_r; + bool validPointer; + bool touched; + float pointerAngle; + s32 x; + s32 y; + } PadData; + + s32 chan; + s32 chanIdx; + PadData data; + PadData lastData; + +}; + +#endif diff --git a/source/gui/GuiDragListener.cpp b/source/gui/GuiDragListener.cpp new file mode 100644 index 0000000..8ea1190 --- /dev/null +++ b/source/gui/GuiDragListener.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * based on GuiButton by dimok + * + * 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 . + ****************************************************************************/ +#include "GuiDragListener.h" +#include "GuiController.h" +#include + +/** + * Constructor for the GuiDragListener class. + */ + +GuiDragListener::GuiDragListener(f32 w,f32 h){ + width = w; + height = h; + for(s32 i = 0; i < iMaxGuiTriggers; i++) + { + trigger[i] = NULL; + } +} + +/** + * Destructor for the GuiDragListener class. + */ +GuiDragListener::~GuiDragListener(){ +} + +void GuiDragListener::setState(s32 i, s32 c){ + GuiElement::setState(i,c); +} + +void GuiDragListener::setTrigger(GuiTrigger * t, s32 idx){ + if(idx >= 0 && idx < iMaxGuiTriggers) + { + trigger[idx] = t; + } + else + { + for(s32 i = 0; i < iMaxGuiTriggers; i++) + { + if(!trigger[i]) + { + trigger[i] = t; + break; + } + } + } +} + +void GuiDragListener::update(GuiController * c){ + if(!c || isStateSet(STATE_DISABLED|STATE_HIDDEN|STATE_DISABLE_INPUT, c->chan)) + return; + else if(parentElement && (parentElement->isStateSet(STATE_DISABLED|STATE_HIDDEN|STATE_DISABLE_INPUT, c->chan))) + return; + + for(s32 i = 0; i < iMaxGuiTriggers; i++){ + if(!trigger[i]){ + continue; + } + + bool isHeld = trigger[i]->held(c); + + + if(isHeld && this->isInside(c->data.x, c->data.y)){ + s32 dx = c->data.x - c->lastData.x; + s32 dy = c->data.y - c->lastData.y; + + if(dx == 0 && dy == 0) continue; + + dragged(this, c, trigger[i],dx,dy); + } + } +} diff --git a/source/gui/GuiDragListener.h b/source/gui/GuiDragListener.h new file mode 100644 index 0000000..fe40eed --- /dev/null +++ b/source/gui/GuiDragListener.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * + * 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 . + ****************************************************************************/ +#ifndef GUI_DRAG_LISTENER_H_ +#define GUI_DRAG_LISTENER_H_ + +#include "GuiElement.h" +#include "GuiController.h" +#include "GuiTrigger.h" +#include "GuiButton.h" + +class GuiDragListener : public GuiElement +{ + public: + //!Constructor + //!\param w Width + //!\param h Height + GuiDragListener(f32 w,f32 h); + //!Destructor + virtual ~GuiDragListener(); + + void setState(s32 i, s32 c); + + //!Set a new GuiTrigger for the element + //!\param i Index of trigger array to set + //!\param t Pointer to GuiTrigger + void setTrigger(GuiTrigger * t, s32 idx = -1); + + //!Constantly called to allow the GuiDragListener to respond to updated input data + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void update(GuiController * c); + + sigslot::signal5 dragged; + protected: + static const s32 iMaxGuiTriggers = 10; + + GuiTrigger * trigger[iMaxGuiTriggers]; //!< GuiTriggers (input actions) that this element responds to +}; + +#endif diff --git a/source/gui/GuiElement.cpp b/source/gui/GuiElement.cpp new file mode 100644 index 0000000..ace8f0d --- /dev/null +++ b/source/gui/GuiElement.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiElement.h" + +//! TODO remove this! +static s32 screenwidth = 1280; +static s32 screenheight = 720; + +/** + * Constructor for the Object class. + */ +GuiElement::GuiElement() +{ + xoffset = 0.0f; + yoffset = 0.0f; + zoffset = 0.0f; + width = 0.0f; + height = 0.0f; + alpha = 1.0f; + scaleX = 1.0f; + scaleY = 1.0f; + scaleZ = 1.0f; + for(s32 i = 0; i < 4; i++) + state[i] = STATE_DEFAULT; + stateChan = -1; + parentElement = NULL; + rumble = true; + selectable = false; + clickable = false; + holdable = false; + drawOverOnlyWhenSelected = false; + visible = true; + yoffsetDyn = 0; + xoffsetDyn = 0; + alphaDyn = -1; + scaleDyn = 1; + effects = EFFECT_NONE; + effectAmount = 0; + effectTarget = 0; + effectsOver = EFFECT_NONE; + effectAmountOver = 0; + effectTargetOver = 0; + angle = 0.0f; + + // default alignment - align to top left + alignment = (ALIGN_CENTER | ALIGN_MIDDLE); +} + +/** + * Get the left position of the GuiElement. + * @see SetLeft() + * @return Left position in pixel. + */ +f32 GuiElement::getLeft() +{ + f32 pWidth = 0; + f32 pLeft = 0; + f32 pScaleX = 1.0f; + + if(parentElement) + { + pWidth = parentElement->getWidth(); + pLeft = parentElement->getLeft(); + pScaleX = parentElement->getScaleX(); + } + + pLeft += xoffsetDyn; + + f32 x = pLeft; + + //! TODO: the conversion from int to float and back to int is bad for performance, change that + if(alignment & ALIGN_CENTER) + { + x = pLeft + pWidth * 0.5f * pScaleX - width * 0.5f * getScaleX(); + } + else if(alignment & ALIGN_RIGHT) + { + x = pLeft + pWidth * pScaleX - width * getScaleX(); + } + + return x + xoffset; +} + +/** + * Get the top position of the GuiElement. + * @see SetTop() + * @return Top position in pixel. + */ +f32 GuiElement::getTop() +{ + f32 pHeight = 0; + f32 pTop = 0; + f32 pScaleY = 1.0f; + + if(parentElement) + { + pHeight = parentElement->getHeight(); + pTop = parentElement->getTop(); + pScaleY = parentElement->getScaleY(); + } + + pTop += yoffsetDyn; + + f32 y = pTop; + + //! TODO: the conversion from int to float and back to int is bad for performance, change that + if(alignment & ALIGN_MIDDLE) + { + y = pTop + pHeight * 0.5f * pScaleY - getHeight() * 0.5f * getScaleY(); + } + else if(alignment & ALIGN_BOTTOM) + { + y = pTop + pHeight * pScaleY - getHeight() * getScaleY(); + } + + return y + yoffset; +} + +void GuiElement::setEffect(s32 eff, s32 amount, s32 target) +{ + if(eff & EFFECT_SLIDE_IN) + { + // these calculations overcompensate a little + if(eff & EFFECT_SLIDE_TOP) + { + if(eff & EFFECT_SLIDE_FROM) + yoffsetDyn = (s32) -getHeight()*scaleY; + else + yoffsetDyn = -screenheight; + } + else if(eff & EFFECT_SLIDE_LEFT) + { + if(eff & EFFECT_SLIDE_FROM) + xoffsetDyn = (s32) -getWidth()*scaleX; + else + xoffsetDyn = -screenwidth; + } + else if(eff & EFFECT_SLIDE_BOTTOM) + { + if(eff & EFFECT_SLIDE_FROM) + yoffsetDyn = (s32) getHeight()*scaleY; + else + yoffsetDyn = screenheight; + } + else if(eff & EFFECT_SLIDE_RIGHT) + { + if(eff & EFFECT_SLIDE_FROM) + xoffsetDyn = (s32) getWidth()*scaleX; + else + xoffsetDyn = screenwidth; + } + } + if((eff & EFFECT_FADE) && amount > 0) + { + alphaDyn = 0; + } + else if((eff & EFFECT_FADE) && amount < 0) + { + alphaDyn = alpha; + } + effects |= eff; + effectAmount = amount; + effectTarget = target; +} + +//!Sets an effect to be enabled on wiimote cursor over +//!\param e Effect to enable +//!\param a Amount of the effect (usage varies on effect) +//!\param t Target amount of the effect (usage varies on effect) +void GuiElement::setEffectOnOver(s32 e, s32 a, s32 t) +{ + effectsOver |= e; + effectAmountOver = a; + effectTargetOver = t; +} + +void GuiElement::resetEffects() +{ + yoffsetDyn = 0; + xoffsetDyn = 0; + alphaDyn = -1; + scaleDyn = 1; + effects = EFFECT_NONE; + effectAmount = 0; + effectTarget = 0; + effectsOver = EFFECT_NONE; + effectAmountOver = 0; + effectTargetOver = 0; +} +void GuiElement::updateEffects() +{ + if(!this->isVisible() && parentElement) + return; + + if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT | EFFECT_SLIDE_FROM)) + { + if(effects & EFFECT_SLIDE_IN) + { + if(effects & EFFECT_SLIDE_LEFT) + { + xoffsetDyn += effectAmount; + + if(xoffsetDyn >= 0) + { + xoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } + else if(effects & EFFECT_SLIDE_RIGHT) + { + xoffsetDyn -= effectAmount; + + if(xoffsetDyn <= 0) + { + xoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } + else if(effects & EFFECT_SLIDE_TOP) + { + yoffsetDyn += effectAmount; + + if(yoffsetDyn >= 0) + { + yoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } + else if(effects & EFFECT_SLIDE_BOTTOM) + { + yoffsetDyn -= effectAmount; + + if(yoffsetDyn <= 0) + { + yoffsetDyn = 0; + effects = 0; + effectFinished(this); + } + } + } + else + { + if(effects & EFFECT_SLIDE_LEFT) + { + xoffsetDyn -= effectAmount; + + if(xoffsetDyn <= -screenwidth) { + effects = 0; // shut off effect + effectFinished(this); + } + else if((effects & EFFECT_SLIDE_FROM) && xoffsetDyn <= -getWidth()) { + effects = 0; // shut off effect + effectFinished(this); + } + } + else if(effects & EFFECT_SLIDE_RIGHT) + { + xoffsetDyn += effectAmount; + + if(xoffsetDyn >= screenwidth) { + effects = 0; // shut off effect + effectFinished(this); + } + else if((effects & EFFECT_SLIDE_FROM) && xoffsetDyn >= getWidth()*scaleX) { + effects = 0; // shut off effect + effectFinished(this); + } + } + else if(effects & EFFECT_SLIDE_TOP) + { + yoffsetDyn -= effectAmount; + + if(yoffsetDyn <= -screenheight) { + effects = 0; // shut off effect + effectFinished(this); + } + else if((effects & EFFECT_SLIDE_FROM) && yoffsetDyn <= -getHeight()) { + effects = 0; // shut off effect + effectFinished(this); + } + } + else if(effects & EFFECT_SLIDE_BOTTOM) + { + yoffsetDyn += effectAmount; + + if(yoffsetDyn >= screenheight) { + effects = 0; // shut off effect + effectFinished(this); + } + else if((effects & EFFECT_SLIDE_FROM) && yoffsetDyn >= getHeight()) { + effects = 0; // shut off effect + effectFinished(this); + } + } + } + } + else if(effects & EFFECT_FADE) + { + alphaDyn += effectAmount * (1.0f / 255.0f); + + if(effectAmount < 0 && alphaDyn <= 0) + { + alphaDyn = 0; + effects = 0; // shut off effect + effectFinished(this); + } + else if(effectAmount > 0 && alphaDyn >= alpha) + { + alphaDyn = alpha; + effects = 0; // shut off effect + effectFinished(this); + } + } + else if(effects & EFFECT_SCALE) + { + scaleDyn += effectAmount * 0.01f; + + if((effectAmount < 0 && scaleDyn <= (effectTarget * 0.01f)) + || (effectAmount > 0 && scaleDyn >= (effectTarget * 0.01f))) + { + scaleDyn = effectTarget * 0.01f; + effects = 0; // shut off effect + effectFinished(this); + } + } +} diff --git a/source/gui/GuiElement.h b/source/gui/GuiElement.h new file mode 100644 index 0000000..030d154 --- /dev/null +++ b/source/gui/GuiElement.h @@ -0,0 +1,529 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_ELEMENT_H_ +#define GUI_ELEMENT_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sigslot.h" +#include +#include +#include +#include +#include + +#include "resources/Resources.h" + +enum +{ + EFFECT_NONE = 0x00, + EFFECT_SLIDE_TOP = 0x01, + EFFECT_SLIDE_BOTTOM = 0x02, + EFFECT_SLIDE_RIGHT = 0x04, + EFFECT_SLIDE_LEFT = 0x08, + EFFECT_SLIDE_IN = 0x10, + EFFECT_SLIDE_OUT = 0x20, + EFFECT_SLIDE_FROM = 0x40, + EFFECT_FADE = 0x80, + EFFECT_SCALE = 0x100, + EFFECT_COLOR_TRANSITION = 0x200 +}; + +enum +{ + ALIGN_LEFT = 0x01, + ALIGN_CENTER = 0x02, + ALIGN_RIGHT = 0x04, + ALIGN_TOP = 0x10, + ALIGN_MIDDLE = 0x20, + ALIGN_BOTTOM = 0x40, + ALIGN_TOP_LEFT = ALIGN_LEFT | ALIGN_TOP, + ALIGN_TOP_CENTER = ALIGN_CENTER | ALIGN_TOP, + ALIGN_TOP_RIGHT = ALIGN_RIGHT | ALIGN_TOP, + ALIGN_CENTERED = ALIGN_CENTER | ALIGN_MIDDLE, +}; + +//!Forward declaration +class GuiController; +class CVideo; + +//!Primary GUI class. Most other classes inherit from this class. +class GuiElement : public AsyncDeleter::Element +{ + public: + //!Constructor + GuiElement(); + //!Destructor + virtual ~GuiElement() {} + //!Set the element's parent + //!\param e Pointer to parent element + virtual void setParent(GuiElement * e) { parentElement = e; } + //!Gets the element's parent + //!\return Pointer to parent element + virtual GuiElement * getParent() { return parentElement; } + //!Gets the current leftmost coordinate of the element + //!Considers horizontal alignment, x offset, width, and parent element's GetLeft() / GetWidth() values + //!\return left coordinate + virtual f32 getLeft(); + //!Gets the current topmost coordinate of the element + //!Considers vertical alignment, y offset, height, and parent element's GetTop() / GetHeight() values + //!\return top coordinate + virtual f32 getTop(); + //!Gets the current Z coordinate of the element + //!\return Z coordinate + virtual f32 getDepth() + { + f32 zParent = 0.0f; + + if(parentElement) + zParent = parentElement->getDepth(); + + return zParent+zoffset; + } + + virtual f32 getCenterX(void) + { + f32 pCenterX = 0.0f; + + if(parentElement) + pCenterX = parentElement->getCenterX(); + + pCenterX += xoffset + xoffsetDyn; + + if(alignment & ALIGN_LEFT) + { + f32 pWidth = 0.0f; + f32 pScale = 0.0f; + + if(parentElement) + { + pWidth = parentElement->getWidth(); + pScale = parentElement->getScaleX(); + } + + pCenterX -= pWidth * 0.5f * pScale - width * 0.5f * getScaleX(); + } + else if(alignment & ALIGN_RIGHT) + { + f32 pWidth = 0.0f; + f32 pScale = 0.0f; + + if(parentElement) + { + pWidth = parentElement->getWidth(); + pScale = parentElement->getScaleX(); + } + + pCenterX += pWidth * 0.5f * pScale - width * 0.5f * getScaleX(); + } + return pCenterX; + } + + virtual f32 getCenterY(void) + { + f32 pCenterY = 0.0f; + + if(parentElement) + pCenterY = parentElement->getCenterY(); + + pCenterY += yoffset + yoffsetDyn; + + if(alignment & ALIGN_TOP) + { + f32 pHeight = 0.0f; + f32 pScale = 0.0f; + + if(parentElement) + { + pHeight = parentElement->getHeight(); + pScale = parentElement->getScaleY(); + } + + pCenterY += pHeight * 0.5f * pScale - getHeight() * 0.5f * getScaleY(); + } + else if(alignment & ALIGN_BOTTOM) + { + f32 pHeight = 0.0f; + f32 pScale = 0.0f; + + if(parentElement) + { + pHeight = parentElement->getHeight(); + pScale = parentElement->getScaleY(); + } + + pCenterY -= pHeight * 0.5f * pScale - getHeight() * 0.5f * getScaleY(); + } + return pCenterY; + } + //!Gets elements xoffset + virtual f32 getOffsetX() { return xoffset; } + //!Gets elements yoffset + virtual f32 getOffsetY() { return yoffset; } + //!Gets the current width of the element. Does not currently consider the scale + //!\return width + virtual f32 getWidth() { return width; }; + //!Gets the height of the element. Does not currently consider the scale + //!\return height + virtual f32 getHeight() { return height; } + //!Sets the size (width/height) of the element + //!\param w Width of element + //!\param h Height of element + virtual void setSize(f32 w, f32 h) + { + width = w; + height = h; + } + //!Sets the element's visibility + //!\param v Visibility (true = visible) + virtual void setVisible(bool v) + { + visible = v; + visibleChanged(this, v); + } + //!Checks whether or not the element is visible + //!\return true if visible, false otherwise + virtual bool isVisible() const { return !isStateSet(STATE_HIDDEN) && visible; }; + //!Checks whether or not the element is selectable + //!\return true if selectable, false otherwise + virtual bool isSelectable() + { + return !isStateSet(STATE_DISABLED) && selectable; + } + virtual bool isDrawOverOnlyWhenSelected() + { + return drawOverOnlyWhenSelected; + } + virtual void setdrawOverOnlyWhenSelected(bool s) { drawOverOnlyWhenSelected = s; } + //!Checks whether or not the element is clickable + //!\return true if clickable, false otherwise + virtual bool isClickable() + { + return !isStateSet(STATE_DISABLED) && clickable; + } + //!Checks whether or not the element is holdable + //!\return true if holdable, false otherwise + virtual bool isHoldable() { return !isStateSet(STATE_DISABLED) && holdable; } + //!Sets whether or not the element is selectable + //!\param s Selectable + virtual void setSelectable(bool s) { selectable = s; } + //!Sets whether or not the element is clickable + //!\param c Clickable + virtual void setClickable(bool c) { clickable = c; } + //!Sets whether or not the element is holdable + //!\param c Holdable + virtual void setHoldable(bool d) { holdable = d; } + //!Sets the element's state + //!\param s State (STATE_DEFAULT, STATE_SELECTED, STATE_CLICKED, STATE_DISABLED) + //!\param c Controller channel (0-3, -1 = none) + virtual void setState(s32 s, s32 c = -1) + { + if(c >= 0 && c < 4) + { + state[c] |= s; + } + else + { + for(s32 i = 0; i < 4; i++) + state[i] |= s; + } + stateChan = c; + stateChanged(this, s, c); + } + virtual void clearState(s32 s, s32 c = -1) + { + if(c >= 0 && c < 4) + { + state[c] &= ~s; + } + else + { + for(s32 i = 0; i < 4; i++) + state[i] &= ~s; + } + stateChan = c; + stateChanged(this, s, c); + } + virtual bool isStateSet(s32 s, s32 c = -1) const + { + if(c >= 0 && c < 4) + { + return (state[c] & s) != 0; + } + else + { + for(s32 i = 0; i < 4; i++) + if((state[i] & s) != 0) + return true; + + return false; + } + } + //!Gets the element's current state + //!\return state + virtual s32 getState(s32 c = 0) { return state[c]; }; + //!Gets the controller channel that last changed the element's state + //!\return Channel number (0-3, -1 = no channel) + virtual s32 getStateChan() { return stateChan; }; + //!Resets the element's state to STATE_DEFAULT + virtual void resetState() + { + for(s32 i = 0; i < 4; i++) + state[i] = STATE_DEFAULT; + stateChan = -1; + } + //!Sets the element's alpha value + //!\param a alpha value + virtual void setAlpha(f32 a) { alpha = a; } + //!Gets the element's alpha value + //!Considers alpha, alphaDyn, and the parent element's getAlpha() value + //!\return alpha + virtual f32 getAlpha() + { + f32 a; + + if(alphaDyn >= 0) + a = alphaDyn; + else + a = alpha; + + if(parentElement) + a = (a * parentElement->getAlpha()); + + return a; + } + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScale(float s) + { + scaleX = s; + scaleY = s; + scaleZ = s; + } + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScaleX(float s) { scaleX = s; } + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScaleY(float s) { scaleY = s; } + //!Sets the element's scale + //!\param s scale (1 is 100%) + virtual void setScaleZ(float s) { scaleZ = s; } + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScale() + { + float s = 0.5f * (scaleX+scaleY) * scaleDyn; + + if(parentElement) + s *= parentElement->getScale(); + + return s; + } + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScaleX() + { + float s = scaleX * scaleDyn; + + if(parentElement) + s *= parentElement->getScaleX(); + + return s; + } + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScaleY() + { + float s = scaleY * scaleDyn; + + if(parentElement) + s *= parentElement->getScaleY(); + + return s; + } + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's getScale() value + virtual float getScaleZ() + { + float s = scaleZ; + + if(parentElement) + s *= parentElement->getScaleZ(); + + return s; + } + //!Checks whether rumble was requested by the element + //!\return true is rumble was requested, false otherwise + virtual bool isRumbleActive() { return rumble; } + //!Sets whether or not the element is requesting a rumble event + //!\param r true if requesting rumble, false if not + virtual void setRumble(bool r) { rumble = r; } + //!Set an effect for the element + //!\param e Effect to enable + //!\param a Amount of the effect (usage varies on effect) + //!\param t Target amount of the effect (usage varies on effect) + virtual void setEffect(s32 e, s32 a, s32 t=0); + //!Sets an effect to be enabled on wiimote cursor over + //!\param e Effect to enable + //!\param a Amount of the effect (usage varies on effect) + //!\param t Target amount of the effect (usage varies on effect) + virtual void setEffectOnOver(s32 e, s32 a, s32 t=0); + //!Shortcut to SetEffectOnOver(EFFECT_SCALE, 4, 110) + virtual void setEffectGrow() { setEffectOnOver(EFFECT_SCALE, 4, 110); } + //!Reset all applied effects + virtual void resetEffects(); + //!Gets the current element effects + //!\return element effects + virtual s32 getEffect() const { return effects; } + //!\return true if element animation is on going + virtual bool isAnimated() const { return (parentElement != 0) && (getEffect() > 0); } + //!Checks whether the specified coordinates are within the element's boundaries + //!\param x X coordinate + //!\param y Y coordinate + //!\return true if contained within, false otherwise + virtual bool isInside(f32 x, f32 y) + { + return ( x > (this->getCenterX() - getScaleX() * getWidth() * 0.5f) + && x < (this->getCenterX() + getScaleX() * getWidth() * 0.5f) + && y > (this->getCenterY() - getScaleY() * getHeight() * 0.5f) + && y < (this->getCenterY() + getScaleY() * getHeight() * 0.5f)); + } + //!Sets the element's position + //!\param x X coordinate + //!\param y Y coordinate + virtual void setPosition(f32 x, f32 y) + { + xoffset = x; + yoffset = y; + } + //!Sets the element's position + //!\param x X coordinate + //!\param y Y coordinate + //!\param z Z coordinate + virtual void setPosition(f32 x, f32 y, f32 z) + { + xoffset = x; + yoffset = y; + zoffset = z; + } + //!Gets whether or not the element is in STATE_SELECTED + //!\return true if selected, false otherwise + virtual s32 getSelected() { return -1; } + //!Sets the element's alignment respective to its parent element + //!Bitwise ALIGN_LEFT | ALIGN_RIGHT | ALIGN_CENTRE, ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE) + //!\param align Alignment + virtual void setAlignment(s32 a) { alignment = a; } + //!Gets the element's alignment + virtual s32 getAlignment() const { return alignment; } + //!Angle of the object + virtual void setAngle(f32 a) { angle = a; } + //!Angle of the object + virtual f32 getAngle() const { f32 r_angle = angle; if(parentElement) r_angle += parentElement->getAngle(); return r_angle; } + //!Called constantly to allow the element to respond to the current input data + //!\param t Pointer to a GuiController, containing the current input data from PAD/WPAD/VPAD + virtual void update(GuiController * t) { } + //!Called constantly to redraw the element + virtual void draw(CVideo * v) { } + //!Called constantly to process stuff in the element + virtual void process() { } + + //!Updates the element's effects (dynamic values) + //!Called by Draw(), used for animation purposes + virtual void updateEffects(); + + typedef struct _POINT { + s32 x; + s32 y; + } POINT; + + enum + { + STATE_DEFAULT = 0, + STATE_SELECTED = 0x01, + STATE_CLICKED = 0x02, + STATE_HELD = 0x04, + STATE_OVER = 0x08, + STATE_HIDDEN = 0x10, + STATE_DISABLE_INPUT = 0x20, + STATE_CLICKED_TOUCH = 0x40, + STATE_DISABLED = 0x80 + }; + + //! Switch pointer from control to screen position + POINT PtrToScreen(POINT p) + { + //! TODO for 3D + //POINT r = { p.x + getLeft(), p.y + getTop() }; + return p; + } + //! Switch pointer screen to control position + POINT PtrToControl(POINT p) + { + //! TODO for 3D + //POINT r = { p.x - getLeft(), p.y - getTop() }; + return p; + } + //! Signals + sigslot::signal2 visibleChanged; + sigslot::signal3 stateChanged; + sigslot::signal1 effectFinished; + protected: + bool rumble; //!< Wiimote rumble (on/off) - set to on when this element requests a rumble event + bool visible; //!< Visibility of the element. If false, Draw() is skipped + bool selectable; //!< Whether or not this element selectable (can change to SELECTED state) + bool clickable; //!< Whether or not this element is clickable (can change to CLICKED state) + bool holdable; //!< Whether or not this element is holdable (can change to HELD state) + bool drawOverOnlyWhenSelected; //!< Whether or not this element is holdable (can change to HELD state) + f32 width; //!< Element width + f32 height; //!< Element height + f32 xoffset; //!< Element X offset + f32 yoffset; //!< Element Y offset + f32 zoffset; //!< Element Z offset + f32 alpha; //!< Element alpha value (0-255) + f32 angle; //!< Angle of the object (0-360) + f32 scaleX; //!< Element scale (1 = 100%) + f32 scaleY; //!< Element scale (1 = 100%) + f32 scaleZ; //!< Element scale (1 = 100%) + s32 alignment; //!< Horizontal element alignment, respective to parent element + s32 state[4]; //!< Element state (DEFAULT, SELECTED, CLICKED, DISABLED) + s32 stateChan; //!< Which controller channel is responsible for the last change in state + GuiElement * parentElement; //!< Parent element + + //! TODO: Move me to some Animator class + s32 xoffsetDyn; //!< Element X offset, dynamic (added to xoffset value for animation effects) + s32 yoffsetDyn; //!< Element Y offset, dynamic (added to yoffset value for animation effects) + f32 alphaDyn; //!< Element alpha, dynamic (multiplied by alpha value for blending/fading effects) + f32 scaleDyn; //!< Element scale, dynamic (multiplied by alpha value for blending/fading effects) + s32 effects; //!< Currently enabled effect(s). 0 when no effects are enabled + s32 effectAmount; //!< Effect amount. Used by different effects for different purposes + s32 effectTarget; //!< Effect target amount. Used by different effects for different purposes + s32 effectsOver; //!< Effects to enable when wiimote cursor is over this element. Copied to effects variable on over event + s32 effectAmountOver; //!< EffectAmount to set when wiimote cursor is over this element + s32 effectTargetOver; //!< EffectTarget to set when wiimote cursor is over this element +}; + +#endif diff --git a/source/gui/GuiFrame.cpp b/source/gui/GuiFrame.cpp new file mode 100644 index 0000000..a25e8ce --- /dev/null +++ b/source/gui/GuiFrame.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiFrame.h" + +GuiFrame::GuiFrame(GuiFrame *p) +{ + parent = p; + width = 0; + height = 0; + dim = false; + + if(parent) + parent->append(this); +} + +GuiFrame::GuiFrame(f32 w, f32 h, GuiFrame *p) +{ + parent = p; + width = w; + height = h; + dim = false; + + if(parent) + parent->append(this); +} + +GuiFrame::~GuiFrame() +{ + closing(this); + + if(parent) + parent->remove(this); +} + +void GuiFrame::append(GuiElement* e) +{ + if (e == NULL) + return; + + remove(e); + elements.push_back(e); + e->setParent(this); +} + +void GuiFrame::insert(GuiElement* e, u32 index) +{ + if (e == NULL || (index >= elements.size())) + return; + + remove(e); + elements.insert(elements.begin()+index, e); + e->setParent(this); +} + +void GuiFrame::remove(GuiElement* e) +{ + if (e == NULL) + return; + + for (u32 i = 0; i < elements.size(); ++i) + { + if(e == elements[i]) + { + elements.erase(elements.begin()+i); + break; + } + } +} + +void GuiFrame::removeAll() +{ + elements.clear(); +} + +void GuiFrame::close() +{ + //Application::instance()->pushForDelete(this); +} + +void GuiFrame::dimBackground(bool d) +{ + dim = d; +} + +GuiElement* GuiFrame::getGuiElementAt(u32 index) const +{ + if (index >= elements.size()) + return NULL; + return elements[index]; +} + +u32 GuiFrame::getSize() +{ + return elements.size(); +} + +void GuiFrame::resetState() +{ + GuiElement::resetState(); + + for (u32 i = 0; i < elements.size(); ++i) + { + elements[i]->resetState(); + } +} + +void GuiFrame::setState(s32 s, s32 c) +{ + GuiElement::setState(s, c); + + for (u32 i = 0; i < elements.size(); ++i) + { + elements[i]->setState(s, c); + } +} + +void GuiFrame::clearState(s32 s, s32 c) +{ + GuiElement::clearState(s, c); + + for (u32 i = 0; i < elements.size(); ++i) + { + elements[i]->clearState(s, c); + } +} + +void GuiFrame::setVisible(bool v) +{ + visible = v; + + for (u32 i = 0; i < elements.size(); ++i) + { + elements[i]->setVisible(v); + } +} + +s32 GuiFrame::getSelected() +{ + // find selected element + s32 found = -1; + for (u32 i = 0; i < elements.size(); ++i) + { + if(elements[i]->isStateSet(STATE_SELECTED | STATE_OVER)) + { + found = i; + break; + } + } + return found; +} + +void GuiFrame::draw(CVideo * v) +{ + if(!this->isVisible() && parentElement) + return; + + if(parentElement && dim == true) + { + //GXColor dimColor = (GXColor){0, 0, 0, 0x70}; + //Menu_DrawRectangle(0, 0, GetZPosition(), screenwidth,screenheight, &dimColor, false, true); + } + + //! render appended items next frame but allow stop of render if size is reached + u32 size = elements.size(); + + for (u32 i = 0; i < size && i < elements.size(); ++i) + { + elements[i]->draw(v); + } +} + +void GuiFrame::updateEffects() +{ + if(!this->isVisible() && parentElement) + return; + + GuiElement::updateEffects(); + + //! render appended items next frame but allow stop of render if size is reached + u32 size = elements.size(); + + for (u32 i = 0; i < size && i < elements.size(); ++i) + { + elements[i]->updateEffects(); + } +} + +void GuiFrame::process() +{ + if(!this->isVisible() && parentElement) + return; + + GuiElement::process(); + + //! render appended items next frame but allow stop of render if size is reached + u32 size = elements.size(); + + for (u32 i = 0; i < size && i < elements.size(); ++i) + { + elements[i]->process(); + } +} + +void GuiFrame::update(GuiController * c) +{ + if(isStateSet(STATE_DISABLED) && parentElement) + return; + + //! update appended items next frame + u32 size = elements.size(); + + for (u32 i = 0; i < size && i < elements.size(); ++i) + { + elements[i]->update(c); + } +} diff --git a/source/gui/GuiFrame.h b/source/gui/GuiFrame.h new file mode 100644 index 0000000..1d127c8 --- /dev/null +++ b/source/gui/GuiFrame.h @@ -0,0 +1,98 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_FRAME_H_ +#define GUI_FRAME_H_ + +#include +#include "GuiElement.h" +#include "sigslot.h" + +//!Allows GuiElements to be grouped together into a "window" +class GuiFrame : public GuiElement +{ + public: + //!Constructor + GuiFrame(GuiFrame *parent = 0); + //!\overload + //!\param w Width of window + //!\param h Height of window + GuiFrame(f32 w, f32 h, GuiFrame *parent = 0); + //!Destructor + virtual ~GuiFrame(); + //!Appends a GuiElement to the GuiFrame + //!\param e The GuiElement to append. If it is already in the GuiFrame, it is removed first + void append(GuiElement* e); + //!Inserts a GuiElement into the GuiFrame at the specified index + //!\param e The GuiElement to insert. If it is already in the GuiFrame, it is removed first + //!\param i Index in which to insert the element + void insert(GuiElement* e, u32 i); + //!Removes the specified GuiElement from the GuiFrame + //!\param e GuiElement to be removed + void remove(GuiElement* e); + //!Removes all GuiElements + void removeAll(); + //!Bring element to front of the window + void bringToFront(GuiElement *e) { remove(e); append(e); } + //!Returns the GuiElement at the specified index + //!\param index The index of the element + //!\return A pointer to the element at the index, NULL on error (eg: out of bounds) + GuiElement* getGuiElementAt(u32 index) const; + //!Returns the size of the list of elements + //!\return The size of the current element list + u32 getSize(); + //!Sets the visibility of the window + //!\param v visibility (true = visible) + void setVisible(bool v); + //!Resets the window's state to STATE_DEFAULT + void resetState(); + //!Sets the window's state + //!\param s State + void setState(s32 s, s32 c = -1); + void clearState(s32 s, s32 c = -1); + //!Gets the index of the GuiElement inside the window that is currently selected + //!\return index of selected GuiElement + s32 getSelected(); + //!Dim the Window's background + void dimBackground(bool d); + //!Draws all the elements in this GuiFrame + void draw(CVideo * v); + //!Updates the window and all elements contains within + //!Allows the GuiFrame and all elements to respond to the input data specified + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void update(GuiController * t); + //!virtual Close Window - this will put the object on the delete queue in MainWindow + virtual void close(); + //!virtual show window function + virtual void show() {} + //!virtual hide window function + virtual void hide() {} + //!virtual enter main loop function (blocking) + virtual void exec() {} + //!virtual updateEffects which is called by the main loop + virtual void updateEffects(); + //!virtual process which is called by the main loop + virtual void process(); + //! Signals + //! On Closing + sigslot::signal1 closing; + protected: + bool dim; //! Enable/disable dim of a window only + GuiFrame *parent; //!< Parent Window + std::vector elements; //!< Contains all elements within the GuiFrame +}; + +#endif diff --git a/source/gui/GuiImage.cpp b/source/gui/GuiImage.cpp new file mode 100644 index 0000000..34aefb8 --- /dev/null +++ b/source/gui/GuiImage.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiImage.h" +#include "video/CVideo.h" +#include "video/shaders/Texture2DShader.h" +#include "video/shaders/ColorShader.h" + +static const f32 fPiDiv180 = ((f32)M_PI / 180.0f); + +GuiImage::GuiImage(GuiImageData * img) +{ + if(img && img->getTexture()) + { + width = img->getWidth(); + height = img->getHeight(); + } + + internalInit(width, height); + imageData = img; +} + +GuiImage::GuiImage(s32 w, s32 h, const GX2Color & c, s32 type) +{ + internalInit(w, h); + imgType = type; + colorCount = ColorShader::cuColorVtxsSize / ColorShader::cuColorAttrSize; + + colorVtxs = (u8 *) memalign(GX2_VERTEX_BUFFER_ALIGNMENT, colorCount * ColorShader::cuColorAttrSize); + if(colorVtxs) + { + for(u32 i = 0; i < colorCount; i++) + setImageColor(c, i); + } +} + +GuiImage::GuiImage(s32 w, s32 h, const GX2Color *c, u32 color_count, s32 type) +{ + internalInit(w, h); + imgType = type; + colorCount = ColorShader::cuColorVtxsSize / ColorShader::cuColorAttrSize; + if(colorCount < color_count) + colorCount = color_count; + + colorVtxs = (u8 *) memalign(GX2_VERTEX_BUFFER_ALIGNMENT, colorCount * ColorShader::cuColorAttrSize); + if(colorVtxs) + { + for(u32 i = 0; i < colorCount; i++) + { + // take the last as reference if not enough colors defined + s32 idx = (i < color_count) ? i : (color_count - 1); + setImageColor(c[idx], i); + } + } +} + +/** + * Destructor for the GuiImage class. + */ +GuiImage::~GuiImage() +{ + if(colorVtxs) { + free(colorVtxs); + colorVtxs = NULL; + } +} + +void GuiImage::internalInit(s32 w, s32 h) +{ + imageData = NULL; + width = w; + height = h; + tileHorizontal = -1; + tileVertical = -1; + imgType = IMAGE_TEXTURE; + colorVtxsDirty = false; + colorVtxs = NULL; + colorCount = 0; + posVtxs = NULL; + texCoords = NULL; + vtxCount = 4; + primitive = GX2_PRIMITIVE_QUADS; + + imageAngle = 0.0f; + blurDirection = glm::vec3(0.0f); + positionOffsets = glm::vec3(0.0f); + scaleFactor = glm::vec3(1.0f); + colorIntensity = glm::vec4(1.0f); +} + +void GuiImage::setImageData(GuiImageData * img) +{ + imageData = img; + width = 0; + height = 0; + if(img && img->getTexture()) + { + width = img->getWidth(); + height = img->getHeight(); + } + imgType = IMAGE_TEXTURE; +} + +GX2Color GuiImage::getPixel(s32 x, s32 y) +{ + if(!imageData || this->getWidth() <= 0 || x < 0 || y < 0 || x >= this->getWidth() || y >= this->getHeight()) + return (GX2Color){0, 0, 0, 0}; + + u32 pitch = imageData->getTexture()->surface.pitch; + u32 *imagePtr = (u32*)imageData->getTexture()->surface.image_data; + + u32 color_u32 = imagePtr[y * pitch + x]; + GX2Color color; + color.r = (color_u32 >> 24) & 0xFF; + color.g = (color_u32 >> 16) & 0xFF; + color.b = (color_u32 >> 8) & 0xFF; + color.a = (color_u32 >> 0) & 0xFF; + return color; +} + +void GuiImage::setPixel(s32 x, s32 y, const GX2Color & color) +{ + if(!imageData || this->getWidth() <= 0 || x < 0 || y < 0 || x >= this->getWidth() || y >= this->getHeight()) + return; + + + u32 pitch = imageData->getTexture()->surface.pitch; + u32 *imagePtr = (u32*)imageData->getTexture()->surface.image_data; + imagePtr[y * pitch + x] = (color.r << 24) | (color.g << 16) | (color.b << 8) | (color.a << 0); +} + +void GuiImage::setImageColor(const GX2Color & c, s32 idx) +{ + if(!colorVtxs) { + return; + } + + if(idx >= 0 && idx < (s32)colorCount) + { + colorVtxs[(idx << 2) + 0] = c.r; + colorVtxs[(idx << 2) + 1] = c.g; + colorVtxs[(idx << 2) + 2] = c.b; + colorVtxs[(idx << 2) + 3] = c.a; + colorVtxsDirty = true; + } + else if(colorVtxs) + { + for(u32 i = 0; i < (ColorShader::cuColorVtxsSize / sizeof(u8)); i += 4) + { + colorVtxs[i + 0] = c.r; + colorVtxs[i + 1] = c.g; + colorVtxs[i + 2] = c.b; + colorVtxs[i + 3] = c.a; + } + colorVtxsDirty = true; + } +} + +void GuiImage::setSize(s32 w, s32 h) +{ + width = w; + height = h; +} + +void GuiImage::setPrimitiveVertex(s32 prim, const f32 *posVtx, const f32 *texCoord, u32 vtxcount) +{ + primitive = prim; + vtxCount = vtxcount; + posVtxs = posVtx; + texCoords = texCoord; + + if(imgType == IMAGE_COLOR) + { + u8 * newColorVtxs = (u8 *) memalign(0x40, ColorShader::cuColorAttrSize * vtxCount); + + for(u32 i = 0; i < vtxCount; i++) + { + s32 newColorIdx = (i << 2); + s32 colorIdx = (i < colorCount) ? (newColorIdx) : ((colorCount - 1) << 2); + + newColorVtxs[newColorIdx + 0] = colorVtxs[colorIdx + 0]; + newColorVtxs[newColorIdx + 1] = colorVtxs[colorIdx + 1]; + newColorVtxs[newColorIdx + 2] = colorVtxs[colorIdx + 2]; + newColorVtxs[newColorIdx + 3] = colorVtxs[colorIdx + 3]; + } + + free(colorVtxs); + colorVtxs = newColorVtxs; + colorCount = vtxCount; + colorVtxsDirty = true; + } +} + +void GuiImage::draw(CVideo *pVideo) +{ + if(!this->isVisible() || tileVertical == 0 || tileHorizontal == 0) + return; + + f32 currScaleX = getScaleX(); + f32 currScaleY = getScaleY(); + + positionOffsets[0] = getCenterX() * pVideo->getWidthScaleFactor() * 2.0f; + positionOffsets[1] = getCenterY() * pVideo->getHeightScaleFactor() * 2.0f; + positionOffsets[2] = getDepth() * pVideo->getDepthScaleFactor() * 2.0f; + + scaleFactor[0] = currScaleX * getWidth() * pVideo->getWidthScaleFactor(); + scaleFactor[1] = currScaleY * getHeight() * pVideo->getHeightScaleFactor(); + scaleFactor[2] = getScaleZ(); + + //! add other colors intensities parameters + colorIntensity[3] = getAlpha(); + + //! angle of the object + imageAngle = DegToRad(getAngle()); + +// if(image && tileHorizontal > 0 && tileVertical > 0) +// { +// for(s32 n=0; n 0) +// { +// for(s32 i=0; i 0) +// { +// for(s32 i=0; isetShaders(); + ColorShader::instance()->setAttributeBuffer(colorVtxs, posVtxs, vtxCount); + ColorShader::instance()->setAngle(imageAngle); + ColorShader::instance()->setOffset(positionOffsets); + ColorShader::instance()->setScale(scaleFactor); + ColorShader::instance()->setColorIntensity(colorIntensity); + ColorShader::instance()->draw(primitive, vtxCount); + } + else if(imageData) + { + Texture2DShader::instance()->setShaders(); + Texture2DShader::instance()->setAttributeBuffer(texCoords, posVtxs, vtxCount); + Texture2DShader::instance()->setAngle(imageAngle); + Texture2DShader::instance()->setOffset(positionOffsets); + Texture2DShader::instance()->setScale(scaleFactor); + Texture2DShader::instance()->setColorIntensity(colorIntensity); + Texture2DShader::instance()->setBlurring(blurDirection); + Texture2DShader::instance()->setTextureAndSampler(imageData->getTexture(), imageData->getSampler()); + Texture2DShader::instance()->draw(primitive, vtxCount); + } +} diff --git a/source/gui/GuiImage.h b/source/gui/GuiImage.h new file mode 100644 index 0000000..bcd9794 --- /dev/null +++ b/source/gui/GuiImage.h @@ -0,0 +1,110 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_IMAGE_H_ +#define GUI_IMAGE_H_ + +#include "video/shaders/Shader.h" +#include "GuiElement.h" +#include "GuiImageData.h" + +//!Display, manage, and manipulate images in the GUI +class GuiImage : public GuiElement +{ +public: + enum ImageTypes + { + IMAGE_TEXTURE, + IMAGE_COLOR + }; + + //!\overload + //!\param img Pointer to GuiImageData element + GuiImage(GuiImageData * img); + //!\overload + //!Creates an image filled with the specified color + //!\param w Image width + //!\param h Image height + //!\param c Array with 4 x image color (BL, BR, TL, TR) + GuiImage(s32 w, s32 h, const GX2Color & c, s32 imgType = IMAGE_COLOR); + GuiImage(s32 w, s32 h, const GX2Color * c, u32 colorCount = 1, s32 imgType = IMAGE_COLOR); + //!Destructor + virtual ~GuiImage(); + //!Sets the number of times to draw the image horizontally + //!\param t Number of times to draw the image + void setTileHorizontal(s32 t) { tileHorizontal = t; } + //!Sets the number of times to draw the image vertically + //!\param t Number of times to draw the image + void setTileVertical(s32 t) { tileVertical = t; } + //!Constantly called to draw the image + void draw(CVideo *pVideo); + //!Gets the image data + //!\return pointer to image data + GuiImageData * getImageData() const { return imageData; } + //!Sets up a new image using the GuiImageData object specified + //!\param img Pointer to GuiImageData object + void setImageData(GuiImageData * img); + //!Gets the pixel color at the specified coordinates of the image + //!\param x X coordinate + //!\param y Y coordinate + GX2Color getPixel(s32 x, s32 y); + //!Sets the pixel color at the specified coordinates of the image + //!\param x X coordinate + //!\param y Y coordinate + //!\param color Pixel color + void setPixel(s32 x, s32 y, const GX2Color & color); + //!Change ImageColor + void setImageColor(const GX2Color & c, s32 idx = -1); + //!Change ImageColor + void setSize(s32 w, s32 h); + + void setPrimitiveVertex(s32 prim, const f32 *pos, const f32 *tex, u32 count); + + void setBlurDirection(u8 dir, f32 value) + { + if(dir < 2) { + blurDirection[dir] = value; + } + } + void setColorIntensity(const glm::vec4 & col) + { + colorIntensity = col; + } +protected: + void internalInit(s32 w, s32 h); + + s32 imgType; //!< Type of image data (IMAGE_TEXTURE, IMAGE_COLOR, IMAGE_DATA) + GuiImageData * imageData; //!< Poiner to image data. May be shared with GuiImageData data + s32 tileHorizontal; //!< Number of times to draw (tile) the image horizontally + s32 tileVertical; //!< Number of times to draw (tile) the image vertically + + //! Internally used variables for rendering + u8 *colorVtxs; + u32 colorCount; + bool colorVtxsDirty; + glm::vec3 positionOffsets; + glm::vec3 scaleFactor; + glm::vec4 colorIntensity; + f32 imageAngle; + glm::vec3 blurDirection; + + const f32 * posVtxs; + const f32 * texCoords; + u32 vtxCount; + s32 primitive; +}; + +#endif diff --git a/source/gui/GuiImageAsync.cpp b/source/gui/GuiImageAsync.cpp new file mode 100644 index 0000000..91f31d1 --- /dev/null +++ b/source/gui/GuiImageAsync.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include "GuiImageAsync.h" +#include + +std::vector GuiImageAsync::imageQueue; +CThread * GuiImageAsync::pThread = NULL; +CMutex * GuiImageAsync::pMutex = NULL; +u32 GuiImageAsync::threadRefCounter = 0; +bool GuiImageAsync::bExitRequested = false; +GuiImageAsync * GuiImageAsync::pInUse = NULL; + +GuiImageAsync::GuiImageAsync(const u8 *imageBuffer, const u32 & imageBufferSize, GuiImageData * preloadImg) + : GuiImage(preloadImg) + , imgData(NULL) + , imgBuffer(imageBuffer) + , imgBufferSize(imageBufferSize) +{ + threadInit(); + threadAddImage(this); +} + +GuiImageAsync::GuiImageAsync(const std::string & file, GuiImageData * preloadImg) + : GuiImage(preloadImg) + , imgData(NULL) + , filename(file) + , imgBuffer(NULL) + , imgBufferSize(0) +{ + threadInit(); + threadAddImage(this); +} + +GuiImageAsync::~GuiImageAsync() +{ + threadRemoveImage(this); + while(pInUse == this) + os_usleep(1000); + + if (imgData) + delete imgData; + + //threadExit(); +} + +void GuiImageAsync::threadAddImage(GuiImageAsync *Image) +{ + pMutex->lock(); + imageQueue.push_back(Image); + pMutex->unlock(); + pThread->resumeThread(); +} + +void GuiImageAsync::threadRemoveImage(GuiImageAsync *image) +{ + pMutex->lock(); + for(u32 i = 0; i < imageQueue.size(); ++i) + { + if(imageQueue[i] == image) + { + imageQueue.erase(imageQueue.begin() + i); + break; + } + } + pMutex->unlock(); +} + +void GuiImageAsync::clearQueue() +{ + pMutex->lock(); + imageQueue.clear(); + pMutex->unlock(); +} + +void GuiImageAsync::guiImageAsyncThread(CThread *thread, void *arg) +{ + while(!bExitRequested) + { + if(imageQueue.empty() && !bExitRequested) + pThread->suspendThread(); + + if(!imageQueue.empty() && !bExitRequested) + { + pMutex->lock(); + pInUse = imageQueue.front(); + imageQueue.erase(imageQueue.begin()); + pMutex->unlock(); + + if (!pInUse) + continue; + + + if(pInUse->imgBuffer && pInUse->imgBufferSize) + { + pInUse->imgData = new GuiImageData(pInUse->imgBuffer, pInUse->imgBufferSize); + } + else + { + u8 *buffer = NULL; + u32 bufferSize = 0; + + s32 iResult = FSUtils::LoadFileToMem(pInUse->filename.c_str(), &buffer, &bufferSize); + if(iResult > 0) + { + pInUse->imgData = new GuiImageData(buffer, bufferSize, GX2_TEX_CLAMP_MIRROR); + + //! free original image buffer which is converted to texture now and not needed anymore + free(buffer); + } + } + + if(pInUse->imgData) + { + if(pInUse->imgData->getTexture()) + { + pInUse->width = pInUse->imgData->getWidth(); + pInUse->height = pInUse->imgData->getHeight(); + pInUse->imageData = pInUse->imgData; + } + else + { + delete pInUse->imgData; + pInUse->imgData = NULL; + } + } + pInUse->imageLoaded(pInUse); + pInUse = NULL; + } + } +} + +void GuiImageAsync::threadInit() +{ + if (pThread == NULL) + { + bExitRequested = false; + pMutex = new CMutex(); + pThread = CThread::create(GuiImageAsync::guiImageAsyncThread, NULL, CThread::eAttributeAffCore1 | CThread::eAttributePinnedAff, 10); + pThread->resumeThread(); + } + + ++threadRefCounter; +} + +void GuiImageAsync::threadExit() +{ + if(threadRefCounter){ + --threadRefCounter; + } + + if(/*(threadRefCounter == 0) &&*/ (pThread != NULL)) + { + bExitRequested = true; + delete pThread; + delete pMutex; + pThread = NULL; + pMutex = NULL; + } +} diff --git a/source/gui/GuiImageAsync.h b/source/gui/GuiImageAsync.h new file mode 100644 index 0000000..17536f3 --- /dev/null +++ b/source/gui/GuiImageAsync.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef _GUIIMAGEASYNC_H_ +#define _GUIIMAGEASYNC_H_ + +#include +#include "GuiImage.h" +#include +#include +#include + +class GuiImageAsync : public GuiImage +{ + public: + GuiImageAsync(const u8 *imageBuffer, const u32 & imageBufferSize, GuiImageData * preloadImg); + GuiImageAsync(const std::string & filename, GuiImageData * preloadImg); + virtual ~GuiImageAsync(); + + static void clearQueue(); + static void removeFromQueue(GuiImageAsync * image) { + threadRemoveImage(image); + } + + //! don't forget to LOCK GUI if using this asynchron call + sigslot::signal1 imageLoaded; + static void threadExit(); + + private: + static void threadInit(); + + GuiImageData *imgData; + std::string filename; + const u8 *imgBuffer; + const u32 imgBufferSize; + + static void guiImageAsyncThread(CThread *thread, void *arg); + static void threadAddImage(GuiImageAsync* Image); + static void threadRemoveImage(GuiImageAsync* Image); + + static std::vector imageQueue; + static CThread *pThread; + static CMutex * pMutex; + static u32 threadRefCounter; + static GuiImageAsync * pInUse; + static bool bExitRequested; +}; + +#endif /*_GUIIMAGEASYNC_H_*/ diff --git a/source/gui/GuiImageData.cpp b/source/gui/GuiImageData.cpp new file mode 100644 index 0000000..37c40f9 --- /dev/null +++ b/source/gui/GuiImageData.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "GuiImageData.h" +#include +/** + * Constructor for the GuiImageData class. + */ +GuiImageData::GuiImageData() +{ + texture = NULL; + sampler = NULL; + memoryType = eMemTypeMEM2; +} + +/** + * Constructor for the GuiImageData class. + */ +GuiImageData::GuiImageData(const u8 * img, s32 imgSize, s32 textureClamp, s32 textureFormat) +{ + texture = NULL; + sampler = NULL; + loadImage(img, imgSize, textureClamp, textureFormat); +} + +/** + * Destructor for the GuiImageData class. + */ +GuiImageData::~GuiImageData() +{ + releaseData(); +} + +void GuiImageData::releaseData(void) +{ + if(texture) { + if(texture->surface.image_data) + { + switch(memoryType) + { + default: + case eMemTypeMEM2: + free(texture->surface.image_data); + break; + case eMemTypeMEM1: + MEM1_free(texture->surface.image_data); + break; + case eMemTypeMEMBucket: + MEMBucket_free(texture->surface.image_data); + break; + } + } + delete texture; + texture = NULL; + } + if(sampler) { + delete sampler; + sampler = NULL; + } +} + +void GuiImageData::loadImage(const u8 *img, s32 imgSize, s32 textureClamp, s32 textureFormat) +{ + if(!img || (imgSize < 8)) + return; + + releaseData(); + gdImagePtr gdImg = 0; + + if (img[0] == 0xFF && img[1] == 0xD8) + { + //! not needed for now therefore comment out to safe ELF size + //! if needed uncomment, adds 200 kb to the ELF size + // IMAGE_JPEG + gdImg = gdImageCreateFromJpegPtr(imgSize, (u8*) img); + } + else if (img[0] == 'B' && img[1] == 'M') + { + // IMAGE_BMP + gdImg = gdImageCreateFromBmpPtr(imgSize, (u8*) img); + } + else if (img[0] == 0x89 && img[1] == 'P' && img[2] == 'N' && img[3] == 'G') + { + // IMAGE_PNG + gdImg = gdImageCreateFromPngPtr(imgSize, (u8*) img); + } + //!This must be last since it can also intefere with outher formats + else if(img[0] == 0x00) + { + // Try loading TGA image + gdImg = gdImageCreateFromTgaPtr(imgSize, (u8*) img); + } + + if(gdImg == 0) + return; + + u32 width = (gdImageSX(gdImg)); + u32 height = (gdImageSY(gdImg)); + + //! Initialize texture + texture = new GX2Texture; + GX2InitTexture(texture, width, height, 1, 0, textureFormat, GX2_SURFACE_DIM_2D, GX2_TILE_MODE_LINEAR_ALIGNED); + + //! if this fails something went horribly wrong + if(texture->surface.image_size == 0) { + delete texture; + texture = NULL; + gdImageDestroy(gdImg); + return; + } + + //! allocate memory for the surface + memoryType = eMemTypeMEM2; + texture->surface.image_data = memalign(texture->surface.align, texture->surface.image_size); + //! try MEM1 on failure + if(!texture->surface.image_data) { + memoryType = eMemTypeMEM1; + texture->surface.image_data = MEM1_alloc(texture->surface.image_size, texture->surface.align); + } + //! try MEM bucket on failure + if(!texture->surface.image_data) { + memoryType = eMemTypeMEMBucket; + texture->surface.image_data = MEMBucket_alloc(texture->surface.image_size, texture->surface.align); + } + //! check if memory is available for image + if(!texture->surface.image_data) { + gdImageDestroy(gdImg); + delete texture; + texture = NULL; + return; + } + //! set mip map data pointer + texture->surface.mip_data = NULL; + //! convert image to texture + switch(textureFormat) + { + default: + case GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM: + gdImageToUnormR8G8B8A8(gdImg, (u32*)texture->surface.image_data, texture->surface.width, texture->surface.height, texture->surface.pitch); + break; + case GX2_SURFACE_FORMAT_TCS_R5_G6_B5_UNORM: + gdImageToUnormR5G6B5(gdImg, (u16*)texture->surface.image_data, texture->surface.width, texture->surface.height, texture->surface.pitch); + break; + } + + //! free memory of image as its not needed anymore + gdImageDestroy(gdImg); + + //! invalidate the memory + GX2Invalidate(GX2_INVALIDATE_CPU_TEXTURE, texture->surface.image_data, texture->surface.image_size); + //! initialize the sampler + sampler = new GX2Sampler; + GX2InitSampler(sampler, textureClamp, GX2_TEX_XY_FILTER_BILINEAR); +} + +void GuiImageData::gdImageToUnormR8G8B8A8(gdImagePtr gdImg, u32 *imgBuffer, u32 width, u32 height, u32 pitch) +{ + for(u32 y = 0; y < height; ++y) + { + for(u32 x = 0; x < width; ++x) + { + u32 pixel = gdImageGetPixel(gdImg, x, y); + + u8 a = 254 - 2*((u8)gdImageAlpha(gdImg, pixel)); + if(a == 254) a++; + + u8 r = gdImageRed(gdImg, pixel); + u8 g = gdImageGreen(gdImg, pixel); + u8 b = gdImageBlue(gdImg, pixel); + + imgBuffer[y * pitch + x] = (r << 24) | (g << 16) | (b << 8) | (a); + } + } +} + +//! TODO: figure out why this seems to not work correct yet +void GuiImageData::gdImageToUnormR5G6B5(gdImagePtr gdImg, u16 *imgBuffer, u32 width, u32 height, u32 pitch) +{ + for(u32 y = 0; y < height; ++y) + { + for(u32 x = 0; x < width; ++x) + { + u32 pixel = gdImageGetPixel(gdImg, x, y); + + u8 r = gdImageRed(gdImg, pixel); + u8 g = gdImageGreen(gdImg, pixel); + u8 b = gdImageBlue(gdImg, pixel); + + imgBuffer[y * pitch + x] = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); + } + } +} diff --git a/source/gui/GuiImageData.h b/source/gui/GuiImageData.h new file mode 100644 index 0000000..099b8c8 --- /dev/null +++ b/source/gui/GuiImageData.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_IMAGEDATA_H_ +#define GUI_IMAGEDATA_H_ + + +#include +#include +#include + +class GuiImageData : public AsyncDeleter::Element +{ +public: + //!Constructor + GuiImageData(); + //!\param img Image data + //!\param imgSize The image size + GuiImageData(const u8 * img, s32 imgSize, s32 textureClamp = GX2_TEX_CLAMP_CLAMP, s32 textureFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM); + //!Destructor + virtual ~GuiImageData(); + //!Load image from buffer + //!\param img Image data + //!\param imgSize The image size + void loadImage(const u8 * img, s32 imgSize, s32 textureClamp = GX2_TEX_CLAMP_CLAMP, s32 textureFormat = GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM); + //! getter functions + const GX2Texture * getTexture() const { return texture; }; + const GX2Sampler * getSampler() const { return sampler; }; + //!Gets the image width + //!\return image width + s32 getWidth() const { if(texture) return texture->surface.width; else return 0; }; + //!Gets the image height + //!\return image height + s32 getHeight() const { if(texture) return texture->surface.height; else return 0; }; + //! release memory of the image data + void releaseData(void); +private: + void gdImageToUnormR8G8B8A8(gdImagePtr gdImg, u32 *imgBuffer, u32 width, u32 height, u32 pitch); + void gdImageToUnormR5G6B5(gdImagePtr gdImg, u16 *imgBuffer, u32 width, u32 height, u32 pitch); + + GX2Texture *texture; + GX2Sampler *sampler; + + enum eMemoryTypes + { + eMemTypeMEM2, + eMemTypeMEM1, + eMemTypeMEMBucket + }; + + u8 memoryType; +}; + +#endif diff --git a/source/gui/GuiParticleImage.cpp b/source/gui/GuiParticleImage.cpp new file mode 100644 index 0000000..b9630cc --- /dev/null +++ b/source/gui/GuiParticleImage.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiParticleImage.h" +#include "video/CVideo.h" +#include "video/shaders/ColorShader.h" + +#define CIRCLE_VERTEX_COUNT 36 + +static inline f32 getRandZeroToOneF32() +{ + return (rand() % 10000) * 0.0001f; +} + +static inline f32 getRandMinusOneToOneF32() +{ + return getRandZeroToOneF32() * 2.0f - 1.0f; +} + +GuiParticleImage::GuiParticleImage(s32 w, s32 h, u32 particleCount) + : GuiImage(NULL) +{ + width = w; + height = h; + imgType = IMAGE_COLOR; + + posVertexs = (f32 *) memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ColorShader::cuVertexAttrSize * CIRCLE_VERTEX_COUNT); + colorVertexs = (u8 *) memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ColorShader::cuColorAttrSize * CIRCLE_VERTEX_COUNT); + + for(u32 i = 0; i < CIRCLE_VERTEX_COUNT; i++) + { + posVertexs[i * 3 + 0] = cosf(DegToRad(i * 360.0f / CIRCLE_VERTEX_COUNT)); + posVertexs[i * 3 + 1] = sinf(DegToRad(i * 360.0f / CIRCLE_VERTEX_COUNT)); + posVertexs[i * 3 + 2] = 0.0f; + + colorVertexs[i * 4 + 0] = 0xff; + colorVertexs[i * 4 + 1] = 0xff; + colorVertexs[i * 4 + 2] = 0xff; + colorVertexs[i * 4 + 3] = 0xff; + } + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, posVertexs, ColorShader::cuVertexAttrSize * CIRCLE_VERTEX_COUNT); + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, colorVertexs, ColorShader::cuColorAttrSize * CIRCLE_VERTEX_COUNT); + + particles.resize(particleCount); + + for(u32 i = 0; i < particleCount; i++) + { + particles[i].position.x = getRandMinusOneToOneF32() * getWidth() * 0.5f; + particles[i].position.y = getRandMinusOneToOneF32() * getHeight() * 0.5f; + particles[i].position.z = 0.0f; + particles[i].colors = glm::vec4(1.0f, 1.0f, 1.0f, (getRandZeroToOneF32() * 0.6f) + 0.05f); + particles[i].radius = getRandZeroToOneF32() * 30.0f + 60.0f; + particles[i].speed = (getRandZeroToOneF32() * 0.4f) + 0.6f; + particles[i].direction = getRandMinusOneToOneF32(); + } +} + +GuiParticleImage::~GuiParticleImage() +{ + free(posVertexs); + free(colorVertexs); +} + +void GuiParticleImage::draw(CVideo *pVideo) +{ + if(!this->isVisible()) + return; + + + f32 currScaleX = getScaleX(); + f32 currScaleY = getScaleY(); + + positionOffsets[2] = getDepth() * pVideo->getDepthScaleFactor() * 2.0f; + + scaleFactor[2] = getScaleZ(); + + //! add other colors intensities parameters + colorIntensity[3] = getAlpha(); + + for(u32 i = 0; i < particles.size(); ++i) + { + if(particles[i].position.y > (getHeight() * 0.5f + 30.0f)) + { + particles[i].position.x = getRandMinusOneToOneF32() * getWidth() * 0.5f; + particles[i].position.y = -getHeight() * 0.5f - 30.0f; + particles[i].colors = glm::vec4(1.0f, 1.0f, 1.0f, (getRandZeroToOneF32() * 0.6f) + 0.05f); + particles[i].radius = getRandZeroToOneF32() * 30.0f + 60.0f; + particles[i].speed = (getRandZeroToOneF32() * 0.4f) + 0.6f; + particles[i].direction = getRandMinusOneToOneF32(); + } + if(particles[i].position.x < (-getWidth() * 0.5f - 50.0f)) + { + particles[i].position.x = -particles[i].position.x; + } + + + particles[i].direction += getRandMinusOneToOneF32() * 0.03f; + particles[i].position.x += particles[i].speed * particles[i].direction; + particles[i].position.y += particles[i].speed; + + positionOffsets[0] = (getCenterX() + particles[i].position.x) * pVideo->getWidthScaleFactor() * 2.0f; + positionOffsets[1] = (getCenterY() + particles[i].position.y) * pVideo->getHeightScaleFactor() * 2.0f; + + scaleFactor[0] = currScaleX * particles[i].radius * pVideo->getWidthScaleFactor(); + scaleFactor[1] = currScaleY * particles[i].radius * pVideo->getHeightScaleFactor(); + + ColorShader::instance()->setShaders(); + ColorShader::instance()->setAttributeBuffer(colorVertexs, posVertexs, CIRCLE_VERTEX_COUNT); + ColorShader::instance()->setAngle(0.0f); + ColorShader::instance()->setOffset(positionOffsets); + ColorShader::instance()->setScale(scaleFactor); + ColorShader::instance()->setColorIntensity(colorIntensity * particles[i].colors); + ColorShader::instance()->draw(GX2_PRIMITIVE_TRIANGLE_FAN, CIRCLE_VERTEX_COUNT); + } +} diff --git a/source/gui/GuiParticleImage.h b/source/gui/GuiParticleImage.h new file mode 100644 index 0000000..b62eb0c --- /dev/null +++ b/source/gui/GuiParticleImage.h @@ -0,0 +1,45 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef _GUI_PARTICLE_IMAGE_H_ +#define _GUI_PARTICLE_IMAGE_H_ + +#include "GuiImage.h" + +class GuiParticleImage : public GuiImage, public sigslot::has_slots<> +{ +public: + GuiParticleImage(s32 w, s32 h, u32 particleCount); + virtual ~GuiParticleImage(); + + void draw(CVideo *pVideo); +private: + f32 *posVertexs; + u8 *colorVertexs; + + typedef struct + { + glm::vec3 position; + glm::vec4 colors; + f32 radius; + f32 speed; + f32 direction; + } Particle; + + std::vector particles; +}; + +#endif // _GUI_ICON_GRID_H_ diff --git a/source/gui/GuiScrollbar.cpp b/source/gui/GuiScrollbar.cpp new file mode 100644 index 0000000..490b68f --- /dev/null +++ b/source/gui/GuiScrollbar.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (C) 2011 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#include "GuiScrollbar.h" +#include "resources/Resources.h" + +Scrollbar::Scrollbar(s32 h) + : touchTrigger(GuiTrigger::CHANNEL_1, GuiTrigger::VPAD_TOUCH) + , wpadTouchTrigger(GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5, GuiTrigger::BUTTON_A) +{ + SelItem = 0; + SelInd = 0; + PageSize = 0; + EntrieCount = 0; + ScrollSpeed = 15; + ScrollState = 0; + + listChanged.connect(this, &Scrollbar::setScrollboxPosition); + + btnSoundClick = Resources::GetSound("button_click.mp3"); + scrollbarLine = Resources::GetImageData("scrollbarLine.png"); + arrowDown = Resources::GetImageData("scrollbarArrowDown.png"); + arrowUp = Resources::GetImageData("scrollbarArrowUp.png"); + scrollbarBox = Resources::GetImageData("scrollbarButton.png"); + + height = h; + width = scrollbarBox->getWidth(); + + MaxHeight = height * 0.5f - (scrollbarBox ? (scrollbarBox->getHeight() * 0.5f) : 0) - (arrowUp ? arrowUp->getHeight() : 0); + MinHeight = -height * 0.5f + (scrollbarBox ? (scrollbarBox->getHeight() * 0.5f) : 0) + (arrowDown ? arrowDown->getHeight() : 0); + + scrollbarLineImg = new GuiImage(scrollbarLine); + scrollbarLineImg->setParent(this); + scrollbarLineImg->setAlignment(ALIGN_CENTER | ALIGN_MIDDLE); + scrollbarLineImg->setPosition(0, 0); + + arrowDownImg = new GuiImage(arrowDown); + arrowUpImg = new GuiImage(arrowUp); + scrollbarBoxImg = new GuiImage(scrollbarBox); + + arrowUpBtn = new GuiButton(arrowUpImg->getWidth(), arrowUpImg->getHeight()); + arrowUpBtn->setParent(this); + arrowUpBtn->setImage(arrowUpImg); + arrowUpBtn->setAlignment(ALIGN_CENTER | ALIGN_TOP); + arrowUpBtn->setPosition(0, 0); + arrowUpBtn->setTrigger(&touchTrigger, 0); + arrowUpBtn->setTrigger(&wpadTouchTrigger, 1); + arrowUpBtn->setSoundClick(btnSoundClick); + arrowUpBtn->setEffectGrow(); + arrowUpBtn->clicked.connect(this, &Scrollbar::OnUpButtonClick); + + arrowDownBtn = new GuiButton(arrowDownImg->getWidth(), arrowDownImg->getHeight()); + arrowDownBtn->setParent(this); + arrowDownBtn->setImage(arrowDownImg); + arrowDownBtn->setAlignment(ALIGN_CENTER | ALIGN_BOTTOM); + arrowDownBtn->setPosition(0, 0); + arrowDownBtn->setTrigger(&touchTrigger, 0); + arrowDownBtn->setTrigger(&wpadTouchTrigger, 1); + arrowDownBtn->setSoundClick(btnSoundClick); + arrowDownBtn->setEffectGrow(); + arrowDownBtn->clicked.connect(this, &Scrollbar::OnDownButtonClick); + + scrollbarBoxBtn = new GuiButton(scrollbarBoxImg->getWidth(), height); + scrollbarBoxBtn->setParent(this); + scrollbarBoxBtn->setImage(scrollbarBoxImg); + scrollbarBoxBtn->setAlignment(ALIGN_CENTER | ALIGN_TOP); + scrollbarBoxBtn->setPosition(0, MaxHeight); + scrollbarBoxBtn->setHoldable(true); + scrollbarBoxBtn->setTrigger(&touchTrigger, 0); + scrollbarBoxBtn->setTrigger(&wpadTouchTrigger, 1); + scrollbarBoxBtn->setEffectGrow(); + scrollbarBoxBtn->held.connect(this, &Scrollbar::OnBoxButtonHold); +} + +Scrollbar::~Scrollbar() +{ + Resources::RemoveSound(btnSoundClick); + Resources::RemoveImageData(scrollbarLine); + Resources::RemoveImageData(arrowDown); + Resources::RemoveImageData(arrowUp); + Resources::RemoveImageData(scrollbarBox); + + delete arrowUpBtn; + delete arrowDownBtn; + delete scrollbarBoxBtn; + + delete scrollbarLineImg; + + delete arrowDownImg; + delete arrowUpImg; + delete scrollbarBoxImg; +} + +void Scrollbar::ScrollOneUp() +{ + if(SelItem == 0 && SelInd > 0) + { + // move list up by 1 + --SelInd; + } + else if(SelInd+SelItem > 0) + { + --SelItem; + } +} + +void Scrollbar::ScrollOneDown() +{ + if(SelInd+SelItem + 1 < EntrieCount) + { + if(SelItem == PageSize-1) + { + // move list down by 1 + SelInd++; + } + else + { + SelItem++; + } + } +} + +void Scrollbar::OnUpButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) +{ + if(ScrollState < ScrollSpeed) + return; + + ScrollOneUp(); + + ScrollState = 0; + listChanged(SelItem, SelInd); +} + +void Scrollbar::OnDownButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) +{ + if(ScrollState < ScrollSpeed) + return; + + ScrollOneDown(); + + ScrollState = 0; + listChanged(SelItem, SelInd); +} + +void Scrollbar::OnBoxButtonHold(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) +{ + if(EntrieCount == 0){ + return; + } + + if(!controller->data.validPointer){ + return; + } + + s32 y = controller->data.y - this->getCenterY(); + + s32 positionWiimote = LIMIT(y - MinHeight, 0, MaxHeight - MinHeight); + + s32 newSelected = (EntrieCount - 1) - (s32) ((float) positionWiimote / (float) (MaxHeight-MinHeight) * (float) (EntrieCount-1)); + + s32 diff = newSelected-SelInd-SelItem; + + if(newSelected <= 0) + { + SelItem = 0; + SelInd = 0; + } + else if(newSelected >= EntrieCount-1) + { + SelItem = (PageSize-1 < EntrieCount-1) ? PageSize-1 : EntrieCount-1; + SelInd = EntrieCount-PageSize; + } + else if(newSelected < PageSize && SelInd == 0 && diff < 0) + { + SelItem = std::max(SelItem+diff, (s32)0); + } + else if(EntrieCount-newSelected < PageSize && SelInd == EntrieCount-PageSize && diff > 0) + { + SelItem = std::min(SelItem+diff, PageSize-1); + } + else + { + SelInd = LIMIT(SelInd+diff, 0, ((EntrieCount-PageSize < 0) ? 0 : EntrieCount-PageSize)); + } + + ScrollState = 0; + listChanged(SelItem, SelInd); +} + +void Scrollbar::SetPageSize(s32 size) +{ + if(PageSize == size) + return; + + PageSize = size; + listChanged(SelItem, SelInd); +} + +void Scrollbar::SetSelectedItem(s32 pos) +{ + if(SelItem == pos) + return; + + SelItem = LIMIT(pos, 0, EntrieCount-1); + listChanged(SelItem, SelInd); +} + +void Scrollbar::SetSelectedIndex(s32 pos) +{ + if(SelInd == pos) + return; + + SelInd = pos; + listChanged(SelItem, SelInd); +} + +void Scrollbar::SetEntrieCount(s32 cnt) +{ + if(EntrieCount == cnt) + return; + + EntrieCount = cnt; + listChanged(SelItem, SelInd); +} + +void Scrollbar::setScrollboxPosition(s32 SelItem, s32 SelInd) +{ + s32 position = MaxHeight-(MaxHeight-MinHeight)*(SelInd+SelItem)/(EntrieCount-1); + + if(position < MinHeight || (SelInd+SelItem >= EntrieCount-1)) + position = MinHeight; + else if(position > MaxHeight || (SelInd+SelItem) == 0) + position = MaxHeight; + + scrollbarBoxBtn->setPosition(0, position); +} + +void Scrollbar::draw(CVideo * video) +{ + scrollbarLineImg->draw(video); + arrowUpBtn->draw(video); + arrowDownBtn->draw(video); + scrollbarBoxBtn->draw(video); + + updateEffects(); +} + +void Scrollbar::update(GuiController * t) +{ + if(this->isStateSet(STATE_DISABLED)) + return; + + arrowUpBtn->update(t); + arrowDownBtn->update(t); + scrollbarBoxBtn->update(t); + + ++ScrollState; +} + diff --git a/source/gui/GuiScrollbar.h b/source/gui/GuiScrollbar.h new file mode 100644 index 0000000..1900a03 --- /dev/null +++ b/source/gui/GuiScrollbar.h @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (C) 2011 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#ifndef SCROLLBAR_HPP_ +#define SCROLLBAR_HPP_ + +#include "gui/GuiElement.h" +#include "gui/GuiButton.h" + +class Scrollbar : public GuiElement, public sigslot::has_slots<> +{ + public: + Scrollbar(s32 height); + virtual ~Scrollbar(); + void ScrollOneUp(); + void ScrollOneDown(); + s32 GetSelectedItem() { return SelItem; } + s32 GetSelectedIndex() { return SelInd; } + void draw(CVideo * video); + void update(GuiController * t); + + //! Signals + sigslot::signal2 listChanged; + //! Slots + void SetPageSize(s32 size); + void SetRowSize(s32 size); + void SetSelectedItem(s32 pos); + void SetSelectedIndex(s32 pos); + void SetEntrieCount(s32 cnt); + protected: + void setScrollboxPosition(s32 SelItem, s32 SelInd); + void OnUpButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + void OnDownButtonClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + void OnBoxButtonHold(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + + u32 ScrollState; + u16 ScrollSpeed; + + s32 MinHeight; + s32 MaxHeight; + s32 SelItem; + s32 SelInd; + s32 PageSize; + s32 EntrieCount; + s32 pressedChan; + + GuiButton * arrowUpBtn; + GuiButton * arrowDownBtn; + GuiButton * scrollbarBoxBtn; + GuiImage * scrollbarLineImg; + GuiImage * arrowDownImg; + GuiImage * arrowUpImg; + GuiImage * scrollbarBoxImg; + GuiImageData * scrollbarLine; + GuiImageData * arrowDown; + GuiImageData * arrowUp; + GuiImageData * scrollbarBox; + GuiSound * btnSoundClick; + + GuiTrigger touchTrigger; + GuiTrigger wpadTouchTrigger; +}; + +#endif diff --git a/source/gui/GuiSelectBox.cpp b/source/gui/GuiSelectBox.cpp new file mode 100644 index 0000000..6ebccef --- /dev/null +++ b/source/gui/GuiSelectBox.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * + * 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 . + ****************************************************************************/ +#include +#include +#include "GuiSelectBox.h" +#include "GuiImage.h" +#include "GuiTrigger.h" +#include "GuiImageData.h" +#include "utils/StringTools.h" +/** + * Constructor for the GuiCheckBox class. + */ + +GuiSelectBox::GuiSelectBox(std::string caption,GuiFrame *parent) + : GuiFrame(300,300,parent) + ,selected(0) + ,captionText(caption) + ,topValueImageData(Resources::GetImageData("gameSettingsButton.png")) + ,topValueImage(topValueImageData) + ,topValueImageSelectedData(Resources::GetImageData("gameSettingsButtonSelected.png")) + ,topValueImageSelected(topValueImageSelectedData) + ,topValueButton(topValueImage.getWidth(),topValueImage.getHeight()) + ,valueImageData(Resources::GetImageData("gameSettingsButtonEx.png")) + ,valueSelectedImageData(Resources::GetImageData("gameSettingsButtonExSelected.png")) + ,valueHighlightedImageData(Resources::GetImageData("gameSettingsButtonExHighlighted.png")) + ,touchTrigger(GuiTrigger::CHANNEL_1, GuiTrigger::VPAD_TOUCH) + ,wpadTouchTrigger(GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5, GuiTrigger::BUTTON_A) + ,buttonATrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::BUTTON_A, true) + ,buttonBTrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::BUTTON_B, true) + ,buttonUpTrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::BUTTON_UP | GuiTrigger::STICK_L_UP, true) + ,buttonDownTrigger(GuiTrigger::CHANNEL_ALL, GuiTrigger::BUTTON_DOWN | GuiTrigger::STICK_L_DOWN, true) + ,DPADButtons(5,5) + ,buttonClickSound(Resources::GetSound("settings_click_2.mp3")) + { + showValues = false; + bChanged = false; + bSelectedChanged = false; + opened = false; + topValueText.setFontSize(32); + topValueText.setAlignment(ALIGN_LEFT); + topValueText.setPosition(10,-7); + topValueButton.setLabel(&topValueText); + topValueButton.setImage(&topValueImage); + topValueButton.setIconOver(&topValueImageSelected); + topValueButton.setTrigger(&touchTrigger); + topValueButton.setTrigger(&wpadTouchTrigger); + topValueButton.setSoundClick(buttonClickSound); + topValueButton.clicked.connect(this, &GuiSelectBox::OnTopValueClicked); + + valuesFrame.setState(STATE_HIDDEN); + + DPADButtons.setTrigger(&buttonBTrigger); + DPADButtons.setTrigger(&buttonATrigger); + DPADButtons.setTrigger(&buttonDownTrigger); + DPADButtons.setTrigger(&buttonUpTrigger); + DPADButtons.clicked.connect(this, &GuiSelectBox::OnDPADClick); + DPADButtons.setState(STATE_DISABLE_INPUT); + + append(&DPADButtons); + append(&valuesFrame); + append(&topValueButton); + + showValues = false; + bChanged = true; +} + +void GuiSelectBox::OnValueClicked(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) +{ + for(u32 i = 0; i < valueButtons.size(); ++i){ + if(valueButtons[i].valueButton == button){ + selected = i; + SelectValue(i); + break; + } + } +} + +void GuiSelectBox::SelectValue(u32 value){ + if(value < valueButtons.size()){ + const wchar_t* w_text = valueButtons[value].valueButtonText->getText(); + std::wstring ws(w_text); + std::string text(ws.begin(), ws.end()); + topValueText.setText(text.c_str()); + + std::string real_value = buttonToValue[valueButtons[value].valueButton]; + if(real_value.compare(std::string()) == 0) real_value = ""; + + valueChanged(this,real_value); + ShowHideValues(false); + } +} + +void GuiSelectBox::OnTopValueClicked(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) +{ + ShowHideValues(!showValues); +} + +void GuiSelectBox::ShowHideValues(bool showhide) +{ + showValues = showhide; + bChanged = true; +} + +void GuiSelectBox::OnDPADClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger) +{ + if(opened == true){ + if(trigger == &buttonATrigger) + { + //! do not auto launch when wiimote is pointing to screen and presses A + if((controller->chan & (GuiTrigger::CHANNEL_2 | GuiTrigger::CHANNEL_3 | GuiTrigger::CHANNEL_4 | GuiTrigger::CHANNEL_5)) && controller->data.validPointer) + { + return; + } + SelectValue(selected); + } + else if(trigger == &buttonBTrigger) + { + if(button == &DPADButtons){ + ShowHideValues(false); + }else{ + } + }else if(trigger == &buttonUpTrigger){ + if(selected > 0 ) selected--; + bSelectedChanged = true; + } + else if(trigger == &buttonDownTrigger){ + selected++; + if(selected >= valueButtons.size()) selected = valueButtons.size() - 1; + bSelectedChanged = true; + } + } +} + +void GuiSelectBox::Init(std::map values, s32 valueID) +{ + if((u32)valueID >= values.size()){ + valueID = 0; + } + + selected = valueID; + bSelectedChanged = true; + + DeleteValueData(); + + valueButtons.resize(values.size()); + + s32 i = 0; + f32 imgScale = 1.0f; + std::map::iterator itr; + for(itr = values.begin(); itr != values.end(); itr++) { + if(i == valueID){ + topValueText.setText(itr->first.c_str()); + } + + valueButtons[i].valueButtonImg = new GuiImage(valueImageData); + + valueButtons[i].valueButtonCheckedImg = new GuiImage(valueSelectedImageData); + valueButtons[i].valueButtonHighlightedImg = new GuiImage(valueHighlightedImageData); + valueButtons[i].valueButton = new GuiButton(valueButtons[i].valueButtonImg->getWidth() * imgScale, valueButtons[i].valueButtonImg->getHeight() * imgScale); + valueButtons[i].valueButtonText = new GuiText(itr->first.c_str(),32,glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); + + valueButtons[i].valueButtonText->setMaxWidth(valueButtons[i].valueButtonImg->getWidth() * imgScale - 20.0f, GuiText::WRAP); + valueButtons[i].valueButtonText->setPosition(0, 0); + + valueButtons[i].valueButtonImg->setScale(imgScale); + valueButtons[i].valueButtonCheckedImg->setScale(imgScale); + + valueButtons[i].valueButton->setImage(valueButtons[i].valueButtonImg); + valueButtons[i].valueButton->setIconOver(valueButtons[i].valueButtonHighlightedImg); + valueButtons[i].valueButton->setTrigger(&touchTrigger); + valueButtons[i].valueButton->setTrigger(&wpadTouchTrigger); + valueButtons[i].valueButton->clicked.connect(this,&GuiSelectBox::OnValueClicked); + valueButtons[i].valueButton->setSoundClick(buttonClickSound); + valueButtons[i].valueButton->setLabel(valueButtons[i].valueButtonText); + + //valueButtons[i].valueButton->setState(STATE_HIDDEN); //Wont get disabled soon enough + + buttonToValue[valueButtons[i].valueButton] = itr->second; + s32 ypos = (((valueButtons[i].valueButtonImg->getHeight()*getScale()) * (i))+ (topValueImage.getHeight()-5)*getScale())*-1.0f; + valueButtons[i].valueButton->setPosition(0, ypos); + valuesFrame.append(valueButtons[i].valueButton); + + i++; + } + + //Collapse the thing! + showValues = false; + bChanged = true; +} + +void GuiSelectBox::DeleteValueData() +{ + for(u32 i = 0; i < valueButtons.size(); ++i) + { + valuesFrame.remove(valueButtons[i].valueButton); + delete valueButtons[i].valueButtonImg; + delete valueButtons[i].valueButtonCheckedImg; + delete valueButtons[i].valueButtonHighlightedImg; + delete valueButtons[i].valueButton; + delete valueButtons[i].valueButtonText; + } + buttonToValue.clear(); + valueButtons.clear(); +} + +/** + * Destructor for the GuiButton class. + */ +GuiSelectBox::~GuiSelectBox() +{ + DeleteValueData(); + bChanged = false; + selected = 0; + showValues = false; + Resources::RemoveSound(buttonClickSound); + Resources::RemoveImageData(topValueImageData); + Resources::RemoveImageData(topValueImageSelectedData); + Resources::RemoveImageData(valueImageData); + Resources::RemoveImageData(valueHighlightedImageData); + Resources::RemoveImageData(valueSelectedImageData); +} + + +void GuiSelectBox::setState(s32 s, s32 c) +{ + GuiElement::setState(s, c); +} + +void GuiSelectBox::OnValueCloseEffectFinish(GuiElement *element) +{ + valuesFrame.effectFinished.disconnect(this); +} + +f32 GuiSelectBox::getTopValueHeight() { + return topValueImage.getHeight(); +} + +f32 GuiSelectBox::getTopValueWidth() { + return topValueImage.getWidth(); +} + +f32 GuiSelectBox::getHeight(){ + return getTopValueHeight(); +} + +f32 GuiSelectBox::getWidth(){ + return getTopValueWidth(); +} + + +void GuiSelectBox::OnValueOpenEffectFinish(GuiElement *element) +{ + valuesFrame.effectFinished.disconnect(this); + opened = true; +} + +void GuiSelectBox::update(GuiController * c){ + if(bChanged){ + showhide(this,showValues); + if(showValues){ + for(u32 i = 0; i < valueButtons.size(); ++i){ //TODO: only set when it really changed + if(i == selected){ + valueButtons[i].valueButton->setImage(valueButtons[i].valueButtonCheckedImg); + }else{ + valueButtons[i].valueButton->setImage(valueButtons[i].valueButtonImg); + } + } + valuesFrame.clearState(STATE_HIDDEN); + DPADButtons.clearState(STATE_DISABLE_INPUT); + valuesFrame.setEffect(EFFECT_FADE, 10, 255); + valuesFrame.effectFinished.connect(this, &GuiSelectBox::OnValueCloseEffectFinish); + }else{ + opened = false; + valuesFrame.setState(STATE_HIDDEN); + DPADButtons.setState(STATE_DISABLE_INPUT); + valuesFrame.setEffect(EFFECT_FADE, -10, 0); + valuesFrame.effectFinished.connect(this, &GuiSelectBox::OnValueOpenEffectFinish); + } + + bChanged = false; + } + if(bSelectedChanged){ + for(u32 i = 0; i < valueButtons.size(); ++i){ + if(i == selected){ + valueButtons[i].valueButton->setState(STATE_SELECTED); + }else{ + valueButtons[i].valueButton->clearState(STATE_SELECTED); + } + } + } + topValueButton.setState(getState()); + GuiFrame::update(c); +} diff --git a/source/gui/GuiSelectBox.h b/source/gui/GuiSelectBox.h new file mode 100644 index 0000000..e3b3082 --- /dev/null +++ b/source/gui/GuiSelectBox.h @@ -0,0 +1,104 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * + * 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 . + ****************************************************************************/ +#ifndef GUI_SELECTBOX_H_ +#define GUI_SELECTBOX_H_ + +#include "Gui.h" +#include "GuiImage.h" +#include "GuiImageData.h" + +//!A simple CheckBox +class GuiSelectBox : public GuiFrame, public sigslot::has_slots<>{ + public: + //!Constructor + //!\param checked Checked + GuiSelectBox(std::string caption,GuiFrame *parent = 0); + //!Destructor + virtual ~GuiSelectBox(); + + sigslot::signal2 valueChanged; + sigslot::signal2 showhide; + void OnTopValueClicked(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + void Init(std::map values, s32 valueID); + + void setState(s32 s, s32 c = -1); + + virtual f32 getTopValueHeight(); + virtual f32 getTopValueWidth(); + + virtual f32 getHeight(); + virtual f32 getWidth(); + + protected: + void DeleteValueData(); + void update(GuiController * c); + + void OnValueClicked(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + + void OnDPADClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + void OnValueOpenEffectFinish(GuiElement *element); + void OnValueCloseEffectFinish(GuiElement *element); + void ShowHideValues(bool showhide); + void SelectValue(u32 value); + + u32 selected; + bool bChanged; + bool bSelectedChanged; + bool showValues; + bool opened; + std::string captionText; + GuiFrame valuesFrame; + GuiImageData *topValueImageData; + GuiImage topValueImage; + GuiImageData *topValueImageSelectedData; + GuiImage topValueImageSelected; + + GuiButton topValueButton; + GuiImageData * valueImageData; + GuiImageData * valueSelectedImageData; + GuiImageData * valueHighlightedImageData; + GuiText topValueText; + + GuiTrigger touchTrigger; + GuiTrigger wpadTouchTrigger; + + GuiTrigger buttonATrigger; + GuiTrigger buttonBTrigger; + GuiTrigger buttonLeftTrigger; + GuiTrigger buttonRightTrigger; + GuiTrigger buttonUpTrigger; + GuiTrigger buttonDownTrigger; + + GuiButton DPADButtons; + + GuiSound* buttonClickSound; + + typedef struct + { + GuiImage *valueButtonImg; + GuiImage *valueButtonCheckedImg; + GuiImage *valueButtonHighlightedImg; + GuiButton *valueButton; + GuiText *valueButtonText; + } SelectBoxValueButton; + + std::map buttonToValue; + std::vector valueButtons; + +}; + +#endif diff --git a/source/gui/GuiSound.cpp b/source/gui/GuiSound.cpp new file mode 100644 index 0000000..ac5f4f6 --- /dev/null +++ b/source/gui/GuiSound.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiSound.h" +#include "sounds/SoundHandler.hpp" +#include + +GuiSound::GuiSound(const char * filepath) +{ + voice = -1; + Load(filepath); +} + +GuiSound::GuiSound(const u8 * snd, s32 length) +{ + voice = -1; + Load(snd, length); +} + +GuiSound::~GuiSound() +{ + if(voice >= 0) + { + SoundHandler::instance()->RemoveDecoder(voice); + } +} + + +bool GuiSound::Load(const char * filepath) +{ + if(voice >= 0) + { + SoundHandler::instance()->RemoveDecoder(voice); + voice = -1; + } + + //! find next free decoder + for(s32 i = 0; i < MAX_DECODERS; i++) + { + SoundDecoder * decoder = SoundHandler::instance()->getDecoder(i); + if(decoder == NULL) + { + SoundHandler::instance()->AddDecoder(i, filepath); + decoder = SoundHandler::instance()->getDecoder(i); + if(decoder) + { + voice = i; + SoundHandler::instance()->ThreadSignal(); + } + break; + } + } + + if(voice < 0){ + return false; + } + + return true; +} + +bool GuiSound::Load(const u8 * snd, s32 len) +{ + if(voice >= 0) + { + SoundHandler::instance()->RemoveDecoder(voice); + voice = -1; + } + + if(!snd) + return false; + + //! find next free decoder + for(s32 i = 0; i < MAX_DECODERS; i++) + { + SoundDecoder * decoder = SoundHandler::instance()->getDecoder(i); + if(decoder == NULL) + { + SoundHandler::instance()->AddDecoder(i, snd, len); + decoder = SoundHandler::instance()->getDecoder(i); + if(decoder) + { + voice = i; + SoundHandler::instance()->ThreadSignal(); + } + break; + } + } + + if(voice < 0){ + return false; + } + + return true; +} + +void GuiSound::Play() +{ + Stop(); + + Voice * v = SoundHandler::instance()->getVoice(voice); + if(v) + v->setState(Voice::STATE_START); + + +} + +void GuiSound::Stop() +{ + Voice * v = SoundHandler::instance()->getVoice(voice); + if(v) + { + if((v->getState() != Voice::STATE_STOP) && (v->getState() != Voice::STATE_STOPPED)) + v->setState(Voice::STATE_STOP); + + while(v->getState() != Voice::STATE_STOPPED) + os_usleep(1000); + } + + SoundDecoder * decoder = SoundHandler::instance()->getDecoder(voice); + if(decoder) + { + decoder->Lock(); + decoder->Rewind(); + decoder->ClearBuffer(); + SoundHandler::instance()->ThreadSignal(); + decoder->Unlock(); + } +} + +void GuiSound::Pause() +{ + if(!IsPlaying()) + return; + + Voice * v = SoundHandler::instance()->getVoice(voice); + if(v) + v->setState(Voice::STATE_STOP); +} + +void GuiSound::Resume() +{ + if(IsPlaying()) + return; + + Voice * v = SoundHandler::instance()->getVoice(voice); + if(v) + v->setState(Voice::STATE_START); +} + +bool GuiSound::IsPlaying() +{ + Voice * v = SoundHandler::instance()->getVoice(voice); + if(v){ + return v->getState() == Voice::STATE_PLAYING; + } + return false; +} + +void GuiSound::SetVolume(u32 vol) +{ + if(vol > 100) + vol = 100; + + u32 volumeConv = ( (0x8000 * vol) / 100 ) << 16; + + Voice * v = SoundHandler::instance()->getVoice(voice); + if(v) + v->setVolume(volumeConv); +} + +void GuiSound::SetLoop(bool l) +{ + SoundDecoder * decoder = SoundHandler::instance()->getDecoder(voice); + if(decoder) + decoder->SetLoop(l); +} + +void GuiSound::Rewind() +{ + Stop(); +} diff --git a/source/gui/GuiSound.h b/source/gui/GuiSound.h new file mode 100644 index 0000000..495a67a --- /dev/null +++ b/source/gui/GuiSound.h @@ -0,0 +1,60 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_SOUND_H_ +#define GUI_SOUND_H_ + + +#include + +//!Sound conversion and playback. A wrapper for other sound libraries - ASND, libmad, ltremor, etc +class GuiSound : public AsyncDeleter::Element +{ + public: + //!Constructor + //!\param sound Pointer to the sound data + //!\param filesize Length of sound data + GuiSound(const char * filepath); + GuiSound(const u8 * sound, s32 length); + //!Destructor + virtual ~GuiSound(); + //!Load a file and replace the old one + bool Load(const char * filepath); + //!Load a file and replace the old one + bool Load(const u8 * snd, s32 len); + //!Start sound playback + void Play(); + //!Stop sound playback + void Stop(); + //!Pause sound playback + void Pause(); + //!Resume sound playback + void Resume(); + //!Checks if the sound is currently playing + //!\return true if sound is playing, false otherwise + bool IsPlaying(); + //!Rewind the music + void Rewind(); + //!Set sound volume + //!\param v Sound volume (0-100) + void SetVolume(u32 v); + //!\param l Loop (true to loop) + void SetLoop(bool l); + protected: + s32 voice; //!< Currently assigned ASND voice channel +}; + +#endif diff --git a/source/gui/GuiSwitch.cpp b/source/gui/GuiSwitch.cpp new file mode 100644 index 0000000..64cf66a --- /dev/null +++ b/source/gui/GuiSwitch.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * 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 . + ****************************************************************************/ +#include "GuiSwitch.h" +#include "GuiImage.h" +#include "GuiImageData.h" +#include +/** + * Constructor for the GuiSwitch class. + */ + +GuiSwitch::GuiSwitch(bool checked,f32 w, f32 h) + : GuiToggle(checked,w,h){ + +} +/** + * Destructor for the GuiSwitch class. + */ +GuiSwitch::~GuiSwitch(){ + +} + +void GuiSwitch::setImageBackground(GuiImage* img){ + backgroundImg = img; + if(img){ + img->setParent(this); + + } + setImage(img); +} + +void GuiSwitch::setImageOn(GuiImage* img){ + onImg = img; + if(img){ + img->setParent(this); + img->setAlignment(ALIGN_RIGHT); + } +} + +void GuiSwitch::setImageOff(GuiImage* img){ + offImg = img; + if(img){ + img->setParent(this); + img->setAlignment(ALIGN_LEFT); + } +} + +void GuiSwitch::setImageHighlighted(GuiImage* img){ + highlightedImg = img; + setIconOver(img); +} + +void GuiSwitch::draw(CVideo *v){ + GuiToggle::draw(v); + if(getValue()){ + if(onImg != NULL){ onImg->draw(v); } + }else{ + if(offImg != NULL){ offImg->draw(v); } + } +} diff --git a/source/gui/GuiSwitch.h b/source/gui/GuiSwitch.h new file mode 100644 index 0000000..054d22c --- /dev/null +++ b/source/gui/GuiSwitch.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * 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 . + ****************************************************************************/ +#ifndef GUI_SWTICH_H_ +#define GUI_SWTICH_H_ + +#include "GuiToggle.h" +#include "GuiImage.h" +#include "GuiImageData.h" + +//!A simple switch +class GuiSwitch : public GuiToggle{ + public: + //!Constructor + //!\param checked Checked + GuiSwitch(bool checked,f32 w = 0.0f, f32 h = 0.0f); + //!Destructor + virtual ~GuiSwitch(); + + void setImageBackground(GuiImage* img); + + void setImageOn(GuiImage* img); + + void setImageOff(GuiImage* img); + + void setImageHighlighted(GuiImage* img); + + protected: + + GuiImage * backgroundImg; + GuiImage * onImg; + GuiImage * offImg; + GuiImage * highlightedImg; + + void draw(CVideo * v); +}; + +#endif diff --git a/source/gui/GuiText.cpp b/source/gui/GuiText.cpp new file mode 100644 index 0000000..e9a9673 --- /dev/null +++ b/source/gui/GuiText.cpp @@ -0,0 +1,615 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiText.h" +#include "FreeTypeGX.h" +#include "video/CVideo.h" + +FreeTypeGX * GuiText::presentFont = NULL; +s32 GuiText::presetSize = 28; +float GuiText::presetInternalRenderingScale = 2.0f; //Lets render the font at the doubled size. This make it even smoother! +s32 GuiText::presetMaxWidth = 0xFFFF; +s32 GuiText::presetAlignment = ALIGN_CENTER | ALIGN_MIDDLE; +GX2ColorF32 GuiText::presetColor = (GX2ColorF32){ 1.0f, 1.0f, 1.0f, 1.0f }; + +#define TEXT_SCROLL_DELAY 6 +#define TEXT_SCROLL_INITIAL_DELAY 10 +#define MAX_LINES_TO_DRAW 10 + +/** + * Constructor for the GuiText class. + */ + +GuiText::GuiText() +{ + text = NULL; + size = presetSize; + currentSize = size; + color = glm::vec4(presetColor.r, presetColor.g, presetColor.b, presetColor.a); + alpha = presetColor.a; + alignment = presetAlignment; + maxWidth = presetMaxWidth; + wrapMode = 0; + textWidth = 0; + font = presentFont; + linestodraw = MAX_LINES_TO_DRAW; + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + textScrollDelay = TEXT_SCROLL_DELAY; + defaultBlur = 4.0f; + blurGlowIntensity = 0.0f; + blurAlpha = 0.0f; + blurGlowColor = glm::vec4(0.0f); + internalRenderingScale = presetInternalRenderingScale; +} + +GuiText::GuiText(const char * t, s32 s, const glm::vec4 & c) +{ + text = NULL; + size = s; + currentSize = size; + color = c; + alpha = c[3]; + alignment = ALIGN_CENTER | ALIGN_MIDDLE; + maxWidth = presetMaxWidth; + wrapMode = 0; + textWidth = 0; + font = presentFont; + linestodraw = MAX_LINES_TO_DRAW; + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + textScrollDelay = TEXT_SCROLL_DELAY; + defaultBlur = 4.0f; + blurGlowIntensity = 0.0f; + blurAlpha = 0.0f; + blurGlowColor = glm::vec4(0.0f); + internalRenderingScale = presetInternalRenderingScale; + + if(t) + { + text = FreeTypeGX::charToWideChar(t); + if(!text) + return; + + textWidth = font->getWidth(text, currentSize); + } +} + +GuiText::GuiText(const wchar_t * t, s32 s, const glm::vec4 & c) +{ + text = NULL; + size = s; + currentSize = size; + color = c; + alpha = c[3]; + alignment = ALIGN_CENTER | ALIGN_MIDDLE; + maxWidth = presetMaxWidth; + wrapMode = 0; + textWidth = 0; + font = presentFont; + linestodraw = MAX_LINES_TO_DRAW; + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + textScrollDelay = TEXT_SCROLL_DELAY; + defaultBlur = 4.0f; + blurGlowIntensity = 0.0f; + blurAlpha = 0.0f; + blurGlowColor = glm::vec4(0.0f); + internalRenderingScale = presetInternalRenderingScale; + + if(t) + { + text = new (std::nothrow) wchar_t[wcslen(t)+1]; + if(!text) + return; + + wcscpy(text, t); + + textWidth = font->getWidth(text, currentSize); + } +} + +/** + * Constructor for the GuiText class, uses presets + */ +GuiText::GuiText(const char * t) +{ + text = NULL; + size = presetSize; + currentSize = size; + color = glm::vec4(presetColor.r, presetColor.g, presetColor.b, presetColor.a); + alpha = presetColor.a; + alignment = presetAlignment; + maxWidth = presetMaxWidth; + wrapMode = 0; + textWidth = 0; + font = presentFont; + linestodraw = MAX_LINES_TO_DRAW; + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + textScrollDelay = TEXT_SCROLL_DELAY; + defaultBlur = 4.0f; + blurGlowIntensity = 0.0f; + blurAlpha = 0.0f; + blurGlowColor = glm::vec4(0.0f); + internalRenderingScale = presetInternalRenderingScale; + + if(t) + { + text = FreeTypeGX::charToWideChar(t); + if(!text) + return; + + textWidth = font->getWidth(text, currentSize); + } +} + + +/** + * Destructor for the GuiText class. + */ +GuiText::~GuiText() +{ + if(text) + delete [] text; + text = NULL; + + clearDynamicText(); +} + +void GuiText::setText(const char * t) +{ + if(text) + delete [] text; + text = NULL; + + clearDynamicText(); + + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + + if(t) + { + text = FreeTypeGX::charToWideChar(t); + if(!text) + return; + + textWidth = font->getWidth(text, currentSize); + } +} + +void GuiText::setTextf(const char *format, ...) +{ + if(!format) + { + setText((char *) NULL); + return; + } + + s32 max_len = strlen(format) + 8192; + char *tmp = new char[max_len]; + va_list va; + va_start(va, format); + if((vsnprintf(tmp, max_len, format, va) >= 0) && tmp) + { + setText(tmp); + } + va_end(va); + + if(tmp) + delete [] tmp; +} + + +void GuiText::setText(const wchar_t * t) +{ + if(text) + delete [] text; + text = NULL; + + clearDynamicText(); + + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + + if(t) + { + text = new (std::nothrow) wchar_t[wcslen(t)+1]; + if(!text) + return; + + wcscpy(text, t); + + textWidth = font->getWidth(text, currentSize); + } +} + +void GuiText::clearDynamicText() +{ + for(u32 i = 0; i < textDyn.size(); i++) + { + if(textDyn[i]) + delete [] textDyn[i]; + } + textDyn.clear(); + textDynWidth.clear(); +} + +void GuiText::setPresets(s32 sz, const glm::vec4 & c, s32 w, s32 a) +{ + presetSize = sz; + presetColor = (GX2ColorF32) { (f32)c.r / 255.0f, (f32)c.g / 255.0f, (f32)c.b / 255.0f, (f32)c.a / 255.0f }; + presetMaxWidth = w; + presetAlignment = a; +} + +void GuiText::setPresetFont(FreeTypeGX *f) +{ + presentFont = f; +} + +void GuiText::setFontSize(s32 s) +{ + size = s; +} + +void GuiText::setMaxWidth(s32 width, s32 w) +{ + maxWidth = width; + wrapMode = w; + + if(w == SCROLL_HORIZONTAL) + { + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + textScrollDelay = TEXT_SCROLL_DELAY; + } + + clearDynamicText(); +} + +void GuiText::setColor(const glm::vec4 & c) +{ + color = c; + alpha = c[3]; +} + +void GuiText::setBlurGlowColor(float blur, const glm::vec4 & c) +{ + blurGlowColor = c; + blurGlowIntensity = blur; + blurAlpha = c[3]; +} + +s32 GuiText::getTextWidth(s32 ind) +{ + if(ind < 0 || ind >= (s32) textDyn.size()) + return this->getTextWidth(); + + return font->getWidth(textDyn[ind], currentSize); +} + +const wchar_t * GuiText::getDynText(s32 ind) +{ + if(ind < 0 || ind >= (s32) textDyn.size()) + return text; + + return textDyn[ind]; +} + +/** + * Change font + */ +bool GuiText::setFont(FreeTypeGX *f) +{ + if(!f) + return false; + + font = f; + textWidth = font->getWidth(text, currentSize); + return true; +} + +std::string GuiText::toUTF8(void) const +{ + if(!text) + return std::string(); + + char *pUtf8 = FreeTypeGX::wideCharToUTF8(text); + if(!pUtf8) + return std::string(); + + std::string strOutput(pUtf8); + + delete [] pUtf8; + + return strOutput; +} + +void GuiText::makeDottedText() +{ + s32 pos = textDyn.size(); + textDyn.resize(pos + 1); + + s32 i = 0, currentWidth = 0; + textDyn[pos] = new (std::nothrow) wchar_t[maxWidth]; + if(!textDyn[pos]) { + textDyn.resize(pos); + return; + } + + while (text[i]) + { + currentWidth += font->getCharWidth(text[i], currentSize, i > 0 ? text[i - 1] : 0); + if (currentWidth >= maxWidth && i > 2) + { + textDyn[pos][i - 2] = '.'; + textDyn[pos][i - 1] = '.'; + textDyn[pos][i] = '.'; + i++; + break; + } + + textDyn[pos][i] = text[i]; + + i++; + } + textDyn[pos][i] = 0; +} + +void GuiText::scrollText(u32 frameCount) +{ + if (textDyn.size() == 0) + { + s32 pos = textDyn.size(); + s32 i = 0, currentWidth = 0; + textDyn.resize(pos + 1); + + textDyn[pos] = new (std::nothrow) wchar_t[maxWidth]; + if(!textDyn[pos]) { + textDyn.resize(pos); + return; + } + + while (text[i] && currentWidth < maxWidth) + { + textDyn[pos][i] = text[i]; + + currentWidth += font->getCharWidth(text[i], currentSize, i > 0 ? text[i - 1] : 0); + + ++i; + } + textDyn[pos][i] = 0; + + return; + } + + if (frameCount % textScrollDelay != 0) + { + return; + } + + if (textScrollInitialDelay) + { + --textScrollInitialDelay; + return; + } + + s32 stringlen = wcslen(text); + + ++textScrollPos; + if (textScrollPos > stringlen) + { + textScrollPos = 0; + textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; + } + + s32 ch = textScrollPos; + s32 pos = textDyn.size() - 1; + + if (!textDyn[pos]) + textDyn[pos] = new (std::nothrow) wchar_t[maxWidth]; + + if(!textDyn[pos]) { + textDyn.resize(pos); + return; + } + + s32 i = 0, currentWidth = 0; + + while (currentWidth < maxWidth) + { + if (ch > stringlen - 1) + { + textDyn[pos][i++] = ' '; + currentWidth += font->getCharWidth(L' ', currentSize, ch > 0 ? text[ch - 1] : 0); + textDyn[pos][i++] = ' '; + currentWidth += font->getCharWidth(L' ', currentSize, L' '); + textDyn[pos][i++] = ' '; + currentWidth += font->getCharWidth(L' ', currentSize, L' '); + ch = 0; + + if(currentWidth >= maxWidth) + break; + } + + textDyn[pos][i] = text[ch]; + currentWidth += font->getCharWidth(text[ch], currentSize, ch > 0 ? text[ch - 1] : 0); + ++ch; + ++i; + } + textDyn[pos][i] = 0; +} + +void GuiText::wrapText() +{ + if (textDyn.size() > 0) return; + + s32 i = 0; + s32 ch = 0; + s32 linenum = 0; + s32 lastSpace = -1; + s32 lastSpaceIndex = -1; + s32 currentWidth = 0; + + while (text[ch] && linenum < linestodraw) + { + if (linenum >= (s32) textDyn.size()) + { + textDyn.resize(linenum + 1); + textDyn[linenum] = new (std::nothrow) wchar_t[maxWidth]; + if(!textDyn[linenum]) { + textDyn.resize(linenum); + break; + } + } + + textDyn[linenum][i] = text[ch]; + textDyn[linenum][i + 1] = 0; + + currentWidth += font->getCharWidth(text[ch], currentSize, ch > 0 ? text[ch - 1] : 0x0000); + + if (currentWidth >= maxWidth || (text[ch] == '\n')) + { + if(text[ch] == '\n') + { + lastSpace = -1; + lastSpaceIndex = -1; + } + else if (lastSpace >= 0) + { + textDyn[linenum][lastSpaceIndex] = 0; // discard space, and everything after + ch = lastSpace; // go backwards to the last space + lastSpace = -1; // we have used this space + lastSpaceIndex = -1; + } + + if (linenum + 1 == linestodraw && text[ch + 1] != 0x0000) + { + if(i < 2) + i = 2; + + textDyn[linenum][i - 2] = '.'; + textDyn[linenum][i - 1] = '.'; + textDyn[linenum][i] = '.'; + textDyn[linenum][i + 1] = 0; + } + + currentWidth = 0; + ++linenum; + i = -1; + } + if (text[ch] == ' ' && i >= 0) + { + lastSpace = ch; + lastSpaceIndex = i; + } + ++ch; + ++i; + } +} + +/** + * Draw the text on screen + */ +void GuiText::draw(CVideo *pVideo) +{ + if(!text) + return; + + if(!isVisible()) + return; + + color[3] = getAlpha(); + blurGlowColor[3] = blurAlpha * getAlpha(); + + float finalRenderingScale = 2.0f * internalRenderingScale; + + s32 newSize = size * getScale() * finalRenderingScale; + s32 normal_size = size * getScale(); + + if(newSize != currentSize) + { + currentSize = normal_size; + + if(text) + textWidth = font->getWidth(text, normal_size); + } + + f32 x_pos = getCenterX() * finalRenderingScale; + f32 y_pos = getCenterY() * finalRenderingScale; + + if(maxWidth > 0 && maxWidth <= textWidth) + { + if(wrapMode == DOTTED) // text dotted + { + if(textDyn.size() == 0) + makeDottedText(); + + if(textDynWidth.size() != textDyn.size()) + { + textDynWidth.resize(textDyn.size()); + + for(u32 i = 0; i < textDynWidth.size(); i++) + textDynWidth[i] = font->getWidth(textDyn[i], newSize); + } + + + if(textDyn.size() > 0) + font->drawText(pVideo, x_pos, y_pos, getDepth(), textDyn[textDyn.size()-1], newSize, color, alignment, textDynWidth[textDyn.size()-1], defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); + } + + else if(wrapMode == SCROLL_HORIZONTAL) + { + scrollText(pVideo->getFrameCount()); + + if(textDyn.size() > 0) + font->drawText(pVideo, x_pos, y_pos, getDepth(), textDyn[textDyn.size()-1], newSize, color, alignment, maxWidth*finalRenderingScale, defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); + + } + else if(wrapMode == WRAP) + { + s32 lineheight = newSize + 6; + s32 yoffset = 0; + s32 voffset = 0; + + if(textDyn.size() == 0) + wrapText(); + + if(textDynWidth.size() != textDyn.size()) + { + textDynWidth.resize(textDyn.size()); + + for(u32 i = 0; i < textDynWidth.size(); i++) + textDynWidth[i] = font->getWidth(textDyn[i], newSize); + } + + if(alignment & ALIGN_MIDDLE) + voffset = (lineheight * (textDyn.size()-1)) >> 1; + + for(u32 i = 0; i < textDyn.size(); i++) + { + font->drawText(pVideo, x_pos, y_pos + voffset + yoffset, getDepth(), textDyn[i], newSize, color, alignment, textDynWidth[i], defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); + yoffset -= lineheight; + } + } + } + else + { + uint16_t newtextWidth = font->getWidth(text, newSize); + font->drawText(pVideo, x_pos, y_pos, getDepth(), text, newSize, color, alignment, newtextWidth, defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); + } +} diff --git a/source/gui/GuiText.h b/source/gui/GuiText.h new file mode 100644 index 0000000..aaf4704 --- /dev/null +++ b/source/gui/GuiText.h @@ -0,0 +1,141 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_TEXT_H_ +#define GUI_TEXT_H_ + +#include "GuiElement.h" +//!Forward declaration +class FreeTypeGX; + +//!Display, manage, and manipulate text in the GUI +class GuiText : public GuiElement +{ +public: + //!Constructor + GuiText(); + //!\param t Text + //!\param s Font size + //!\param c Font color + GuiText(const char * t, s32 s, const glm::vec4 & c); + //!\overload + //!\param t Text + //!\param s Font size + //!\param c Font color + GuiText(const wchar_t * t, s32 s, const glm::vec4 & c); + //!\overload + //!\Assumes SetPresets() has been called to setup preferred text attributes + //!\param t Text + GuiText(const char * t); + //!Destructor + virtual ~GuiText(); + //!Sets the text of the GuiText element + //!\param t Text + virtual void setText(const char * t); + virtual void setText(const wchar_t * t); + virtual void setTextf(const char *format, ...) __attribute__((format(printf,2,3))); + //!Sets up preset values to be used by GuiText(t) + //!Useful when printing multiple text elements, all with the same attributes set + //!\param sz Font size + //!\param c Font color + //!\param w Maximum width of texture image (for text wrapping) + //!\param wrap Wrapmode when w>0 + //!\param a Text alignment + static void setPresets(s32 sz, const glm::vec4 & c, s32 w, s32 a); + static void setPresetFont(FreeTypeGX *font); + //!Sets the font size + //!\param s Font size + void setFontSize(s32 s); + //!Sets the maximum width of the drawn texture image + //!If the text exceeds this, it is wrapped to the next line + //!\param w Maximum width + //!\param m WrapMode + void setMaxWidth(s32 w = 0, s32 m = WRAP); + //!Sets the font color + //!\param c Font color + void setColor(const glm::vec4 & c); + + void setBlurGlowColor(float blurIntensity, const glm::vec4 & c); + + void setTextBlur(float blur) { defaultBlur = blur; } + //!Get the original text as char + virtual const wchar_t * getText() const { return text; } + virtual std::string toUTF8(void) const; + //!Get the Horizontal Size of Text + s32 getTextWidth() { return textWidth; } + s32 getTextWidth(s32 ind); + //!Get the max textwidth + s32 getTextMaxWidth() { return maxWidth; } + //!Get fontsize + s32 getFontSize() { return size; }; + //!Set max lines to draw + void setLinesToDraw(s32 l) { linestodraw = l; } + //!Get current Textline (for position calculation) + const wchar_t * getDynText(s32 ind = 0); + virtual const wchar_t * getTextLine(s32 ind) { return getDynText(ind); }; + //!Change the font + bool setFont(FreeTypeGX *font); + //! virtual function used in child classes + virtual s32 getStartWidth() { return 0; }; + //!Constantly called to draw the text + void draw(CVideo *pVideo); + //! text enums + enum + { + WRAP, + DOTTED, + SCROLL_HORIZONTAL, + SCROLL_NONE + }; +protected: + static FreeTypeGX * presentFont; + static s32 presetSize; + static s32 presetMaxWidth; + static float presetInternalRenderingScale; + static s32 presetAlignment; + static GX2ColorF32 presetColor; + + //!Clear the dynamic text + void clearDynamicText(); + //!Create a dynamic dotted text if the text is too long + void makeDottedText(); + //!Scroll the text once + void scrollText(u32 frameCount); + //!Wrap the text to several lines + void wrapText(); + + wchar_t * text; + std::vector textDyn; + std::vector textDynWidth; + s32 wrapMode; //!< Wrapping toggle + s32 textScrollPos; //!< Current starting index of text string for scrolling + s32 textScrollInitialDelay; //!< Delay to wait before starting to scroll + s32 textScrollDelay; //!< Scrolling speed + s32 size; //!< Font size + s32 maxWidth; //!< Maximum width of the generated text object (for text wrapping) + FreeTypeGX *font; + s32 textWidth; + s32 currentSize; + s32 linestodraw; + glm::vec4 color; + float defaultBlur; + float blurGlowIntensity; + float blurAlpha; + glm::vec4 blurGlowColor; + float internalRenderingScale; +}; + +#endif diff --git a/source/gui/GuiToggle.cpp b/source/gui/GuiToggle.cpp new file mode 100644 index 0000000..c59eb6b --- /dev/null +++ b/source/gui/GuiToggle.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * + * 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 . + ****************************************************************************/ +#include "GuiToggle.h" +/** + * Constructor for the GuiToggle class. + */ + +GuiToggle::GuiToggle(bool checked,f32 width,f32 height) + : GuiButton(width,height) +{ + bChanged = false; + selected = checked; + clicked.connect(this,&GuiToggle::OnToggleClick); +} + +/** + * Destructor for the GuiButton class. + */ +GuiToggle::~GuiToggle() +{ + bChanged = false; + selected = false; +} + +void GuiToggle::OnToggleClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger){ + if(!isStateSet(STATE_DISABLED | STATE_HIDDEN | STATE_DISABLE_INPUT)){ + if(selected){ + setUnchecked(); + }else{ + setChecked(); + } + } +} + +void GuiToggle::update(GuiController * c){ + GuiButton::update(c); +} + diff --git a/source/gui/GuiToggle.h b/source/gui/GuiToggle.h new file mode 100644 index 0000000..c6dc213 --- /dev/null +++ b/source/gui/GuiToggle.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * Copyright (C) 2016 Maschell + * + * 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 . + ****************************************************************************/ +#ifndef GUI_TOGGLE_H_ +#define GUI_TOGGLE_H_ + +#include "GuiButton.h" +#include "GuiFrame.h" + +//!A simple CheckBox +class GuiToggle : public GuiButton, public sigslot::has_slots<> +{ + public: + //!Constructor + //!\param checked Checked + GuiToggle(bool checked,f32 width,f32 height); + //!Destructor + virtual ~GuiToggle(); + void setValue(bool checked){ + if(selected != checked){ + selected = checked; + bChanged=true; + valueChanged(this,selected); + } + } + void setChecked(){ + setValue(true); + + } + void setUnchecked(){ + setValue(false); + } + bool getValue(){ + return selected; + } + sigslot::signal2 valueChanged; + void OnToggleClick(GuiButton *button, const GuiController *controller, GuiTrigger *trigger); + protected: + + bool selected; + bool bChanged; + + void update(GuiController * c); +}; + +#endif diff --git a/source/gui/GuiTrigger.cpp b/source/gui/GuiTrigger.cpp new file mode 100644 index 0000000..1d1c5f6 --- /dev/null +++ b/source/gui/GuiTrigger.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include "GuiElement.h" +#include "GuiController.h" +#include "GuiTrigger.h" + +/** + * Constructor for the GuiTrigger class. + */ +GuiTrigger::GuiTrigger() + : chan(CHANNEL_ALL) + , btns(BUTTON_NONE) + , bClickEverywhere(false) + , bHoldEverywhere(false) + , bSelectionClickEverywhere(false) + , bLastTouched(false) +{ +} + +GuiTrigger::GuiTrigger(u32 ch, u32 btn, bool clickEverywhere, bool holdEverywhere, bool selectionClickEverywhere) + : chan(ch) + , btns(btn) + , bClickEverywhere(clickEverywhere) + , bHoldEverywhere(holdEverywhere) + , bSelectionClickEverywhere(selectionClickEverywhere) + , bLastTouched(false) +{ +} + +/** + * Destructor for the GuiTrigger class. + */ +GuiTrigger::~GuiTrigger() +{ +} + +/** + * Sets a simple trigger. Requires: + * - Element is selected + * - Trigger button is pressed + */ +void GuiTrigger::setTrigger(u32 ch, u32 btn) +{ + chan = ch; + btns = btn; +} + +bool GuiTrigger::left(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return false; + } + if((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_LEFT | STICK_L_LEFT)) + { + return true; + } + return false; +} + +bool GuiTrigger::right(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return false; + } + if((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_RIGHT | STICK_L_RIGHT)) + { + return true; + } + return false; +} + +bool GuiTrigger::up(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return false; + } + if((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_UP | STICK_L_UP)) + { + return true; + } + return false; +} + +bool GuiTrigger::down(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return false; + } + if((controller->data.buttons_h | controller->data.buttons_d) & (BUTTON_DOWN | STICK_L_DOWN)) + { + return true; + } + return false; +} + +s32 GuiTrigger::clicked(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return CLICKED_NONE; + } + + s32 bResult = CLICKED_NONE; + + if(controller->data.touched && controller->data.validPointer && (btns & VPAD_TOUCH) && !controller->lastData.touched) + { + bResult = CLICKED_TOUCH; + } + + if(controller->data.buttons_d & btns) + { + bResult = CLICKED_BUTTON; + } + return bResult; +} + +bool GuiTrigger::held(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return false; + } + + bool bResult = false; + + if(controller->data.touched && (btns & VPAD_TOUCH) && controller->data.validPointer && controller->lastData.touched && controller->lastData.validPointer) + { + bResult = true; + } + + if(controller->data.buttons_h & btns) + { + bResult = true; + } + + return bResult; +} + +bool GuiTrigger::released(const GuiController *controller) const +{ + if((controller->chan & chan) == 0) { + return false; + } + + if(clicked(controller) || held(controller)) + return false; + + bool bResult = false; + + if(!controller->data.touched && (btns & VPAD_TOUCH) && controller->lastData.touched && controller->lastData.validPointer) + { + bResult = true; + } + + if(controller->data.buttons_r & btns) + { + bResult = true; + } + + return bResult; +} + diff --git a/source/gui/GuiTrigger.h b/source/gui/GuiTrigger.h new file mode 100644 index 0000000..3dabd5a --- /dev/null +++ b/source/gui/GuiTrigger.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef GUI_TRIGGER_H_ +#define GUI_TRIGGER_H_ + +#include + + +//!Menu input trigger management. Determine if action is neccessary based on input data by comparing controller input data to a specific trigger element. +class GuiTrigger +{ +public: + enum eClicked{ + CLICKED_NONE = 0x00, + CLICKED_TOUCH = 0x01, + CLICKED_BUTTON = 0x02, + }; + enum eChannels { + CHANNEL_1 = 0x01, + CHANNEL_2 = 0x02, + CHANNEL_3 = 0x04, + CHANNEL_4 = 0x08, + CHANNEL_5 = 0x10, + CHANNEL_ALL = 0xFF + }; + enum eButtons { + BUTTON_NONE = 0x0000, + VPAD_TOUCH = 0x80000000, + BUTTON_Z = 0x20000, + BUTTON_C = 0x10000, + BUTTON_A = 0x8000, + BUTTON_B = 0x4000, + BUTTON_X = 0x2000, + BUTTON_Y = 0x1000, + BUTTON_1 = BUTTON_Y, + BUTTON_2 = BUTTON_X, + BUTTON_LEFT = 0x0800, + BUTTON_RIGHT = 0x0400, + BUTTON_UP = 0x0200, + BUTTON_DOWN = 0x0100, + BUTTON_ZL = 0x0080, + BUTTON_ZR = 0x0040, + BUTTON_L = 0x0020, + BUTTON_R = 0x0010, + BUTTON_PLUS = 0x0008, + BUTTON_MINUS = 0x0004, + BUTTON_HOME = 0x0002, + BUTTON_SYNC = 0x0001, + STICK_R_LEFT = 0x04000000, + STICK_R_RIGHT = 0x02000000, + STICK_R_UP = 0x01000000, + STICK_R_DOWN = 0x00800000, + STICK_L_LEFT = 0x40000000, + STICK_L_RIGHT = 0x20000000, + STICK_L_UP = 0x10000000, + STICK_L_DOWN = 0x08000000 + }; + + //!Constructor + GuiTrigger(); + //!Constructor + GuiTrigger(u32 ch, u32 btns, bool clickEverywhere = false, bool holdEverywhere = false, bool selectionClickEverywhere = false); + //!Destructor + virtual ~GuiTrigger(); + //!Sets a simple trigger. Requires: element is selected, and trigger button is pressed + void setTrigger(u32 ch, u32 btns); + + void setClickEverywhere(bool b) { bClickEverywhere = b; } + void setHoldOnly(bool b) { bHoldEverywhere = b; } + void setSelectionClickEverywhere(bool b) { bSelectionClickEverywhere = b; } + + bool isClickEverywhere() const { return bClickEverywhere; } + bool isHoldEverywhere() const { return bHoldEverywhere; } + bool isSelectionClickEverywhere() const { return bSelectionClickEverywhere; } + + bool left(const GuiController *controller) const; + bool right(const GuiController *controller) const; + bool up(const GuiController *controller) const; + bool down(const GuiController *controller) const; + s32 clicked(const GuiController *controller) const; + bool held(const GuiController *controller) const; + bool released(const GuiController *controller) const; +private: + u32 chan; + u32 btns; + bool bClickEverywhere; + bool bHoldEverywhere; + bool bSelectionClickEverywhere; + bool bLastTouched; +}; + +#endif diff --git a/source/gui/VPadController.h b/source/gui/VPadController.h new file mode 100644 index 0000000..682eafd --- /dev/null +++ b/source/gui/VPadController.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef VPAD_CONTROLLER_H_ +#define VPAD_CONTROLLER_H_ + +#include "GuiController.h" +#include + +class VPadController : public GuiController +{ +public: + //!Constructor + VPadController(s32 channel) + : GuiController(channel) + { + memset(&vpad, 0, sizeof(vpad)); + } + + //!Destructor + virtual ~VPadController() {} + + bool update(s32 width, s32 height) + { + lastData = data; + + s32 vpadError = -1; + VPADRead(0, &vpad, 1, &vpadError); + + if(vpadError == 0){ + data.buttons_r = vpad.btns_r; + data.buttons_h = vpad.btns_h; + data.buttons_d = vpad.btns_d; + data.validPointer = !vpad.tpdata.invalid; + data.touched = vpad.tpdata.touched; + + VPADGetTPCalibratedPoint(0, &tpCalib, &vpad.tpdata1); + + //! calculate the screen offsets + data.x = -(width >> 1) + (s32)(((float)tpCalib.x / 1280.0f) * (float)width); + data.y = -(height >> 1) + (s32)(float)height - (((float)tpCalib.y / 720.0f) * (float)height); + + return true; + } + return false; + } + +private: + VPADData vpad; + VPADTPData tpCalib; +}; + +#endif diff --git a/source/gui/WPadController.h b/source/gui/WPadController.h new file mode 100644 index 0000000..7cdca02 --- /dev/null +++ b/source/gui/WPadController.h @@ -0,0 +1,179 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef WPAD_CONTROLLER_H_ +#define WPAD_CONTROLLER_H_ + +#include "GuiController.h" +#include + +class WPadController : public GuiController +{ +public: + //!Constructor + WPadController(s32 channel) + : GuiController(channel) + { + memset(&kpadData, 0, sizeof(kpadData)); + } + + //!Destructor + virtual ~WPadController() {} + + u32 remapWiiMoteButtons(u32 buttons) + { + u32 conv_buttons = 0; + + if(buttons & WPAD_BUTTON_LEFT) + conv_buttons |= GuiTrigger::BUTTON_LEFT; + + if(buttons & WPAD_BUTTON_RIGHT) + conv_buttons |= GuiTrigger::BUTTON_RIGHT; + + if(buttons & WPAD_BUTTON_DOWN) + conv_buttons |= GuiTrigger::BUTTON_DOWN; + + if(buttons & WPAD_BUTTON_UP) + conv_buttons |= GuiTrigger::BUTTON_UP; + + if(buttons & WPAD_BUTTON_PLUS) + conv_buttons |= GuiTrigger::BUTTON_PLUS; + + if(buttons & WPAD_BUTTON_2) + conv_buttons |= GuiTrigger::BUTTON_2; + + if(buttons & WPAD_BUTTON_1) + conv_buttons |= GuiTrigger::BUTTON_1; + + if(buttons & WPAD_BUTTON_B) + conv_buttons |= GuiTrigger::BUTTON_B; + + if(buttons & WPAD_BUTTON_A) + conv_buttons |= GuiTrigger::BUTTON_A; + + if(buttons & WPAD_BUTTON_MINUS) + conv_buttons |= GuiTrigger::BUTTON_MINUS; + + if(buttons & WPAD_BUTTON_Z) + conv_buttons |= GuiTrigger::BUTTON_Z; + + if(buttons & WPAD_BUTTON_C) + conv_buttons |= GuiTrigger::BUTTON_C; + + if(buttons & WPAD_BUTTON_HOME) + conv_buttons |= GuiTrigger::BUTTON_HOME; + + return conv_buttons; + } + u32 remapClassicButtons(u32 buttons) + { + u32 conv_buttons = 0; + + if(buttons & WPAD_CLASSIC_BUTTON_LEFT) + conv_buttons |= GuiTrigger::BUTTON_LEFT; + + if(buttons & WPAD_CLASSIC_BUTTON_RIGHT) + conv_buttons |= GuiTrigger::BUTTON_RIGHT; + + if(buttons & WPAD_CLASSIC_BUTTON_DOWN) + conv_buttons |= GuiTrigger::BUTTON_DOWN; + + if(buttons & WPAD_CLASSIC_BUTTON_UP) + conv_buttons |= GuiTrigger::BUTTON_UP; + + if(buttons & WPAD_CLASSIC_BUTTON_PLUS) + conv_buttons |= GuiTrigger::BUTTON_PLUS; + + if(buttons & WPAD_CLASSIC_BUTTON_X) + conv_buttons |= GuiTrigger::BUTTON_X; + + if(buttons & WPAD_CLASSIC_BUTTON_Y) + conv_buttons |= GuiTrigger::BUTTON_Y; + + if(buttons & WPAD_CLASSIC_BUTTON_B) + conv_buttons |= GuiTrigger::BUTTON_B; + + if(buttons & WPAD_CLASSIC_BUTTON_A) + conv_buttons |= GuiTrigger::BUTTON_A; + + if(buttons & WPAD_CLASSIC_BUTTON_MINUS) + conv_buttons |= GuiTrigger::BUTTON_MINUS; + + if(buttons & WPAD_CLASSIC_BUTTON_HOME) + conv_buttons |= GuiTrigger::BUTTON_HOME; + + if(buttons & WPAD_CLASSIC_BUTTON_ZR) + conv_buttons |= GuiTrigger::BUTTON_ZR; + + if(buttons & WPAD_CLASSIC_BUTTON_ZL) + conv_buttons |= GuiTrigger::BUTTON_ZL; + + if(buttons & WPAD_CLASSIC_BUTTON_R) + conv_buttons |= GuiTrigger::BUTTON_R; + + if(buttons & WPAD_CLASSIC_BUTTON_L) + conv_buttons |= GuiTrigger::BUTTON_L; + + return conv_buttons; + } + + bool update(s32 width, s32 height) + { + lastData = data; + + u32 controller_type; + + //! check if the controller is connected + if(WPADProbe(chanIdx-1, &controller_type) != 0) + return false; + + KPADRead(chanIdx-1, &kpadData, 1); + + if(kpadData.device_type <= 1) + { + data.buttons_r = remapWiiMoteButtons(kpadData.btns_r); + data.buttons_h = remapWiiMoteButtons(kpadData.btns_h); + data.buttons_d = remapWiiMoteButtons(kpadData.btns_d); + } + else + { + data.buttons_r = remapClassicButtons(kpadData.classic.btns_r); + data.buttons_h = remapClassicButtons(kpadData.classic.btns_h); + data.buttons_d = remapClassicButtons(kpadData.classic.btns_d); + } + + data.validPointer = (kpadData.pos_valid == 1 || kpadData.pos_valid == 2) && (kpadData.pos_x >= -1.0f && kpadData.pos_x <= 1.0f) && (kpadData.pos_y >= -1.0f && kpadData.pos_y <= 1.0f); + //! calculate the screen offsets if pointer is valid else leave old value + if(data.validPointer) + { + data.x = (width >> 1) * kpadData.pos_x; + data.y = (height >> 1) * (-kpadData.pos_y); + + if(kpadData.angle_y > 0.0f) + data.pointerAngle = (-kpadData.angle_x + 1.0f) * 0.5f * 180.0f; + else + data.pointerAngle = (kpadData.angle_x + 1.0f) * 0.5f * 180.0f - 180.0f; + } + + return true; + } + +private: + KPADData kpadData; + u32 lastButtons; +}; + +#endif diff --git a/source/gui/sigslot.h b/source/gui/sigslot.h new file mode 100644 index 0000000..d1f3844 --- /dev/null +++ b/source/gui/sigslot.h @@ -0,0 +1,2731 @@ +// sigslot.h: Signal/Slot classes +// +// Written by Sarah Thompson (sarah@telergy.com) 2002. +// +// License: Public domain. You are free to use this code however you like, with the proviso that +// the author takes on no responsibility or liability for any use. +// +// QUICK DOCUMENTATION +// +// (see also the full documentation at http://sigslot.sourceforge.net/) +// +// #define switches +// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables +// all of the thread safety support on platforms where it is +// available. +// +// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than +// gcc on a platform that supports Posix threads. (When using gcc, +// this is the default - use SIGSLOT_PURE_ISO to disable this if +// necessary) +// +// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global. +// Otherwise, the default is single_threaded. #define this yourself to +// override the default. In pure ISO mode, anything other than +// single_threaded will cause a compiler error. +// +// PLATFORM NOTES +// +// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream +// compilers do this by default, but you may need to define it +// yourself if your build environment is less standard. This causes +// the Win32 thread support to be compiled in and used automatically. +// +// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads +// available, so they are used automatically. You can override this +// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using +// something other than gcc but still want to use Posix threads, you +// need to #define SIGSLOT_USE_POSIX_THREADS. +// +// ISO C++ - If none of the supported platforms are detected, or if +// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off, +// along with any code that might cause a pure ISO C++ environment to +// complain. Before you ask, gcc -ansi -pedantic won't compile this +// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of +// errors that aren't really there. If you feel like investigating this, +// please contact the author. +// +// +// THREADING MODES +// +// single_threaded - Your program is assumed to be single threaded from the point of view +// of signal/slot usage (i.e. all objects using signals and slots are +// created and destroyed from a single thread). Behaviour if objects are +// destroyed concurrently is undefined (i.e. you'll get the occasional +// segmentation fault/memory exception). +// +// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and +// slots can be safely created and destroyed from any thread, even when +// connections exist. In multi_threaded_global mode, this is achieved by a +// single global mutex (actually a critical section on Windows because they +// are faster). This option uses less OS resources, but results in more +// opportunities for contention, possibly resulting in more context switches +// than are strictly necessary. +// +// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global, +// except that each signal, and each object that inherits has_slots, all +// have their own mutex/critical section. In practice, this means that +// mutex collisions (and hence context switches) only happen if they are +// absolutely essential. However, on some platforms, creating a lot of +// mutexes can slow down the whole OS, so use this option with care. +// +// USING THE LIBRARY +// +// See the full documentation at http://sigslot.sourceforge.net/ +// +// +#ifndef SIGSLOT_H__ +#define SIGSLOT_H__ + +#include +#include + +#define _SIGSLOT_SINGLE_THREADED + +#ifndef SIGSLOT_DEFAULT_MT_POLICY +# ifdef _SIGSLOT_SINGLE_THREADED +# define SIGSLOT_DEFAULT_MT_POLICY single_threaded +# else +# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local +# endif +#endif + + +namespace sigslot { + + class single_threaded + { + public: + single_threaded() + { + ; + } + + virtual ~single_threaded() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#ifdef _SIGSLOT_HAS_WIN32_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + static bool isinitialised = false; + + if(!isinitialised) + { + InitializeCriticalSection(get_critsec()); + isinitialised = true; + } + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + EnterCriticalSection(get_critsec()); + } + + virtual void unlock() + { + LeaveCriticalSection(get_critsec()); + } + + private: + CRITICAL_SECTION* get_critsec() + { + static CRITICAL_SECTION g_critsec; + return &g_critsec; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + InitializeCriticalSection(&m_critsec); + } + + multi_threaded_local(const multi_threaded_local&) + { + InitializeCriticalSection(&m_critsec); + } + + virtual ~multi_threaded_local() + { + DeleteCriticalSection(&m_critsec); + } + + virtual void lock() + { + EnterCriticalSection(&m_critsec); + } + + virtual void unlock() + { + LeaveCriticalSection(&m_critsec); + } + + private: + CRITICAL_SECTION m_critsec; + }; +#endif // _SIGSLOT_HAS_WIN32_THREADS + +#ifdef _SIGSLOT_HAS_POSIX_THREADS + // The multi threading policies only get compiled in if they are enabled. + class multi_threaded_global + { + public: + multi_threaded_global() + { + pthread_mutex_init(get_mutex(), NULL); + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + pthread_mutex_lock(get_mutex()); + } + + virtual void unlock() + { + pthread_mutex_unlock(get_mutex()); + } + + private: + pthread_mutex_t* get_mutex() + { + static pthread_mutex_t g_mutex; + return &g_mutex; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + pthread_mutex_init(&m_mutex, NULL); + } + + multi_threaded_local(const multi_threaded_local&) + { + pthread_mutex_init(&m_mutex, NULL); + } + + virtual ~multi_threaded_local() + { + pthread_mutex_destroy(&m_mutex); + } + + virtual void lock() + { + pthread_mutex_lock(&m_mutex); + } + + virtual void unlock() + { + pthread_mutex_unlock(&m_mutex); + } + + private: + pthread_mutex_t m_mutex; + }; +#endif // _SIGSLOT_HAS_POSIX_THREADS + +#ifdef _SIGSLOT_HAS_LWP_THREADS + + class multi_threaded_global + { + public: + multi_threaded_global() + { + ; + } + + multi_threaded_global(const multi_threaded_global&) + { + ; + } + + virtual ~multi_threaded_global() + { + ; + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + + class multi_threaded_local + { + public: + multi_threaded_local() + { + ; + } + + multi_threaded_local(const multi_threaded_local&) + { + ; + } + + virtual ~multi_threaded_local() + { + } + + virtual void lock() + { + ; + } + + virtual void unlock() + { + ; + } + }; + +#endif // _SIGSLOT_HAS_LWP_THREADS + + template + class lock_block + { + public: + mt_policy *m_mutex; + + lock_block(mt_policy *mtx) + : m_mutex(mtx) + { + m_mutex->lock(); + } + + ~lock_block() + { + m_mutex->unlock(); + } + }; + + template + class has_slots; + + template + class _connection_base0 + { + public: + virtual ~_connection_base0() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit() = 0; + virtual _connection_base0* clone() = 0; + virtual _connection_base0* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base1 + { + public: + virtual ~_connection_base1() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type) = 0; + virtual _connection_base1* clone() = 0; + virtual _connection_base1* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base2 + { + public: + virtual ~_connection_base2() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type) = 0; + virtual _connection_base2* clone() = 0; + virtual _connection_base2* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base3 + { + public: + virtual ~_connection_base3() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type) = 0; + virtual _connection_base3* clone() = 0; + virtual _connection_base3* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base4 + { + public: + virtual ~_connection_base4() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0; + virtual _connection_base4* clone() = 0; + virtual _connection_base4* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base5 + { + public: + virtual ~_connection_base5() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type) = 0; + virtual _connection_base5* clone() = 0; + virtual _connection_base5* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base6 + { + public: + virtual ~_connection_base6() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type) = 0; + virtual _connection_base6* clone() = 0; + virtual _connection_base6* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base7 + { + public: + virtual ~_connection_base7() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type) = 0; + virtual _connection_base7* clone() = 0; + virtual _connection_base7* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _connection_base8 + { + public: + virtual ~_connection_base8() { ; } + virtual has_slots* getdest() const = 0; + virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, + arg6_type, arg7_type, arg8_type) = 0; + virtual _connection_base8* clone() = 0; + virtual _connection_base8* duplicate(has_slots* pnewdest) = 0; + }; + + template + class _signal_base : public mt_policy + { + public: + virtual void slot_disconnect(has_slots* pslot) = 0; + virtual void slot_duplicate(const has_slots* poldslot, has_slots* pnewslot) = 0; + }; + + template + class has_slots : public mt_policy + { + private: + typedef typename std::set<_signal_base *> sender_set; + typedef typename sender_set::const_iterator const_iterator; + + public: + has_slots() + { + ; + } + + has_slots(const has_slots& hs) + : mt_policy(hs) + { + lock_block lock(this); + const_iterator it = hs.m_senders.begin(); + const_iterator itEnd = hs.m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_duplicate(&hs, this); + m_senders.insert(*it); + ++it; + } + } + + void signal_connect(_signal_base* sender) + { + lock_block lock(this); + m_senders.insert(sender); + } + + void signal_disconnect(_signal_base* sender) + { + lock_block lock(this); + m_senders.erase(sender); + } + + virtual ~has_slots() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_senders.begin(); + const_iterator itEnd = m_senders.end(); + + while(it != itEnd) + { + (*it)->slot_disconnect(this); + ++it; + } + + m_senders.erase(m_senders.begin(), m_senders.end()); + } + + private: + sender_set m_senders; + }; + + template + class _signal_base0 : public _signal_base + { + public: + typedef typename std::list<_connection_base0 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base0() + { + ; + } + + _signal_base0(const _signal_base0& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + ~_signal_base0() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base1 : public _signal_base + { + public: + typedef typename std::list<_connection_base1 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base1() + { + ; + } + + _signal_base1(const _signal_base1& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base1() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base2 : public _signal_base + { + public: + typedef typename std::list<_connection_base2 *> + connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base2() + { + ; + } + + _signal_base2(const _signal_base2& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base2() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base3 : public _signal_base + { + public: + typedef std::list<_connection_base3 *> + connections_list; + + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + _signal_base3() + { + ; + } + + _signal_base3(const _signal_base3& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base3() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base4 : public _signal_base + { + public: + typedef std::list<_connection_base4 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base4() + { + ; + } + + _signal_base4(const _signal_base4& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base4() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + this->m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base5 : public _signal_base + { + public: + typedef std::list<_connection_base5 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base5() + { + ; + } + + _signal_base5(const _signal_base5& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base5() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base6 : public _signal_base + { + public: + typedef std::list<_connection_base6 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base6() + { + ; + } + + _signal_base6(const _signal_base6& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base6() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base7 : public _signal_base + { + public: + typedef std::list<_connection_base7 *> connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base7() + { + ; + } + + _signal_base7(const _signal_base7& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base7() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + template + class _signal_base8 : public _signal_base + { + public: + typedef std::list<_connection_base8 *> + connections_list; + typedef typename connections_list::const_iterator const_iterator; + typedef typename connections_list::iterator iterator; + + _signal_base8() + { + ; + } + + _signal_base8(const _signal_base8& s) + : _signal_base(s) + { + lock_block lock(this); + const_iterator it = s.m_connected_slots.begin(); + const_iterator itEnd = s.m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_connect(this); + m_connected_slots.push_back((*it)->clone()); + + ++it; + } + } + + void slot_duplicate(const has_slots* oldtarget, has_slots* newtarget) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == oldtarget) + { + m_connected_slots.push_back((*it)->duplicate(newtarget)); + } + + ++it; + } + } + + ~_signal_base8() + { + disconnect_all(); + } + + void disconnect_all() + { + lock_block lock(this); + const_iterator it = m_connected_slots.begin(); + const_iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + (*it)->getdest()->signal_disconnect(this); + delete *it; + + ++it; + } + + m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end()); + } + + void disconnect(has_slots* pclass) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + if((*it)->getdest() == pclass) + { + delete *it; + m_connected_slots.erase(it); + pclass->signal_disconnect(this); + return; + } + + ++it; + } + } + + bool connected() + { + return m_connected_slots.size() != 0; + } + + void slot_disconnect(has_slots* pslot) + { + lock_block lock(this); + iterator it = m_connected_slots.begin(); + iterator itEnd = m_connected_slots.end(); + + while(it != itEnd) + { + iterator itNext = it; + ++itNext; + + if((*it)->getdest() == pslot) + { + delete *it; + m_connected_slots.erase(it); + // delete *it; + } + + it = itNext; + } + } + + protected: + connections_list m_connected_slots; + }; + + + template + class _connection0 : public _connection_base0 + { + public: + _connection0() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection0(dest_type* pobject, void (dest_type::*pmemfun)()) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection0() + { + ; + } + + virtual _connection_base0* clone() + { + return new _connection0(*this); + } + + virtual _connection_base0* duplicate(has_slots* pnewdest) + { + return new _connection0((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit() + { + (m_pobject->*m_pmemfun)(); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(); + }; + + template + class _connection1 : public _connection_base1 + { + public: + _connection1() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection1() + { + ; + } + + virtual _connection_base1* clone() + { + return new _connection1(*this); + } + + virtual _connection_base1* duplicate(has_slots* pnewdest) + { + return new _connection1((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1) + { + (m_pobject->*m_pmemfun)(a1); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type); + }; + + template + class _connection2 : public _connection_base2 + { + public: + _connection2() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection2() + { + ; + } + + + virtual _connection_base2* clone() + { + return new _connection2(*this); + } + + virtual _connection_base2* duplicate(has_slots* pnewdest) + { + return new _connection2((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2) + { + (m_pobject->*m_pmemfun)(a1, a2); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type); + }; + + template + class _connection3 : public _connection_base3 + { + public: + _connection3() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection3() + { + ; + } + + + virtual _connection_base3* clone() + { + return new _connection3(*this); + } + + virtual _connection_base3* duplicate(has_slots* pnewdest) + { + return new _connection3((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + (m_pobject->*m_pmemfun)(a1, a2, a3); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type); + }; + + template + class _connection4 : public _connection_base4 + { + public: + _connection4() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection4() + { + ; + } + + virtual _connection_base4* clone() + { + return new _connection4(*this); + } + + virtual _connection_base4* duplicate(has_slots* pnewdest) + { + return new _connection4((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, + arg4_type a4) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, + arg4_type); + }; + + template + class _connection5 : public _connection_base5 + { + public: + _connection5() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection5() + { + ; + } + + virtual _connection_base5* clone() + { + return new _connection5(*this); + } + + virtual _connection_base5* duplicate(has_slots* pnewdest) + { + return new _connection5((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type); + }; + + template + class _connection6 : public _connection_base6 + { + public: + _connection6() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection6() + { + ; + } + + virtual _connection_base6* clone() + { + return new _connection6(*this); + } + + virtual _connection_base6* duplicate(has_slots* pnewdest) + { + return new _connection6((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type); + }; + + template + class _connection7 : public _connection_base7 + { + public: + _connection7() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection7() + { + ; + } + + virtual _connection_base7* clone() + { + return new _connection7(*this); + } + + virtual _connection_base7* duplicate(has_slots* pnewdest) + { + return new _connection7((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type); + }; + + template + class _connection8 : public _connection_base8 + { + public: + _connection8() + { + this->pobject = NULL; + this->pmemfun = NULL; + } + + _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + m_pobject = pobject; + m_pmemfun = pmemfun; + } + + virtual ~_connection8() + { + ; + } + + virtual _connection_base8* clone() + { + return new _connection8(*this); + } + + virtual _connection_base8* duplicate(has_slots* pnewdest) + { + return new _connection8((dest_type *)pnewdest, m_pmemfun); + } + + virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8); + } + + virtual has_slots* getdest() const + { + return m_pobject; + } + + private: + dest_type* m_pobject; + void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type, + arg5_type, arg6_type, arg7_type, arg8_type); + }; + + template + class signal0 : public _signal_base0 + { + public: + typedef typename _signal_base0::connections_list::const_iterator const_iterator; + signal0() + { + ; + } + + signal0(const signal0& s) + : _signal_base0(s) + { + ; + } + + virtual ~signal0() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)()) + { + lock_block lock(this); + _connection0* conn = + new _connection0(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit() + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + + void operator()() + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(); + + it = itNext; + } + } + }; + + template + class signal1 : public _signal_base1 + { + public: + typedef typename _signal_base1::connections_list::const_iterator const_iterator; + signal1() + { + ; + } + + signal1(const signal1& s) + : _signal_base1(s) + { + ; + } + + virtual ~signal1() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type)) + { + lock_block lock(this); + _connection1* conn = + new _connection1(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + + void operator()(arg1_type a1) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1); + + it = itNext; + } + } + }; + + template + class signal2 : public _signal_base2 + { + public: + typedef typename _signal_base2::connections_list::const_iterator const_iterator; + signal2() + { + ; + } + + signal2(const signal2& s) + : _signal_base2(s) + { + ; + } + + virtual ~signal2() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type)) + { + lock_block lock(this); + _connection2* conn = new + _connection2(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2); + + it = itNext; + } + } + }; + + template + class signal3 : public _signal_base3 + { + public: + typedef typename _signal_base3::connections_list::const_iterator const_iterator; + signal3() + { + ; + } + + signal3(const signal3& s) + : _signal_base3(s) + { + ; + } + + virtual ~signal3() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type)) + { + lock_block lock(this); + _connection3* conn = + new _connection3(pclass, + pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3); + + it = itNext; + } + } + }; + + template + class signal4 : public _signal_base4 + { + public: + typedef typename _signal_base4::connections_list::const_iterator const_iterator; + signal4() + { + ; + } + + signal4(const signal4& s) + : _signal_base4(s) + { + ; + } + + virtual ~signal4() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type)) + { + lock_block lock(this); + _connection4* + conn = new _connection4(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4); + + it = itNext; + } + } + }; + + template + class signal5 : public _signal_base5 + { + public: + typedef typename _signal_base5::connections_list::const_iterator const_iterator; + signal5() + { + ; + } + + signal5(const signal5& s) + : _signal_base5(s) + { + ; + } + + virtual ~signal5() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type)) + { + lock_block lock(this); + _connection5* conn = new _connection5(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5); + + it = itNext; + } + } + }; + + + template + class signal6 : public _signal_base6 + { + public: + typedef typename _signal_base6::connections_list::const_iterator const_iterator; + signal6() + { + ; + } + + signal6(const signal6& s) + : _signal_base6(s) + { + ; + } + + virtual ~signal6() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type)) + { + lock_block lock(this); + _connection6* conn = + new _connection6(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6); + + it = itNext; + } + } + }; + + template + class signal7 : public _signal_base7 + { + public: + typedef typename _signal_base7::connections_list::const_iterator const_iterator; + signal7() + { + ; + } + + signal7(const signal7& s) + : _signal_base7(s) + { + ; + } + + virtual ~signal7() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type)) + { + lock_block lock(this); + _connection7* conn = + new _connection7(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7); + + it = itNext; + } + } + }; + + template + class signal8 : public _signal_base8 + { + public: + typedef typename _signal_base8::connections_list::const_iterator const_iterator; + signal8() + { + ; + } + + signal8(const signal8& s) + : _signal_base8(s) + { + ; + } + + virtual ~signal8() + { + ; + } + + template + void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type, + arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, + arg7_type, arg8_type)) + { + lock_block lock(this); + _connection8* conn = + new _connection8(pclass, pmemfun); + this->m_connected_slots.push_back(conn); + pclass->signal_connect(this); + } + + void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + + void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, + arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8) + { + lock_block lock(this); + const_iterator itNext, it = this->m_connected_slots.begin(); + const_iterator itEnd = this->m_connected_slots.end(); + + while(it != itEnd) + { + itNext = it; + ++itNext; + + (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8); + + it = itNext; + } + } + }; + +}; // namespace sigslot + +#endif // SIGSLOT_H__ diff --git a/source/resources/Resources.cpp b/source/resources/Resources.cpp new file mode 100644 index 0000000..13c9e0d --- /dev/null +++ b/source/resources/Resources.cpp @@ -0,0 +1,208 @@ + +#include +#include +#include "Resources.h" +#include "filelist.h" +#include +#include +#include "gui/GuiImageAsync.h" +#include "gui/GuiSound.h" +#include "utils/logger.h" + +Resources * Resources::instance = NULL; + +void Resources::Clear(){ + ResourceFile * ResourceList = getResourceList(); + if(ResourceList == NULL) return; + + for(s32 i = 0; ResourceList[i].filename != NULL; ++i) + { + if(ResourceList[i].CustomFile) + { + free(ResourceList[i].CustomFile); + ResourceList[i].CustomFile = NULL; + } + + if(ResourceList[i].CustomFileSize != 0) + ResourceList[i].CustomFileSize = 0; + } + + if(instance) + delete instance; + + instance = NULL; +} + +bool Resources::LoadFiles(const char * path) +{ + if(!path) + return false; + + bool result = false; + Clear(); + + ResourceFile * ResourceList = getResourceList(); + if(ResourceList == NULL) return false; + + for(s32 i = 0; ResourceList[i].filename != NULL; ++i) + { + std::string fullpath(path); + fullpath += "/"; + fullpath += ResourceList[i].filename; + + u8 * buffer = NULL; + u32 filesize = 0; + + FSUtils::LoadFileToMem(fullpath.c_str(), &buffer, &filesize); + + ResourceList[i].CustomFile = buffer; + ResourceList[i].CustomFileSize = (u32) filesize; + result |= (buffer != 0); + } + + return result; +} + +const u8 * Resources::GetFile(const char * filename) +{ + DEBUG_FUNCTION_LINE(".\n"); + ResourceFile * ResourceList = getResourceList(); + if(ResourceList == NULL) return NULL; + + for(s32 i = 0; ResourceList[i].filename != NULL; ++i) + { + if(strcasecmp(filename, ResourceList[i].filename) == 0) + { + return (ResourceList[i].CustomFile ? ResourceList[i].CustomFile : ResourceList[i].DefaultFile); + } + } + + return NULL; +} + +u32 Resources::GetFileSize(const char * filename) +{ + ResourceFile * ResourceList = getResourceList(); + if(ResourceList == NULL) return 0; + + for(s32 i = 0; ResourceList[i].filename != NULL; ++i) + { + if(strcasecmp(filename, ResourceList[i].filename) == 0) + { + return (ResourceList[i].CustomFile ? ResourceList[i].CustomFileSize : ResourceList[i].DefaultFileSize); + } + } + return 0; +} + +GuiImageData * Resources::GetImageData(const char * filename) +{ + if(!instance) + instance = new Resources; + + ResourceFile * ResourceList = getResourceList(); + if(ResourceList == NULL) return NULL; + + std::map >::iterator itr = instance->imageDataMap.find(std::string(filename)); + if(itr != instance->imageDataMap.end()) + { + itr->second.first++; + return itr->second.second; + } + + for(s32 i = 0; ResourceList[i].filename != NULL; ++i) + { + if(strcasecmp(filename, ResourceList[i].filename) == 0) + { + const u8 * buff = ResourceList[i].CustomFile ? ResourceList[i].CustomFile : ResourceList[i].DefaultFile; + const u32 size = ResourceList[i].CustomFile ? ResourceList[i].CustomFileSize : ResourceList[i].DefaultFileSize; + + if(buff == NULL) + return NULL; + + GuiImageData * image = new GuiImageData(buff, size); + instance->imageDataMap[std::string(filename)].first = 1; + instance->imageDataMap[std::string(filename)].second = image; + + return image; + } + } + + return NULL; +} + +void Resources::RemoveImageData(GuiImageData * image) +{ + std::map >::iterator itr; + + for(itr = instance->imageDataMap.begin(); itr != instance->imageDataMap.end(); itr++) + { + if(itr->second.second == image) + { + itr->second.first--; + + if(itr->second.first == 0) + { + AsyncDeleter::pushForDelete( itr->second.second ); + instance->imageDataMap.erase(itr); + } + break; + } + } +} + +GuiSound * Resources::GetSound(const char * filename) +{ + if(!instance) + instance = new Resources; + + std::map >::iterator itr = instance->soundDataMap.find(std::string(filename)); + if(itr != instance->soundDataMap.end()) + { + itr->second.first++; + return itr->second.second; + } + + ResourceFile * ResourceList = getResourceList(); + if(ResourceList == NULL) return NULL; + + for(s32 i = 0; ResourceList[i].filename != NULL; ++i) + { + if(strcasecmp(filename, ResourceList[i].filename) == 0) + { + const u8 * buff = ResourceList[i].CustomFile ? ResourceList[i].CustomFile : ResourceList[i].DefaultFile; + const u32 size = ResourceList[i].CustomFile ? ResourceList[i].CustomFileSize : ResourceList[i].DefaultFileSize; + + if(buff == NULL) + return NULL; + + GuiSound * sound = new GuiSound(buff, size); + instance->soundDataMap[std::string(filename)].first = 1; + instance->soundDataMap[std::string(filename)].second = sound; + + return sound; + } + } + + return NULL; +} + +void Resources::RemoveSound(GuiSound * sound) +{ + std::map >::iterator itr; + + for(itr = instance->soundDataMap.begin(); itr != instance->soundDataMap.end(); itr++) + { + if(itr->second.second == sound) + { + itr->second.first--; + + if(itr->second.first == 0) + { + AsyncDeleter::pushForDelete( itr->second.second ); + instance->soundDataMap.erase(itr); + } + break; + } + } +} diff --git a/source/resources/Resources.h b/source/resources/Resources.h new file mode 100644 index 0000000..c58159d --- /dev/null +++ b/source/resources/Resources.h @@ -0,0 +1,34 @@ +#ifndef RECOURCES_H_ +#define RECOURCES_H_ + +#include +#include + +//! forward declaration +class GuiImageData; +class GuiSound; + +class Resources +{ +public: + static void Clear(); + static bool LoadFiles(const char * path); + static const u8 * GetFile(const char * filename); + static u32 GetFileSize(const char * filename); + + static GuiImageData * GetImageData(const char * filename); + static void RemoveImageData(GuiImageData * image); + + static GuiSound * GetSound(const char * filename); + static void RemoveSound(GuiSound * sound); +private: + static Resources *instance; + + Resources() {} + ~Resources() {} + + std::map > imageDataMap; + std::map > soundDataMap; +}; + +#endif diff --git a/source/resources/filelist.h b/source/resources/filelist.h new file mode 100644 index 0000000..e499743 --- /dev/null +++ b/source/resources/filelist.h @@ -0,0 +1,16 @@ +#ifndef _FILELIST_H_ +#define _FILELIST_H_ +#include + +typedef struct _ResourceFile +{ + const char *filename; + const u8 *DefaultFile; + const u32 &DefaultFileSize; + u8 *CustomFile; + u32 CustomFileSize; +} ResourceFile; + +ResourceFile * getResourceList(); + +#endif diff --git a/source/sounds/BufferCircle.cpp b/source/sounds/BufferCircle.cpp new file mode 100644 index 0000000..481e4a9 --- /dev/null +++ b/source/sounds/BufferCircle.cpp @@ -0,0 +1,142 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include "utils/utils.h" +#include "BufferCircle.hpp" + +BufferCircle::BufferCircle() +{ + which = 0; + BufferBlockSize = 0; +} + +BufferCircle::~BufferCircle() +{ + FreeBuffer(); + SoundBuffer.clear(); + BufferSize.clear(); + BufferReady.clear(); +} + +void BufferCircle::SetBufferBlockSize(s32 size) +{ + if(size < 0) + return; + + BufferBlockSize = size; + + for(s32 i = 0; i < Size(); i++) + { + if(SoundBuffer[i] != NULL) + free(SoundBuffer[i]); + + SoundBuffer[i] = (u8 *) memalign(32, ALIGN32(BufferBlockSize)); + BufferSize[i] = 0; + BufferReady[i] = false; + } +} + +void BufferCircle::Resize(s32 size) +{ + while(size < Size()) + RemoveBuffer(Size()-1); + + s32 oldSize = Size(); + + SoundBuffer.resize(size); + BufferSize.resize(size); + BufferReady.resize(size); + + for(s32 i = oldSize; i < Size(); i++) + { + if(BufferBlockSize > 0) + SoundBuffer[i] = (u8 *) memalign(32, ALIGN32(BufferBlockSize)); + else + SoundBuffer[i] = NULL; + BufferSize[i] = 0; + BufferReady[i] = false; + } +} + +void BufferCircle::RemoveBuffer(s32 pos) +{ + if(!Valid(pos)) + return; + + if(SoundBuffer[pos] != NULL) + free(SoundBuffer[pos]); + + SoundBuffer.erase(SoundBuffer.begin()+pos); + BufferSize.erase(BufferSize.begin()+pos); + BufferReady.erase(BufferReady.begin()+pos); +} + +void BufferCircle::ClearBuffer() +{ + for(s32 i = 0; i < Size(); i++) + { + BufferSize[i] = 0; + BufferReady[i] = false; + } + which = 0; +} + +void BufferCircle::FreeBuffer() +{ + for(s32 i = 0; i < Size(); i++) + { + if(SoundBuffer[i] != NULL) + free(SoundBuffer[i]); + + SoundBuffer[i] = NULL; + BufferSize[i] = 0; + BufferReady[i] = false; + } +} + +void BufferCircle::LoadNext() +{ + BufferReady[which] = false; + BufferSize[which] = 0; + + which = Next(); +} + +void BufferCircle::SetBufferReady(s32 pos, bool state) +{ + if(!Valid(pos)) + return; + + BufferReady[pos] = state; +} + +void BufferCircle::SetBufferSize(s32 pos, s32 size) +{ + if(!Valid(pos)) + return; + + BufferSize[pos] = size; +} diff --git a/source/sounds/BufferCircle.hpp b/source/sounds/BufferCircle.hpp new file mode 100644 index 0000000..8118aaf --- /dev/null +++ b/source/sounds/BufferCircle.hpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef BUFFER_CIRCLE_HPP_ +#define BUFFER_CIRCLE_HPP_ + +#include +#include + +class BufferCircle +{ + public: + //!> Constructor + BufferCircle(); + //!> Destructor + ~BufferCircle(); + //!> Set circle size + void Resize(s32 size); + //!> Get the circle size + s32 Size() { return SoundBuffer.size(); }; + //!> Set/resize the buffer size + void SetBufferBlockSize(s32 size); + //!> Remove a buffer + void RemoveBuffer(s32 pos); + //!> Set all buffers clear + void ClearBuffer(); + //!> Free all buffers + void FreeBuffer(); + //!> Switch to next buffer + void LoadNext(); + //!> Get the current buffer + u8 * GetBuffer() { return GetBuffer(which); }; + //!> Get a buffer at a position + u8 * GetBuffer(s32 pos) { if(!Valid(pos)) return NULL; else return SoundBuffer[pos]; }; + //!> Get current buffer size + u32 GetBufferSize() { return GetBufferSize(which); }; + //!> Get buffer size at position + u32 GetBufferSize(s32 pos) { if(!Valid(pos)) return 0; else return BufferSize[pos]; }; + //!> Is current buffer ready + bool IsBufferReady() { return IsBufferReady(which); }; + //!> Is a buffer at a position ready + bool IsBufferReady(s32 pos) { if(!Valid(pos)) return false; else return BufferReady[pos]; }; + //!> Set a buffer at a position to a ready state + void SetBufferReady(s32 pos, bool st); + //!> Set the buffersize at a position + void SetBufferSize(s32 pos, s32 size); + //!> Get the current position in the circle + u16 Which() { return which; }; + + //!> Get the next location + inline u16 Next() { return (which+1 >= Size()) ? 0 : which+1; } + inline u16 Prev() { if(Size() == 0) return 0; else return ((s32)which-1 < 0) ? Size()-1 : which-1; } + protected: + //!> Check if the position is a valid position in the vector + bool Valid(s32 pos) { return !(pos < 0 || pos >= Size()); }; + + u16 which; + u32 BufferBlockSize; + std::vector SoundBuffer; + std::vector BufferSize; + std::vector BufferReady; +}; + +#endif diff --git a/source/sounds/Mp3Decoder.cpp b/source/sounds/Mp3Decoder.cpp new file mode 100644 index 0000000..fd48907 --- /dev/null +++ b/source/sounds/Mp3Decoder.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "Mp3Decoder.hpp" + +Mp3Decoder::Mp3Decoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_MP3; + ReadBuffer = NULL; + mad_timer_reset(&Timer); + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + + if(!file_fd) + return; + + OpenFile(); +} + +Mp3Decoder::Mp3Decoder(const u8 * snd, s32 len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_MP3; + ReadBuffer = NULL; + mad_timer_reset(&Timer); + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + + if(!file_fd) + return; + + OpenFile(); +} + +Mp3Decoder::~Mp3Decoder() +{ + ExitRequested = true; + while(Decoding) + os_usleep(100); + + mad_synth_finish(&Synth); + mad_frame_finish(&Frame); + mad_stream_finish(&Stream); + + if(ReadBuffer) + free(ReadBuffer); + ReadBuffer = NULL; +} + +void Mp3Decoder::OpenFile() +{ + GuardPtr = NULL; + ReadBuffer = (u8 *) memalign(32, SoundBlockSize*SoundBlocks); + if(!ReadBuffer) + { + if(file_fd) + delete file_fd; + file_fd = NULL; + return; + } + + u8 dummybuff[4096]; + s32 ret = Read(dummybuff, 4096, 0); + if(ret <= 0) + { + if(file_fd) + delete file_fd; + file_fd = NULL; + return; + } + + SampleRate = (u32) Frame.header.samplerate; + Format = ((MAD_NCHANNELS(&Frame.header) == 2) ? (FORMAT_PCM_16_BIT | CHANNELS_STEREO) : (FORMAT_PCM_16_BIT | CHANNELS_MONO)); + Rewind(); +} + +s32 Mp3Decoder::Rewind() +{ + mad_synth_finish(&Synth); + mad_frame_finish(&Frame); + mad_stream_finish(&Stream); + mad_timer_reset(&Timer); + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + SynthPos = 0; + GuardPtr = NULL; + + if(!file_fd) + return -1; + + return SoundDecoder::Rewind(); +} + +static inline s16 FixedToShort(mad_fixed_t Fixed) +{ + /* Clipping */ + if(Fixed>=MAD_F_ONE) + return(SHRT_MAX); + if(Fixed<=-MAD_F_ONE) + return(-SHRT_MAX); + + Fixed=Fixed>>(MAD_F_FRACBITS-15); + return((s16)Fixed); +} + +s32 Mp3Decoder::Read(u8 * buffer, s32 buffer_size, s32 pos) +{ + if(!file_fd) + return -1; + + if(Format == (FORMAT_PCM_16_BIT | CHANNELS_STEREO)) + buffer_size &= ~0x0003; + else + buffer_size &= ~0x0001; + + u8 * write_pos = buffer; + u8 * write_end = buffer+buffer_size; + + while(1) + { + while(SynthPos < Synth.pcm.length) + { + if(write_pos >= write_end) + return write_pos-buffer; + + *((s16 *) write_pos) = FixedToShort(Synth.pcm.samples[0][SynthPos]); + write_pos += 2; + + if(MAD_NCHANNELS(&Frame.header) == 2) + { + *((s16 *) write_pos) = FixedToShort(Synth.pcm.samples[1][SynthPos]); + write_pos += 2; + } + SynthPos++; + } + + if(Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) + { + u8 * ReadStart = ReadBuffer; + s32 ReadSize = SoundBlockSize*SoundBlocks; + s32 Remaining = 0; + + if(Stream.next_frame != NULL) + { + Remaining = Stream.bufend - Stream.next_frame; + memmove(ReadBuffer, Stream.next_frame, Remaining); + ReadStart += Remaining; + ReadSize -= Remaining; + } + + ReadSize = file_fd->read(ReadStart, ReadSize); + if(ReadSize <= 0) + { + GuardPtr = ReadStart; + memset(GuardPtr, 0, MAD_BUFFER_GUARD); + ReadSize = MAD_BUFFER_GUARD; + } + + CurPos += ReadSize; + mad_stream_buffer(&Stream, ReadBuffer, Remaining+ReadSize); + } + + if(mad_frame_decode(&Frame,&Stream)) + { + if(MAD_RECOVERABLE(Stream.error)) + { + if(Stream.error != MAD_ERROR_LOSTSYNC || !GuardPtr) + continue; + } + else + { + if(Stream.error != MAD_ERROR_BUFLEN) + return -1; + else if(Stream.error == MAD_ERROR_BUFLEN && GuardPtr) + return -1; + } + } + + mad_timer_add(&Timer,Frame.header.duration); + mad_synth_frame(&Synth,&Frame); + SynthPos = 0; + } + return 0; +} diff --git a/source/sounds/Mp3Decoder.hpp b/source/sounds/Mp3Decoder.hpp new file mode 100644 index 0000000..d8f4c53 --- /dev/null +++ b/source/sounds/Mp3Decoder.hpp @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include + +#include "SoundDecoder.hpp" + +class Mp3Decoder : public SoundDecoder +{ + public: + Mp3Decoder(const char * filepath); + Mp3Decoder(const u8 * sound, s32 len); + virtual ~Mp3Decoder(); + s32 Rewind(); + s32 Read(u8 * buffer, s32 buffer_size, s32 pos); + protected: + void OpenFile(); + struct mad_stream Stream; + struct mad_frame Frame; + struct mad_synth Synth; + mad_timer_t Timer; + u8 * GuardPtr; + u8 * ReadBuffer; + u32 SynthPos; +}; diff --git a/source/sounds/OggDecoder.cpp b/source/sounds/OggDecoder.cpp new file mode 100644 index 0000000..d2c0411 --- /dev/null +++ b/source/sounds/OggDecoder.cpp @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include "OggDecoder.hpp" + +static int ogg_read(void * punt, int bytes, int blocks, int *f) +{ + return ((CFile *) f)->read((u8 *) punt, bytes*blocks); +} + +static int ogg_seek(int *f, ogg_int64_t offset, int mode) +{ + return ((CFile *) f)->seek((u64) offset, mode); +} + +static int ogg_close(int *f) +{ + ((CFile *) f)->close(); + return 0; +} + +static long ogg_tell(int *f) +{ + return (long) ((CFile *) f)->tell(); +} + +static ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) ogg_read, + (int (*)(void *, ogg_int64_t, int)) ogg_seek, + (int (*)(void *)) ogg_close, + (long (*)(void *)) ogg_tell +}; + +OggDecoder::OggDecoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_OGG; + + if(!file_fd) + return; + + OpenFile(); +} + +OggDecoder::OggDecoder(const u8 * snd, s32 len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_OGG; + + if(!file_fd) + return; + + OpenFile(); +} + +OggDecoder::~OggDecoder() +{ + ExitRequested = true; + while(Decoding) + os_usleep(100); + + if(file_fd) + ov_clear(&ogg_file); +} + +void OggDecoder::OpenFile() +{ + if (ov_open_callbacks(file_fd, &ogg_file, NULL, 0, callbacks) < 0) + { + delete file_fd; + file_fd = NULL; + return; + } + + ogg_info = ov_info(&ogg_file, -1); + if(!ogg_info) + { + ov_clear(&ogg_file); + delete file_fd; + file_fd = NULL; + return; + } + + Format = ((ogg_info->channels == 2) ? (FORMAT_PCM_16_BIT | CHANNELS_STEREO) : (FORMAT_PCM_16_BIT | CHANNELS_MONO)); + SampleRate = ogg_info->rate; +} + +s32 OggDecoder::Rewind() +{ + if(!file_fd) + return -1; + + s32 ret = ov_time_seek(&ogg_file, 0); + CurPos = 0; + EndOfFile = false; + + return ret; +} + +s32 OggDecoder::Read(u8 * buffer, s32 buffer_size, s32 pos) +{ + if(!file_fd) + return -1; + + s32 bitstream = 0; + + s32 read = (s32) ov_read(&ogg_file, (char *) buffer, (int) buffer_size, (int *)&bitstream); + + if(read > 0) + CurPos += read; + + return read; +} diff --git a/source/sounds/OggDecoder.hpp b/source/sounds/OggDecoder.hpp new file mode 100644 index 0000000..9c4a947 --- /dev/null +++ b/source/sounds/OggDecoder.hpp @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include + +#include "SoundDecoder.hpp" + +class OggDecoder : public SoundDecoder +{ + public: + OggDecoder(const char * filepath); + OggDecoder(const u8 * snd, s32 len); + virtual ~OggDecoder(); + s32 Rewind(); + s32 Read(u8 * buffer, s32 buffer_size, s32 pos); + protected: + void OpenFile(); + OggVorbis_File ogg_file; + vorbis_info *ogg_info; +}; diff --git a/source/sounds/SoundDecoder.cpp b/source/sounds/SoundDecoder.cpp new file mode 100644 index 0000000..987db20 --- /dev/null +++ b/source/sounds/SoundDecoder.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** + * Copyright (C) 2009-2013 Dimok + * + * 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 . + ****************************************************************************/ + +#include +#include +#include +#include +#include "SoundDecoder.hpp" + +static const u32 FixedPointShift = 15; +static const u32 FixedPointScale = 1 << FixedPointShift; + +SoundDecoder::SoundDecoder() +{ + file_fd = NULL; + Init(); +} + +SoundDecoder::SoundDecoder(const std::string & filepath) +{ + file_fd = new CFile(filepath, CFile::ReadOnly); + Init(); +} + +SoundDecoder::SoundDecoder(const u8 * buffer, s32 size) +{ + file_fd = new CFile(buffer, size); + Init(); +} + +SoundDecoder::~SoundDecoder() +{ + ExitRequested = true; + while(Decoding) + os_usleep(1000); + + //! lock unlock once to make sure it's really not decoding + Lock(); + Unlock(); + + if(file_fd) + delete file_fd; + file_fd = NULL; + + if(ResampleBuffer) + free(ResampleBuffer); +} + +void SoundDecoder::Init() +{ + SoundType = SOUND_RAW; + SoundBlocks = 8; + SoundBlockSize = 0x4000; + ResampleTo48kHz = false; + CurPos = 0; + whichLoad = 0; + Loop = false; + EndOfFile = false; + Decoding = false; + ExitRequested = false; + SoundBuffer.SetBufferBlockSize(SoundBlockSize); + SoundBuffer.Resize(SoundBlocks); + ResampleBuffer = NULL; + ResampleRatio = 0; +} + +s32 SoundDecoder::Rewind() +{ + CurPos = 0; + EndOfFile = false; + file_fd->rewind(); + + return 0; +} + +s32 SoundDecoder::Read(u8 * buffer, s32 buffer_size, s32 pos) +{ + s32 ret = file_fd->read(buffer, buffer_size); + CurPos += ret; + + return ret; +} + +void SoundDecoder::EnableUpsample(void) +{ + if( (ResampleBuffer == NULL) + && IsStereo() && Is16Bit() + && SampleRate != 32000 + && SampleRate != 48000) + { + ResampleBuffer = (u8*)memalign(32, SoundBlockSize); + ResampleRatio = ( FixedPointScale * SampleRate ) / 48000; + SoundBlockSize = ( SoundBlockSize * ResampleRatio ) / FixedPointScale; + SoundBlockSize &= ~0x03; + // set new sample rate + SampleRate = 48000; + } +} + +void SoundDecoder::Upsample(s16 *src, s16 *dst, u32 nr_src_samples, u32 nr_dst_samples) +{ + s32 timer = 0; + + for(u32 i = 0, n = 0; i < nr_dst_samples; i += 2) + { + if((n+3) < nr_src_samples) { + // simple fixed point linear interpolation + dst[i] = src[n] + ( ((src[n+2] - src[n] ) * timer) >> FixedPointShift ); + dst[i+1] = src[n+1] + ( ((src[n+3] - src[n+1]) * timer) >> FixedPointShift ); + } + else { + dst[i] = src[n]; + dst[i+1] = src[n+1]; + } + + timer += ResampleRatio; + + if(timer >= (s32)FixedPointScale) { + n += 2; + timer -= FixedPointScale; + } + } +} + +void SoundDecoder::Decode() +{ + if(!file_fd || ExitRequested || EndOfFile) + return; + + // check if we are not at the pre-last buffer (last buffer is playing) + u16 whichPlaying = SoundBuffer.Which(); + if( ((whichPlaying == 0) && (whichLoad == SoundBuffer.Size()-2)) + || ((whichPlaying == 1) && (whichLoad == SoundBuffer.Size()-1)) + || (whichLoad == (whichPlaying-2))) + { + return; + } + + Decoding = true; + + s32 done = 0; + u8 * write_buf = SoundBuffer.GetBuffer(whichLoad); + if(!write_buf) + { + ExitRequested = true; + Decoding = false; + return; + } + + if(ResampleTo48kHz && !ResampleBuffer) + EnableUpsample(); + + while(done < SoundBlockSize) + { + s32 ret = Read(&write_buf[done], SoundBlockSize-done, Tell()); + + if(ret <= 0) + { + if(Loop) + { + Rewind(); + continue; + } + else + { + EndOfFile = true; + break; + } + } + + done += ret; + } + + if(done > 0) + { + // check if we need to resample + if(ResampleBuffer && ResampleRatio) + { + memcpy(ResampleBuffer, write_buf, done); + + s32 src_samples = done >> 1; + s32 dest_samples = ( src_samples * FixedPointScale ) / ResampleRatio; + dest_samples &= ~0x01; + Upsample((s16*)ResampleBuffer, (s16*)write_buf, src_samples, dest_samples); + done = dest_samples << 1; + } + + //! TODO: remove this later and add STEREO support with two voices, for now we convert to MONO + if(IsStereo()) + { + s16* monoBuf = (s16*)write_buf; + done = done >> 1; + + for(s32 i = 0; i < done; i++) + monoBuf[i] = monoBuf[i << 1]; + } + + DCFlushRange(write_buf, done); + SoundBuffer.SetBufferSize(whichLoad, done); + SoundBuffer.SetBufferReady(whichLoad, true); + if(++whichLoad >= SoundBuffer.Size()) + whichLoad = 0; + } + + // check if next in queue needs to be filled as well and do so + if(!SoundBuffer.IsBufferReady(whichLoad)) + Decode(); + + Decoding = false; +} + diff --git a/source/sounds/SoundDecoder.hpp b/source/sounds/SoundDecoder.hpp new file mode 100644 index 0000000..495f107 --- /dev/null +++ b/source/sounds/SoundDecoder.hpp @@ -0,0 +1,105 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef SOUND_DECODER_HPP +#define SOUND_DECODER_HPP + +#include +#include +#include "BufferCircle.hpp" + +class SoundDecoder +{ +public: + SoundDecoder(); + SoundDecoder(const std::string & filepath); + SoundDecoder(const u8 * buffer, s32 size); + virtual ~SoundDecoder(); + virtual void Lock() { mutex.lock(); } + virtual void Unlock() { mutex.unlock(); } + virtual s32 Read(u8 * buffer, s32 buffer_size, s32 pos); + virtual s32 Tell() { return CurPos; } + virtual s32 Seek(s32 pos) { CurPos = pos; return file_fd->seek(CurPos, SEEK_SET); } + virtual s32 Rewind(); + virtual u16 GetFormat() { return Format; } + virtual u16 GetSampleRate() { return SampleRate; } + virtual void Decode(); + virtual bool IsBufferReady() { return SoundBuffer.IsBufferReady(); } + virtual u8 * GetBuffer() { return SoundBuffer.GetBuffer(); } + virtual u32 GetBufferSize() { return SoundBuffer.GetBufferSize(); } + virtual void LoadNext() { SoundBuffer.LoadNext(); } + virtual bool IsEOF() { return EndOfFile; } + virtual void SetLoop(bool l) { Loop = l; EndOfFile = false; } + virtual u8 GetSoundType() { return SoundType; } + virtual void ClearBuffer() { SoundBuffer.ClearBuffer(); whichLoad = 0; } + virtual bool IsStereo() { return (GetFormat() & CHANNELS_STEREO) != 0; } + virtual bool Is16Bit() { return ((GetFormat() & 0xFF) == FORMAT_PCM_16_BIT); } + virtual bool IsDecoding() { return Decoding; } + + void EnableUpsample(void); + + enum SoundFormats + { + FORMAT_PCM_16_BIT = 0x0A, + FORMAT_PCM_8_BIT = 0x19, + }; + enum SoundChannels + { + CHANNELS_MONO = 0x100, + CHANNELS_STEREO = 0x200 + }; + + enum SoundType + { + SOUND_RAW = 0, + SOUND_MP3, + SOUND_OGG, + SOUND_WAV + }; +protected: + void Init(); + void Upsample(s16 *src, s16 *dst, u32 nr_src_samples, u32 nr_dst_samples); + + CFile * file_fd; + BufferCircle SoundBuffer; + u8 SoundType; + u16 whichLoad; + u16 SoundBlocks; + s32 SoundBlockSize; + s32 CurPos; + bool ResampleTo48kHz; + bool Loop; + bool EndOfFile; + bool Decoding; + bool ExitRequested; + u16 Format; + u16 SampleRate; + u8 *ResampleBuffer; + u32 ResampleRatio; + CMutex mutex; +}; + + +#endif diff --git a/source/sounds/SoundHandler.cpp b/source/sounds/SoundHandler.cpp new file mode 100644 index 0000000..05bf13a --- /dev/null +++ b/source/sounds/SoundHandler.cpp @@ -0,0 +1,351 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +//#include "common/common.h" +#include +#include "fs/CFile.hpp" +#include "SoundHandler.hpp" +#include "WavDecoder.hpp" +#include "Mp3Decoder.hpp" +#include "OggDecoder.hpp" + +SoundHandler * SoundHandler::handlerInstance = NULL; + +SoundHandler::SoundHandler() + : CThread(CThread::eAttributeAffCore1 | CThread::eAttributePinnedAff, 0, 0x8000) +{ + Decoding = false; + ExitRequested = false; + for(u32 i = 0; i < MAX_DECODERS; ++i) + { + DecoderList[i] = NULL; + voiceList[i] = NULL; + } + + resumeThread(); + + //! wait for initialization + while(!isThreadSuspended()) + os_usleep(1000); +} + +SoundHandler::~SoundHandler() +{ + ExitRequested = true; + ThreadSignal(); + + ClearDecoderList(); +} + +void SoundHandler::AddDecoder(s32 voice, const char * filepath) +{ + if(voice < 0 || voice >= MAX_DECODERS) + return; + + if(DecoderList[voice] != NULL) + RemoveDecoder(voice); + + DecoderList[voice] = GetSoundDecoder(filepath); +} + +void SoundHandler::AddDecoder(s32 voice, const u8 * snd, s32 len) +{ + if(voice < 0 || voice >= MAX_DECODERS) + return; + + if(DecoderList[voice] != NULL) + RemoveDecoder(voice); + + DecoderList[voice] = GetSoundDecoder(snd, len); +} + +void SoundHandler::RemoveDecoder(s32 voice) +{ + if(voice < 0 || voice >= MAX_DECODERS) + return; + + if(DecoderList[voice] != NULL) + { + if(voiceList[voice] && voiceList[voice]->getState() != Voice::STATE_STOPPED) + { + if(voiceList[voice]->getState() != Voice::STATE_STOP) + voiceList[voice]->setState(Voice::STATE_STOP); + + while(voiceList[voice]->getState() != Voice::STATE_STOPPED) + os_usleep(1000); + } + SoundDecoder *decoder = DecoderList[voice]; + decoder->Lock(); + DecoderList[voice] = NULL; + decoder->Unlock(); + delete decoder; + } +} + +void SoundHandler::ClearDecoderList() +{ + for(u32 i = 0; i < MAX_DECODERS; ++i) + RemoveDecoder(i); +} + +static inline bool CheckMP3Signature(const u8 * buffer) +{ + const char MP3_Magic[][3] = + { + {'I', 'D', '3'}, //'ID3' + {0xff, 0xfe}, //'MPEG ADTS, layer III, v1.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xff}, //'MPEG ADTS, layer III, v1.0', 'mp3', 'audio/mpeg'), + {0xff, 0xfa}, //'MPEG ADTS, layer III, v1.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xfb}, //'MPEG ADTS, layer III, v1.0', 'mp3', 'audio/mpeg'), + {0xff, 0xf2}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xf3}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'), + {0xff, 0xf4}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xf5}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'), + {0xff, 0xf6}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xf7}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'), + {0xff, 0xe2}, //'MPEG ADTS, layer III, v2.5 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xe3}, //'MPEG ADTS, layer III, v2.5', 'mp3', 'audio/mpeg'), + }; + + if(buffer[0] == MP3_Magic[0][0] && buffer[1] == MP3_Magic[0][1] && + buffer[2] == MP3_Magic[0][2]) + { + return true; + } + + for(s32 i = 1; i < 13; i++) + { + if(buffer[0] == MP3_Magic[i][0] && buffer[1] == MP3_Magic[i][1]) + return true; + } + + return false; +} + +SoundDecoder * SoundHandler::GetSoundDecoder(const char * filepath) +{ + u32 magic; + CFile f(filepath, CFile::ReadOnly); + if(f.size() == 0) + return NULL; + + do + { + f.read((u8 *) &magic, 1); + } + while(((u8 *) &magic)[0] == 0 && f.tell() < f.size()); + + if(f.tell() == f.size()) + return NULL; + + f.seek(f.tell()-1, SEEK_SET); + f.read((u8 *) &magic, 4); + f.close(); + + if(magic == 0x4f676753) // 'OggS' + { + return new OggDecoder(filepath); + } + else if(magic == 0x52494646) // 'RIFF' + { + return new WavDecoder(filepath); + } + else if(CheckMP3Signature((u8 *) &magic) == true) + { + return new Mp3Decoder(filepath); + } + + return new SoundDecoder(filepath); +} + +SoundDecoder * SoundHandler::GetSoundDecoder(const u8 * sound, s32 length) +{ + const u8 * check = sound; + s32 counter = 0; + + while(check[0] == 0 && counter < length) + { + check++; + counter++; + } + + if(counter >= length) + return NULL; + + u32 * magic = (u32 *) check; + + if(magic[0] == 0x4f676753) // 'OggS' + { + return new OggDecoder(sound, length); + } + else if(magic[0] == 0x52494646) // 'RIFF' + { + return new WavDecoder(sound, length); + } + else if(CheckMP3Signature(check) == true) + { + return new Mp3Decoder(sound, length); + } + + return new SoundDecoder(sound, length); +} + +void SoundHandler::executeThread() +{ + // v2 sound lib can not properly end transition audio on old firmwares + if (OS_FIRMWARE >= 400 && OS_FIRMWARE <= 410) + { + ProperlyEndTransitionAudio(); + } + + //! initialize 48 kHz renderer + u32 params[3] = { 1, 0, 0 }; + + if(AXInitWithParams != 0) + AXInitWithParams(params); + else + AXInit(); + + // The problem with last voice on 500 was caused by it having priority 0 + // We would need to change this priority distribution if for some reason + // we would need MAX_DECODERS > Voice::PRIO_MAX + for(u32 i = 0; i < MAX_DECODERS; ++i) + { + s32 priority = (MAX_DECODERS - i) * Voice::PRIO_MAX / MAX_DECODERS; + voiceList[i] = new Voice(priority); // allocate voice 0 with highest priority + } + + AXRegisterFrameCallback((void*)&axFrameCallback); + + + u16 i = 0; + while (!ExitRequested) + { + suspendThread(); + + for(i = 0; i < MAX_DECODERS; ++i) + { + if(DecoderList[i] == NULL) + continue; + + Decoding = true; + if(DecoderList[i]) + DecoderList[i]->Lock(); + if(DecoderList[i]) + DecoderList[i]->Decode(); + if(DecoderList[i]) + DecoderList[i]->Unlock(); + } + Decoding = false; + } + + for(u32 i = 0; i < MAX_DECODERS; ++i) + voiceList[i]->stop(); + + AXRegisterFrameCallback(NULL); + AXQuit(); + + for(u32 i = 0; i < MAX_DECODERS; ++i) + { + delete voiceList[i]; + voiceList[i] = NULL; + } +} + +void SoundHandler::axFrameCallback(void) +{ + for (u32 i = 0; i < MAX_DECODERS; i++) + { + Voice *voice = handlerInstance->getVoice(i); + + switch (voice->getState()) + { + default: + case Voice::STATE_STOPPED: + break; + + case Voice::STATE_START: { + SoundDecoder * decoder = handlerInstance->getDecoder(i); + decoder->Lock(); + if(decoder->IsBufferReady()) + { + const u8 *buffer = decoder->GetBuffer(); + const u32 bufferSize = decoder->GetBufferSize(); + decoder->LoadNext(); + + const u8 *nextBuffer = NULL; + u32 nextBufferSize = 0; + + if(decoder->IsBufferReady()) + { + nextBuffer = decoder->GetBuffer(); + nextBufferSize = decoder->GetBufferSize(); + decoder->LoadNext(); + } + + voice->play(buffer, bufferSize, nextBuffer, nextBufferSize, decoder->GetFormat() & 0xff, decoder->GetSampleRate()); + + handlerInstance->ThreadSignal(); + + voice->setState(Voice::STATE_PLAYING); + } + decoder->Unlock(); + break; + } + case Voice::STATE_PLAYING: + if(voice->getInternState() == 1) + { + if(voice->isBufferSwitched()) + { + SoundDecoder * decoder = handlerInstance->getDecoder(i); + decoder->Lock(); + if(decoder->IsBufferReady()) + { + voice->setNextBuffer(decoder->GetBuffer(), decoder->GetBufferSize()); + decoder->LoadNext(); + handlerInstance->ThreadSignal(); + } + else if(decoder->IsEOF()) + { + voice->setState(Voice::STATE_STOP); + } + decoder->Unlock(); + } + } + else + { + voice->setState(Voice::STATE_STOPPED); + } + break; + case Voice::STATE_STOP: + if(voice->getInternState() != 0) + voice->stop(); + voice->setState(Voice::STATE_STOPPED); + break; + } + } +} diff --git a/source/sounds/SoundHandler.hpp b/source/sounds/SoundHandler.hpp new file mode 100644 index 0000000..928e876 --- /dev/null +++ b/source/sounds/SoundHandler.hpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef SOUNDHANDLER_H_ +#define SOUNDHANDLER_H_ + +#include + +#include +#include "SoundDecoder.hpp" +#include "Voice.h" + +#define MAX_DECODERS 16 // can be increased up to 96 + +class SoundHandler : public CThread +{ +public: + static SoundHandler * instance() { + if (!handlerInstance) + handlerInstance = new SoundHandler(); + return handlerInstance; + } + + static void DestroyInstance() { delete handlerInstance; handlerInstance = NULL; } + + void AddDecoder(s32 voice, const char * filepath); + void AddDecoder(s32 voice, const u8 * snd, s32 len); + void RemoveDecoder(s32 voice); + + SoundDecoder * getDecoder(s32 i) { return ((i < 0 || i >= MAX_DECODERS) ? NULL : DecoderList[i]); }; + Voice * getVoice(s32 i) { return ((i < 0 || i >= MAX_DECODERS) ? NULL : voiceList[i]); }; + + void ThreadSignal() { resumeThread(); }; + bool IsDecoding() { return Decoding; }; +protected: + SoundHandler(); + ~SoundHandler(); + + static void axFrameCallback(void); + + void executeThread(void); + void ClearDecoderList(); + + SoundDecoder * GetSoundDecoder(const char * filepath); + SoundDecoder * GetSoundDecoder(const u8 * sound, s32 length); + + static SoundHandler * handlerInstance; + + bool Decoding; + bool ExitRequested; + + Voice * voiceList[MAX_DECODERS]; + SoundDecoder * DecoderList[MAX_DECODERS]; +}; + +#endif diff --git a/source/sounds/Voice.h b/source/sounds/Voice.h new file mode 100644 index 0000000..18985a1 --- /dev/null +++ b/source/sounds/Voice.h @@ -0,0 +1,170 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef _AXSOUND_H_ +#define _AXSOUND_H_ + +#include +#include + +class Voice +{ +public: + + enum VoicePriorities + { + PRIO_MIN = 1, + PRIO_MAX = 31 + }; + + enum VoiceStates + { + STATE_STOPPED, + STATE_START, + STATE_PLAYING, + STATE_STOP, + }; + + Voice(s32 prio) + : state(STATE_STOPPED) + { + lastLoopCounter = 0; + nextBufferSize = 0; + + voice = AXAcquireVoice(prio, 0, 0); + if(voice) + { + AXVoiceBegin(voice); + AXSetVoiceType(voice, 0); + setVolume(0x80000000); + + u32 mix[24]; + memset(mix, 0, sizeof(mix)); + mix[0] = 0x80000000; + mix[4] = 0x80000000; + + AXSetVoiceDeviceMix(voice, 0, 0, mix); + AXSetVoiceDeviceMix(voice, 1, 0, mix); + + AXVoiceEnd(voice); + } + } + + ~Voice() + { + if(voice) + { + AXFreeVoice(voice); + } + } + + void play(const u8 *buffer, u32 bufferSize, const u8 *nextBuffer, u32 nextBufSize, u16 format, u32 sampleRate) + { + if(!voice) + return; + + memset(&voiceBuffer, 0, sizeof(voiceBuffer)); + + voiceBuffer.samples = buffer; + voiceBuffer.format = format; + voiceBuffer.loop = (nextBuffer == NULL) ? 0 : 1; + voiceBuffer.cur_pos = 0; + voiceBuffer.end_pos = bufferSize >> 1; + voiceBuffer.loop_offset = ((nextBuffer - buffer) >> 1); + nextBufferSize = nextBufSize; + + u32 samplesPerSec = (AXGetInputSamplesPerSec != 0) ? AXGetInputSamplesPerSec() : 32000; + + ratioBits[0] = (u32)(0x00010000 * ((f32)sampleRate / (f32)samplesPerSec)); + ratioBits[1] = 0; + ratioBits[2] = 0; + ratioBits[3] = 0; + + AXSetVoiceOffsets(voice, &voiceBuffer); + AXSetVoiceSrc(voice, ratioBits); + AXSetVoiceSrcType(voice, 1); + AXSetVoiceState(voice, 1); + } + + void stop() + { + if(voice) + AXSetVoiceState(voice, 0); + } + + void setVolume(u32 vol) + { + if(voice) + AXSetVoiceVe(voice, &vol); + } + + + void setNextBuffer(const u8 *buffer, u32 bufferSize) + { + voiceBuffer.loop_offset = ((buffer - voiceBuffer.samples) >> 1); + nextBufferSize = bufferSize; + + AXSetVoiceLoopOffset(voice, voiceBuffer.loop_offset); + } + + bool isBufferSwitched() + { + u32 loopCounter = AXGetVoiceLoopCount(voice); + if(lastLoopCounter != loopCounter) + { + lastLoopCounter = loopCounter; + AXSetVoiceEndOffset(voice, voiceBuffer.loop_offset + (nextBufferSize >> 1)); + return true; + } + return false; + } + + u32 getInternState() const { + if(voice) + return ((u32 *)voice)[1]; + return 0; + } + u32 getState() const { + return state; + } + void setState(u32 s) { + state = s; + } + + void * getVoice() const { + return voice; + } + +private: + void *voice; + u32 ratioBits[4]; + + typedef struct _ax_buffer_t { + u16 format; + u16 loop; + u32 loop_offset; + u32 end_pos; + u32 cur_pos; + const unsigned char *samples; + } ax_buffer_t; + + ax_buffer_t voiceBuffer; + u32 state; + u32 nextBufferSize; + u32 lastLoopCounter; +}; + +#endif // _AXSOUND_H_ diff --git a/source/sounds/WavDecoder.cpp b/source/sounds/WavDecoder.cpp new file mode 100644 index 0000000..09142f2 --- /dev/null +++ b/source/sounds/WavDecoder.cpp @@ -0,0 +1,154 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include "WavDecoder.hpp" +#include "utils/utils.h" + +WavDecoder::WavDecoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_WAV; + SampleRate = 48000; + Format = CHANNELS_STEREO | FORMAT_PCM_16_BIT; + + if(!file_fd) + return; + + OpenFile(); +} + +WavDecoder::WavDecoder(const u8 * snd, s32 len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_WAV; + SampleRate = 48000; + Format = CHANNELS_STEREO | FORMAT_PCM_16_BIT; + + if(!file_fd) + return; + + OpenFile(); +} + +WavDecoder::~WavDecoder() +{ +} + + +void WavDecoder::OpenFile() +{ + SWaveHdr Header; + SWaveFmtChunk FmtChunk; + memset(&Header, 0, sizeof(SWaveHdr)); + memset(&FmtChunk, 0, sizeof(SWaveFmtChunk)); + + file_fd->read((u8 *) &Header, sizeof(SWaveHdr)); + file_fd->read((u8 *) &FmtChunk, sizeof(SWaveFmtChunk)); + + if (Header.magicRIFF != 0x52494646) // 'RIFF' + { + CloseFile(); + return; + } + else if(Header.magicWAVE != 0x57415645) // 'WAVE' + { + CloseFile(); + return; + } + else if(FmtChunk.magicFMT != 0x666d7420) // 'fmt ' + { + CloseFile(); + return; + } + + DataOffset = sizeof(SWaveHdr)+le32(FmtChunk.size)+8; + file_fd->seek(DataOffset, SEEK_SET); + SWaveChunk DataChunk; + file_fd->read((u8 *) &DataChunk, sizeof(SWaveChunk)); + + while(DataChunk.magicDATA != 0x64617461) // 'data' + { + DataOffset += 8+le32(DataChunk.size); + file_fd->seek(DataOffset, SEEK_SET); + s32 ret = file_fd->read((u8 *) &DataChunk, sizeof(SWaveChunk)); + if(ret <= 0) + { + CloseFile(); + return; + } + } + + DataOffset += 8; + DataSize = le32(DataChunk.size); + Is16Bit = (le16(FmtChunk.bps) == 16); + SampleRate = le32(FmtChunk.freq); + + if (le16(FmtChunk.channels) == 1 && le16(FmtChunk.bps) == 8 && le16(FmtChunk.alignment) <= 1) + Format = CHANNELS_MONO | FORMAT_PCM_8_BIT; + else if (le16(FmtChunk.channels) == 1 && le16(FmtChunk.bps) == 16 && le16(FmtChunk.alignment) <= 2) + Format = CHANNELS_MONO | FORMAT_PCM_16_BIT; + else if (le16(FmtChunk.channels) == 2 && le16(FmtChunk.bps) == 8 && le16(FmtChunk.alignment) <= 2) + Format = CHANNELS_STEREO | FORMAT_PCM_8_BIT; + else if (le16(FmtChunk.channels) == 2 && le16(FmtChunk.bps) == 16 && le16(FmtChunk.alignment) <= 4) + Format = CHANNELS_STEREO | FORMAT_PCM_16_BIT; +} + +void WavDecoder::CloseFile() +{ + if(file_fd) + delete file_fd; + + file_fd = NULL; +} + +s32 WavDecoder::Read(u8 * buffer, s32 buffer_size, s32 pos) +{ + if(!file_fd) + return -1; + + if(CurPos >= (s32) DataSize) + return 0; + + file_fd->seek(DataOffset+CurPos, SEEK_SET); + + if(buffer_size > (s32) DataSize-CurPos) + buffer_size = DataSize-CurPos; + + s32 read = file_fd->read(buffer, buffer_size); + if(read > 0) + { + if (Is16Bit) + { + read &= ~0x0001; + + for (u32 i = 0; i < (u32) (read / sizeof (u16)); ++i) + ((u16 *) buffer)[i] = le16(((u16 *) buffer)[i]); + } + CurPos += read; + } + + return read; +} diff --git a/source/sounds/WavDecoder.hpp b/source/sounds/WavDecoder.hpp new file mode 100644 index 0000000..67de796 --- /dev/null +++ b/source/sounds/WavDecoder.hpp @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef WAVDECODER_HPP_ +#define WAVDECODER_HPP_ + +#include "SoundDecoder.hpp" + +typedef struct +{ + u32 magicRIFF; + u32 size; + u32 magicWAVE; +} SWaveHdr; + +typedef struct +{ + u32 magicFMT; + u32 size; + u16 format; + u16 channels; + u32 freq; + u32 avgBps; + u16 alignment; + u16 bps; +} SWaveFmtChunk; + +typedef struct +{ + u32 magicDATA; + u32 size; +} SWaveChunk; + +class WavDecoder : public SoundDecoder +{ + public: + WavDecoder(const char * filepath); + WavDecoder(const u8 * snd, s32 len); + virtual ~WavDecoder(); + s32 Read(u8 * buffer, s32 buffer_size, s32 pos); + protected: + void OpenFile(); + void CloseFile(); + u32 DataOffset; + u32 DataSize; + bool Is16Bit; +}; + +#endif diff --git a/source/video/CVideo.cpp b/source/video/CVideo.cpp new file mode 100644 index 0000000..cdb4a29 --- /dev/null +++ b/source/video/CVideo.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "CVideo.h" +#include +#include "shaders/Texture2DShader.h" +#include "shaders/ColorShader.h" +#include "shaders/Shader3D.h" +#include "shaders/ShaderFractalColor.h" +#include "shaders/FXAAShader.h" +#include + +CVideo::CVideo(s32 forceTvScanMode, s32 forceDrcScanMode) +{ + tvEnabled = false; + drcEnabled = false; + + //! allocate MEM2 command buffer memory + gx2CommandBuffer = MEM2_alloc(GX2_COMMAND_BUFFER_SIZE, 0x40); + + //! initialize GX2 command buffer + u32 gx2_init_attributes[9]; + gx2_init_attributes[0] = GX2_INIT_ATTRIB_CB_BASE; + gx2_init_attributes[1] = (u32)gx2CommandBuffer; + gx2_init_attributes[2] = GX2_INIT_ATTRIB_CB_SIZE; + gx2_init_attributes[3] = GX2_COMMAND_BUFFER_SIZE; + gx2_init_attributes[4] = GX2_INIT_ATTRIB_ARGC; + gx2_init_attributes[5] = 0; + gx2_init_attributes[6] = GX2_INIT_ATTRIB_ARGV; + gx2_init_attributes[7] = 0; + gx2_init_attributes[8] = GX2_INIT_ATTRIB_NULL; + GX2Init(gx2_init_attributes); + + //! GX2 resources are not used in this application but if needed, the allocator is setup + GX2RSetAllocator(&CVideo::GX2RAlloc, &CVideo::GX2RFree); + + u32 scanBufferSize = 0; + s32 scaleNeeded = 0; + + s32 tvScanMode = (forceTvScanMode >= 0) ? forceTvScanMode : GX2GetSystemTVScanMode(); + s32 drcScanMode = (forceDrcScanMode >= 0) ? forceDrcScanMode : GX2GetSystemDRCScanMode(); + + s32 tvRenderMode; + u32 tvWidth = 0; + u32 tvHeight = 0; + + switch(tvScanMode) + { + case GX2_TV_SCAN_MODE_480I: + case GX2_TV_SCAN_MODE_480P: + tvWidth = 854; + tvHeight = 480; + tvRenderMode = GX2_TV_RENDER_480_WIDE; + break; + case GX2_TV_SCAN_MODE_1080I: + case GX2_TV_SCAN_MODE_1080P: + tvWidth = 1920; + tvHeight = 1080; + tvRenderMode = GX2_TV_RENDER_1080; + break; + case GX2_TV_SCAN_MODE_720P: + default: + tvWidth = 1280; + tvHeight = 720; + tvRenderMode = GX2_TV_RENDER_720; + break; + } + + s32 tvAAMode = GX2_AA_MODE_1X; + s32 drcAAMode = GX2_AA_MODE_4X; + + //! calculate the scale factor for later texture resize + widthScaleFactor = 1.0f / (f32)tvWidth; + heightScaleFactor = 1.0f / (f32)tvHeight; + depthScaleFactor = widthScaleFactor; + + //! calculate the size needed for the TV scan buffer and allocate the buffer from bucket memory + GX2CalcTVSize(tvRenderMode, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GX2_BUFFERING_DOUBLE, &scanBufferSize, &scaleNeeded); + tvScanBuffer = MEMBucket_alloc(scanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT); + GX2Invalidate(GX2_INVALIDATE_CPU, tvScanBuffer, scanBufferSize); + GX2SetTVBuffer(tvScanBuffer, scanBufferSize, tvRenderMode, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GX2_BUFFERING_DOUBLE); + + //! calculate the size needed for the DRC scan buffer and allocate the buffer from bucket memory + GX2CalcDRCSize(drcScanMode, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GX2_BUFFERING_DOUBLE, &scanBufferSize, &scaleNeeded); + drcScanBuffer = MEMBucket_alloc(scanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT); + GX2Invalidate(GX2_INVALIDATE_CPU, drcScanBuffer, scanBufferSize); + GX2SetDRCBuffer(drcScanBuffer, scanBufferSize, drcScanMode, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GX2_BUFFERING_DOUBLE); + + //! Setup color buffer for TV rendering + GX2InitColorBuffer(&tvColorBuffer, GX2_SURFACE_DIM_2D, tvWidth, tvHeight, 1, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, tvAAMode); + tvColorBuffer.surface.image_data = MEM1_alloc(tvColorBuffer.surface.image_size, tvColorBuffer.surface.align); + GX2Invalidate(GX2_INVALIDATE_CPU, tvColorBuffer.surface.image_data, tvColorBuffer.surface.image_size); + + //! due to AA we can only use 16 bit depth buffer in MEM1 otherwise we would have to switch to mem2 for depth buffer + //! this should be ok for our purpose i guess + + //! Setup TV depth buffer (can be the same for both if rendered one after another) + u32 size, align; + GX2InitDepthBuffer(&tvDepthBuffer, GX2_SURFACE_DIM_2D, tvColorBuffer.surface.width, tvColorBuffer.surface.height, 1, GX2_SURFACE_FORMAT_TCD_R32_FLOAT, tvAAMode); + tvDepthBuffer.surface.image_data = MEM1_alloc(tvDepthBuffer.surface.image_size, tvDepthBuffer.surface.align); + GX2Invalidate(GX2_INVALIDATE_CPU, tvDepthBuffer.surface.image_data, tvDepthBuffer.surface.image_size); + + //! Setup TV HiZ buffer + GX2CalcDepthBufferHiZInfo(&tvDepthBuffer, &size, &align); + tvDepthBuffer.hiZ_data = MEM1_alloc(size, align); + GX2Invalidate(GX2_INVALIDATE_CPU, tvDepthBuffer.hiZ_data, size); + GX2InitDepthBufferHiZEnable(&tvDepthBuffer, GX2_ENABLE); + + //! Setup color buffer for DRC rendering + GX2InitColorBuffer(&drcColorBuffer, GX2_SURFACE_DIM_2D, 854, 480, 1, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, drcAAMode); + drcColorBuffer.surface.image_data = MEM1_alloc(drcColorBuffer.surface.image_size, drcColorBuffer.surface.align); + GX2Invalidate(GX2_INVALIDATE_CPU, drcColorBuffer.surface.image_data, drcColorBuffer.surface.image_size); + + //! Setup DRC depth buffer (can be the same for both if rendered one after another) + GX2InitDepthBuffer(&drcDepthBuffer, GX2_SURFACE_DIM_2D, drcColorBuffer.surface.width, drcColorBuffer.surface.height, 1, GX2_SURFACE_FORMAT_TCD_R32_FLOAT, drcAAMode); + drcDepthBuffer.surface.image_data = MEM1_alloc(drcDepthBuffer.surface.image_size, drcDepthBuffer.surface.align); + GX2Invalidate(GX2_INVALIDATE_CPU, drcDepthBuffer.surface.image_data, drcDepthBuffer.surface.image_size); + + //! Setup DRC HiZ buffer + GX2CalcDepthBufferHiZInfo(&drcDepthBuffer, &size, &align); + drcDepthBuffer.hiZ_data = MEM1_alloc(size, align); + GX2Invalidate(GX2_INVALIDATE_CPU, drcDepthBuffer.hiZ_data, size); + GX2InitDepthBufferHiZEnable(&drcDepthBuffer, GX2_ENABLE); + + + //! allocate auxilary buffer last as there might not be enough MEM1 left for other stuff after that + if (tvColorBuffer.surface.aa) + { + u32 auxSize, auxAlign; + GX2CalcColorBufferAuxInfo(&tvColorBuffer, &auxSize, &auxAlign); + tvColorBuffer.aux_data = MEM1_alloc(auxSize, auxAlign); + if(!tvColorBuffer.aux_data) + tvColorBuffer.aux_data = MEM2_alloc(auxSize, auxAlign); + + tvColorBuffer.aux_size = auxSize; + memset(tvColorBuffer.aux_data, GX2_AUX_BUFFER_CLEAR_VALUE, auxSize); + GX2Invalidate(GX2_INVALIDATE_CPU, tvColorBuffer.aux_data, auxSize); + } + + if (drcColorBuffer.surface.aa) + { + u32 auxSize, auxAlign; + GX2CalcColorBufferAuxInfo(&drcColorBuffer, &auxSize, &auxAlign); + drcColorBuffer.aux_data = MEM1_alloc(auxSize, auxAlign); + if(!drcColorBuffer.aux_data) + drcColorBuffer.aux_data = MEM2_alloc(auxSize, auxAlign); + drcColorBuffer.aux_size = auxSize; + memset(drcColorBuffer.aux_data, GX2_AUX_BUFFER_CLEAR_VALUE, auxSize); + GX2Invalidate(GX2_INVALIDATE_CPU, drcColorBuffer.aux_data, auxSize ); + } + + //! allocate memory and setup context state TV + tvContextState = (GX2ContextState*)MEM2_alloc(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT); + GX2SetupContextStateEx(tvContextState, GX2_TRUE); + + //! allocate memory and setup context state DRC + drcContextState = (GX2ContextState*)MEM2_alloc(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT); + GX2SetupContextStateEx(drcContextState, GX2_TRUE); + + //! set initial context state and render buffers + GX2SetContextState(tvContextState); + GX2SetColorBuffer(&tvColorBuffer, GX2_RENDER_TARGET_0); + GX2SetDepthBuffer(&tvDepthBuffer); + + GX2SetContextState(drcContextState); + GX2SetColorBuffer(&drcColorBuffer, GX2_RENDER_TARGET_0); + GX2SetDepthBuffer(&drcDepthBuffer); + + //! set initial viewport + GX2SetViewport(0.0f, 0.0f, tvColorBuffer.surface.width, tvColorBuffer.surface.height, 0.0f, 1.0f); + GX2SetScissor(0, 0, tvColorBuffer.surface.width, tvColorBuffer.surface.height); + + //! this is not necessary but can be used for swap counting and vsyncs + GX2SetSwapInterval(1); + + //GX2SetTVGamma(0.8f); + //GX2SetDRCGamma(0.8f); + + //! initialize perspective matrix + const float cam_X_rot = 25.0f; + + projectionMtx = glm::perspective(45.0f, 1.0f, 0.1f, 100.0f); + + viewMtx = glm::mat4(1.0f); + viewMtx = glm::translate(viewMtx, glm::vec3(0.0f, 0.0f, -2.5f)); + viewMtx = glm::rotate(viewMtx, DegToRad(cam_X_rot), glm::vec3(1.0f, 0.0f, 0.0f)); + + GX2InitSampler(&aaSampler, GX2_TEX_CLAMP_CLAMP, GX2_TEX_XY_FILTER_BILINEAR); + GX2InitTexture(&tvAaTexture, tvColorBuffer.surface.width, tvColorBuffer.surface.height, 1, 0, GX2_SURFACE_FORMAT_TCS_R8_G8_B8_A8_UNORM, GX2_SURFACE_DIM_2D, GX2_TILE_MODE_DEFAULT); + tvAaTexture.surface.image_data = tvColorBuffer.surface.image_data; + tvAaTexture.surface.image_size = tvColorBuffer.surface.image_size; + tvAaTexture.surface.mip_data = tvColorBuffer.surface.mip_data; +} + +CVideo::~CVideo() +{ + //! flush buffers + GX2Flush(); + GX2DrawDone(); + //! shutdown + GX2Shutdown(); + //! free command buffer memory + MEM2_free(gx2CommandBuffer); + //! free scan buffers + MEMBucket_free(tvScanBuffer); + MEMBucket_free(drcScanBuffer); + //! free color buffers + MEM1_free(tvColorBuffer.surface.image_data); + MEM1_free(drcColorBuffer.surface.image_data); + //! free depth buffers + MEM1_free(tvDepthBuffer.surface.image_data); + MEM1_free(tvDepthBuffer.hiZ_data); + MEM1_free(drcDepthBuffer.surface.image_data); + MEM1_free(drcDepthBuffer.hiZ_data); + //! free context buffers + MEM2_free(tvContextState); + MEM2_free(drcContextState); + //! free aux buffer + if(tvColorBuffer.aux_data) + { + if(((u32)tvColorBuffer.aux_data & 0xF0000000) == 0xF0000000) + MEM1_free(tvColorBuffer.aux_data); + else + MEM2_free(tvColorBuffer.aux_data); + } + if(drcColorBuffer.aux_data) + { + if(((u32)drcColorBuffer.aux_data & 0xF0000000) == 0xF0000000) + MEM1_free(drcColorBuffer.aux_data); + else + MEM2_free(drcColorBuffer.aux_data); + } + //! destroy shaders + ColorShader::destroyInstance(); + FXAAShader::destroyInstance(); + Shader3D::destroyInstance(); + ShaderFractalColor::destroyInstance(); + Texture2DShader::destroyInstance(); +} + +void CVideo::renderFXAA(const GX2Texture * texture, const GX2Sampler *sampler) +{ + resolution[0] = texture->surface.width; + resolution[1] = texture->surface.height; + + GX2Invalidate(GX2_INVALIDATE_COLOR_BUFFER | GX2_INVALIDATE_TEXTURE, texture->surface.image_data, texture->surface.image_size); + + GX2SetDepthOnlyControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_ALWAYS); + FXAAShader::instance()->setShaders(); + FXAAShader::instance()->setAttributeBuffer(); + FXAAShader::instance()->setResolution(resolution); + FXAAShader::instance()->setTextureAndSampler(texture, sampler); + FXAAShader::instance()->draw(); + GX2SetDepthOnlyControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_LEQUAL); +} + +void* CVideo::GX2RAlloc(u32 flags, u32 size, u32 align) +{ + //! min. alignment + if (align < 4) + align = 4; + + if ((flags & 0x2040E) && !(flags & 0x40000)) + return MEM1_alloc(size, align); + else + return MEM2_alloc(size, align); +} + +void CVideo::GX2RFree(u32 flags, void* p) +{ + if ((flags & 0x2040E) && !(flags & 0x40000)) + MEM1_free(p); + else + MEM2_free(p); +} diff --git a/source/video/CVideo.h b/source/video/CVideo.h new file mode 100644 index 0000000..40ccefa --- /dev/null +++ b/source/video/CVideo.h @@ -0,0 +1,202 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef __CVIDEO_H_ +#define __CVIDEO_H_ + +#include +#include "shaders/Shader.h" + +class CVideo +{ +public: + CVideo(s32 forceTvScanMode = -1, s32 forceDrcScanMode = -1); + virtual ~CVideo(); + + void prepareTvRendering(void) { + currContextState = tvContextState; + currColorBuffer = &tvColorBuffer; + currDepthBuffer = &tvDepthBuffer; + prepareRendering(); + } + + void prepareDrcRendering(void) { + currContextState = drcContextState; + currColorBuffer = &drcColorBuffer; + currDepthBuffer = &drcDepthBuffer; + prepareRendering(); + } + + void prepareRendering(void) { + GX2ClearColor(currColorBuffer, 0.0f, 0.0f, 0.0f, 1.0f); + GX2ClearDepthStencilEx(currDepthBuffer, currDepthBuffer->clear_depth, currDepthBuffer->clear_stencil, GX2_CLEAR_BOTH); + + GX2SetContextState(currContextState); + GX2SetViewport(0.0f, 0.0f, currColorBuffer->surface.width, currColorBuffer->surface.height, 0.0f, 1.0f); + GX2SetScissor(0, 0, currColorBuffer->surface.width, currColorBuffer->surface.height); + + GX2SetDepthOnlyControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_LEQUAL); + GX2SetColorControl(GX2_LOGIC_OP_COPY, 1, GX2_DISABLE, GX2_ENABLE); + GX2SetBlendControl(GX2_RENDER_TARGET_0, GX2_BLEND_SRC_ALPHA, GX2_BLEND_ONE_MINUS_SRC_ALPHA, GX2_BLEND_COMBINE_ADD, GX2_ENABLE, GX2_BLEND_SRC_ALPHA, GX2_BLEND_ONE_MINUS_SRC_ALPHA, GX2_BLEND_COMBINE_ADD); + GX2SetCullOnlyControl(GX2_FRONT_FACE_CCW, GX2_DISABLE, GX2_ENABLE); + } + + void setStencilRender(bool bEnable) + { + if(bEnable) + { + GX2SetStencilMask(0xff, 0xff, 0x01, 0xff, 0xff, 0x01); + GX2SetDepthStencilControl(GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_LEQUAL, GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_ALWAYS, GX2_STENCIL_KEEP, GX2_STENCIL_KEEP, GX2_STENCIL_REPLACE, + GX2_COMPARE_ALWAYS, GX2_STENCIL_KEEP, GX2_STENCIL_KEEP, GX2_STENCIL_REPLACE); + } + else + { + GX2SetStencilMask(0xff, 0xff, 0xff, 0xff, 0xff, 0xff); + GX2SetDepthStencilControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_LEQUAL, GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_NEVER, GX2_STENCIL_KEEP, GX2_STENCIL_KEEP, GX2_STENCIL_KEEP, + GX2_COMPARE_NEVER, GX2_STENCIL_KEEP, GX2_STENCIL_KEEP, GX2_STENCIL_KEEP); + } + } + + void drcDrawDone(void) { + //! on DRC we do a hardware AA because FXAA does not look good + //renderFXAA(&drcAaTexture, &aaSampler); + GX2CopyColorBufferToScanBuffer(&drcColorBuffer, GX2_SCAN_TARGET_DRC_FIRST); + } + + void tvDrawDone(void) { + renderFXAA(&tvAaTexture, &aaSampler); + GX2CopyColorBufferToScanBuffer(&tvColorBuffer, GX2_SCAN_TARGET_TV); + GX2SwapScanBuffers(); + GX2Flush(); + } + + void waitForVSync(void) { + GX2WaitForVsync(); + frameCount++; + } + + void tvEnable(bool bEnable) { + if(tvEnabled != bEnable) + { + GX2SetTVEnable(bEnable ? GX2_ENABLE : GX2_DISABLE); + tvEnabled = bEnable; + } + } + void drcEnable(bool bEnable) { + if(drcEnabled != bEnable) + { + GX2SetDRCEnable(bEnable ? GX2_ENABLE : GX2_DISABLE); + drcEnabled = bEnable; + } + } + + u32 getFrameCount(void) const { + return frameCount; + } + + u32 getTvWidth(void) const { + return tvColorBuffer.surface.width; + } + u32 getTvHeight(void) const { + return tvColorBuffer.surface.height; + } + + u32 getDrcWidth(void) const { + return drcColorBuffer.surface.width; + } + u32 getDrcHeight(void) const { + return drcColorBuffer.surface.height; + } + + const glm::mat4 & getProjectionMtx(void) const { + return projectionMtx; + } + const glm::mat4 & getViewMtx(void) const { + return viewMtx; + } + + f32 getWidthScaleFactor(void) const { + return widthScaleFactor; + } + f32 getHeightScaleFactor(void) const { + return heightScaleFactor; + } + f32 getDepthScaleFactor(void) const { + return depthScaleFactor; + } + + void screenPosToWorldRay(f32 posX, f32 posY, glm::vec3 & rayOrigin, glm::vec3 & rayDirection) + { + //! normalize positions + posX = 2.0f * posX * getWidthScaleFactor(); + posY = 2.0f * posY * getHeightScaleFactor(); + + glm::vec4 rayStart(posX, posY, 0.0f, 1.0f); + glm::vec4 rayEnd(posX, posY, 1.0f, 1.0f); + + glm::mat4 IMV = glm::inverse(projectionMtx * viewMtx); + glm::vec4 rayStartWorld = IMV * rayStart; + rayStartWorld /= rayStartWorld.w; + + glm::vec4 rayEndWorld = IMV * rayEnd; + rayEndWorld /= rayEndWorld.w; + + glm::vec3 rayDirectionWorld(rayEndWorld - rayStartWorld); + rayDirectionWorld = glm::normalize(rayDirectionWorld); + + rayOrigin = glm::vec3(rayStartWorld); + rayDirection = glm::normalize(rayDirectionWorld); + } +private: + static void *GX2RAlloc(u32 flags, u32 size, u32 align); + static void GX2RFree(u32 flags, void* p); + + void renderFXAA(const GX2Texture * texture, const GX2Sampler *sampler); + + void *gx2CommandBuffer; + + void *tvScanBuffer; + void *drcScanBuffer; + + u32 frameCount; + f32 widthScaleFactor; + f32 heightScaleFactor; + f32 depthScaleFactor; + + bool tvEnabled; + bool drcEnabled; + + GX2ColorBuffer tvColorBuffer; + GX2DepthBuffer tvDepthBuffer; + GX2ColorBuffer drcColorBuffer; + GX2DepthBuffer drcDepthBuffer; + + GX2ContextState *tvContextState; + GX2ContextState *drcContextState; + + GX2ContextState *currContextState; + GX2ColorBuffer *currColorBuffer; + GX2DepthBuffer *currDepthBuffer; + + GX2Texture tvAaTexture; + GX2Sampler aaSampler; + + glm::mat4 projectionMtx; + glm::mat4 viewMtx; + glm::vec2 resolution; +}; + +#endif // __GX2_VIDEO_H_ diff --git a/source/video/CursorDrawer.cpp b/source/video/CursorDrawer.cpp new file mode 100644 index 0000000..3991740 --- /dev/null +++ b/source/video/CursorDrawer.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * 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 . + ****************************************************************************/ +#include +#include +#include + +#include +#include "video/shaders/ColorShader.h" +#include "video/shaders/FXAAShader.h" +#include "video/shaders/Shader3D.h" +#include "video/shaders/ShaderFractalColor.h" +#include "video/shaders/Texture2DShader.h" +#include "CursorDrawer.h" + +CursorDrawer *CursorDrawer::instance = NULL; + +CursorDrawer::CursorDrawer() +{ + init_colorVtxs(); +} + +CursorDrawer::~CursorDrawer() +{ + //! destroy shaders + ColorShader::destroyInstance(); + FXAAShader::destroyInstance(); + Shader3D::destroyInstance(); + ShaderFractalColor::destroyInstance(); + Texture2DShader::destroyInstance(); + if(this->colorVtxs){ + free(this->colorVtxs); + this->colorVtxs = NULL; + } +} + +void CursorDrawer::init_colorVtxs(){ + if(!this->colorVtxs){ + this->colorVtxs = (u8*)memalign(0x40, sizeof(u8) * 16); + if(this->colorVtxs == NULL) return; + + } + memset(this->colorVtxs,0xFF,16*sizeof(u8)); + + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, this->colorVtxs, 16 * sizeof(u8)); +} + +// Could be improved. It be more generic. +void CursorDrawer::draw_Cursor(f32 x,f32 y) +{ + if(this->colorVtxs == NULL){ + init_colorVtxs(); + return; + } + + f32 widthScaleFactor = 1.0f / (f32)1280; + f32 heightScaleFactor = 1.0f / (f32)720; + + s32 width = 20; + + glm::vec3 positionOffsets = glm::vec3(0.0f); + + positionOffsets[0] = (x-((1280)/2)+(width/2)) * widthScaleFactor * 2.0f; + positionOffsets[1] = -(y-((720)/2)+(width/2)) * heightScaleFactor * 2.0f; + + glm::vec3 scale(width*widthScaleFactor,width*heightScaleFactor,1.0f); + + ColorShader::instance()->setShaders(); + ColorShader::instance()->setAttributeBuffer(this->colorVtxs, NULL, 4); + ColorShader::instance()->setAngle(0); + ColorShader::instance()->setOffset(positionOffsets); + ColorShader::instance()->setScale(scale); + ColorShader::instance()->setColorIntensity(glm::vec4(1.0f)); + ColorShader::instance()->draw(GX2_PRIMITIVE_QUADS, 4); +} diff --git a/source/video/CursorDrawer.h b/source/video/CursorDrawer.h new file mode 100644 index 0000000..d7d057b --- /dev/null +++ b/source/video/CursorDrawer.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * Copyright (C) 2016,2017 Maschell + * + * 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 . + ****************************************************************************/ +#ifndef _CursorDrawer_H_ +#define _CursorDrawer_H_ + +#include + +#include + +#include + +class CursorDrawer +{ + +public: + static CursorDrawer *getInstance() { + if(!instance) + instance = new CursorDrawer(); + return instance; + } + + static void destroyInstance() { + if(instance){ + delete instance; + instance = NULL; + } + } + + static void draw(f32 x, f32 y) + { + CursorDrawer * cur_instance = getInstance(); + if(cur_instance == NULL) return; + cur_instance->draw_Cursor(x,y); + } + +private: + //!Constructor + CursorDrawer(); + //!Destructor + ~CursorDrawer(); + static CursorDrawer *instance; + void draw_Cursor(f32 x, f32 y); + void init_colorVtxs(); + + u8 * colorVtxs = NULL; +}; + +#endif diff --git a/source/video/shaders/ColorShader.cpp b/source/video/shaders/ColorShader.cpp new file mode 100644 index 0000000..a631084 --- /dev/null +++ b/source/video/shaders/ColorShader.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "ColorShader.h" + +static const u32 cpVertexShaderProgram[] = +{ + 0x00000000,0x00008009,0x20000000,0x000078a0, + 0x3c200000,0x88060094,0x00c00000,0x88062014, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00a11f00,0xfc00620f,0x02490001,0x80000040, + 0xfd041f80,0x900c0060,0x83f9223e,0x0000803f, + 0xfe282001,0x10000040,0xfe001f80,0x00080060, + 0xfeac9f80,0xfd00624f,0xdb0f49c0,0xdb0fc940, + 0xfea81f80,0x9000e02f,0x83f9223e,0x00000000, + 0xfe041f80,0x00370000,0xffa01f00,0x80000000, + 0xff101f00,0x800c0020,0x7f041f80,0x80370000, + 0x0000103f,0x00000000,0x02c51f00,0x80000000, + 0xfea41f00,0x80000020,0xffa09f00,0x80000040, + 0xff001f80,0x800c0060,0x398ee33f,0x0000103f, + 0x02c41f00,0x9000e00f,0x02c59f01,0x80000020, + 0xfea81f00,0x80000040,0x02c19f80,0x9000e06f, + 0x398ee33f,0x00000000,0x02c11f01,0x80000000, + 0x02c49f80,0x80000060,0x02e08f01,0xfe0c620f, + 0x02c01f80,0x7f00622f,0xfe242000,0x10000000, + 0xfe20a080,0x10000020,0xf2178647,0x49c0e9fb, + 0xfbbdb2ab,0x768ac733 +}; + +static const u32 cpVertexShaderRegs[] = { + 0x00000103,0x00000000,0x00000000,0x00000001, + 0xffffff00,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0x00000000,0xfffffffc, + 0x00000002,0x00000001,0x00000000,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x00000000,0x0000000e,0x00000010 +}; + +static const u32 cpPixelShaderProgram[] = +{ + 0x20000000,0x00000ca0,0x00000000,0x88062094, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00002000,0x90000000,0x0004a000,0x90000020, + 0x00082001,0x90000040,0x000ca081,0x90000060, + 0xbb7dd898,0x9746c59c,0xc69b00e7,0x03c36218 +}; +static const u32 cpPixelShaderRegs[] = { + 0x00000001,0x00000002,0x14000001,0x00000000, + 0x00000001,0x00000100,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x0000000f,0x00000001,0x00000010, + 0x00000000 +}; + +ColorShader * ColorShader::shaderInstance = NULL; + +ColorShader::ColorShader() + : vertexShader(cuAttributeCount) +{ + //! create pixel shader + pixelShader.setProgram(cpPixelShaderProgram, sizeof(cpPixelShaderProgram), cpPixelShaderRegs, sizeof(cpPixelShaderRegs)); + + colorIntensityLocation = 0; + pixelShader.addUniformVar((GX2UniformVar){ "unf_color_intensity", GX2_VAR_TYPE_VEC4, 1, colorIntensityLocation, 0xffffffff }); + + //! create vertex shader + vertexShader.setProgram(cpVertexShaderProgram, sizeof(cpVertexShaderProgram), cpVertexShaderRegs, sizeof(cpVertexShaderRegs)); + + angleLocation = 0; + offsetLocation = 4; + scaleLocation = 8; + vertexShader.addUniformVar((GX2UniformVar){ "unf_angle", GX2_VAR_TYPE_FLOAT, 1, angleLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "unf_offset", GX2_VAR_TYPE_VEC3, 1, offsetLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "unf_scale", GX2_VAR_TYPE_VEC3, 1, scaleLocation, 0xffffffff }); + + colorLocation = 1; + positionLocation = 0; + vertexShader.addAttribVar((GX2AttribVar){ "attr_color", GX2_VAR_TYPE_VEC4, 0, colorLocation }); + vertexShader.addAttribVar((GX2AttribVar){ "attr_position", GX2_VAR_TYPE_VEC3, 0, positionLocation }); + + //! setup attribute streams + GX2InitAttribStream(vertexShader.getAttributeBuffer(0), positionLocation, 0, 0, GX2_ATTRIB_FORMAT_32_32_32_FLOAT); + GX2InitAttribStream(vertexShader.getAttributeBuffer(1), colorLocation, 1, 0, GX2_ATTRIB_FORMAT_8_8_8_8_UNORM); + + //! create fetch shader + fetchShader = new FetchShader(vertexShader.getAttributeBuffer(), vertexShader.getAttributesCount()); + + //! model vertex has to be align and cannot be in unknown regions for GX2 like 0xBCAE1000 + positionVtxs = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, cuPositionVtxsSize); + if(positionVtxs) + { + //! position vertex structure + s32 i = 0; + positionVtxs[i++] = -1.0f; positionVtxs[i++] = -1.0f; positionVtxs[i++] = 0.0f; + positionVtxs[i++] = 1.0f; positionVtxs[i++] = -1.0f; positionVtxs[i++] = 0.0f; + positionVtxs[i++] = 1.0f; positionVtxs[i++] = 1.0f; positionVtxs[i++] = 0.0f; + positionVtxs[i++] = -1.0f; positionVtxs[i++] = 1.0f; positionVtxs[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, positionVtxs, cuPositionVtxsSize); + } +} + +ColorShader::~ColorShader() +{ + if(positionVtxs) + { + free(positionVtxs); + positionVtxs = NULL; + } + + delete fetchShader; + fetchShader = NULL; +} diff --git a/source/video/shaders/ColorShader.h b/source/video/shaders/ColorShader.h new file mode 100644 index 0000000..35c13b6 --- /dev/null +++ b/source/video/shaders/ColorShader.h @@ -0,0 +1,100 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef __COLOR_SHADER_H_ +#define __COLOR_SHADER_H_ + +#include "VertexShader.h" +#include "PixelShader.h" +#include "FetchShader.h" + +class ColorShader : public Shader +{ +private: + ColorShader(); + virtual ~ColorShader(); + + static const u32 cuAttributeCount = 2; + static const u32 cuPositionVtxsSize = 4 * cuVertexAttrSize; + + static ColorShader *shaderInstance; + + FetchShader *fetchShader; + VertexShader vertexShader; + PixelShader pixelShader; + + f32 *positionVtxs; + + u32 angleLocation; + u32 offsetLocation; + u32 scaleLocation; + u32 colorLocation; + u32 colorIntensityLocation; + u32 positionLocation; +public: + static const u32 cuColorVtxsSize = 4 * cuColorAttrSize; + + static ColorShader *instance() { + if(!shaderInstance) { + shaderInstance = new ColorShader(); + } + return shaderInstance; + } + static void destroyInstance() { + if(shaderInstance) { + delete shaderInstance; + shaderInstance = NULL; + } + } + + void setShaders(void) const + { + fetchShader->setShader(); + vertexShader.setShader(); + pixelShader.setShader(); + } + + void setAttributeBuffer(const u8 * colorAttr, const f32 * posVtxs_in = NULL, const u32 & vtxCount = 0) const + { + if(posVtxs_in && vtxCount) { + VertexShader::setAttributeBuffer(0, vtxCount * cuVertexAttrSize, cuVertexAttrSize, posVtxs_in); + VertexShader::setAttributeBuffer(1, vtxCount * cuColorAttrSize, cuColorAttrSize, colorAttr); + } + else { + VertexShader::setAttributeBuffer(0, cuPositionVtxsSize, cuVertexAttrSize, positionVtxs); + VertexShader::setAttributeBuffer(1, cuColorVtxsSize, cuColorAttrSize, colorAttr); + } + } + + void setAngle(const float & val) + { + VertexShader::setUniformReg(angleLocation, 4, &val); + } + void setOffset(const glm::vec3 & vec) + { + VertexShader::setUniformReg(offsetLocation, 4, &vec[0]); + } + void setScale(const glm::vec3 & vec) + { + VertexShader::setUniformReg(scaleLocation, 4, &vec[0]); + } + void setColorIntensity(const glm::vec4 & vec) + { + PixelShader::setUniformReg(colorIntensityLocation, 4, &vec[0]); + } +}; + +#endif // __COLOR_SHADER_H_ diff --git a/source/video/shaders/FXAAShader.cpp b/source/video/shaders/FXAAShader.cpp new file mode 100644 index 0000000..88986e1 --- /dev/null +++ b/source/video/shaders/FXAAShader.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "FXAAShader.h" + +static const u32 cpVertexShaderProgram[] = +{ + 0x00000000,0x00008009,0x20000000,0x000004a0, + 0x3ca00000,0x88060094,0x00400000,0xff0f2094, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xfd001f80,0x900c2060,0x0000803f,0x00000000, + 0xc1a229f5,0xd0eddc33,0x426618fd,0x8509cfe7 +}; + +static const u32 cpVertexShaderRegs[] = { + 0x00000102,0x00000000,0x00000000,0x00000001, + 0xffffffff,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0x00000000,0xfffffffe, + 0x00000001,0x00000000,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x00000000,0x0000000e,0x00000010 +}; + +static const u32 cpPixelShaderProgram[] = +{ + 0x20000000,0x00003ca0,0xa0000000,0x000c8080, + 0x30000000,0x000010a1,0xa8000000,0x0010c080, + 0x75000000,0x000088a0,0x00800100,0x88062094, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00241f02,0x1000e00f,0x00241f00,0x1000e02f, + 0x00201f02,0x00000040,0x00201f00,0x00000060, + 0x00011f80,0x10332060,0xff000000,0xff102200, + 0xfd001f00,0x900cc020,0xffc09f01,0x90004040, + 0xffc01f01,0x90000060,0x00051f80,0x1033a040, + 0x0000803f,0x00000000,0xffe00f00,0x90004000, + 0xff008000,0xff102220,0xffe08f00,0x90000440, + 0x010c0000,0x010c4660,0xff008080,0xff004220, + 0x01a01f00,0x00280000,0x01a49f00,0x00280020, + 0x01a81f01,0x00280040,0xfd0c1f00,0x1028e06f, + 0x00208081,0x90002000,0x8716993e,0xa245163f, + 0xd578e93d,0x00000080,0x03a01f00,0x00280000, + 0x03a49f00,0x00280020,0x03a81f01,0x1028e04f, + 0xfd0c1f00,0x00280060,0x00a40081,0x90002020, + 0x8716993e,0xa245163f,0xd578e93d,0x00000080, + 0x04a01f00,0x00280000,0x04a49f00,0x1028a02f, + 0x04a81f01,0x00280040,0xfd0c1f00,0x00280060, + 0x7fcc1f80,0x1000c02f,0x8716993e,0xa245163f, + 0xd578e93d,0x00000080,0x02a01f00,0x1028e00f, + 0x02a49f00,0x00280020,0x02a81f01,0x00280040, + 0xfd0c1f00,0x00280060,0x7fcc1f80,0x1000e02f, + 0x8716993e,0xa245163f,0xd578e93d,0x00000080, + 0x7dc41f00,0x00020000,0x7fec0f01,0x00020020, + 0x7fc81f00,0x00000040,0x7dc41f00,0x00000060, + 0x7fec0f81,0x9001802f,0xfef88f00,0x1000e00f, + 0xfedc8f00,0x00000420,0x7de40f00,0x80010040, + 0x7ec49f01,0x00001060,0xfec41f80,0x10024060, + 0xfed49f00,0x80020000,0xfe141f00,0x900c802f, + 0xfeac1f00,0x80000040,0xfec01f02,0x80020060, + 0x7cc41f81,0x90010060,0x0000003d,0x00000000, + 0xfd001f00,0x900c6000,0xfea89f00,0x80010020, + 0xfec09f81,0x00020040,0x0000803f,0x0000003e, + 0xfec41f81,0x00000020,0xfe041f80,0x00330000, + 0x7fe01f00,0x80000040,0x7ce41f80,0x80000060, + 0xfea81f00,0x80010000,0xfeac1f80,0x80010020, + 0x000000c1,0x00000000,0xfea01f00,0x00020040, + 0xfea41f80,0x00020060,0x00000041,0x00000000, + 0x05c81f01,0x9000e00f,0x01cc9f81,0x9000e06f, + 0xfeac1f00,0x01004200,0xfea01f00,0x01044220, + 0xfeac9f00,0x01002240,0xfea09f00,0x01042260, + 0xfe8c1f80,0x01008600,0xacaa2a3e,0xaaaa2abe, + 0x7f9c1f00,0x0100a200,0x7f801f00,0x01048220, + 0x7f901f80,0x0104a240,0x02080001,0x7000a00f, + 0x02000000,0x7000c04f,0x02048000,0x7000e06f, + 0x01a81f80,0x9000e00f,0xd578e93d,0x00000000, + 0x04a80001,0x1000c00f,0x04a48000,0x00000020, + 0x04a00000,0x00000040,0xfe081f00,0xe00c0060, + 0xfe0c1f80,0xe00c0000,0x01a41f00,0x7f00620f, + 0xfea89f00,0xfe0c822f,0xfea49f00,0xff00a24f, + 0x7d001f80,0xe00c0060,0xa245163f,0x0000803e, + 0x7ea01f00,0xfe0ce20f,0x01a09f80,0xfe006a4f, + 0x0000803e,0x8716993e,0xfe088001,0x9001c00f, + 0xfe488001,0x1002e44f,0xfea01f80,0x80000000, + 0xd578e93d,0x00000000,0x7ca41f00,0x00280000, + 0x7da89f00,0x00280020,0xff201f00,0x00280040, + 0xfd081f80,0x00280060,0x8716993e,0xa245163f, + 0x00000080,0x00000000,0x7fc81f00,0x80060000, + 0xfec00f80,0x80060060,0xfec09f81,0xfb80634f, + 0xfe888f00,0x7e886300,0xfea80f01,0x7f8c6320, + 0xfee80f00,0x7d806340,0xfe680080,0x06846f60, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x10000100,0x01101df0,0x00008010,0xecdfea0d, + 0x10000200,0x03101df0,0x00002050,0xecdfea0d, + 0x10000000,0x04101df0,0x00003071,0xecdfea0d, + 0x10000200,0x02101df0,0x0000b070,0xecdfea0d, + 0x10000200,0x02101df0,0x00008010,0xecdfea0d, + 0x10000100,0x00101df0,0x0000a051,0xecdfea0d, + 0x10000400,0x04101df0,0x00008010,0xecdfea0d, + 0x10000500,0x05101df0,0x00000011,0xecdfea0d, + 0x10000100,0x01101df0,0x00008010,0xecdfea0d, + 0xfe2e963a,0x0269a9a3,0x38f88096,0x400cf48b +}; +static const u32 cpPixelShaderRegs[] = { + 0x00000007,0x00000002,0x04000101,0x00000000, + 0x00000001,0x00000100,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x0000000f,0x00000001,0x00000010, + 0x00000000 +}; + +FXAAShader * FXAAShader::shaderInstance = NULL; + +FXAAShader::FXAAShader() + : vertexShader(cuAttributeCount) +{ + //! create pixel shader + pixelShader.setProgram(cpPixelShaderProgram, sizeof(cpPixelShaderProgram), cpPixelShaderRegs, sizeof(cpPixelShaderRegs)); + + resolutionLocation = 0; + pixelShader.addUniformVar((GX2UniformVar){ "unf_resolution", GX2_VAR_TYPE_VEC2, 1, resolutionLocation, 0xffffffff }); + + samplerLocation = 0; + pixelShader.addSamplerVar((GX2SamplerVar){ "sampl_texture", GX2_SAMPLER_TYPE_2D, samplerLocation }); + + //! create vertex shader + vertexShader.setProgram(cpVertexShaderProgram, sizeof(cpVertexShaderProgram), cpVertexShaderRegs, sizeof(cpVertexShaderRegs)); + + positionLocation = 0; + texCoordLocation = 1; + vertexShader.addAttribVar((GX2AttribVar){ "attr_position", GX2_VAR_TYPE_VEC3, 0, positionLocation }); + vertexShader.addAttribVar((GX2AttribVar){ "attr_texture_coord", GX2_VAR_TYPE_VEC2, 0, texCoordLocation }); + + //! setup attribute streams + GX2InitAttribStream(vertexShader.getAttributeBuffer(0), positionLocation, 0, 0, GX2_ATTRIB_FORMAT_32_32_32_FLOAT); + GX2InitAttribStream(vertexShader.getAttributeBuffer(1), texCoordLocation, 1, 0, GX2_ATTRIB_FORMAT_32_32_FLOAT); + + //! create fetch shader + fetchShader = new FetchShader(vertexShader.getAttributeBuffer(), vertexShader.getAttributesCount()); + + //! model vertex has to be align and cannot be in unknown regions for GX2 like 0xBCAE1000 + posVtxs = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciPositionVtxsSize); + texCoords = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciTexCoordsVtxsSize); + + //! position vertex structure and texture coordinate vertex structure + s32 i = 0; + posVtxs[i++] = -1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = -1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, posVtxs, ciPositionVtxsSize); + + i = 0; + texCoords[i++] = 0.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 0.0f; + texCoords[i++] = 0.0f; texCoords[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, texCoords, ciTexCoordsVtxsSize); +} + +FXAAShader::~FXAAShader() +{ + if(posVtxs) + { + free(posVtxs); + posVtxs = NULL; + } + if(texCoords) + { + free(texCoords); + texCoords = NULL; + } + + delete fetchShader; + fetchShader = NULL; +} diff --git a/source/video/shaders/FXAAShader.h b/source/video/shaders/FXAAShader.h new file mode 100644 index 0000000..b7604bc --- /dev/null +++ b/source/video/shaders/FXAAShader.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef __FXAA_SHADER_H_ +#define __FXAA_SHADER_H_ + +#include "VertexShader.h" +#include "PixelShader.h" +#include "FetchShader.h" + +class FXAAShader : public Shader +{ +public: + static FXAAShader *instance() { + if(!shaderInstance) { + shaderInstance = new FXAAShader(); + } + return shaderInstance; + } + static void destroyInstance() { + if(shaderInstance) { + delete shaderInstance; + shaderInstance = NULL; + } + } + + void setShaders(void) const + { + fetchShader->setShader(); + vertexShader.setShader(); + pixelShader.setShader(); + } + + void setAttributeBuffer() const + { + VertexShader::setAttributeBuffer(0, ciPositionVtxsSize, cuVertexAttrSize, posVtxs); + VertexShader::setAttributeBuffer(1, ciTexCoordsVtxsSize, cuTexCoordAttrSize, texCoords); + } + + void setResolution(const glm::vec2 & vec) + { + PixelShader::setUniformReg(resolutionLocation, 4, &vec[0]); + } + + void setTextureAndSampler(const GX2Texture *texture, const GX2Sampler *sampler) const { + GX2SetPixelTexture(texture, samplerLocation); + GX2SetPixelSampler(sampler, samplerLocation); + } + +private: + FXAAShader(); + virtual ~FXAAShader(); + + static const u32 cuAttributeCount = 2; + static const u32 ciPositionVtxsSize = 4 * cuVertexAttrSize; + static const u32 ciTexCoordsVtxsSize = 4 * cuTexCoordAttrSize; + + static FXAAShader *shaderInstance; + + FetchShader *fetchShader; + VertexShader vertexShader; + PixelShader pixelShader; + + f32 *posVtxs; + f32 *texCoords; + + u32 samplerLocation; + u32 positionLocation; + u32 texCoordLocation; + u32 resolutionLocation; +}; + +#endif // __FXAA_SHADER_H_ diff --git a/source/video/shaders/FetchShader.h b/source/video/shaders/FetchShader.h new file mode 100644 index 0000000..292052f --- /dev/null +++ b/source/video/shaders/FetchShader.h @@ -0,0 +1,58 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef FETCH_SHADER_H +#define FETCH_SHADER_H + +#include "Shader.h" + +class FetchShader : public Shader +{ +public: + FetchShader(GX2AttribStream * attributes, u32 attrCount, s32 type = GX2_FETCH_SHADER_TESSELATION_NONE, s32 tess = GX2_TESSELLATION_MODE_DISCRETE) + : fetchShader(NULL) + , fetchShaderProgramm(NULL) + { + u32 shaderSize = GX2CalcFetchShaderSizeEx(attrCount, type, tess); + fetchShaderProgramm = memalign(GX2_SHADER_ALIGNMENT, shaderSize); + if(fetchShaderProgramm) + { + fetchShader = new GX2FetchShader; + GX2InitFetchShaderEx(fetchShader, fetchShaderProgramm, attrCount, attributes, type, tess); + GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, fetchShaderProgramm, shaderSize); + } + } + virtual ~FetchShader() { + if(fetchShaderProgramm) + free(fetchShaderProgramm); + if(fetchShader) + delete fetchShader; + } + + GX2FetchShader *getFetchShader() const { + return fetchShader; + } + + void setShader(void) const { + GX2SetFetchShader(fetchShader); + } + +protected: + GX2FetchShader *fetchShader; + void *fetchShaderProgramm; +}; + +#endif // FETCH_SHADER_H diff --git a/source/video/shaders/PixelShader.h b/source/video/shaders/PixelShader.h new file mode 100644 index 0000000..a1fa76b --- /dev/null +++ b/source/video/shaders/PixelShader.h @@ -0,0 +1,150 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef PIXEL_SHADER_H +#define PIXEL_SHADER_H + +#include "Shader.h" + +class PixelShader : public Shader +{ +public: + PixelShader() + : pixelShader((GX2PixelShader*) memalign(0x40, sizeof(GX2PixelShader))) + { + if(pixelShader) + { + memset(pixelShader, 0, sizeof(GX2PixelShader)); + pixelShader->shader_mode = GX2_SHADER_MODE_UNIFORM_REGISTER; + } + } + virtual ~PixelShader() + { + if(pixelShader) + { + if(pixelShader->shader_data) + free(pixelShader->shader_data); + + for(u32 i = 0; i < pixelShader->uniform_blocks_count; i++) + free((void*)pixelShader->uniform_block[i].name); + + if(pixelShader->uniform_block) + free((void*)pixelShader->uniform_block); + + for(u32 i = 0; i < pixelShader->uniform_vars_count; i++) + free((void*)pixelShader->uniform_var[i].name); + + if(pixelShader->uniform_var) + free((void*)pixelShader->uniform_var); + + if(pixelShader->initial_value) + free((void*)pixelShader->initial_value); + + for(u32 i = 0; i < pixelShader->sampler_vars_count; i++) + free((void*)pixelShader->sampler_var[i].name); + + if(pixelShader->sampler_var) + free((void*)pixelShader->sampler_var); + + if(pixelShader->loops_data) + free((void*)pixelShader->loops_data); + + free(pixelShader); + } + } + + void setProgram(const u32 * program, const u32 & programSize, const u32 * regs, const u32 & regsSize) + { + if(!pixelShader) + return; + + //! this must be moved into an area where the graphic engine has access to and must be aligned to 0x100 + pixelShader->shader_size = programSize; + pixelShader->shader_data = memalign(GX2_SHADER_ALIGNMENT, pixelShader->shader_size); + if(pixelShader->shader_data) + { + memcpy(pixelShader->shader_data, program, pixelShader->shader_size); + GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, pixelShader->shader_data, pixelShader->shader_size); + } + + memcpy(pixelShader->regs, regs, regsSize); + } + + void addUniformVar(const GX2UniformVar & var) + { + if(!pixelShader) + return; + + u32 idx = pixelShader->uniform_vars_count; + + GX2UniformVar* newVar = (GX2UniformVar*) malloc((pixelShader->uniform_vars_count + 1) * sizeof(GX2UniformVar)); + if(newVar) + { + if(pixelShader->uniform_var) + { + memcpy(newVar, pixelShader->uniform_var, pixelShader->uniform_vars_count * sizeof(GX2UniformVar)); + free(pixelShader->uniform_var); + } + pixelShader->uniform_var = newVar; + + memcpy(pixelShader->uniform_var + idx, &var, sizeof(GX2UniformVar)); + pixelShader->uniform_var[idx].name = (char*) malloc(strlen(var.name) + 1); + strcpy((char*)pixelShader->uniform_var[idx].name, var.name); + + pixelShader->uniform_vars_count++; + } + } + + void addSamplerVar(const GX2SamplerVar & var) + { + if(!pixelShader) + return; + + u32 idx = pixelShader->sampler_vars_count; + + GX2SamplerVar* newVar = (GX2SamplerVar*) malloc((pixelShader->sampler_vars_count + 1) * sizeof(GX2SamplerVar)); + if(newVar) + { + if(pixelShader->sampler_var) + { + memcpy(newVar, pixelShader->sampler_var, pixelShader->sampler_vars_count * sizeof(GX2SamplerVar)); + free(pixelShader->sampler_var); + } + pixelShader->sampler_var = newVar; + + memcpy(pixelShader->sampler_var + idx, &var, sizeof(GX2SamplerVar)); + pixelShader->sampler_var[idx].name = (char*) malloc(strlen(var.name) + 1); + strcpy((char*)pixelShader->sampler_var[idx].name, var.name); + + pixelShader->sampler_vars_count++; + } + } + GX2PixelShader * getPixelShader() const { + return pixelShader; + } + + void setShader(void) const { + GX2SetPixelShader(pixelShader); + } + + static inline void setUniformReg(u32 location, u32 size, const void * reg) { + GX2SetPixelUniformReg(location, size, reg); + } +protected: + GX2PixelShader *pixelShader; +}; + +#endif // PIXEL_SHADER_H diff --git a/source/video/shaders/Shader.h b/source/video/shaders/Shader.h new file mode 100644 index 0000000..c3eb7e2 --- /dev/null +++ b/source/video/shaders/Shader.h @@ -0,0 +1,74 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef SHADER_H_ +#define SHADER_H_ + +#include "glm/glm.hpp" +#include "glm/gtc/matrix_transform.hpp" +#include +#include + +class Shader +{ +protected: + Shader() {} + virtual ~Shader() {} +public: + static const u16 cuVertexAttrSize = sizeof(f32) * 3; + static const u16 cuTexCoordAttrSize = sizeof(f32) * 2; + static const u16 cuColorAttrSize = sizeof(u8) * 4; + + static void setLineWidth(const f32 & width) { + GX2SetLineWidth(width); + } + + static void draw(s32 primitive = GX2_PRIMITIVE_QUADS, u32 vtxCount = 4) + { + switch(primitive) + { + default: + case GX2_PRIMITIVE_QUADS: + { + GX2DrawEx(GX2_PRIMITIVE_QUADS, vtxCount, 0, 1); + break; + } + case GX2_PRIMITIVE_TRIANGLES: + { + GX2DrawEx(GX2_PRIMITIVE_TRIANGLES, vtxCount, 0, 1); + break; + } + case GX2_PRIMITIVE_TRIANGLE_FAN: + { + GX2DrawEx(GX2_PRIMITIVE_TRIANGLE_FAN, vtxCount, 0, 1); + break; + } + case GX2_PRIMITIVE_LINES: + { + GX2DrawEx(GX2_PRIMITIVE_LINES, vtxCount, 0, 1); + break; + } + case GX2_PRIMITIVE_LINE_STRIP: + { + GX2DrawEx(GX2_PRIMITIVE_LINE_STRIP, vtxCount, 0, 1); + break; + } + //! TODO: add other primitives later + }; + } +}; + +#endif // SHADER_H_ diff --git a/source/video/shaders/Shader3D.cpp b/source/video/shaders/Shader3D.cpp new file mode 100644 index 0000000..38feee9 --- /dev/null +++ b/source/video/shaders/Shader3D.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "Shader3D.h" + +static const u32 cpVertexShaderProgram[] = +{ + 0x00000000,0x00008009,0x20000000,0x0000e4a1, + 0x00c00100,0x88048093,0x01c00300,0x98060014, + 0x9a000000,0x000058a0,0x3c200200,0x88062094, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x0765a101,0x9000e00f,0x0761a101,0x9000e02f, + 0x01081f00,0x900ce040,0x01041f00,0x900ce060, + 0x01001f80,0x900ce000,0x02001f00,0x900c6000, + 0x02041f00,0x900c6020,0x076da101,0x9000e04f, + 0x0769a181,0x9000e06f,0x0745a101,0x9000c00f, + 0x0741a181,0x9000c02f,0x074da101,0x9000c04f, + 0x0749a181,0x9000c06f,0x0bc9a000,0x7f00e20f, + 0x0bc92080,0x7f04e22f,0x0bc9a001,0x7f08e24f, + 0x0bc92081,0x7f0ce26f,0x0725a101,0x9000a00f, + 0x0721a181,0x9000a02f,0x072da101,0x9000a04f, + 0x0729a181,0x9000a06f,0x0ac9a000,0x7e00c20f, + 0x0ac92080,0x7e04c22f,0x0ac9a001,0x7e08c24f, + 0x0ac92081,0x7e0cc26f,0x0ba5a000,0x7f00e20f, + 0x0ba52080,0x7f04e22f,0x0ba5a001,0x7f08e24f, + 0x0ba52081,0x7f0ce26f,0x08eda000,0x9000800f, + 0x08ed2080,0x9000802f,0x08eda001,0x9000804f, + 0x08ed2081,0x9000806f,0x09c9a000,0x7d00a20f, + 0x09c92080,0x7d04a22f,0x09c9a001,0x7d08a24f, + 0x09c92081,0x7d0ca26f,0x0aa5a000,0x7e00c20f, + 0x0aa52080,0x7e04c22f,0x0aa5a001,0x7e08c24f, + 0x0aa52081,0x7e0cc26f,0x0b81a000,0x7f004200, + 0x0b812080,0x7f044220,0x0b81a001,0x7f082240, + 0x0b812081,0x7f0c0260,0x08c9a000,0x7c00820f, + 0x08c92080,0x7c04822f,0x08c9a001,0x7c08824f, + 0x08c92081,0x7c0c826f,0x09a5a000,0x7d00a20f, + 0x09a52080,0x7d04a22f,0x09a5a001,0x7d08a24f, + 0x09a52081,0x7d0ca26f,0x0a81a000,0x7e000200, + 0x0a812080,0x7e040220,0x0a81a001,0x7e080240, + 0x0a812081,0x7e0c2260,0x0240a001,0x9000c00f, + 0x0244a001,0x9000c02f,0x0148a001,0x9000c04f, + 0x004ca001,0x9000c06f,0x0264a081,0x9000e02f, + 0x0260a001,0x9000e00f,0x0224a001,0x90002020, + 0x0168a001,0x9000e04f,0x006ca001,0x9000e06f, + 0x0220a081,0x90002000,0x08a5a000,0x7c00820f, + 0x08a52080,0x7c04822f,0x08a5a001,0x7c08824f, + 0x08a52081,0x7c0c826f,0x0981a000,0x7d008200, + 0x09812080,0x7d048220,0x0981a001,0x7d084240, + 0x09812081,0x7d0c4260,0x02090000,0x7e00c20f, + 0x02098000,0x7e04c22f,0x0128a001,0x9000a04f, + 0x002ca001,0x9000c06f,0x02298081,0x7e0caa6f, + 0x03090000,0x7f00e20f,0x03098000,0x7f04e22f, + 0x02090001,0x7e08f64f,0x03298001,0x7f0ce26f, + 0x03090081,0x7f08ca4f,0x0881a000,0x7c00c200, + 0x08812080,0x7c04e220,0x0881a001,0x7c08a240, + 0x08812081,0x7c0c8260,0x0200a001,0x9000800f, + 0x0204a001,0x9000802f,0x0108a001,0x9000804f, + 0x000ca001,0x9000806f,0x01098080,0x0104aa2f, + 0x01090000,0x0100a20f,0x02858000,0x7e04c22f, + 0x01090001,0x7d08a24f,0x01298081,0x7e0cc26f, + 0x02850000,0x7e00f60f,0x03858000,0x7f04622f, + 0x02450001,0x7f08e24f,0x02458001,0x7d0ca26f, + 0x03850080,0x7f00ca0f,0x00090000,0x7c004200, + 0x00098000,0x7c04b220,0x03450001,0x7e08c24f, + 0x03458001,0x7f0ce26f,0x03e18080,0xfe042620, + 0x01850000,0x7d00a200,0x01858000,0x7d04622f, + 0x00090001,0x7c086240,0x00298081,0x7c0c0260, + 0x02c10000,0x7f000200,0x02e18000,0x7e040620, + 0x01450001,0x7d088240,0x01458001,0x7e0c6260, + 0x01e18080,0xfe04c620,0x03c10000,0x7e002200, + 0x03818001,0x7f0c4220,0x02a10001,0x7f081640, + 0x02818001,0x7d0c3660,0x03a10081,0x7e082a40, + 0x07080000,0x0100c20f,0x07088000,0x0104622f, + 0x00458001,0x000cea4f,0x07288081,0x0204f66f, + 0x00850000,0x0200620f,0x00858000,0x05046a2f, + 0x07080001,0x0108c24f,0x01818001,0x030c726f, + 0x07cc8080,0xfe04c22f,0x01c10000,0x0500660f, + 0x00e18000,0xfe04622f,0x00450001,0x0308624f, + 0x07cc9f01,0x7f0ce26f,0x00c10080,0xfe00e60f, + 0x07cc1f00,0x7e00660f,0x00a10001,0xfe08c22f, + 0x01a10001,0x0408624f,0x00818001,0x7f086a6f, + 0x07c09f80,0x7e048200,0x07e00f00,0xfe008220, + 0x07cc1f01,0x7e086a4f,0x07c09f81,0x7f0c8240, + 0x07c08f80,0xfe088260,0x2c34800d,0xe3b4f15e, + 0x7642ed30,0x7408600d +}; + +static const u32 cpVertexShaderRegs[] = { + 0x00000108,0x00000000,0x00000002,0x00000001, + 0xffff0001,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0x00000000,0xfffffffc, + 0x00000002,0x00000000,0x00000001,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x00000000,0x0000000e,0x00000010 +}; + +static const u32 cPixelShaderProgram[] = +{ + 0x20000000,0x000008a4,0x03000000,0x01004085, + 0x23000000,0x000044a8,0x35000000,0x000000a4, + 0x06000000,0x01004085,0x36000000,0x00002ca8, + 0x50000000,0x0000c080,0x42000000,0x00001ca0, + 0x00800000,0x88062094,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xfd001f80,0x900c0060,0x0000803f,0x00000000, + 0x02011f80,0x8c110000,0xf8402000,0x9006a00f, + 0x02552001,0x00000020,0x01248082,0x80020060, + 0xfe3c1f00,0x1000e04f,0xfe041f80,0x1033c00f, + 0xfe482081,0x80060020,0xfee40f81,0x0289e30f, + 0x02c51f80,0x80060060,0xfeec0f80,0x0285634f, + 0xfec80f80,0x80000060,0xfe4ca081,0x9000e04f, + 0xfe281f00,0x80060000,0xf8c01f81,0x9006e02f, + 0xfee00f81,0xfd80636f,0x0000803f,0x00000000, + 0x7fc49f81,0xf880e34f,0xfe381f80,0x00000000, + 0x7de00f81,0xfe800360,0x01011f80,0x8c100000, + 0x00a81f00,0x9000e02f,0x00000082,0x80020060, + 0x00002040,0x00000000,0xfeac9f80,0xfd00624f, + 0x3333333f,0x00002040,0xfee88f80,0x0101620f, + 0x00cc1f80,0x9000e06f,0xf8c09f01,0x80060020, + 0xfe2c1f80,0x9006e04f,0xfee48f81,0xf880630f, + 0x7fc81f80,0xfd800360,0x0000803f,0x00000000, + 0x000ca001,0x80000000,0x00091f00,0x800c0020, + 0x00051f00,0x800c0040,0x00011f80,0x800c0060, + 0xfe2c0000,0x90002000,0xfe288000,0x90002020, + 0xfe240001,0x90002040,0xfe208081,0x90002060, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x10000100,0x01100df0,0x00008010,0xecdfea0d, + 0x99720984,0x041cab0d,0xa28a9ccd,0x95d199a5 +}; +static const u32 cPixelShaderRegs[] = { + 0x00000102,0x00000002,0x14000002,0x00000000, + 0x00000002,0x00000100,0x00000101,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x0000000f,0x00000001,0x00000010, + 0x00000000 +}; + +Shader3D * Shader3D::shaderInstance = NULL; + +Shader3D::Shader3D() + : vertexShader(cuAttributeCount) +{ + //! create pixel shader + pixelShader.setProgram(cPixelShaderProgram, sizeof(cPixelShaderProgram), cPixelShaderRegs, sizeof(cPixelShaderRegs)); + + colorIntensityLocation = 0; + fadeDistanceLocation = 4; + fadeOutLocation = 8; + pixelShader.addUniformVar((GX2UniformVar){ "unf_color_intensity", GX2_VAR_TYPE_VEC4, 1, colorIntensityLocation, 0xffffffff }); + pixelShader.addUniformVar((GX2UniformVar){ "unf_fade_distance", GX2_VAR_TYPE_FLOAT, 1, fadeDistanceLocation, 0xffffffff }); + pixelShader.addUniformVar((GX2UniformVar){ "unf_fade_out_alpha", GX2_VAR_TYPE_VEC4, 1, fadeOutLocation, 0xffffffff }); + + samplerLocation = 0; + pixelShader.addSamplerVar((GX2SamplerVar){ "sampl_texture", GX2_SAMPLER_TYPE_2D, samplerLocation }); + + //! create vertex shader + vertexShader.setProgram(cpVertexShaderProgram, sizeof(cpVertexShaderProgram), cpVertexShaderRegs, sizeof(cpVertexShaderRegs)); + + modelMatrixLocation = 0; + projectionMatrixLocation = 16; + viewMatrixLocation = 32; + vertexShader.addUniformVar((GX2UniformVar){ "modelMatrix", GX2_VAR_TYPE_MAT4, 1, modelMatrixLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "viewMatrix", GX2_VAR_TYPE_MAT4, 1, projectionMatrixLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "projectionMatrix", GX2_VAR_TYPE_MAT4, 1, viewMatrixLocation, 0xffffffff }); + + positionLocation = 0; + texCoordLocation = 1; + vertexShader.addAttribVar((GX2AttribVar){ "attr_position", GX2_VAR_TYPE_VEC3, 0, positionLocation }); + vertexShader.addAttribVar((GX2AttribVar){ "attr_texture_coord", GX2_VAR_TYPE_VEC2, 0, texCoordLocation }); + + //! setup attribute streams + GX2InitAttribStream(vertexShader.getAttributeBuffer(0), positionLocation, 0, 0, GX2_ATTRIB_FORMAT_32_32_32_FLOAT); + GX2InitAttribStream(vertexShader.getAttributeBuffer(1), texCoordLocation, 1, 0, GX2_ATTRIB_FORMAT_32_32_FLOAT); + + //! create fetch shader + fetchShader = new FetchShader(vertexShader.getAttributeBuffer(), vertexShader.getAttributesCount()); + + //! initialize default quad texture vertexes as those are very commonly used + //! model vertex has to be align and cannot be in unknown regions for GX2 like 0xBCAE1000 + posVtxs = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciPositionVtxsSize); + texCoords = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciTexCoordsVtxsSize); + + //! position vertex structure and texture coordinate vertex structure + s32 i = 0; + posVtxs[i++] = -1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = -1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, posVtxs, ciPositionVtxsSize); + + i = 0; + texCoords[i++] = 0.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 0.0f; + texCoords[i++] = 0.0f; texCoords[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, texCoords, ciTexCoordsVtxsSize); +} + +Shader3D::~Shader3D() +{ + if(posVtxs) + { + free(posVtxs); + posVtxs = NULL; + } + if(texCoords) + { + free(texCoords); + texCoords = NULL; + } + + delete fetchShader; + fetchShader = NULL; +} diff --git a/source/video/shaders/Shader3D.h b/source/video/shaders/Shader3D.h new file mode 100644 index 0000000..7289152 --- /dev/null +++ b/source/video/shaders/Shader3D.h @@ -0,0 +1,119 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef SHADER_3D_H_ +#define SHADER_3D_H_ + +#include "VertexShader.h" +#include "PixelShader.h" +#include "FetchShader.h" + +class Shader3D : public Shader +{ +private: + Shader3D(); + virtual ~Shader3D(); + + static Shader3D * shaderInstance; + + static const unsigned char cuAttributeCount = 2; + static const u32 ciPositionVtxsSize = 4 * cuVertexAttrSize; + static const u32 ciTexCoordsVtxsSize = 4 * cuTexCoordAttrSize; + + FetchShader *fetchShader; + VertexShader vertexShader; + PixelShader pixelShader; + + f32 *posVtxs; + f32 *texCoords; + + u32 modelMatrixLocation; + u32 viewMatrixLocation; + u32 projectionMatrixLocation; + u32 positionLocation; + u32 texCoordLocation; + + u32 colorIntensityLocation; + u32 fadeDistanceLocation; + u32 fadeOutLocation; + u32 samplerLocation; +public: + static Shader3D *instance() { + if(!shaderInstance) { + shaderInstance = new Shader3D(); + } + return shaderInstance; + } + static void destroyInstance() { + if(shaderInstance) { + delete shaderInstance; + shaderInstance = NULL; + } + } + + void setShaders(void) const + { + fetchShader->setShader(); + vertexShader.setShader(); + pixelShader.setShader(); + } + + void setAttributeBuffer(const u32 & vtxCount = 0, const f32 * posVtxs_in = NULL, const f32 * texCoords_in = NULL) const + { + if(posVtxs_in && texCoords_in && vtxCount) + { + VertexShader::setAttributeBuffer(0, vtxCount * cuVertexAttrSize, cuVertexAttrSize, posVtxs_in); + VertexShader::setAttributeBuffer(1, vtxCount * cuTexCoordAttrSize, cuTexCoordAttrSize, texCoords_in); + } + else { + //! use default quad vertex and texture coordinates if nothing is passed + VertexShader::setAttributeBuffer(0, ciPositionVtxsSize, cuVertexAttrSize, posVtxs); + VertexShader::setAttributeBuffer(1, ciTexCoordsVtxsSize, cuTexCoordAttrSize, texCoords); + } + } + + void setProjectionMtx(const glm::mat4 & mtx) + { + VertexShader::setUniformReg(projectionMatrixLocation, 16, &mtx[0][0]); + } + void setViewMtx(const glm::mat4 & mtx) + { + VertexShader::setUniformReg(viewMatrixLocation, 16, &mtx[0][0]); + } + void setModelViewMtx(const glm::mat4 & mtx) + { + VertexShader::setUniformReg(modelMatrixLocation, 16, &mtx[0][0]); + } + void setColorIntensity(const glm::vec4 & vec) + { + PixelShader::setUniformReg(colorIntensityLocation, 4, &vec[0]); + } + void setAlphaFadeOut(const glm::vec4 & vec) + { + PixelShader::setUniformReg(fadeOutLocation, 4, &vec[0]); + } + void setDistanceFadeOut(const float & value) + { + PixelShader::setUniformReg(fadeDistanceLocation, 4, &value); + } + + void setTextureAndSampler(const GX2Texture *texture, const GX2Sampler *sampler) const { + GX2SetPixelTexture(texture, samplerLocation); + GX2SetPixelSampler(sampler, samplerLocation); + } +}; + +#endif // SHADER_3D_H_ diff --git a/source/video/shaders/ShaderFractalColor.cpp b/source/video/shaders/ShaderFractalColor.cpp new file mode 100644 index 0000000..72c2032 --- /dev/null +++ b/source/video/shaders/ShaderFractalColor.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "ShaderFractalColor.h" + +static const u32 cpVertexShaderProgram[] = +{ + 0x00000000,0x00008009,0x20000000,0x0000eca1, + 0x00c00000,0x88068093,0x01400200,0x9a048013, + 0x9c000000,0x000044a0,0x3c200000,0x88060094, + 0x02400000,0x88062014,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x0765a101,0x9000e00f,0x0761a101,0x9000e02f, + 0x03001f00,0x900c8040,0x03041f80,0x900c8060, + 0x076da101,0x9000e04f,0x0769a181,0x9000e06f, + 0x0745a101,0x9000c00f,0x0741a181,0x9000c02f, + 0x074da101,0x9000c04f,0x0749a181,0x9000c06f, + 0x0bc9a000,0x7f00e20f,0x0bc92080,0x7f04e22f, + 0x0bc9a001,0x7f08e24f,0x0bc92081,0x7f0ce26f, + 0x0725a101,0x9000a00f,0x0721a181,0x9000a02f, + 0x072da101,0x9000a04f,0x0729a181,0x9000a06f, + 0x0ac9a000,0x7e00c20f,0x0ac92080,0x7e04c22f, + 0x0ac9a001,0x7e08c24f,0x0ac92081,0x7e0cc26f, + 0x0ba5a000,0x7f00e20f,0x0ba52080,0x7f04e22f, + 0x0ba5a001,0x7f08e24f,0x0ba52081,0x7f0ce26f, + 0x08eda000,0x9000800f,0x08ed2080,0x9000802f, + 0x08eda001,0x9000804f,0x08ed2081,0x9000806f, + 0x09c9a000,0x7d00a20f,0x09c92080,0x7d04a22f, + 0x09c9a001,0x7d08a24f,0x09c92081,0x7d0ca26f, + 0x0aa5a000,0x7e00c20f,0x0aa52080,0x7e04c22f, + 0x0aa5a001,0x7e08c24f,0x0aa52081,0x7e0cc26f, + 0x0b81a000,0x7f006200,0x0b812080,0x7f046220, + 0x0b81a001,0x7f080240,0x0b812081,0x7f0c0260, + 0x08c9a000,0x7c00820f,0x08c92080,0x7c04822f, + 0x08c9a001,0x7c08824f,0x08c92081,0x7c0c826f, + 0x09a5a000,0x7d00a20f,0x09a52080,0x7d04a22f, + 0x09a5a001,0x7d08a24f,0x09a52081,0x7d0ca26f, + 0x0a81a000,0x7e008200,0x0a812080,0x7e048220, + 0x0a81a001,0x7e086240,0x0a812081,0x7e0c4260, + 0x0340a001,0x9000c00f,0x0344a001,0x9000c02f, + 0x0048a001,0x9000c04f,0x004ca001,0x9000c06f, + 0x0364a081,0x9000e02f,0x0360a001,0x9000e00f, + 0x0324a001,0x90000020,0x0068a001,0x9000e04f, + 0x006ca001,0x9000e06f,0x0320a081,0x90000000, + 0x08a5a000,0x7c00820f,0x08a52080,0x7c04822f, + 0x08a5a001,0x7c08824f,0x08a52081,0x7c0c826f, + 0x0981a000,0x7d00a200,0x09812080,0x7d04a220, + 0x0981a001,0x7d08a240,0x09812081,0x7d0c6260, + 0x02890000,0x7e00c20f,0x02898000,0x7e04c22f, + 0x0028a001,0x9000a04f,0x002ca001,0x9000c06f, + 0x02498081,0x7e0caa6f,0x03890000,0x7f00e20f, + 0x03898000,0x7f04e22f,0x02690001,0x7e08f64f, + 0x03498001,0x7f0ce26f,0x03690081,0x7f08ca4f, + 0x0881a000,0x7c00c200,0x08812080,0x7c04c220, + 0x0881a001,0x7c08e240,0x08812081,0x7c0ca260, + 0x0300a001,0x9000800f,0x0304a001,0x9000802f, + 0x0008a001,0x9000804f,0x000ca001,0x9000806f, + 0x01898080,0x0004aa2f,0x01890000,0x0000a20f, + 0x02a58000,0x7e04c22f,0x01690001,0x7d08a24f, + 0x01498081,0x7e0cc26f,0x02a50000,0x7e00f60f, + 0x03a58000,0x7f04622f,0x02a50001,0x7f08e24f, + 0x02658001,0x7d0ca26f,0x03a50080,0x7f00ca0f, + 0x00890000,0x7c00820f,0x00898000,0x7c049220, + 0x03a50001,0x7e08c24f,0x03658001,0x7f0ce26f, + 0x03c18080,0xfe04862f,0x01a50000,0x7d008200, + 0x01a58000,0x7d04622f,0x00690001,0x7c086240, + 0x00498081,0x7c0c4260,0x02c10000,0x7f00e20f, + 0x02c18000,0x7e04c62f,0x01a50001,0x7d080240, + 0x01658001,0x7e0c0260,0x01c18080,0xfe040620, + 0x03c10000,0x7e00620f,0x03a18001,0x7f0c622f, + 0x02e10001,0x7f08764f,0x02a18001,0x7d0c766f, + 0x03e10081,0x7e084a0f,0x02e80f00,0xfe000e00, + 0x02c88f00,0x7c046220,0x02c81f01,0xff00c240, + 0x02c89f01,0xfe04c260,0x00a50080,0x7c00aa00, + 0x01c10000,0x0400760f,0x00a58000,0x0404622f, + 0x00a50001,0x0308e24f,0x00658001,0x020c626f, + 0x00c10080,0x0500ea0f,0x02c41f00,0x0000620f, + 0x00c18000,0xfe04c22f,0x01e10001,0x0008624f, + 0x01a18001,0x000c666f,0x00a18081,0xfe0ce66f, + 0x00e10001,0x7f08620f,0x02048000,0x03046a2f, + 0x02c41f01,0x06086a4f,0x02c49f01,0x060c6a6f, + 0x02e00f80,0xfe000220,0x02c08f00,0xfe040200, + 0x02e08f01,0xfe0c0240,0x02c01f80,0xfe080260, + 0x8aa480ad,0x2bfc5ca6,0xb5e05b5b,0xd48dc71c +}; + +static const u32 cpVertexShaderRegs[] = { + 0x00000108,0x00000000,0x00000004,0x00000001, + 0xff000201,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0x00000000,0xfffffff8, + 0x00000003,0x00000001,0x00000000,0x00000002, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x00000000,0x0000000e,0x00000010 +}; + +static const u32 cpPixelShaderProgram[] = +{ + 0x20000000,0x000008a4,0x04000000,0x01004085, + 0x23000000,0x0000eca1,0x9f000000,0x0000e0a8, + 0xd8000000,0x000000a4,0x07000000,0x01004085, + 0xd9000000,0x000048a8,0xec000000,0x000000a4, + 0x0a000000,0x01004085,0xed000000,0x000050a8, + 0x02010000,0x000030a0,0x00000000,0x88062094, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0xfd001f80,0x900c0060,0x0000803f,0x00000000, + 0x03011f80,0x8c210000,0xfd001f00,0x900c0000, + 0xfd001f00,0x900ca02f,0x00000002,0x80020040, + 0x00048002,0x80020060,0xf8001f80,0x900cc04f, + 0x0000803f,0x00000000,0xfea81f00,0x9000e00f, + 0xfeac1f00,0x9000e02f,0xf8001f00,0x900c804f, + 0xf8001f80,0x900ca06f,0x00000040,0x00000000, + 0xfea01f00,0x00280000,0xfea41f00,0x00280020, + 0xfe041f00,0x00280040,0xfd041f80,0x00280060, + 0xaf67bb3e,0x00000080,0x7fc41f00,0x00000000, + 0x7fc01f80,0x00000020,0xfe041f00,0x100ac00f, + 0xfe001f80,0x100ac02f,0xfea01f00,0x00280000, + 0xfea41f00,0x00280020,0xfe041f00,0x00280040, + 0xfd041f00,0x1028e06f,0x7fc01f82,0x00000000, + 0x8c65583e,0x00000080,0x7ea41f00,0x80000000, + 0x7ea01f00,0x80000020,0xfee01f00,0x10006040, + 0x7fc48f82,0x00000860,0xa7c4623b,0x00000000, + 0xfea81f00,0x1000e00f,0x7fcc9f01,0x10006020, + 0xfe001f00,0x000a0040,0xfe041f00,0x000a0060, + 0xfea89f80,0x10008040,0x8c65583e,0x3acd13bf, + 0xfeb81f00,0x7e04c20f,0xfebc1f00,0x7e00822f, + 0x03c89f00,0x80060040,0xfea49f00,0x1000e06f, + 0xfea41f81,0x10006060,0x00809043,0x8c65583e, + 0x3acd13bf,0x00000000,0xfea81f00,0xf880a30f, + 0xfe001f00,0x900ca02f,0x7dc41f00,0x1000e04f, + 0xfe081f00,0xfd80636f,0x04081f80,0x900c800f, + 0x0000803f,0x00000000,0xfea81f00,0xf900620f, + 0xfea41f00,0xf900622f,0xfe0c1f00,0x900ca04f, + 0xfec00f00,0x1000c46f,0xfefc0f80,0x10006000, + 0x00000842,0x00000000,0xfeac1f00,0xf900620f, + 0x7fc81f00,0x9000c02f,0x7dc49f00,0x9000e04f, + 0x7df08f01,0x10008060,0x030c1f80,0x900ca02f, + 0x00000842,0x00000000,0xfea41f00,0x80000000, + 0x7ecc1f00,0x9000e02f,0x7e688000,0x80000040, + 0xfea81f00,0x80000060,0x7d6c8081,0x80000000, + 0xa7c4623b,0x00000000,0xfe001f00,0x000a0000, + 0xfe0c1f00,0x000a0020,0xfea41f00,0x80000040, + 0x03648000,0xfe08626f,0x7d648081,0xff00420f, + 0xa7c4623b,0x00000000,0xfeb01f00,0x7e04620f, + 0xfeb41f00,0x7f08662f,0x7c800001,0xff006e4f, + 0xfe081f00,0x000a0060,0x03680081,0xfe0c4e0f, + 0x00809043,0x00000000,0xfebc1f00,0x7f04620f, + 0x7cc41f00,0x00000020,0x7cc49f00,0x1000e04f, + 0xff901f00,0x00000060,0xfe981f80,0x00000000, + 0x00809043,0x00000000,0xfea81f00,0xf900620f, + 0x7cc41f00,0x00000020,0x00c09f00,0x1000c04f, + 0xfe0c1f00,0x80010060,0xff001f80,0x80010000, + 0x00000842,0x00000000,0xfea81f00,0xf900620f, + 0xfecc9f01,0x80000020,0x7fc81f00,0x9000e04f, + 0x7dc89f00,0x1000c86f,0xffe01f80,0x80000000, + 0x00000842,0x00000000,0xfeac1f00,0xf900620f, + 0x7ec81f00,0x9000802f,0xfec49f00,0x9000a040, + 0xfea89f00,0x80000060,0xffe01f80,0x9000a060, + 0x00000842,0xa7c4623b,0xfea41f00,0x80000000, + 0x7ecc1f00,0x9000e02f,0xfe0c1f00,0x000a0040, + 0x7c888081,0x80000000,0xa7c4623b,0x00000000, + 0xfe001f00,0x000a0000,0xfeb81f00,0x7f08622f, + 0xfea49f00,0x80000040,0x048c8081,0xff00420f, + 0x00809043,0xa7c4623b,0xfeb01f00,0x7c04620f, + 0x03600000,0xff00622f,0xfea49f00,0x80000040, + 0xfe081f80,0x000a0060,0x00809043,0x0ccec73c, + 0xfebc1f00,0x7f040200,0xfea09f00,0x90000020, + 0xfe941f00,0x10000040,0xfe081f80,0x30080060, + 0x00809043,0x0ccec73c,0x00041f00,0x20080000, + 0x00a01f00,0x80000020,0x002c1f02,0x1000e04f, + 0x00081f80,0x80010060,0x0ccec73c,0x00000000, + 0xfe201f02,0x1000800f,0xfec81f03,0x80020020, + 0xfe041f00,0x20080040,0xfe881f00,0x00000060, + 0xfecc9f81,0x9000a06f,0xfe0c1f00,0x000a0000, + 0xfe801f00,0x00000020,0xfec01f02,0x80020040, + 0xfe281f02,0x1000c06f,0xfe841f82,0x1000804f, + 0xfe041f00,0x000a0000,0x7fc81f02,0x00000020, + 0xfe8c1f00,0x00000040,0xfecc9f03,0x80020060, + 0xfe881f82,0x1000a00f,0x7cc01f02,0x00000000, + 0xfe8c1f02,0x1000e02f,0xfec49f00,0x80000040, + 0xfe081f00,0x000a0060,0x03c89f80,0x9000e04f, + 0x7ecc9f03,0x00000000,0xfec01f00,0x80000020, + 0x04c81f00,0x80000040,0x7c880f01,0xfe086a6f, + 0x7dac8f81,0x9000800f,0x7da00f00,0xfe04620f, + 0xfec01f00,0x80000020,0x03c01f00,0x80000840, + 0x03ac0f00,0xfe08c66f,0xfebc9f80,0xfd00420f, + 0xe07be53f,0x5c8e5a3f,0xfeb09f00,0xfd00620f, + 0x05e81f00,0x9000f02f,0x7fe48f00,0xfe04624f, + 0x04ec8f00,0xfe08626f,0x03840f81,0x7f08a20f, + 0xe07be53f,0x5c8e5a3f,0x7e0c1f00,0x900ce00f, + 0xfe0c1f00,0x900c802f,0x05cc1f00,0x9000e84f, + 0xfeb89f80,0xfd00626f,0xe07be53f,0x5c8e5a3f, + 0x7cc09f81,0x80000020,0x7fa40f00,0x00280000, + 0xfe848f00,0x00280020,0x7fe80f00,0x00280440, + 0xfd001f80,0x00280060,0x00000080,0x00000000, + 0xfdc01f80,0xf800620f,0x00000243,0x00000000, + 0xfea01f80,0x90000060,0x5555d53f,0x00000000, + 0x02011f80,0x8c110000,0x02448002,0x80020000, + 0xf8402000,0x9006a02f,0x02552081,0x00000040, + 0xfe301f00,0x1000e06f,0xfe081f80,0x1033c02f, + 0xfe4c2081,0x80060040,0xfee88f81,0x0289e32f, + 0x02c59f80,0x80060000,0xfee08f80,0x0285636f, + 0xfecc8f80,0x80000000,0xfe40a081,0x80000060, + 0x00cc9f81,0x9000e04f,0xfe281f00,0x80060000, + 0xf8c01f81,0x9006c02f,0xfee00f81,0xfd80636f, + 0x0000803f,0x00000000,0x7ec49f81,0xf880e34f, + 0xfe381f80,0x00000000,0x7de40f81,0xfe800360, + 0x00011f80,0x8c100000,0xf8001f00,0x900ce00f, + 0x00311f00,0x1000e02f,0x02a41f00,0xf910624f, + 0x02a01f00,0xf910626f,0x00011f80,0x1033e04f, + 0x00000040,0x00000000,0xfecc9f03,0x80020000, + 0xfec81f83,0x80020060,0x7fd49f01,0x00000020, + 0x7fd41f80,0x00000040,0xfe081f00,0x80010000, + 0xfe041f80,0x80010060,0xfee00f01,0x80000000, + 0xfeec0f81,0x80000020,0xfec01f00,0x00280000, + 0xfec49f00,0x00280020,0x7fe00f00,0x00280040, + 0xfd001f80,0x00280060,0x00000080,0x00000000, + 0xfe001f80,0x00350000,0x00ec1f82,0x000c0260, + 0x01011f00,0x800c0000,0x01051f00,0x800c0020, + 0x002c1f00,0x80060040,0xf8008001,0x9006e06f, + 0x01091f80,0x800c0000,0x01c01f00,0x90000000, + 0xfe088001,0xfd80632f,0x01e81f00,0x90000040, + 0x01c49f80,0x90000020,0x0000803f,0x00000000, + 0x7fcc9f80,0xf880630f,0xfe20a081,0x80000000, + 0x01cc1f80,0x90000060,0xc21e82a7,0x62ccc547, + 0x1708607c,0x73ea57a6 +}; +static const u32 cpPixelShaderRegs[] = { + 0x00000106,0x00000002,0x14000003,0x00000000, + 0x00000003,0x00000100,0x00000101,0x00000102, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x0000000f,0x00000001,0x00000010, + 0x00000000 +}; + +ShaderFractalColor * ShaderFractalColor::shaderInstance = NULL; + +ShaderFractalColor::ShaderFractalColor() + : vertexShader(cuAttributeCount) +{ + //! create pixel shader + pixelShader.setProgram(cpPixelShaderProgram, sizeof(cpPixelShaderProgram), cpPixelShaderRegs, sizeof(cpPixelShaderRegs)); + + blurLocation = 0; + colorIntensityLocation = 4; + fadeOutLocation = 8; + fractalLocation = 12; + pixelShader.addUniformVar((GX2UniformVar){ "unf_blur_border", GX2_VAR_TYPE_FLOAT, 1, blurLocation, 0xffffffff }); + pixelShader.addUniformVar((GX2UniformVar){ "unf_color_intensity", GX2_VAR_TYPE_VEC4, 1, colorIntensityLocation, 0xffffffff }); + pixelShader.addUniformVar((GX2UniformVar){ "unf_fade_out_alpha", GX2_VAR_TYPE_VEC4, 1, fadeOutLocation, 0xffffffff }); + pixelShader.addUniformVar((GX2UniformVar){ "unf_fract_alpha", GX2_VAR_TYPE_INT, 1, fractalLocation, 0xffffffff }); + + //! create vertex shader + vertexShader.setProgram(cpVertexShaderProgram, sizeof(cpVertexShaderProgram), cpVertexShaderRegs, sizeof(cpVertexShaderRegs)); + + modelMatrixLocation = 0; + projectionMatrixLocation = 16; + viewMatrixLocation = 32; + vertexShader.addUniformVar((GX2UniformVar){ "modelMatrix", GX2_VAR_TYPE_MAT4, 1, modelMatrixLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "projectionMatrix", GX2_VAR_TYPE_MAT4, 1, projectionMatrixLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "viewMatrix", GX2_VAR_TYPE_MAT4, 1, viewMatrixLocation, 0xffffffff }); + + positionLocation = 0; + colorLocation = 1; + texCoordLocation = 2; + vertexShader.addAttribVar((GX2AttribVar){ "attr_colors", GX2_VAR_TYPE_VEC4, 0, colorLocation }); + vertexShader.addAttribVar((GX2AttribVar){ "attr_position", GX2_VAR_TYPE_VEC3, 0, positionLocation }); + vertexShader.addAttribVar((GX2AttribVar){ "attr_texture_coord", GX2_VAR_TYPE_VEC2, 0, texCoordLocation }); + + //! setup attribute streams + GX2InitAttribStream(vertexShader.getAttributeBuffer(0), positionLocation, 0, 0, GX2_ATTRIB_FORMAT_32_32_32_FLOAT); + GX2InitAttribStream(vertexShader.getAttributeBuffer(1), texCoordLocation, 1, 0, GX2_ATTRIB_FORMAT_32_32_FLOAT); + GX2InitAttribStream(vertexShader.getAttributeBuffer(2), colorLocation, 2, 0, GX2_ATTRIB_FORMAT_8_8_8_8_UNORM); + + //! create fetch shader + fetchShader = new FetchShader(vertexShader.getAttributeBuffer(), vertexShader.getAttributesCount()); + + //! initialize default quad texture vertexes as those are very commonly used + //! model vertex has to be align and cannot be in unknown regions for GX2 like 0xBCAE1000 + posVtxs = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciPositionVtxsSize); + texCoords = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciTexCoordsVtxsSize); + colorVtxs = (u8*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciColorVtxsSize); + + //! position vertex structure and texture coordinate vertex structure + s32 i = 0; + posVtxs[i++] = -1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = -1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, posVtxs, ciPositionVtxsSize); + + i = 0; + texCoords[i++] = 0.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 0.0f; + texCoords[i++] = 0.0f; texCoords[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, texCoords, ciTexCoordsVtxsSize); + + + for(i = 0; i < (s32)ciColorVtxsSize; i++) + colorVtxs[i] = 0xff; + + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, colorVtxs, ciColorVtxsSize); +} + +ShaderFractalColor::~ShaderFractalColor() +{ + if(posVtxs) + { + free(posVtxs); + posVtxs = NULL; + } + if(texCoords) + { + free(texCoords); + texCoords = NULL; + } + if(colorVtxs) + { + free(colorVtxs); + colorVtxs = NULL; + } + + delete fetchShader; + fetchShader = NULL; +} diff --git a/source/video/shaders/ShaderFractalColor.h b/source/video/shaders/ShaderFractalColor.h new file mode 100644 index 0000000..d3d8355 --- /dev/null +++ b/source/video/shaders/ShaderFractalColor.h @@ -0,0 +1,124 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef SHADER_FRACTAL_COLOR_H_ +#define SHADER_FRACTAL_COLOR_H_ + +#include "VertexShader.h" +#include "PixelShader.h" +#include "FetchShader.h" + +class ShaderFractalColor : public Shader +{ +private: + ShaderFractalColor(); + virtual ~ShaderFractalColor(); + + static ShaderFractalColor * shaderInstance; + + static const unsigned char cuAttributeCount = 3; + static const u32 ciPositionVtxsSize = 4 * cuVertexAttrSize; + static const u32 ciTexCoordsVtxsSize = 4 * cuTexCoordAttrSize; + static const u32 ciColorVtxsSize = 4 * cuColorAttrSize; + + FetchShader *fetchShader; + VertexShader vertexShader; + PixelShader pixelShader; + + f32 *posVtxs; + f32 *texCoords; + u8 *colorVtxs; + + u32 modelMatrixLocation; + u32 viewMatrixLocation; + u32 projectionMatrixLocation; + u32 positionLocation; + u32 colorLocation; + u32 texCoordLocation; + + u32 blurLocation; + u32 colorIntensityLocation; + u32 fadeOutLocation; + u32 fractalLocation; +public: + static ShaderFractalColor *instance() { + if(!shaderInstance) { + shaderInstance = new ShaderFractalColor(); + } + return shaderInstance; + } + static void destroyInstance() { + if(shaderInstance) { + delete shaderInstance; + shaderInstance = NULL; + } + } + + void setShaders(void) const + { + fetchShader->setShader(); + vertexShader.setShader(); + pixelShader.setShader(); + } + + void setAttributeBuffer(const u32 & vtxCount = 0, const f32 * posVtxs_in = NULL, const f32 * texCoords_in = NULL, const u8 * colorVtxs_in = NULL) const + { + if(posVtxs_in && texCoords_in && vtxCount) + { + VertexShader::setAttributeBuffer(0, vtxCount * cuVertexAttrSize, cuVertexAttrSize, posVtxs_in); + VertexShader::setAttributeBuffer(1, vtxCount * cuTexCoordAttrSize, cuTexCoordAttrSize, texCoords_in); + VertexShader::setAttributeBuffer(2, vtxCount * cuColorAttrSize, cuColorAttrSize, colorVtxs_in); + } + else { + //! use default quad vertex and texture coordinates if nothing is passed + VertexShader::setAttributeBuffer(0, ciPositionVtxsSize, cuVertexAttrSize, posVtxs); + VertexShader::setAttributeBuffer(1, ciTexCoordsVtxsSize, cuTexCoordAttrSize, texCoords); + VertexShader::setAttributeBuffer(2, ciColorVtxsSize, cuColorAttrSize, colorVtxs); + } + } + + void setProjectionMtx(const glm::mat4 & mtx) + { + VertexShader::setUniformReg(projectionMatrixLocation, 16, &mtx[0][0]); + } + void setViewMtx(const glm::mat4 & mtx) + { + VertexShader::setUniformReg(viewMatrixLocation, 16, &mtx[0][0]); + } + void setModelViewMtx(const glm::mat4 & mtx) + { + VertexShader::setUniformReg(modelMatrixLocation, 16, &mtx[0][0]); + } + + void setBlurBorder(const float & blurBorderSize) + { + PixelShader::setUniformReg(blurLocation, 4, &blurBorderSize); + } + void setColorIntensity(const glm::vec4 & vec) + { + PixelShader::setUniformReg(colorIntensityLocation, 4, &vec[0]); + } + void setAlphaFadeOut(const glm::vec4 & vec) + { + PixelShader::setUniformReg(fadeOutLocation, 4, &vec[0]); + } + void setFractalColor(const int & fractalColorEnable) + { + PixelShader::setUniformReg(fractalLocation, 4, &fractalColorEnable); + } +}; + +#endif // SHADER_FRACTAL_COLOR_H_ diff --git a/source/video/shaders/Texture2DShader.cpp b/source/video/shaders/Texture2DShader.cpp new file mode 100644 index 0000000..a0a1ce8 --- /dev/null +++ b/source/video/shaders/Texture2DShader.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#include +#include +#include "Texture2DShader.h" + +static const u32 cpVertexShaderProgram[] = +{ + 0x00000000,0x00008009,0x20000000,0x000080a0, + 0x3c200100,0x88060094,0x00400000,0x88042014, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x02290001,0x80000000,0x02041f00,0x900c0020, + 0x00a11f00,0xfc00624f,0xfd041f00,0x900c4060, + 0x02001f80,0x900c0000,0x83f9223e,0x0000803f, + 0xfe081f00,0x00080020,0xfe202081,0x10004040, + 0xfea49f80,0xfd00620f,0xdb0f49c0,0xdb0fc940, + 0xfea01f80,0x9000e06f,0x83f9223e,0x00000000, + 0xfe0c1f80,0x00370000,0xffa01f00,0x80000040, + 0xff101f00,0x800c0060,0x7f0c1f80,0x80370040, + 0x0000103f,0x00000000,0xffa01f00,0x80000000, + 0xff001f00,0x800c0020,0x02c51f01,0x80000040, + 0xfeac9f80,0x80000060,0x0000103f,0x398ee33f, + 0xfea01f00,0x80000000,0x02c19f01,0x9000e02f, + 0x01c41f01,0x9000e04f,0x02c59f80,0x80000060, + 0x398ee33f,0x00000000,0x01c49f01,0x80000020, + 0x02c11f80,0x80000040,0x01e08f00,0xfe04624f, + 0x01c01f81,0x7f08626f,0xfe2c2000,0x10004000, + 0xfe28a080,0x10004020,0xeb825790,0xb6f711be, + 0x7c0e2df2,0x81173cfa +}; + +static const u32 cpVertexShaderRegs[] = { + 0x00000103,0x00000000,0x00000000,0x00000001, + 0xffffff00,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0xffffffff,0xffffffff, + 0xffffffff,0xffffffff,0x00000000,0xfffffffc, + 0x00000002,0x00000000,0x00000001,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x000000ff,0x000000ff,0x000000ff, + 0x000000ff,0x00000000,0x0000000e,0x00000010 +}; + +static const u32 cPixelShaderProgram[] = +{ + 0x20000000,0x00000ca4,0x0b000000,0x00000085, + 0x24000000,0x000050a0,0xb0000000,0x000cc080, + 0x39000000,0x00005ca0,0xb8000000,0x000cc080, + 0x51000000,0x000078a0,0xc0000000,0x000cc080, + 0x70000000,0x000064a0,0xc8000000,0x0008c080, + 0x8a000000,0x00005ca0,0x0e000000,0x01008086, + 0xce000000,0x0000c080,0xa2000000,0x00000ca8, + 0x00800000,0x88062094,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00051f00,0x80060000,0x00011f80,0x80060040, + 0xfec81f80,0xfb802320,0x01041f80,0x8c220000, + 0x00a41f00,0xfc10620f,0x010d1f00,0x900c0021, + 0x00091f00,0x80060040,0x00a01f80,0xfc10626f, + 0x00000040,0x00000000,0xfe080000,0xfe8cc300, + 0xfe088080,0xfe80c320,0x00a11f00,0xfe000200, + 0x00a51f00,0xfe040220,0x00a19f00,0xfe000240, + 0x00a59f00,0xfe040260,0x00a11f81,0xfe002600, + 0x4260e5bc,0xa69bc4bc,0x0ad7a3bc,0x00000000, + 0x00a11f00,0x06004200,0x00a59f00,0x06042220, + 0x00a51f00,0x06044240,0x00a11f01,0x06008260, + 0x00a51f81,0x06048620,0x6f1283bc,0x0ad7a3bc, + 0xa69b44bc,0x00000000,0x00a41f00,0x80000000, + 0x00a01f00,0x80000020,0x00ac1f00,0x80000040, + 0x00a81f00,0x80000060,0x00a19f80,0x06000600, + 0xcac3123c,0x6f1203bc,0x03a41f00,0xfe00620f, + 0x03a01f00,0xfe04622f,0x03ac1f00,0xfe08624f, + 0x03a81f00,0xfe0c626f,0x00a59f80,0x06040620, + 0xcc28913b,0x6f1203bc,0x01a41f00,0xfe00620f, + 0x01a01f00,0xfe04622f,0x01ac1f00,0xfe08624f, + 0x01a81f00,0xfe0c626f,0x00a19f80,0x06002600, + 0xe8eab03c,0x6f1283bb,0x02ac1f00,0xfe084200, + 0x02a81f00,0xfe0c4220,0x02a41f00,0xfe004240, + 0x02a01f00,0xfe044260,0x00a59f80,0x06042620, + 0x92bb353d,0x6f1283bb,0x04a81f00,0x0204620f, + 0x04ac1f00,0x0200662f,0x04a41f00,0x0208624f, + 0x04a01f00,0x020c626f,0x00a19f80,0x06004600, + 0xc4139f3d,0x6f12833b,0x00a41f00,0xfe08620f, + 0x00a01f00,0xfe0c622f,0x00ac1f00,0xfe04624f, + 0x00a81f00,0xfe00626f,0x00a59f80,0x06044620, + 0xb950ed3d,0x6f12833b,0x01a41f00,0xfe00620f, + 0x01a01f00,0xfe04622f,0x01ac1f00,0xfe08624f, + 0x01a81f00,0xfe0c626f,0x00a19f80,0x06002600, + 0xecd7163e,0x6f12033c,0x03a41f00,0xfe000200, + 0x03a01f00,0xfe040220,0x03ac1f00,0xfe082240, + 0x03a81f00,0xfe0c2260,0x00a59f80,0x06042620, + 0x2168233e,0x6f12033c,0x00a11f00,0x06006200, + 0x00a51f00,0x06046220,0x00a19f00,0x06006240, + 0x00a59f00,0x06046260,0x00a11f81,0x0600e600, + 0xa69b443c,0x6f12833c,0x0ad7a33c,0x00000000, + 0x02ac1f00,0x0108620f,0x02a81f00,0x010c622f, + 0x02a41f00,0x0000624f,0x02a01f00,0x0004666f, + 0x00a59f80,0x0604e620,0xecd7163e,0x0ad7a33c, + 0x04a81f00,0xfe04620f,0x04ac1f00,0xfe00622f, + 0x04a41f00,0xfe08624f,0x04a01f00,0xfe0c626f, + 0x00a19f80,0x06008600,0xb950ed3d,0xa69bc43c, + 0x05a41f00,0xfe08620f,0x05a01f00,0xfe0c622f, + 0x05ac1f00,0xfe04624f,0x05a81f00,0xfe00626f, + 0x00a59f80,0x06048620,0xc4139f3d,0xa69bc43c, + 0x03a41f00,0xfe00a200,0x03a01f00,0xfe04a220, + 0x03ac1f00,0xfe086240,0x03a81f00,0xfe0c6260, + 0x00a19f80,0x06006600,0x92bb353d,0x4260e53c, + 0x00a51f80,0x06046220,0x4260e53c,0x00000000, + 0x07ac1f00,0x0308620f,0x07a81f00,0x030c622f, + 0x07a41f00,0x0500624f,0x07a01f80,0x0504626f, + 0xe8eab03c,0x00000000,0x04a81f00,0xfe04620f, + 0x04ac1f00,0xfe00622f,0x04a41f00,0xfe08624f, + 0x04a01f80,0xfe0c626f,0xcac3123c,0x00000000, + 0x06a41f00,0xfe08620f,0x06a01f00,0xfe0c622f, + 0x06ac1f00,0xfe04624f,0x06a81f80,0xfe00626f, + 0xcc28913b,0x00000000,0xfe20a000,0x9000e00f, + 0xfe242000,0x9000e02f,0xfe28a001,0x9000e04f, + 0xfe2c2081,0x9000e06f,0xfe28a081,0x80060020, + 0xfee48f00,0x7f842300,0xfee40f00,0x7f802320, + 0xfee48f01,0x7f8c2340,0xfee40f81,0x08842b60, + 0x00202000,0x90002000,0x0024a000,0x90002020, + 0x00282001,0x90002040,0x002ca081,0x90002060, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x10000000,0x03100df0,0x00008010,0xecdfea0d, + 0x10000000,0x00100df0,0x0000a051,0xecdfea0d, + 0x10000100,0x01100df0,0x00008010,0xecdfea0d, + 0x10000200,0x02100df0,0x00000011,0xecdfea0d, + 0x10000400,0x04100df0,0x0000b070,0xecdfea0d, + 0x10000000,0x00100df0,0x00008010,0xecdfea0d, + 0x10000100,0x01100df0,0x00008010,0xecdfea0d, + 0x10000600,0x03100df0,0x00008010,0xecdfea0d, + 0x10000200,0x02100df0,0x00008010,0xecdfea0d, + 0x10000100,0x04100df0,0x00008010,0xecdfea0d, + 0x10000300,0x05100df0,0x00008010,0xecdfea0d, + 0x10000300,0x03100df0,0x0000a051,0xecdfea0d, + 0x10000700,0x07100df0,0x00008010,0xecdfea0d, + 0x10000400,0x04100df0,0x00008010,0xecdfea0d, + 0x10000300,0x06100df0,0x00008010,0xecdfea0d, + 0x10000000,0x00100df0,0x00008010,0xecdfea0d, + 0xc8581837,0x22740275,0x281eddcc,0xfa8b9b65 +}; +static const u32 cPixelShaderRegs[] = { + 0x00000109,0x00000002,0x14000001,0x00000000, + 0x00000001,0x00000100,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0x0000000f,0x00000001,0x00000010, + 0x00000000 +}; + +Texture2DShader * Texture2DShader::shaderInstance = NULL; + +Texture2DShader::Texture2DShader() + : vertexShader(cuAttributeCount) +{ + //! create pixel shader + pixelShader.setProgram(cPixelShaderProgram, sizeof(cPixelShaderProgram), cPixelShaderRegs, sizeof(cPixelShaderRegs)); + + blurLocation = 0; + colorIntensityLocation = 4; + pixelShader.addUniformVar((GX2UniformVar){ "unf_blur_texture_direction", GX2_VAR_TYPE_VEC3, 1, blurLocation, 0xffffffff }); + pixelShader.addUniformVar((GX2UniformVar){ "unf_color_intensity", GX2_VAR_TYPE_VEC4, 1, colorIntensityLocation, 0xffffffff }); + + samplerLocation = 0; + pixelShader.addSamplerVar((GX2SamplerVar){ "sampl_texture", GX2_SAMPLER_TYPE_2D, samplerLocation }); + + //! create vertex shader + vertexShader.setProgram(cpVertexShaderProgram, sizeof(cpVertexShaderProgram), cpVertexShaderRegs, sizeof(cpVertexShaderRegs)); + + angleLocation = 0; + offsetLocation = 4; + scaleLocation = 8; + vertexShader.addUniformVar((GX2UniformVar){ "unf_angle", GX2_VAR_TYPE_FLOAT, 1, angleLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "unf_offset", GX2_VAR_TYPE_VEC3, 1, offsetLocation, 0xffffffff }); + vertexShader.addUniformVar((GX2UniformVar){ "unf_scale", GX2_VAR_TYPE_VEC3, 1, scaleLocation, 0xffffffff }); + + positionLocation = 0; + texCoordLocation = 1; + vertexShader.addAttribVar((GX2AttribVar){ "attr_position", GX2_VAR_TYPE_VEC3, 0, positionLocation }); + vertexShader.addAttribVar((GX2AttribVar){ "attr_texture_coord", GX2_VAR_TYPE_VEC2, 0, texCoordLocation }); + + //! setup attribute streams + GX2InitAttribStream(vertexShader.getAttributeBuffer(0), positionLocation, 0, 0, GX2_ATTRIB_FORMAT_32_32_32_FLOAT); + GX2InitAttribStream(vertexShader.getAttributeBuffer(1), texCoordLocation, 1, 0, GX2_ATTRIB_FORMAT_32_32_FLOAT); + + //! create fetch shader + fetchShader = new FetchShader(vertexShader.getAttributeBuffer(), vertexShader.getAttributesCount()); + + //! model vertex has to be align and cannot be in unknown regions for GX2 like 0xBCAE1000 + posVtxs = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciPositionVtxsSize); + texCoords = (f32*)memalign(GX2_VERTEX_BUFFER_ALIGNMENT, ciTexCoordsVtxsSize); + + //! defaults for normal square + //! position vertex structure and texture coordinate vertex structure + s32 i = 0; + posVtxs[i++] = -1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = -1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = 1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + posVtxs[i++] = -1.0f; posVtxs[i++] = 1.0f; posVtxs[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, posVtxs, ciPositionVtxsSize); + + i = 0; + texCoords[i++] = 0.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 1.0f; + texCoords[i++] = 1.0f; texCoords[i++] = 0.0f; + texCoords[i++] = 0.0f; texCoords[i++] = 0.0f; + GX2Invalidate(GX2_INVALIDATE_CPU_ATTRIB_BUFFER, texCoords, ciTexCoordsVtxsSize); +} + +Texture2DShader::~Texture2DShader() +{ + if(posVtxs) + { + free(posVtxs); + posVtxs = NULL; + } + if(texCoords) + { + free(texCoords); + texCoords = NULL; + } + + delete fetchShader; + fetchShader = NULL; +} diff --git a/source/video/shaders/Texture2DShader.h b/source/video/shaders/Texture2DShader.h new file mode 100644 index 0000000..cddeee0 --- /dev/null +++ b/source/video/shaders/Texture2DShader.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef __TEXTURE_2D_SHADER_H_ +#define __TEXTURE_2D_SHADER_H_ + +#include "VertexShader.h" +#include "PixelShader.h" +#include "FetchShader.h" + +class Texture2DShader : public Shader +{ +private: + Texture2DShader(); + virtual ~Texture2DShader(); + + static const u32 cuAttributeCount = 2; + static const u32 ciPositionVtxsSize = 4 * cuVertexAttrSize; + static const u32 ciTexCoordsVtxsSize = 4 * cuTexCoordAttrSize; + + static Texture2DShader *shaderInstance; + + FetchShader *fetchShader; + VertexShader vertexShader; + PixelShader pixelShader; + + f32 *posVtxs; + f32 *texCoords; + + u32 angleLocation; + u32 offsetLocation; + u32 scaleLocation; + u32 colorIntensityLocation; + u32 blurLocation; + u32 samplerLocation; + u32 positionLocation; + u32 texCoordLocation; +public: + static Texture2DShader *instance() { + if(!shaderInstance) { + shaderInstance = new Texture2DShader(); + } + return shaderInstance; + } + static void destroyInstance() { + if(shaderInstance) { + delete shaderInstance; + shaderInstance = NULL; + } + } + + void setShaders(void) const + { + fetchShader->setShader(); + vertexShader.setShader(); + pixelShader.setShader(); + } + + void setAttributeBuffer(const f32 * texCoords_in = NULL, const f32 * posVtxs_in = NULL, const u32 & vtxCount = 0) const + { + if(posVtxs_in && texCoords_in && vtxCount) + { + VertexShader::setAttributeBuffer(0, vtxCount * cuVertexAttrSize, cuVertexAttrSize, posVtxs_in); + VertexShader::setAttributeBuffer(1, vtxCount * cuTexCoordAttrSize, cuTexCoordAttrSize, texCoords_in); + } + else { + VertexShader::setAttributeBuffer(0, ciPositionVtxsSize, cuVertexAttrSize, posVtxs); + VertexShader::setAttributeBuffer(1, ciTexCoordsVtxsSize, cuTexCoordAttrSize, texCoords); + } + } + + void setAngle(const float & val) + { + VertexShader::setUniformReg(angleLocation, 4, &val); + } + void setOffset(const glm::vec3 & vec) + { + VertexShader::setUniformReg(offsetLocation, 4, &vec[0]); + } + void setScale(const glm::vec3 & vec) + { + VertexShader::setUniformReg(scaleLocation, 4, &vec[0]); + } + void setColorIntensity(const glm::vec4 & vec) + { + PixelShader::setUniformReg(colorIntensityLocation, 4, &vec[0]); + } + void setBlurring(const glm::vec3 & vec) + { + PixelShader::setUniformReg(blurLocation, 4, &vec[0]); + } + + void setTextureAndSampler(const GX2Texture *texture, const GX2Sampler *sampler) const { + GX2SetPixelTexture(texture, samplerLocation); + GX2SetPixelSampler(sampler, samplerLocation); + } +}; + +#endif // __TEXTURE_2D_SHADER_H_ diff --git a/source/video/shaders/VertexShader.h b/source/video/shaders/VertexShader.h new file mode 100644 index 0000000..18dc0fe --- /dev/null +++ b/source/video/shaders/VertexShader.h @@ -0,0 +1,178 @@ +/**************************************************************************** + * Copyright (C) 2015 Dimok + * + * 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 . + ****************************************************************************/ +#ifndef VERTEX_SHADER_H +#define VERTEX_SHADER_H + +#include +#include "Shader.h" + +class VertexShader : public Shader +{ +public: + VertexShader(u32 numAttr) + : attributesCount( numAttr ) + , attributes( new GX2AttribStream[attributesCount] ) + , vertexShader( (GX2VertexShader*) memalign(0x40, sizeof(GX2VertexShader)) ) + { + if(vertexShader) + { + memset(vertexShader, 0, sizeof(GX2VertexShader)); + vertexShader->shader_mode = GX2_SHADER_MODE_UNIFORM_REGISTER; + } + } + + virtual ~VertexShader() { + delete [] attributes; + + if(vertexShader) + { + if(vertexShader->shader_data) + free(vertexShader->shader_data); + + for(u32 i = 0; i < vertexShader->uniform_blocks_count; i++) + free((void*)vertexShader->uniform_block[i].name); + + if(vertexShader->uniform_block) + free((void*)vertexShader->uniform_block); + + for(u32 i = 0; i < vertexShader->uniform_vars_count; i++) + free((void*)vertexShader->uniform_var[i].name); + + if(vertexShader->uniform_var) + free((void*)vertexShader->uniform_var); + + if(vertexShader->initial_value) + free((void*)vertexShader->initial_value); + + for(u32 i = 0; i < vertexShader->sampler_vars_count; i++) + free((void*)vertexShader->sampler_var[i].name); + + if(vertexShader->sampler_var) + free((void*)vertexShader->sampler_var); + + for(u32 i = 0; i < vertexShader->attribute_vars_count; i++) + free((void*)vertexShader->attribute_var[i].name); + + if(vertexShader->attribute_var) + free((void*)vertexShader->attribute_var); + + if(vertexShader->loops_data) + free((void*)vertexShader->loops_data); + + free(vertexShader); + } + } + + void setProgram(const u32 * program, const u32 & programSize, const u32 * regs, const u32 & regsSize) + { + if(!vertexShader) + return; + + //! this must be moved into an area where the graphic engine has access to and must be aligned to 0x100 + vertexShader->shader_size = programSize; + vertexShader->shader_data = memalign(GX2_SHADER_ALIGNMENT, vertexShader->shader_size); + if(vertexShader->shader_data) + { + memcpy(vertexShader->shader_data, program, vertexShader->shader_size); + GX2Invalidate(GX2_INVALIDATE_CPU_SHADER, vertexShader->shader_data, vertexShader->shader_size); + } + + memcpy(vertexShader->regs, regs, regsSize); + } + + void addUniformVar(const GX2UniformVar & var) + { + if(!vertexShader) + return; + + u32 idx = vertexShader->uniform_vars_count; + + GX2UniformVar* newVar = (GX2UniformVar*) malloc((vertexShader->uniform_vars_count + 1) * sizeof(GX2UniformVar)); + if(newVar) + { + if(vertexShader->uniform_vars_count > 0) + { + memcpy(newVar, vertexShader->uniform_var, vertexShader->uniform_vars_count * sizeof(GX2UniformVar)); + free(vertexShader->uniform_var); + } + vertexShader->uniform_var = newVar; + + memcpy(vertexShader->uniform_var + idx, &var, sizeof(GX2UniformVar)); + vertexShader->uniform_var[idx].name = (char*) malloc(strlen(var.name) + 1); + strcpy((char*)vertexShader->uniform_var[idx].name, var.name); + + vertexShader->uniform_vars_count++; + } + } + + void addAttribVar(const GX2AttribVar & var) + { + if(!vertexShader) + return; + + u32 idx = vertexShader->attribute_vars_count; + + GX2AttribVar* newVar = (GX2AttribVar*) malloc((vertexShader->attribute_vars_count + 1) * sizeof(GX2AttribVar)); + if(newVar) + { + if(vertexShader->attribute_vars_count > 0) + { + memcpy(newVar, vertexShader->attribute_var, vertexShader->attribute_vars_count * sizeof(GX2AttribVar)); + free(vertexShader->attribute_var); + } + vertexShader->attribute_var = newVar; + + memcpy(vertexShader->attribute_var + idx, &var, sizeof(GX2AttribVar)); + vertexShader->attribute_var[idx].name = (char*) malloc(strlen(var.name) + 1); + strcpy((char*)vertexShader->attribute_var[idx].name, var.name); + + vertexShader->attribute_vars_count++; + } + } + + static inline void setAttributeBuffer(u32 bufferIdx, u32 bufferSize, u32 stride, const void * buffer) { + GX2SetAttribBuffer(bufferIdx, bufferSize, stride, buffer); + } + + GX2VertexShader *getVertexShader() const { + return vertexShader; + } + + void setShader(void) const { + GX2SetVertexShader(vertexShader); + } + + GX2AttribStream * getAttributeBuffer(u32 idx = 0) const { + if(idx >= attributesCount) { + return NULL; + } + return &attributes[idx]; + } + u32 getAttributesCount() const { + return attributesCount; + } + + static void setUniformReg(u32 location, u32 size, const void * reg) { + GX2SetVertexUniformReg(location, size, reg); + } +protected: + u32 attributesCount; + GX2AttribStream *attributes; + GX2VertexShader *vertexShader; +}; + +#endif // VERTEX_SHADER_H