diff options
Diffstat (limited to 'Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp')
-rw-r--r-- | Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp | 4801 |
1 files changed, 4801 insertions, 0 deletions
diff --git a/Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp b/Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp new file mode 100644 index 00000000..600f7202 --- /dev/null +++ b/Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp @@ -0,0 +1,4801 @@ +/* + LICENSE + ------- +Copyright 2005-2013 Nullsoft, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Nullsoft nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "api__vis_milk2.h" +#include "plugin.h" +#include "resource.h" +#include "support.h" +//#include "evallib\eval.h" // for math. expr. eval - thanks Francis! (in SourceOffSite, it's the 'vis_avs\evallib' project.) +//#include "evallib\compiler.h" +#include "ns-eel2/ns-eel.h" +#include "utility.h" +#include <assert.h> +#include <math.h> + +#define D3DCOLOR_RGBA_01(r,g,b,a) D3DCOLOR_RGBA(((int)(r*255)),((int)(g*255)),((int)(b*255)),((int)(a*255))) +#define FRAND ((warand() % 7381)/7380.0f) + +#define VERT_CLIP 0.75f // warning: top/bottom can get clipped if you go < 0.65! + +int g_title_font_sizes[] = +{ + // NOTE: DO NOT EXCEED 64 FONTS HERE. + 6, 8, 10, 12, 14, 16, + 20, 26, 32, 38, 44, 50, 56, + 64, 72, 80, 88, 96, 104, 112, 120, 128, 136, 144, + 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, + 480, 512 /**/ +}; + +//#define COMPILE_MULTIMON_STUBS 1 +//#include <multimon.h> + +// This function evaluates whether the floating-point +// control Word is set to single precision/round to nearest/ +// exceptions disabled. If not, the +// function changes the control Word to set them and returns +// TRUE, putting the old control Word value in the passback +// location pointed to by pwOldCW. +static void MungeFPCW( WORD *pwOldCW ) +{ +#if 0 + BOOL ret = FALSE; + WORD wTemp, wSave; + + __asm fstcw wSave + if (wSave & 0x300 || // Not single mode + 0x3f != (wSave & 0x3f) || // Exceptions enabled + wSave & 0xC00) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 300h ;; single mode + or ax, 3fh ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + ret = TRUE; + } + if (pwOldCW) *pwOldCW = wSave; + // return ret; +#else + _controlfp(_PC_24, _MCW_PC); // single precision + _controlfp(_RC_NEAR, _MCW_RC); // round to nearest mode + _controlfp(_EM_ZERODIVIDE, _EM_ZERODIVIDE); // disable divide-by-zero +#endif +} + +void RestoreFPCW(WORD wSave) +{ + __asm fldcw wSave +} + +int GetNumToSpawn(float fTime, float fDeltaT, float fRate, float fRegularity, int iNumSpawnedSoFar) +{ + // PARAMETERS + // ------------ + // fTime: sum of all fDeltaT's so far (excluding this one) + // fDeltaT: time window for this frame + // fRate: avg. rate (spawns per second) of generation + // fRegularity: regularity of generation + // 0.0: totally chaotic + // 0.2: getting chaotic / very jittered + // 0.4: nicely jittered + // 0.6: slightly jittered + // 0.8: almost perfectly regular + // 1.0: perfectly regular + // iNumSpawnedSoFar: the total number of spawnings so far + // + // RETURN VALUE + // ------------ + // The number to spawn for this frame (add this to your net count!). + // + // COMMENTS + // ------------ + // The spawn values returned will, over time, match + // (within 1%) the theoretical totals expected based on the + // amount of time passed and the average generation rate. + // + // UNRESOLVED ISSUES + // ----------------- + // actual results of mixed gen. (0 < reg < 1) are about 1% too low + // in the long run (vs. analytical expectations). Decided not + // to bother fixing it since it's only 1% (and VERY consistent). + + float fNumToSpawnReg; + float fNumToSpawnIrreg; + float fNumToSpawn; + + // compute # spawned based on regular generation + fNumToSpawnReg = ((fTime + fDeltaT) * fRate) - iNumSpawnedSoFar; + + // compute # spawned based on irregular (random) generation + if (fDeltaT <= 1.0f / fRate) + { + // case 1: avg. less than 1 spawn per frame + if ((warand() % 16384)/16384.0f < fDeltaT * fRate) + fNumToSpawnIrreg = 1.0f; + else + fNumToSpawnIrreg = 0.0f; + } + else + { + // case 2: avg. more than 1 spawn per frame + fNumToSpawnIrreg = fDeltaT * fRate; + fNumToSpawnIrreg *= 2.0f*(warand() % 16384)/16384.0f; + } + + // get linear combo. of regular & irregular + fNumToSpawn = fNumToSpawnReg*fRegularity + fNumToSpawnIrreg*(1.0f - fRegularity); + + // round to nearest integer for result + return (int)(fNumToSpawn + 0.49f); +} + +bool CPlugin::OnResizeTextWindow() +{ + /* + if (!m_hTextWnd) + return false; + + RECT rect; + GetClientRect(m_hTextWnd, &rect); + + if (rect.right - rect.left != m_nTextWndWidth || + rect.bottom - rect.top != m_nTextWndHeight) + { + m_nTextWndWidth = rect.right - rect.left; + m_nTextWndHeight = rect.bottom - rect.top; + + // first, resize fonts if necessary + //if (!InitFont()) + //return false; + + // then resize the memory bitmap used for double buffering + if (m_memDC) + { + SelectObject(m_memDC, m_oldBM); // delete our doublebuffer + DeleteObject(m_memDC); + DeleteObject(m_memBM); + m_memDC = NULL; + m_memBM = NULL; + m_oldBM = NULL; + } + + HDC hdc = GetDC(m_hTextWnd); + if (!hdc) return false; + + m_memDC = CreateCompatibleDC(hdc); + m_memBM = CreateCompatibleBitmap(hdc, rect.right - rect.left, rect.bottom - rect.top); + m_oldBM = (HBITMAP)SelectObject(m_memDC,m_memBM); + + ReleaseDC(m_hTextWnd, hdc); + + // save new window pos + WriteRealtimeConfig(); + }*/ + + return true; +} + + +void CPlugin::ClearGraphicsWindow() +{ + // clear the window contents, to avoid a 1-pixel-thick border of noise that sometimes sticks around + /* + RECT rect; + GetClientRect(GetPluginWindow(), &rect); + + HDC hdc = GetDC(GetPluginWindow()); + FillRect(hdc, &rect, m_hBlackBrush); + ReleaseDC(GetPluginWindow(), hdc); + */ +} + +/* +bool CPlugin::OnResizeGraphicsWindow() +{ + // NO LONGER NEEDED, SINCE PLUGIN SHELL CREATES A NEW DIRECTX + // OBJECT WHENEVER WINDOW IS RESIZED. +} +*/ + +bool CPlugin::RenderStringToTitleTexture() // m_szSongMessage +{ + if (!m_lpDDSTitle) // this *can* be NULL, if not much video mem! + return false; + + if (m_supertext.szTextW[0]==0) + return false; + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return false; + + wchar_t szTextToDraw[512]; + swprintf(szTextToDraw, L" %s ", m_supertext.szTextW); //add a space @ end for italicized fonts; and at start, too, because it's centered! + + // Remember the original backbuffer and zbuffer + LPDIRECT3DSURFACE9 pBackBuffer=NULL;//, pZBuffer=NULL; + lpDevice->GetRenderTarget( 0, &pBackBuffer ); + //lpDevice->GetDepthStencilSurface( &pZBuffer ); + + // set render target to m_lpDDSTitle + { + lpDevice->SetTexture(0, NULL); + + IDirect3DSurface9* pNewTarget = NULL; + if (m_lpDDSTitle->GetSurfaceLevel(0, &pNewTarget) != D3D_OK) + { + SafeRelease(pBackBuffer); + //SafeRelease(pZBuffer); + return false; + } + lpDevice->SetRenderTarget(0, pNewTarget); + //lpDevice->SetDepthStencilSurface( NULL ); + pNewTarget->Release(); + + lpDevice->SetTexture(0, NULL); + } + + // clear the texture to black + { + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( WFVERTEX_FORMAT ); + lpDevice->SetTexture(0, NULL); + + lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); + + // set up a quad + WFVERTEX verts[4]; + for (int i=0; i<4; i++) + { + verts[i].x = (i%2==0) ? -1.f : 1.f; + verts[i].y = (i/2==0) ? -1.f : 1.f; + verts[i].z = 0; + verts[i].Diffuse = 0xFF000000; + } + + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts, sizeof(WFVERTEX)); + } + + /*// 1. clip title if too many chars + if (m_supertext.bIsSongTitle) + { + // truncate song title if too long; don't clip custom messages, though! + int clip_chars = 32; + int user_title_size = GetFontHeight(SONGTITLE_FONT); + + #define MIN_CHARS 8 // max clip_chars *for BIG FONTS* + #define MAX_CHARS 64 // max clip chars *for tiny fonts* + float t = (user_title_size-10)/(float)(128-10); + t = min(1,max(0,t)); + clip_chars = (int)(MAX_CHARS - (MAX_CHARS-MIN_CHARS)*t); + + if ((int)strlen(szTextToDraw) > clip_chars+3) + lstrcpy(&szTextToDraw[clip_chars], "..."); + }*/ + + bool ret = true; + + // use 2 lines; must leave room for bottom of 'g' characters and such! + RECT rect; + rect.left = 0; + rect.right = m_nTitleTexSizeX; + rect.top = m_nTitleTexSizeY* 1/21; // otherwise, top of '%' could be cut off (1/21 seems safe) + rect.bottom = m_nTitleTexSizeY*17/21; // otherwise, bottom of 'g' could be cut off (18/21 seems safe, but we want some leeway) + + if (!m_supertext.bIsSongTitle) + { + // custom msg -> pick font to use that will best fill the texture + + HFONT gdi_font = NULL; + LPD3DXFONT d3dx_font = NULL; + + int lo = 0; + int hi = sizeof(g_title_font_sizes)/sizeof(int) - 1; + + // limit the size of the font used: + + //int user_title_size = GetFontHeight(SONGTITLE_FONT); + //while (g_title_font_sizes[hi] > user_title_size*2 && hi>4) + // hi--; + + RECT temp; + while (1)//(lo < hi-1) + { + int mid = (lo+hi)/2; + + // create new gdi font at 'mid' size: + gdi_font = CreateFontW( g_title_font_sizes[mid], 0, 0, 0, m_supertext.bBold ? 900 : 400, m_supertext.bItal, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + m_fontinfo[SONGTITLE_FONT].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, + DEFAULT_PITCH, m_supertext.nFontFace ); + if (gdi_font) + { + // create new d3dx font at 'mid' size: + if (pCreateFontW( + lpDevice, + g_title_font_sizes[mid], + 0, + m_supertext.bBold ? 900 : 400, + 1, + m_supertext.bItal, + DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, + ANTIALIASED_QUALITY,//m_fontinfo[SONGTITLE_FONT].bAntiAliased ? ANTIALIASED_QUALITY : DEFAULT_QUALITY, + DEFAULT_PITCH, + m_supertext.nFontFace, + &d3dx_font + ) == D3D_OK) + { + if (lo == hi-1) + break; // DONE; but the 'lo'-size font is ready for use! + + // compute size of text if drawn w/font of THIS size: + temp = rect; + int h = d3dx_font->DrawTextW(NULL, szTextToDraw, -1, &temp, DT_SINGLELINE | DT_CALCRECT /*| DT_NOPREFIX*/, 0xFFFFFFFF); + + // adjust & prepare to reiterate: + if (temp.right >= rect.right || h > rect.bottom-rect.top) + hi = mid; + else + lo = mid; + + SafeRelease(d3dx_font); + } + + DeleteObject(gdi_font); gdi_font=NULL; + } + } + + if (gdi_font && d3dx_font) + { + // do actual drawing + set m_supertext.nFontSizeUsed; use 'lo' size + int h = d3dx_font->DrawTextW(NULL, szTextToDraw, -1, &temp, DT_SINGLELINE | DT_CALCRECT /*| DT_NOPREFIX*/ | DT_CENTER, 0xFFFFFFFF); + temp.left = 0; + temp.right = m_nTitleTexSizeX; // now allow text to go all the way over, since we're actually drawing! + temp.top = m_nTitleTexSizeY/2 - h/2; + temp.bottom = m_nTitleTexSizeY/2 + h/2; + m_supertext.nFontSizeUsed = d3dx_font->DrawTextW(NULL, szTextToDraw, -1, &temp, DT_SINGLELINE /*| DT_NOPREFIX*/ | DT_CENTER, 0xFFFFFFFF); + + ret = true; + } + else + { + ret = false; + } + + // clean up font: + SafeRelease(d3dx_font); + if (gdi_font) DeleteObject(gdi_font); gdi_font=NULL; + } + else // song title + { + wchar_t* str = m_supertext.szTextW; + + // clip the text manually... + // NOTE: DT_END_ELLIPSIS CAUSES NOTHING TO DRAW, IF YOU USE W/D3DX9! + int h; + int max_its = 6; + int it = 0; + while (it < max_its) + { + it++; + + if (!str[0]) + break; + + RECT temp = rect; + h = m_d3dx_title_font_doublesize->DrawTextW(NULL, str, -1, &temp, DT_SINGLELINE | DT_CALCRECT /*| DT_NOPREFIX | DT_END_ELLIPSIS*/, 0xFFFFFFFF); + if (temp.right-temp.left <= m_nTitleTexSizeX) + break; + + // 11/01/2009 DO - disabled as it was causing to users 'random' titles against + // what is expected so we now just work on the ellipse at the end approach which + + // manually clip the text... chop segments off the front + /*wchar_t* p = wcsstr(str, L" - "); + if (p) + { + str = p+3; + continue; + }*/ + + // no more stuff to chop off the front; chop off the end w/ ... + int len = wcslen(str); + float fPercentToKeep = 0.91f * m_nTitleTexSizeX / (float)(temp.right-temp.left); + if (len > 8) + lstrcpyW( &str[ (int)(len*fPercentToKeep) ], L"..."); + break; + } + + // now actually draw it + RECT temp; + temp.left = 0; + temp.right = m_nTitleTexSizeX; // now allow text to go all the way over, since we're actually drawing! + temp.top = m_nTitleTexSizeY/2 - h/2; + temp.bottom = m_nTitleTexSizeY/2 + h/2; + + // NOTE: DT_END_ELLIPSIS CAUSES NOTHING TO DRAW, IF YOU USE W/D3DX9! + m_supertext.nFontSizeUsed = m_d3dx_title_font_doublesize->DrawTextW(NULL, str, -1, &temp, DT_SINGLELINE /*| DT_NOPREFIX | DT_END_ELLIPSIS*/ | DT_CENTER , 0xFFFFFFFF); + } + + // Change the rendertarget back to the original setup + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderTarget( 0, pBackBuffer ); + //lpDevice->SetDepthStencilSurface( pZBuffer ); + SafeRelease(pBackBuffer); + //SafeRelease(pZBuffer); + + return ret; +} + +void CPlugin::LoadPerFrameEvallibVars(CState* pState) +{ + // load the 'var_pf_*' variables in this CState object with the correct values. + // for vars that affect pixel motion, that means evaluating them at time==-1, + // (i.e. no blending w/blendto value); the blending of the file dx/dy + // will be done *after* execution of the per-vertex code. + // for vars that do NOT affect pixel motion, evaluate them at the current time, + // so that if they're blending, both states see the blended value. + + // 1. vars that affect pixel motion: (eval at time==-1) + *pState->var_pf_zoom = (double)pState->m_fZoom.eval(-1);//GetTime()); + *pState->var_pf_zoomexp = (double)pState->m_fZoomExponent.eval(-1);//GetTime()); + *pState->var_pf_rot = (double)pState->m_fRot.eval(-1);//GetTime()); + *pState->var_pf_warp = (double)pState->m_fWarpAmount.eval(-1);//GetTime()); + *pState->var_pf_cx = (double)pState->m_fRotCX.eval(-1);//GetTime()); + *pState->var_pf_cy = (double)pState->m_fRotCY.eval(-1);//GetTime()); + *pState->var_pf_dx = (double)pState->m_fXPush.eval(-1);//GetTime()); + *pState->var_pf_dy = (double)pState->m_fYPush.eval(-1);//GetTime()); + *pState->var_pf_sx = (double)pState->m_fStretchX.eval(-1);//GetTime()); + *pState->var_pf_sy = (double)pState->m_fStretchY.eval(-1);//GetTime()); + // read-only: + *pState->var_pf_time = (double)(GetTime() - m_fStartTime); + *pState->var_pf_fps = (double)GetFps(); + *pState->var_pf_bass = (double)mysound.imm_rel[0]; + *pState->var_pf_mid = (double)mysound.imm_rel[1]; + *pState->var_pf_treb = (double)mysound.imm_rel[2]; + *pState->var_pf_bass_att = (double)mysound.avg_rel[0]; + *pState->var_pf_mid_att = (double)mysound.avg_rel[1]; + *pState->var_pf_treb_att = (double)mysound.avg_rel[2]; + *pState->var_pf_frame = (double)GetFrame(); + //*pState->var_pf_monitor = 0; -leave this as it was set in the per-frame INIT code! + for (int vi=0; vi<NUM_Q_VAR; vi++) + *pState->var_pf_q[vi] = pState->q_values_after_init_code[vi];//0.0f; + *pState->var_pf_monitor = pState->monitor_after_init_code; + *pState->var_pf_progress = (GetTime() - m_fPresetStartTime) / (m_fNextPresetTime - m_fPresetStartTime); + + // 2. vars that do NOT affect pixel motion: (eval at time==now) + *pState->var_pf_decay = (double)pState->m_fDecay.eval(GetTime()); + *pState->var_pf_wave_a = (double)pState->m_fWaveAlpha.eval(GetTime()); + *pState->var_pf_wave_r = (double)pState->m_fWaveR.eval(GetTime()); + *pState->var_pf_wave_g = (double)pState->m_fWaveG.eval(GetTime()); + *pState->var_pf_wave_b = (double)pState->m_fWaveB.eval(GetTime()); + *pState->var_pf_wave_x = (double)pState->m_fWaveX.eval(GetTime()); + *pState->var_pf_wave_y = (double)pState->m_fWaveY.eval(GetTime()); + *pState->var_pf_wave_mystery= (double)pState->m_fWaveParam.eval(GetTime()); + *pState->var_pf_wave_mode = (double)pState->m_nWaveMode; //?!?! -why won't it work if set to pState->m_nWaveMode??? + *pState->var_pf_ob_size = (double)pState->m_fOuterBorderSize.eval(GetTime()); + *pState->var_pf_ob_r = (double)pState->m_fOuterBorderR.eval(GetTime()); + *pState->var_pf_ob_g = (double)pState->m_fOuterBorderG.eval(GetTime()); + *pState->var_pf_ob_b = (double)pState->m_fOuterBorderB.eval(GetTime()); + *pState->var_pf_ob_a = (double)pState->m_fOuterBorderA.eval(GetTime()); + *pState->var_pf_ib_size = (double)pState->m_fInnerBorderSize.eval(GetTime()); + *pState->var_pf_ib_r = (double)pState->m_fInnerBorderR.eval(GetTime()); + *pState->var_pf_ib_g = (double)pState->m_fInnerBorderG.eval(GetTime()); + *pState->var_pf_ib_b = (double)pState->m_fInnerBorderB.eval(GetTime()); + *pState->var_pf_ib_a = (double)pState->m_fInnerBorderA.eval(GetTime()); + *pState->var_pf_mv_x = (double)pState->m_fMvX.eval(GetTime()); + *pState->var_pf_mv_y = (double)pState->m_fMvY.eval(GetTime()); + *pState->var_pf_mv_dx = (double)pState->m_fMvDX.eval(GetTime()); + *pState->var_pf_mv_dy = (double)pState->m_fMvDY.eval(GetTime()); + *pState->var_pf_mv_l = (double)pState->m_fMvL.eval(GetTime()); + *pState->var_pf_mv_r = (double)pState->m_fMvR.eval(GetTime()); + *pState->var_pf_mv_g = (double)pState->m_fMvG.eval(GetTime()); + *pState->var_pf_mv_b = (double)pState->m_fMvB.eval(GetTime()); + *pState->var_pf_mv_a = (double)pState->m_fMvA.eval(GetTime()); + *pState->var_pf_echo_zoom = (double)pState->m_fVideoEchoZoom.eval(GetTime()); + *pState->var_pf_echo_alpha = (double)pState->m_fVideoEchoAlpha.eval(GetTime()); + *pState->var_pf_echo_orient = (double)pState->m_nVideoEchoOrientation; + // new in v1.04: + *pState->var_pf_wave_usedots = (double)pState->m_bWaveDots; + *pState->var_pf_wave_thick = (double)pState->m_bWaveThick; + *pState->var_pf_wave_additive = (double)pState->m_bAdditiveWaves; + *pState->var_pf_wave_brighten = (double)pState->m_bMaximizeWaveColor; + *pState->var_pf_darken_center = (double)pState->m_bDarkenCenter; + *pState->var_pf_gamma = (double)pState->m_fGammaAdj.eval(GetTime()); + *pState->var_pf_wrap = (double)pState->m_bTexWrap; + *pState->var_pf_invert = (double)pState->m_bInvert; + *pState->var_pf_brighten = (double)pState->m_bBrighten; + *pState->var_pf_darken = (double)pState->m_bDarken; + *pState->var_pf_solarize = (double)pState->m_bSolarize; + *pState->var_pf_meshx = (double)m_nGridX; + *pState->var_pf_meshy = (double)m_nGridY; + *pState->var_pf_pixelsx = (double)GetWidth(); + *pState->var_pf_pixelsy = (double)GetHeight(); + *pState->var_pf_aspectx = (double)m_fInvAspectX; + *pState->var_pf_aspecty = (double)m_fInvAspectY; + // new in v2.0: + *pState->var_pf_blur1min = (double)pState->m_fBlur1Min.eval(GetTime()); + *pState->var_pf_blur2min = (double)pState->m_fBlur2Min.eval(GetTime()); + *pState->var_pf_blur3min = (double)pState->m_fBlur3Min.eval(GetTime()); + *pState->var_pf_blur1max = (double)pState->m_fBlur1Max.eval(GetTime()); + *pState->var_pf_blur2max = (double)pState->m_fBlur2Max.eval(GetTime()); + *pState->var_pf_blur3max = (double)pState->m_fBlur3Max.eval(GetTime()); + *pState->var_pf_blur1_edge_darken = (double)pState->m_fBlur1EdgeDarken.eval(GetTime()); +} + +void CPlugin::RunPerFrameEquations(int code) +{ + // run per-frame calculations + + /* + code is only valid when blending. + OLDcomp ~ blend-from preset has a composite shader; + NEWwarp ~ blend-to preset has a warp shader; etc. + + code OLDcomp NEWcomp OLDwarp NEWwarp + 0 + 1 1 + 2 1 + 3 1 1 + 4 1 + 5 1 1 + 6 1 1 + 7 1 1 1 + 8 1 + 9 1 1 + 10 1 1 + 11 1 1 1 + 12 1 1 + 13 1 1 1 + 14 1 1 1 + 15 1 1 1 1 + */ + + // when blending booleans (like darken, invert, etc) for pre-shader presets, + // if blending to/from a pixel-shader preset, we can tune the snap point + // (when it changes during the blend) for a less jumpy transition: + m_fSnapPoint = 0.5f; + if (m_pState->m_bBlending) + { + switch(code) + { + case 4: + case 6: + case 12: + case 14: + // old preset (only) had a comp shader + m_fSnapPoint = -0.01f; + break; + case 1: + case 3: + case 9: + case 11: + // new preset (only) has a comp shader + m_fSnapPoint = 1.01f; + break; + case 0: + case 2: + case 8: + case 10: + // neither old or new preset had a comp shader + m_fSnapPoint = 0.5f; + break; + case 5: + case 7: + case 13: + case 15: + // both old and new presets use a comp shader - so it won't matter + m_fSnapPoint = 0.5f; + break; + } + } + + int num_reps = (m_pState->m_bBlending) ? 2 : 1; + for (int rep=0; rep<num_reps; rep++) + { + CState *pState; + + if (rep==0) + pState = m_pState; + else + pState = m_pOldState; + + // values that will affect the pixel motion (and will be automatically blended + // LATER, when the results of 2 sets of these params creates 2 different U/V + // meshes that get blended together.) + LoadPerFrameEvallibVars(pState); + + // also do just a once-per-frame init for the *per-**VERTEX*** *READ-ONLY* variables + // (the non-read-only ones will be reset/restored at the start of each vertex) + *pState->var_pv_time = *pState->var_pf_time; + *pState->var_pv_fps = *pState->var_pf_fps; + *pState->var_pv_frame = *pState->var_pf_frame; + *pState->var_pv_progress = *pState->var_pf_progress; + *pState->var_pv_bass = *pState->var_pf_bass; + *pState->var_pv_mid = *pState->var_pf_mid; + *pState->var_pv_treb = *pState->var_pf_treb; + *pState->var_pv_bass_att = *pState->var_pf_bass_att; + *pState->var_pv_mid_att = *pState->var_pf_mid_att; + *pState->var_pv_treb_att = *pState->var_pf_treb_att; + *pState->var_pv_meshx = (double)m_nGridX; + *pState->var_pv_meshy = (double)m_nGridY; + *pState->var_pv_pixelsx = (double)GetWidth(); + *pState->var_pv_pixelsy = (double)GetHeight(); + *pState->var_pv_aspectx = (double)m_fInvAspectX; + *pState->var_pv_aspecty = (double)m_fInvAspectY; + //*pState->var_pv_monitor = *pState->var_pf_monitor; + + // execute once-per-frame expressions: +#ifndef _NO_EXPR_ + if (pState->m_pf_codehandle) + { + if (pState->m_pf_codehandle) + { + NSEEL_code_execute(pState->m_pf_codehandle); + } + } +#endif + + // save some things for next frame: + pState->monitor_after_init_code = *pState->var_pf_monitor; + + // save some things for per-vertex code: + for (int vi=0; vi<NUM_Q_VAR; vi++) + *pState->var_pv_q[vi] = *pState->var_pf_q[vi]; + + // (a few range checks:) + *pState->var_pf_gamma = max(0 , min( 8, *pState->var_pf_gamma )); + *pState->var_pf_echo_zoom = max(0.001, min( 1000, *pState->var_pf_echo_zoom)); + + /* + if (m_pState->m_bRedBlueStereo || m_bAlways3D) + { + // override wave colors + *pState->var_pf_wave_r = 0.35f*(*pState->var_pf_wave_r) + 0.65f; + *pState->var_pf_wave_g = 0.35f*(*pState->var_pf_wave_g) + 0.65f; + *pState->var_pf_wave_b = 0.35f*(*pState->var_pf_wave_b) + 0.65f; + } + */ + } + + if (m_pState->m_bBlending) + { + // For all variables that do NOT affect pixel motion, blend them NOW, + // so later the user can just access m_pState->m_pf_whatever. + double mix = (double)CosineInterp(m_pState->m_fBlendProgress); + double mix2 = 1.0 - mix; + *m_pState->var_pf_decay = mix*(*m_pState->var_pf_decay ) + mix2*(*m_pOldState->var_pf_decay ); + *m_pState->var_pf_wave_a = mix*(*m_pState->var_pf_wave_a ) + mix2*(*m_pOldState->var_pf_wave_a ); + *m_pState->var_pf_wave_r = mix*(*m_pState->var_pf_wave_r ) + mix2*(*m_pOldState->var_pf_wave_r ); + *m_pState->var_pf_wave_g = mix*(*m_pState->var_pf_wave_g ) + mix2*(*m_pOldState->var_pf_wave_g ); + *m_pState->var_pf_wave_b = mix*(*m_pState->var_pf_wave_b ) + mix2*(*m_pOldState->var_pf_wave_b ); + *m_pState->var_pf_wave_x = mix*(*m_pState->var_pf_wave_x ) + mix2*(*m_pOldState->var_pf_wave_x ); + *m_pState->var_pf_wave_y = mix*(*m_pState->var_pf_wave_y ) + mix2*(*m_pOldState->var_pf_wave_y ); + *m_pState->var_pf_wave_mystery = mix*(*m_pState->var_pf_wave_mystery) + mix2*(*m_pOldState->var_pf_wave_mystery); + // wave_mode: exempt (integer) + *m_pState->var_pf_ob_size = mix*(*m_pState->var_pf_ob_size ) + mix2*(*m_pOldState->var_pf_ob_size ); + *m_pState->var_pf_ob_r = mix*(*m_pState->var_pf_ob_r ) + mix2*(*m_pOldState->var_pf_ob_r ); + *m_pState->var_pf_ob_g = mix*(*m_pState->var_pf_ob_g ) + mix2*(*m_pOldState->var_pf_ob_g ); + *m_pState->var_pf_ob_b = mix*(*m_pState->var_pf_ob_b ) + mix2*(*m_pOldState->var_pf_ob_b ); + *m_pState->var_pf_ob_a = mix*(*m_pState->var_pf_ob_a ) + mix2*(*m_pOldState->var_pf_ob_a ); + *m_pState->var_pf_ib_size = mix*(*m_pState->var_pf_ib_size ) + mix2*(*m_pOldState->var_pf_ib_size ); + *m_pState->var_pf_ib_r = mix*(*m_pState->var_pf_ib_r ) + mix2*(*m_pOldState->var_pf_ib_r ); + *m_pState->var_pf_ib_g = mix*(*m_pState->var_pf_ib_g ) + mix2*(*m_pOldState->var_pf_ib_g ); + *m_pState->var_pf_ib_b = mix*(*m_pState->var_pf_ib_b ) + mix2*(*m_pOldState->var_pf_ib_b ); + *m_pState->var_pf_ib_a = mix*(*m_pState->var_pf_ib_a ) + mix2*(*m_pOldState->var_pf_ib_a ); + *m_pState->var_pf_mv_x = mix*(*m_pState->var_pf_mv_x ) + mix2*(*m_pOldState->var_pf_mv_x ); + *m_pState->var_pf_mv_y = mix*(*m_pState->var_pf_mv_y ) + mix2*(*m_pOldState->var_pf_mv_y ); + *m_pState->var_pf_mv_dx = mix*(*m_pState->var_pf_mv_dx ) + mix2*(*m_pOldState->var_pf_mv_dx ); + *m_pState->var_pf_mv_dy = mix*(*m_pState->var_pf_mv_dy ) + mix2*(*m_pOldState->var_pf_mv_dy ); + *m_pState->var_pf_mv_l = mix*(*m_pState->var_pf_mv_l ) + mix2*(*m_pOldState->var_pf_mv_l ); + *m_pState->var_pf_mv_r = mix*(*m_pState->var_pf_mv_r ) + mix2*(*m_pOldState->var_pf_mv_r ); + *m_pState->var_pf_mv_g = mix*(*m_pState->var_pf_mv_g ) + mix2*(*m_pOldState->var_pf_mv_g ); + *m_pState->var_pf_mv_b = mix*(*m_pState->var_pf_mv_b ) + mix2*(*m_pOldState->var_pf_mv_b ); + *m_pState->var_pf_mv_a = mix*(*m_pState->var_pf_mv_a ) + mix2*(*m_pOldState->var_pf_mv_a ); + *m_pState->var_pf_echo_zoom = mix*(*m_pState->var_pf_echo_zoom ) + mix2*(*m_pOldState->var_pf_echo_zoom ); + *m_pState->var_pf_echo_alpha = mix*(*m_pState->var_pf_echo_alpha ) + mix2*(*m_pOldState->var_pf_echo_alpha ); + *m_pState->var_pf_echo_orient = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_echo_orient : *m_pState->var_pf_echo_orient; + // added in v1.04: + *m_pState->var_pf_wave_usedots = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_wave_usedots : *m_pState->var_pf_wave_usedots ; + *m_pState->var_pf_wave_thick = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_wave_thick : *m_pState->var_pf_wave_thick ; + *m_pState->var_pf_wave_additive= (mix < m_fSnapPoint) ? *m_pOldState->var_pf_wave_additive : *m_pState->var_pf_wave_additive; + *m_pState->var_pf_wave_brighten= (mix < m_fSnapPoint) ? *m_pOldState->var_pf_wave_brighten : *m_pState->var_pf_wave_brighten; + *m_pState->var_pf_darken_center= (mix < m_fSnapPoint) ? *m_pOldState->var_pf_darken_center : *m_pState->var_pf_darken_center; + *m_pState->var_pf_gamma = mix*(*m_pState->var_pf_gamma ) + mix2*(*m_pOldState->var_pf_gamma ); + *m_pState->var_pf_wrap = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_wrap : *m_pState->var_pf_wrap ; + *m_pState->var_pf_invert = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_invert : *m_pState->var_pf_invert ; + *m_pState->var_pf_brighten = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_brighten : *m_pState->var_pf_brighten ; + *m_pState->var_pf_darken = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_darken : *m_pState->var_pf_darken ; + *m_pState->var_pf_solarize = (mix < m_fSnapPoint) ? *m_pOldState->var_pf_solarize : *m_pState->var_pf_solarize ; + // added in v2.0: + *m_pState->var_pf_blur1min = mix*(*m_pState->var_pf_blur1min ) + mix2*(*m_pOldState->var_pf_blur1min ); + *m_pState->var_pf_blur2min = mix*(*m_pState->var_pf_blur2min ) + mix2*(*m_pOldState->var_pf_blur2min ); + *m_pState->var_pf_blur3min = mix*(*m_pState->var_pf_blur3min ) + mix2*(*m_pOldState->var_pf_blur3min ); + *m_pState->var_pf_blur1max = mix*(*m_pState->var_pf_blur1max ) + mix2*(*m_pOldState->var_pf_blur1max ); + *m_pState->var_pf_blur2max = mix*(*m_pState->var_pf_blur2max ) + mix2*(*m_pOldState->var_pf_blur2max ); + *m_pState->var_pf_blur3max = mix*(*m_pState->var_pf_blur3max ) + mix2*(*m_pOldState->var_pf_blur3max ); + *m_pState->var_pf_blur1_edge_darken = mix*(*m_pState->var_pf_blur1_edge_darken) + mix2*(*m_pOldState->var_pf_blur1_edge_darken); + } +} + +void CPlugin::RenderFrame(int bRedraw) +{ + int i; + + float fDeltaT = 1.0f/GetFps(); + + if (bRedraw) + { + // pre-un-flip buffers, so we are redoing the same work as we did last frame... + IDirect3DTexture9* pTemp = m_lpVS[0]; + m_lpVS[0] = m_lpVS[1]; + m_lpVS[1] = pTemp; + } + + // update time + /* + float fDeltaT = (GetFrame()==0) ? 1.0f/30.0f : GetTime() - m_prev_time; + DWORD dwTime = GetTickCount(); + float fDeltaT = (dwTime - m_dwPrevTickCount)*0.001f; + if (GetFrame() > 64) + { + fDeltaT = (fDeltaT)*0.2f + 0.8f*(1.0f/m_fps); + if (fDeltaT > 2.0f/m_fps) + { + char buf[64]; + sprintf(buf, "fixing time gap of %5.3f seconds", fDeltaT); + dumpmsg(buf); + + fDeltaT = 1.0f/m_fps; + } + } + m_dwPrevTickCount = dwTime; + GetTime() += fDeltaT; + */ + + if (GetFrame()==0) + { + m_fStartTime = GetTime(); + m_fPresetStartTime = GetTime(); + } + + if (m_fNextPresetTime < 0) + { + float dt = m_fTimeBetweenPresetsRand * (warand()%1000)*0.001f; + m_fNextPresetTime = GetTime() + m_fBlendTimeAuto + m_fTimeBetweenPresets + dt; + } + + /* + if (m_bPresetLockedByUser || m_bPresetLockedByCode) + { + // if the user has the preset LOCKED, or if they're in the middle of + // saving it, then keep extending the time at which the auto-switch will occur + // (by the length of this frame). + + m_fPresetStartTime += fDeltaT; + m_fNextPresetTime += fDeltaT; + }*/ + + // update fps + /* + if (GetFrame() < 4) + { + m_fps = 0.0f; + } + else if (GetFrame() <= 64) + { + m_fps = GetFrame() / (float)(GetTime() - m_fTimeHistory[0]); + } + else + { + m_fps = 64.0f / (float)(GetTime() - m_fTimeHistory[m_nTimeHistoryPos]); + } + m_fTimeHistory[m_nTimeHistoryPos] = GetTime(); + m_nTimeHistoryPos = (m_nTimeHistoryPos + 1) % 64; + */ + + // limit fps, if necessary + /* + if (m_nFpsLimit > 0 && (GetFrame() % 64) == 0 && GetFrame() > 64) + { + float spf_now = 1.0f / m_fps; + float spf_desired = 1.0f / (float)m_nFpsLimit; + + float new_sleep = m_fFPSLimitSleep + (spf_desired - spf_now)*1000.0f; + + if (GetFrame() <= 128) + m_fFPSLimitSleep = new_sleep; + else + m_fFPSLimitSleep = m_fFPSLimitSleep*0.8f + 0.2f*new_sleep; + + if (m_fFPSLimitSleep < 0) m_fFPSLimitSleep = 0; + if (m_fFPSLimitSleep > 100) m_fFPSLimitSleep = 100; + + //sprintf(m_szUserMessage, "sleep=%f", m_fFPSLimitSleep); + //m_fShowUserMessageUntilThisTime = GetTime() + 3.0f; + } + + static float deficit; + if (GetFrame()==0) deficit = 0; + float ideal_sleep = (m_fFPSLimitSleep + deficit); + int actual_sleep = (int)ideal_sleep; + if (actual_sleep > 0) + Sleep(actual_sleep); + deficit = ideal_sleep - actual_sleep; + if (deficit < 0) deficit = 0; // just in case + if (deficit > 1) deficit = 1; // just in case + */ + + if (!bRedraw) + { + m_rand_frame = D3DXVECTOR4(FRAND, FRAND, FRAND, FRAND); + + // randomly change the preset, if it's time + if (m_fNextPresetTime < GetTime()) + { + if (m_nLoadingPreset==0) // don't start a load if one is already underway! + LoadRandomPreset(m_fBlendTimeAuto); + } + + // randomly spawn Song Title, if time + if (m_fTimeBetweenRandomSongTitles > 0 && + !m_supertext.bRedrawSuperText && + GetTime() >= m_supertext.fStartTime + m_supertext.fDuration + 1.0f/GetFps()) + { + int n = GetNumToSpawn(GetTime(), fDeltaT, 1.0f/m_fTimeBetweenRandomSongTitles, 0.5f, m_nSongTitlesSpawned); + if (n > 0) + { + LaunchSongTitleAnim(); + m_nSongTitlesSpawned += n; + } + } + + // randomly spawn Custom Message, if time + if (m_fTimeBetweenRandomCustomMsgs > 0 && + !m_supertext.bRedrawSuperText && + GetTime() >= m_supertext.fStartTime + m_supertext.fDuration + 1.0f/GetFps()) + { + int n = GetNumToSpawn(GetTime(), fDeltaT, 1.0f/m_fTimeBetweenRandomCustomMsgs, 0.5f, m_nCustMsgsSpawned); + if (n > 0) + { + LaunchCustomMessage(-1); + m_nCustMsgsSpawned += n; + } + } + + // update m_fBlendProgress; + if (m_pState->m_bBlending) + { + m_pState->m_fBlendProgress = (GetTime() - m_pState->m_fBlendStartTime) / m_pState->m_fBlendDuration; + if (m_pState->m_fBlendProgress > 1.0f) + { + m_pState->m_bBlending = false; + } + } + + // handle hard cuts here (just after new sound analysis) + static float m_fHardCutThresh; + if (GetFrame() == 0) + m_fHardCutThresh = m_fHardCutLoudnessThresh*2.0f; + if (GetFps() > 1.0f && !m_bHardCutsDisabled && !m_bPresetLockedByUser && !m_bPresetLockedByCode) + { + if (mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2] > m_fHardCutThresh*3.0f) + { + if (m_nLoadingPreset==0) // don't start a load if one is already underway! + LoadRandomPreset(0.0f); + m_fHardCutThresh *= 2.0f; + } + else + { + /* + float halflife_modified = m_fHardCutHalflife*0.5f; + //thresh = (thresh - 1.5f)*0.99f + 1.5f; + float k = -0.69315f / halflife_modified;*/ + float k = -1.3863f / (m_fHardCutHalflife*GetFps()); + //float single_frame_multiplier = powf(2.7183f, k / GetFps()); + float single_frame_multiplier = expf(k); + m_fHardCutThresh = (m_fHardCutThresh - m_fHardCutLoudnessThresh)*single_frame_multiplier + m_fHardCutLoudnessThresh; + } + } + + // smooth & scale the audio data, according to m_state, for display purposes + float scale = m_pState->m_fWaveScale.eval(GetTime()) / 128.0f; + mysound.fWave[0][0] *= scale; + mysound.fWave[1][0] *= scale; + float mix2 = m_pState->m_fWaveSmoothing.eval(GetTime()); + float mix1 = scale*(1.0f - mix2); + for (i=1; i<576; i++) + { + mysound.fWave[0][i] = mysound.fWave[0][i]*mix1 + mysound.fWave[0][i-1]*mix2; + mysound.fWave[1][i] = mysound.fWave[1][i]*mix1 + mysound.fWave[1][i-1]*mix2; + } + } + + bool bOldPresetUsesWarpShader = (m_pOldState->m_nWarpPSVersion > 0); + bool bNewPresetUsesWarpShader = (m_pState->m_nWarpPSVersion > 0); + bool bOldPresetUsesCompShader = (m_pOldState->m_nCompPSVersion > 0); + bool bNewPresetUsesCompShader = (m_pState->m_nCompPSVersion > 0); + + // note: 'code' is only meaningful if we are BLENDING. + int code = (bOldPresetUsesWarpShader ? 8 : 0) | + (bOldPresetUsesCompShader ? 4 : 0) | + (bNewPresetUsesWarpShader ? 2 : 0) | + (bNewPresetUsesCompShader ? 1 : 0); + + RunPerFrameEquations(code); + + // restore any lost surfaces + //m_lpDD->RestoreAllSurfaces(); + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + // Remember the original backbuffer and zbuffer + LPDIRECT3DSURFACE9 pBackBuffer=NULL;//, pZBuffer=NULL; + lpDevice->GetRenderTarget( 0, &pBackBuffer ); + //lpDevice->GetDepthStencilSurface( &pZBuffer ); + + // set up render state + { + DWORD texaddr = (*m_pState->var_pf_wrap > m_fSnapPoint) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP; + lpDevice->SetRenderState(D3DRS_WRAP0, 0);//D3DWRAPCOORD_0|D3DWRAPCOORD_1|D3DWRAPCOORD_2|D3DWRAPCOORD_3); + //lpDevice->SetRenderState(D3DRS_WRAP0, (*m_pState->var_pf_wrap) ? D3DWRAP_U|D3DWRAP_V|D3DWRAP_W : 0); + //lpDevice->SetRenderState(D3DRS_WRAP1, (*m_pState->var_pf_wrap) ? D3DWRAP_U|D3DWRAP_V|D3DWRAP_W : 0); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);//texaddr); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);//texaddr); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP);//texaddr); + lpDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); + lpDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); + lpDevice->SetSamplerState(1, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); + + lpDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD ); + lpDevice->SetRenderState( D3DRS_SPECULARENABLE, FALSE ); + lpDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); + lpDevice->SetRenderState( D3DRS_ZENABLE, FALSE ); + lpDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE ); + lpDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); + lpDevice->SetRenderState( D3DRS_COLORVERTEX, TRUE ); + lpDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID ); + lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ); + lpDevice->SetRenderState( D3DRS_AMBIENT, 0xFFFFFFFF ); //? + lpDevice->SetRenderState( D3DRS_CLIPPING, TRUE ); + + // stages 0 and 1 always just use bilinear filtering. + lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + + // note: this texture stage state setup works for 0 or 1 texture. + // if you set a texture, it will be modulated with the current diffuse color. + // if you don't set a texture, it will just use the current diffuse color. + lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + // NOTE: don't forget to call SetTexture and SetVertexShader before drawing! + // Examples: + // SPRITEVERTEX verts[4]; // has texcoords + // lpDevice->SetTexture(0, m_sprite_tex); + // lpDevice->SetVertexShader( SPRITEVERTEX_FORMAT ); + // + // WFVERTEX verts[4]; // no texcoords + // lpDevice->SetTexture(0, NULL); + // lpDevice->SetVertexShader( WFVERTEX_FORMAT ); + } + + // render string to m_lpDDSTitle, if necessary + if (m_supertext.bRedrawSuperText) + { + if (!RenderStringToTitleTexture()) + m_supertext.fStartTime = -1.0f; + m_supertext.bRedrawSuperText = false; + } + + // set up to render [from NULL] to VS0 (for motion vectors). + { + lpDevice->SetTexture(0, NULL); + + IDirect3DSurface9* pNewTarget = NULL; + if (m_lpVS[0]->GetSurfaceLevel(0, &pNewTarget) != D3D_OK) + return; + lpDevice->SetRenderTarget(0, pNewTarget ); + //lpDevice->SetDepthStencilSurface( NULL ); + pNewTarget->Release(); + + lpDevice->SetTexture(0, NULL); + } + + // draw motion vectors to VS0 + DrawMotionVectors(); + + lpDevice->SetTexture(0, NULL); + lpDevice->SetTexture(1, NULL); + + // on first frame, clear OLD VS. + if (m_nFramesSinceResize == 0) + { + IDirect3DSurface9* pNewTarget = NULL; + if (m_lpVS[0]->GetSurfaceLevel(0, &pNewTarget) != D3D_OK) + return; + lpDevice->SetRenderTarget(0, pNewTarget ); + //lpDevice->SetDepthStencilSurface( NULL ); + pNewTarget->Release(); + + lpDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0); + } + + // set up to render [from VS0] to VS1. + { + IDirect3DSurface9* pNewTarget = NULL; + if (m_lpVS[1]->GetSurfaceLevel(0, &pNewTarget) != D3D_OK) + return; + lpDevice->SetRenderTarget(0, pNewTarget ); + //lpDevice->SetDepthStencilSurface( NULL ); + pNewTarget->Release(); + } + + if (m_bAutoGamma && GetFrame()==0) + { + if (strstr(GetDriverDescription(), "nvidia") || + strstr(GetDriverDescription(), "nVidia") || + strstr(GetDriverDescription(), "NVidia") || + strstr(GetDriverDescription(), "NVIDIA")) + m_n16BitGamma = 2; + else if (strstr(GetDriverDescription(), "ATI RAGE MOBILITY M")) + m_n16BitGamma = 2; + else + m_n16BitGamma = 0; + } + + ComputeGridAlphaValues(); + + // do the warping for this frame [warp shader] + if (!m_pState->m_bBlending) + { + // no blend + if (bNewPresetUsesWarpShader) + WarpedBlit_Shaders(1, false, false, false, false); + else + WarpedBlit_NoShaders(1, false, false, false, false); + } + else + { + // blending + // WarpedBlit( nPass, bAlphaBlend, bFlipAlpha, bCullTiles, bFlipCulling ) + // note: alpha values go from 0..1 during a blend. + // note: bFlipCulling==false means tiles with alpha>0 will draw. + // bFlipCulling==true means tiles with alpha<255 will draw. + + if (bOldPresetUsesWarpShader && bNewPresetUsesWarpShader) + { + WarpedBlit_Shaders (0, false, false, true, true); + WarpedBlit_Shaders (1, true, false, true, false); + } + else if (!bOldPresetUsesWarpShader && bNewPresetUsesWarpShader) + { + WarpedBlit_NoShaders(0, false, false, true, true); + WarpedBlit_Shaders (1, true, false, true, false); + } + else if (bOldPresetUsesWarpShader && !bNewPresetUsesWarpShader) + { + WarpedBlit_Shaders (0, false, false, true, true); + WarpedBlit_NoShaders(1, true, false, true, false); + } + else if (!bOldPresetUsesWarpShader && !bNewPresetUsesWarpShader) + { + //WarpedBlit_NoShaders(0, false, false, true, true); + //WarpedBlit_NoShaders(1, true, false, true, false); + + // special case - all the blending just happens in the vertex UV's, so just pretend there's no blend. + WarpedBlit_NoShaders(1, false, false, false, false); + } + } + + if (m_nMaxPSVersion > 0) + BlurPasses(); + + // draw audio data + DrawCustomShapes(); // draw these first; better for feedback if the waves draw *over* them. + DrawCustomWaves(); + DrawWave(mysound.fWave[0], mysound.fWave[1]); + DrawSprites(); + + float fProgress = (GetTime() - m_supertext.fStartTime) / m_supertext.fDuration; + + // if song title animation just ended, burn it into the VS: + if (m_supertext.fStartTime >= 0 && + fProgress >= 1.0f && + !m_supertext.bRedrawSuperText) + { + ShowSongTitleAnim(m_nTexSizeX, m_nTexSizeY, 1.0f); + } + + // Change the rendertarget back to the original setup + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderTarget(0, pBackBuffer ); + //lpDevice->SetDepthStencilSurface( pZBuffer ); + SafeRelease(pBackBuffer); + //SafeRelease(pZBuffer); + + // show it to the user [composite shader] + if (!m_pState->m_bBlending) + { + // no blend + if (bNewPresetUsesCompShader) + ShowToUser_Shaders(1, false, false, false, false); + else + ShowToUser_NoShaders();//1, false, false, false, false); + } + else + { + // blending + // ShowToUser( nPass, bAlphaBlend, bFlipAlpha, bCullTiles, bFlipCulling ) + // note: alpha values go from 0..1 during a blend. + // note: bFlipCulling==false means tiles with alpha>0 will draw. + // bFlipCulling==true means tiles with alpha<255 will draw. + + // NOTE: ShowToUser_NoShaders() must always come before ShowToUser_Shaders(), + // because it always draws the full quad (it can't do tile culling or alpha blending). + // [third case here] + + if (bOldPresetUsesCompShader && bNewPresetUsesCompShader) + { + ShowToUser_Shaders (0, false, false, true, true); + ShowToUser_Shaders (1, true, false, true, false); + } + else if (!bOldPresetUsesCompShader && bNewPresetUsesCompShader) + { + ShowToUser_NoShaders(); + ShowToUser_Shaders (1, true, false, true, false); + } + else if (bOldPresetUsesCompShader && !bNewPresetUsesCompShader) + { + // THA FUNKY REVERSAL + //ShowToUser_Shaders (0); + //ShowToUser_NoShaders(1); + ShowToUser_NoShaders(); + ShowToUser_Shaders (0, true, true, true, true); + } + else if (!bOldPresetUsesCompShader && !bNewPresetUsesCompShader) + { + // special case - all the blending just happens in the blended state vars, so just pretend there's no blend. + ShowToUser_NoShaders();//1, false, false, false, false); + } + } + + // finally, render song title animation to back buffer + if (m_supertext.fStartTime >= 0 && + !m_supertext.bRedrawSuperText) + { + ShowSongTitleAnim(GetWidth(), GetHeight(), min(fProgress, 0.9999f)); + if (fProgress >= 1.0f) + m_supertext.fStartTime = -1.0f; // 'off' state + } + + DrawUserSprites(); + + // flip buffers + IDirect3DTexture9* pTemp = m_lpVS[0]; + m_lpVS[0] = m_lpVS[1]; + m_lpVS[1] = pTemp; + + /* + // FIXME - remove EnforceMaxFPS() if never used + //EnforceMaxFPS(!(m_nLoadingPreset==1 || m_nLoadingPreset==2 || m_nLoadingPreset==4 || m_nLoadingPreset==5)); // this call just turns it on or off; doesn't do it now... + //EnforceMaxFPS(!(m_nLoadingPreset==2 || m_nLoadingPreset==5)); // this call just turns it on or off; doesn't do it now... + + // FIXME - remove this stuff, and change 'm_last_raw_time' in pluginshell (and others) back to private. + static float fOldTime = 0; + float fNewTime = (float)((double)m_last_raw_time/(double)m_high_perf_timer_freq.QuadPart); + float dt = fNewTime-fOldTime; + if (m_nLoadingPreset != 0) { + char buf[256]; + sprintf(buf, "m_nLoadingPreset==%d: dt=%d ms\n", m_nLoadingPreset, (int)(dt*1000) ); + OutputDebugString(buf); + } + fOldTime = fNewTime; + */ +} + +void CPlugin::DrawMotionVectors() +{ + // FLEXIBLE MOTION VECTOR FIELD + if ((float)*m_pState->var_pf_mv_a >= 0.001f) + { + //------------------------------------------------------- + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader(NULL); + lpDevice->SetFVF(WFVERTEX_FORMAT); + //------------------------------------------------------- + + int x,y; + + int nX = (int)(*m_pState->var_pf_mv_x);// + 0.999f); + int nY = (int)(*m_pState->var_pf_mv_y);// + 0.999f); + float dx = (float)*m_pState->var_pf_mv_x - nX; + float dy = (float)*m_pState->var_pf_mv_y - nY; + if (nX > 64) { nX = 64; dx = 0; } + if (nY > 48) { nY = 48; dy = 0; } + + if (nX > 0 && nY > 0) + { + /* + float dx2 = m_fMotionVectorsTempDx;//(*m_pState->var_pf_mv_dx) * 0.05f*GetTime(); // 0..1 range + float dy2 = m_fMotionVectorsTempDy;//(*m_pState->var_pf_mv_dy) * 0.05f*GetTime(); // 0..1 range + if (GetFps() > 2.0f && GetFps() < 300.0f) + { + dx2 += (float)(*m_pState->var_pf_mv_dx) * 0.05f / GetFps(); + dy2 += (float)(*m_pState->var_pf_mv_dy) * 0.05f / GetFps(); + } + if (dx2 > 1.0f) dx2 -= (int)dx2; + if (dy2 > 1.0f) dy2 -= (int)dy2; + if (dx2 < 0.0f) dx2 = 1.0f - (-dx2 - (int)(-dx2)); + if (dy2 < 0.0f) dy2 = 1.0f - (-dy2 - (int)(-dy2)); + // hack: when there is only 1 motion vector on the screem, to keep it in + // the center, we gradually migrate it toward 0.5. + dx2 = dx2*0.995f + 0.5f*0.005f; + dy2 = dy2*0.995f + 0.5f*0.005f; + // safety catch + if (dx2 < 0 || dx2 > 1 || dy2 < 0 || dy2 > 1) + { + dx2 = 0.5f; + dy2 = 0.5f; + } + m_fMotionVectorsTempDx = dx2; + m_fMotionVectorsTempDy = dy2;*/ + float dx2 = (float)(*m_pState->var_pf_mv_dx); + float dy2 = (float)(*m_pState->var_pf_mv_dy); + + float len_mult = (float)*m_pState->var_pf_mv_l; + if (dx < 0) dx = 0; + if (dy < 0) dy = 0; + if (dx > 1) dx = 1; + if (dy > 1) dy = 1; + //dx = dx * 1.0f/(float)nX; + //dy = dy * 1.0f/(float)nY; + float inv_texsize = 1.0f/(float)m_nTexSizeX; + float min_len = 1.0f*inv_texsize; + + WFVERTEX v[(64+1)*2]; + ZeroMemory(v, sizeof(WFVERTEX)*(64+1)*2); + v[0].Diffuse = D3DCOLOR_RGBA_01((float)*m_pState->var_pf_mv_r,(float)*m_pState->var_pf_mv_g,(float)*m_pState->var_pf_mv_b,(float)*m_pState->var_pf_mv_a); + for (x=1; x<(nX+1)*2; x++) + v[x].Diffuse = v[0].Diffuse; + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + for (y=0; y<nY; y++) + { + float fy = (y + 0.25f)/(float)(nY + dy + 0.25f - 1.0f); + + // now move by offset + fy -= dy2; + + if (fy > 0.0001f && fy < 0.9999f) + { + int n = 0; + for (x=0; x<nX; x++) + { + //float fx = (x + 0.25f)/(float)(nX + dx + 0.25f - 1.0f); + float fx = (x + 0.25f)/(float)(nX + dx + 0.25f - 1.0f); + + // now move by offset + fx += dx2; + + if (fx > 0.0001f && fx < 0.9999f) + { + float fx2, fy2; + ReversePropagatePoint(fx, fy, &fx2, &fy2); // NOTE: THIS IS REALLY A REVERSE-PROPAGATION + //fx2 = fx*2 - fx2; + //fy2 = fy*2 - fy2; + //fx2 = fx + 1.0f/(float)m_nTexSize; + //fy2 = 1-(fy + 1.0f/(float)m_nTexSize); + + // enforce minimum trail lengths: + { + float dx = (fx2 - fx); + float dy = (fy2 - fy); + dx *= len_mult; + dy *= len_mult; + float len = sqrtf(dx*dx + dy*dy); + + if (len > min_len) + { + + } + else if (len > 0.00000001f) + { + len = min_len/len; + dx *= len; + dy *= len; + } + else + { + dx = min_len; + dy = min_len; + } + + fx2 = fx + dx; + fy2 = fy + dy; + } + /**/ + + v[n].x = fx * 2.0f - 1.0f; + v[n].y = fy * 2.0f - 1.0f; + v[n+1].x = fx2 * 2.0f - 1.0f; + v[n+1].y = fy2 * 2.0f - 1.0f; + + // actually, project it in the reverse direction + //v[n+1].x = v[n].x*2.0f - v[n+1].x;// + dx*2; + //v[n+1].y = v[n].y*2.0f - v[n+1].y;// + dy*2; + //v[n].x += dx*2; + //v[n].y += dy*2; + + n += 2; + } + } + + // draw it + lpDevice->DrawPrimitiveUP(D3DPT_LINELIST, n/2, v, sizeof(WFVERTEX)); + } + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + } +} + +/* +void CPlugin::UpdateSongInfo() +{ + if (m_bShowSongTitle || m_bSongTitleAnims) + { + char szOldSongMessage[512]; + lstrcpy(szOldSongMessage, m_szSongMessage); + + if (::GetWindowText(m_hWndParent, m_szSongMessage, sizeof(m_szSongMessage))) + { + // remove ' - Winamp' at end + if (strlen(m_szSongMessage) > 9) + { + int check_pos = strlen(m_szSongMessage) - 9; + if (lstrcmp(" - Winamp", (char *)(m_szSongMessage + check_pos)) == 0) + m_szSongMessage[check_pos] = 0; + } + + // remove ' - Winamp [Paused]' at end + if (strlen(m_szSongMessage) > 18) + { + int check_pos = strlen(m_szSongMessage) - 18; + if (lstrcmp(" - Winamp [Paused]", (char *)(m_szSongMessage + check_pos)) == 0) + m_szSongMessage[check_pos] = 0; + } + + // remove song # and period from beginning + char *p = m_szSongMessage; + while (*p >= '0' && *p <= '9') p++; + if (*p == '.' && *(p+1) == ' ') + { + p += 2; + int pos = 0; + while (*p != 0) + { + m_szSongMessage[pos++] = *p; + p++; + } + m_szSongMessage[pos++] = 0; + } + + // fix &'s for display + /* + { + int pos = 0; + int len = strlen(m_szSongMessage); + while (m_szSongMessage[pos]) + { + if (m_szSongMessage[pos] == '&') + { + for (int x=len; x>=pos; x--) + m_szSongMessage[x+1] = m_szSongMessage[x]; + len++; + pos++; + } + pos++; + } + }*/ + /* + if (m_bSongTitleAnims && + ((lstrcmp(szOldSongMessage, m_szSongMessage) != 0) || (GetFrame()==0))) + { + // launch song title animation + LaunchSongTitleAnim(); + + /* + m_supertext.bRedrawSuperText = true; + m_supertext.bIsSongTitle = true; + lstrcpy(m_supertext.szText, m_szSongMessage); + lstrcpy(m_supertext.nFontFace, m_szTitleFontFace); + m_supertext.fFontSize = (float)m_nTitleFontSize; + m_supertext.bBold = m_bTitleFontBold; + m_supertext.bItal = m_bTitleFontItalic; + m_supertext.fX = 0.5f; + m_supertext.fY = 0.5f; + m_supertext.fGrowth = 1.0f; + m_supertext.fDuration = m_fSongTitleAnimDuration; + m_supertext.nColorR = 255; + m_supertext.nColorG = 255; + m_supertext.nColorB = 255; + + m_supertext.fStartTime = GetTime(); + */ +/* } + } + else + { + sprintf(m_szSongMessage, "<couldn't get song title>"); + } + } + + m_nTrackPlaying = SendMessage(m_hWndParent,WM_USER, 0, 125); + + // append song time + if (m_bShowSongTime && m_nSongPosMS >= 0) + { + float time_s = m_nSongPosMS*0.001f; + + int minutes = (int)(time_s/60); + time_s -= minutes*60; + int seconds = (int)time_s; + time_s -= seconds; + int dsec = (int)(time_s*100); + + sprintf(m_szSongTime, "%d:%02d.%02d", minutes, seconds, dsec); + } + + // append song length + if (m_bShowSongLen && m_nSongLenMS > 0) + { + int len_s = m_nSongLenMS/1000; + int minutes = len_s/60; + int seconds = len_s - minutes*60; + + char buf[512]; + sprintf(buf, " / %d:%02d", minutes, seconds); + lstrcat(m_szSongTime, buf); + } +} +*/ + +bool CPlugin::ReversePropagatePoint(float fx, float fy, float *fx2, float *fy2) +{ + //float fy = y/(float)nMotionVectorsY; + int y0 = (int)(fy*m_nGridY); + float dy = fy*m_nGridY - y0; + + //float fx = x/(float)nMotionVectorsX; + int x0 = (int)(fx*m_nGridX); + float dx = fx*m_nGridX - x0; + + int x1 = x0 + 1; + int y1 = y0 + 1; + + if (x0 < 0) return false; + if (y0 < 0) return false; + //if (x1 < 0) return false; + //if (y1 < 0) return false; + //if (x0 > m_nGridX) return false; + //if (y0 > m_nGridY) return false; + if (x1 > m_nGridX) return false; + if (y1 > m_nGridY) return false; + + float tu, tv; + tu = m_verts[y0*(m_nGridX+1)+x0].tu * (1-dx)*(1-dy); + tv = m_verts[y0*(m_nGridX+1)+x0].tv * (1-dx)*(1-dy); + tu += m_verts[y0*(m_nGridX+1)+x1].tu * (dx)*(1-dy); + tv += m_verts[y0*(m_nGridX+1)+x1].tv * (dx)*(1-dy); + tu += m_verts[y1*(m_nGridX+1)+x0].tu * (1-dx)*(dy); + tv += m_verts[y1*(m_nGridX+1)+x0].tv * (1-dx)*(dy); + tu += m_verts[y1*(m_nGridX+1)+x1].tu * (dx)*(dy); + tv += m_verts[y1*(m_nGridX+1)+x1].tv * (dx)*(dy); + + *fx2 = tu; + *fy2 = 1.0f - tv; + return true; +} + +void CPlugin::GetSafeBlurMinMax(CState* pState, float* blur_min, float* blur_max) +{ + blur_min[0] = (float)*pState->var_pf_blur1min; + blur_min[1] = (float)*pState->var_pf_blur2min; + blur_min[2] = (float)*pState->var_pf_blur3min; + blur_max[0] = (float)*pState->var_pf_blur1max; + blur_max[1] = (float)*pState->var_pf_blur2max; + blur_max[2] = (float)*pState->var_pf_blur3max; + + // check that precision isn't wasted in later blur passes [...min-max gap can't grow!] + // also, if min-max are close to each other, push them apart: + const float fMinDist = 0.1f; + if (blur_max[0] - blur_min[0] < fMinDist) { + float avg = (blur_min[0] + blur_max[0])*0.5f; + blur_min[0] = avg - fMinDist*0.5f; + blur_max[0] = avg - fMinDist*0.5f; + } + blur_max[1] = min(blur_max[0], blur_max[1]); + blur_min[1] = max(blur_min[0], blur_min[1]); + if (blur_max[1] - blur_min[1] < fMinDist) { + float avg = (blur_min[1] + blur_max[1])*0.5f; + blur_min[1] = avg - fMinDist*0.5f; + blur_max[1] = avg - fMinDist*0.5f; + } + blur_max[2] = min(blur_max[1], blur_max[2]); + blur_min[2] = max(blur_min[1], blur_min[2]); + if (blur_max[2] - blur_min[2] < fMinDist) { + float avg = (blur_min[2] + blur_max[2])*0.5f; + blur_min[2] = avg - fMinDist*0.5f; + blur_max[2] = avg - fMinDist*0.5f; + } +} + +void CPlugin::BlurPasses() +{ + #if (NUM_BLUR_TEX>0) + + // Note: Blur is currently a little funky. It blurs the *current* frame after warp; + // this way, it lines up well with the composite pass. However, if you switch + // presets instantly, to one whose *warp* shader uses the blur texture, + // it will be outdated (just for one frame). Oh well. + // This also means that when sampling the blurred textures in the warp shader, + // they are one frame old. This isn't too big a deal. Getting them to match + // up for the composite pass is probably more important. + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + int passes = min(NUM_BLUR_TEX, m_nHighestBlurTexUsedThisFrame*2); + if (passes==0) + return; + + LPDIRECT3DSURFACE9 pBackBuffer=NULL;//, pZBuffer=NULL; + lpDevice->GetRenderTarget( 0, &pBackBuffer ); + + //lpDevice->SetFVF( MYVERTEX_FORMAT ); + lpDevice->SetVertexShader( m_BlurShaders[0].vs.ptr ); + lpDevice->SetVertexDeclaration(m_pMyVertDecl); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + DWORD wrap = D3DTADDRESS_CLAMP;//D3DTADDRESS_WRAP;// : D3DTADDRESS_CLAMP; + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, wrap); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, wrap); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSW, wrap); + lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 1); + + IDirect3DSurface9* pNewTarget = NULL; + + // clear texture bindings + for (int i=0; i<16; i++) + lpDevice->SetTexture(i, NULL); + + // set up fullscreen quad + MYVERTEX v[4]; + + v[0].x = -1; + v[0].y = -1; + v[1].x = 1; + v[1].y = -1; + v[2].x = -1; + v[2].y = 1; + v[3].x = 1; + v[3].y = 1; + + v[0].tu = 0; //kiv: upside-down? + v[0].tv = 0; + v[1].tu = 1; + v[1].tv = 0; + v[2].tu = 0; + v[2].tv = 1; + v[3].tu = 1; + v[3].tv = 1; + + const float w[8] = { 4.0f, 3.8f, 3.5f, 2.9f, 1.9f, 1.2f, 0.7f, 0.3f }; //<- user can specify these + float edge_darken = (float)*m_pState->var_pf_blur1_edge_darken; + float blur_min[3], blur_max[3]; + GetSafeBlurMinMax(m_pState, blur_min, blur_max); + + float fscale[3]; + float fbias[3]; + + // figure out the progressive scale & bias needed, at each step, + // to go from one [min..max] range to the next. + float temp_min, temp_max; + fscale[0] = 1.0f / (blur_max[0] - blur_min[0]); + fbias [0] = -blur_min[0] * fscale[0]; + temp_min = (blur_min[1] - blur_min[0]) / (blur_max[0] - blur_min[0]); + temp_max = (blur_max[1] - blur_min[0]) / (blur_max[0] - blur_min[0]); + fscale[1] = 1.0f / (temp_max - temp_min); + fbias [1] = -temp_min * fscale[1]; + temp_min = (blur_min[2] - blur_min[1]) / (blur_max[1] - blur_min[1]); + temp_max = (blur_max[2] - blur_min[1]) / (blur_max[1] - blur_min[1]); + fscale[2] = 1.0f / (temp_max - temp_min); + fbias [2] = -temp_min * fscale[2]; + + // note: warped blit just rendered from VS0 to VS1. + for (int i=0; i<passes; i++) + { + // hook up correct render target + if (m_lpBlur[i]->GetSurfaceLevel(0, &pNewTarget) != D3D_OK) + return; + lpDevice->SetRenderTarget(0, pNewTarget); + pNewTarget->Release(); + + // hook up correct source texture - assume there is only one, at stage 0 + lpDevice->SetTexture(0, (i==0) ? m_lpVS[0] : m_lpBlur[i-1]); + + // set pixel shader + lpDevice->SetPixelShader (m_BlurShaders[i%2].ps.ptr); + + // set constants + LPD3DXCONSTANTTABLE pCT = m_BlurShaders[i%2].ps.CT; + D3DXHANDLE* h = m_BlurShaders[i%2].ps.params.const_handles; + + int srcw = (i==0) ? GetWidth() : m_nBlurTexW[i-1]; + int srch = (i==0) ? GetHeight() : m_nBlurTexH[i-1]; + D3DXVECTOR4 srctexsize = D3DXVECTOR4( (float)srcw, (float)srch, 1.0f/(float)srcw, 1.0f/(float)srch ); + + float fscale_now = fscale[i/2]; + float fbias_now = fbias[i/2]; + + if (i%2==0) + { + // pass 1 (long horizontal pass) + //------------------------------------- + const float w1 = w[0] + w[1]; + const float w2 = w[2] + w[3]; + const float w3 = w[4] + w[5]; + const float w4 = w[6] + w[7]; + const float d1 = 0 + 2*w[1]/w1; + const float d2 = 2 + 2*w[3]/w2; + const float d3 = 4 + 2*w[5]/w3; + const float d4 = 6 + 2*w[7]/w4; + const float w_div = 0.5f/(w1+w2+w3+w4); + //------------------------------------- + //float4 _c0; // source texsize (.xy), and inverse (.zw) + //float4 _c1; // w1..w4 + //float4 _c2; // d1..d4 + //float4 _c3; // scale, bias, w_div, 0 + //------------------------------------- + if (h[0]) pCT->SetVector( lpDevice, h[0], &srctexsize ); + if (h[1]) pCT->SetVector( lpDevice, h[1], &D3DXVECTOR4( w1,w2,w3,w4 )); + if (h[2]) pCT->SetVector( lpDevice, h[2], &D3DXVECTOR4( d1,d2,d3,d4 )); + if (h[3]) pCT->SetVector( lpDevice, h[3], &D3DXVECTOR4( fscale_now,fbias_now,w_div,0)); + } + else + { + // pass 2 (short vertical pass) + //------------------------------------- + const float w1 = w[0]+w[1] + w[2]+w[3]; + const float w2 = w[4]+w[5] + w[6]+w[7]; + const float d1 = 0 + 2*((w[2]+w[3])/w1); + const float d2 = 2 + 2*((w[6]+w[7])/w2); + const float w_div = 1.0f/((w1+w2)*2); + //------------------------------------- + //float4 _c0; // source texsize (.xy), and inverse (.zw) + //float4 _c5; // w1,w2,d1,d2 + //float4 _c6; // w_div, edge_darken_c1, edge_darken_c2, edge_darken_c3 + //------------------------------------- + if (h[0]) pCT->SetVector( lpDevice, h[0], &srctexsize ); + if (h[5]) pCT->SetVector( lpDevice, h[5], &D3DXVECTOR4( w1,w2,d1,d2 )); + if (h[6]) + { + // note: only do this first time; if you do it many times, + // then the super-blurred levels will have big black lines along the top & left sides. + if (i==1) + pCT->SetVector( lpDevice, h[6], &D3DXVECTOR4( w_div,(1-edge_darken),edge_darken,5.0f )); //darken edges + else + pCT->SetVector( lpDevice, h[6], &D3DXVECTOR4( w_div,1.0f,0.0f,5.0f )); // don't darken + } + } + + // draw fullscreen quad + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(MYVERTEX)); + + // clear texture bindings + lpDevice->SetTexture(0, NULL); + } + + lpDevice->SetRenderTarget(0, pBackBuffer); + pBackBuffer->Release(); + lpDevice->SetPixelShader( NULL ); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetTexture(0, NULL); + lpDevice->SetFVF( MYVERTEX_FORMAT ); + #endif + + m_nHighestBlurTexUsedThisFrame = 0; +} + +void CPlugin::ComputeGridAlphaValues() +{ + float fBlend = m_pState->m_fBlendProgress;//max(0,min(1,(m_pState->m_fBlendProgress*1.6f - 0.3f))); + /*switch(code) //if (nPassOverride==0) + { + //case 8: + //case 9: + //case 12: + //case 13: + // note - these are the 4 cases where the old preset uses a warp shader, but new preset doesn't. + fBlend = 1-fBlend; // <-- THIS IS THE KEY - FLIPS THE ALPHAS AND EVERYTHING ELSE JUST WORKS. + break; + }*/ + //fBlend = 1-fBlend; // <-- THIS IS THE KEY - FLIPS THE ALPHAS AND EVERYTHING ELSE JUST WORKS. + bool bBlending = m_pState->m_bBlending;//(fBlend >= 0.0001f && fBlend <= 0.9999f); + + + // warp stuff + float fWarpTime = GetTime() * m_pState->m_fWarpAnimSpeed; + float fWarpScaleInv = 1.0f / m_pState->m_fWarpScale.eval(GetTime()); + float f[4]; + f[0] = 11.68f + 4.0f*cosf(fWarpTime*1.413f + 10); + f[1] = 8.77f + 3.0f*cosf(fWarpTime*1.113f + 7); + f[2] = 10.54f + 3.0f*cosf(fWarpTime*1.233f + 3); + f[3] = 11.49f + 4.0f*cosf(fWarpTime*0.933f + 5); + + // texel alignment + float texel_offset_x = 0.5f / (float)m_nTexSizeX; + float texel_offset_y = 0.5f / (float)m_nTexSizeY; + + int num_reps = (m_pState->m_bBlending) ? 2 : 1; + int start_rep = 0; + + // FIRST WE HAVE 1-2 PASSES FOR CRUNCHING THE PER-VERTEX EQUATIONS + for (int rep=start_rep; rep<num_reps; rep++) + { + // to blend the two PV equations together, we simulate both to get the final UV coords, + // then we blend those final UV coords. We also write out an alpha value so that + // the second DRAW pass below (which might use a different shader) can do blending. + CState *pState; + + if (rep==0) + pState = m_pState; + else + pState = m_pOldState; + + // cache the doubles as floats so that computations are a bit faster + float fZoom = (float)(*pState->var_pf_zoom); + float fZoomExp = (float)(*pState->var_pf_zoomexp); + float fRot = (float)(*pState->var_pf_rot); + float fWarp = (float)(*pState->var_pf_warp); + float fCX = (float)(*pState->var_pf_cx); + float fCY = (float)(*pState->var_pf_cy); + float fDX = (float)(*pState->var_pf_dx); + float fDY = (float)(*pState->var_pf_dy); + float fSX = (float)(*pState->var_pf_sx); + float fSY = (float)(*pState->var_pf_sy); + + int n = 0; + + for (int y=0; y<=m_nGridY; y++) + { + for (int x=0; x<=m_nGridX; x++) + { + // Note: x, y, z are now set at init. time - no need to mess with them! + //m_verts[n].x = i/(float)m_nGridX*2.0f - 1.0f; + //m_verts[n].y = j/(float)m_nGridY*2.0f - 1.0f; + //m_verts[n].z = 0.0f; + + if (pState->m_pp_codehandle) + { + // restore all the variables to their original states, + // run the user-defined equations, + // then move the results into local vars for computation as floats + + *pState->var_pv_x = (double)(m_verts[n].x* 0.5f*m_fAspectX + 0.5f); + *pState->var_pv_y = (double)(m_verts[n].y*-0.5f*m_fAspectY + 0.5f); + *pState->var_pv_rad = (double)m_vertinfo[n].rad; + *pState->var_pv_ang = (double)m_vertinfo[n].ang; + *pState->var_pv_zoom = *pState->var_pf_zoom; + *pState->var_pv_zoomexp = *pState->var_pf_zoomexp; + *pState->var_pv_rot = *pState->var_pf_rot; + *pState->var_pv_warp = *pState->var_pf_warp; + *pState->var_pv_cx = *pState->var_pf_cx; + *pState->var_pv_cy = *pState->var_pf_cy; + *pState->var_pv_dx = *pState->var_pf_dx; + *pState->var_pv_dy = *pState->var_pf_dy; + *pState->var_pv_sx = *pState->var_pf_sx; + *pState->var_pv_sy = *pState->var_pf_sy; + //*pState->var_pv_time = *pState->var_pv_time; // (these are all now initialized + //*pState->var_pv_bass = *pState->var_pv_bass; // just once per frame) + //*pState->var_pv_mid = *pState->var_pv_mid; + //*pState->var_pv_treb = *pState->var_pv_treb; + //*pState->var_pv_bass_att = *pState->var_pv_bass_att; + //*pState->var_pv_mid_att = *pState->var_pv_mid_att; + //*pState->var_pv_treb_att = *pState->var_pv_treb_att; + +#ifndef _NO_EXPR_ + NSEEL_code_execute(pState->m_pp_codehandle); +#endif + + fZoom = (float)(*pState->var_pv_zoom); + fZoomExp = (float)(*pState->var_pv_zoomexp); + fRot = (float)(*pState->var_pv_rot); + fWarp = (float)(*pState->var_pv_warp); + fCX = (float)(*pState->var_pv_cx); + fCY = (float)(*pState->var_pv_cy); + fDX = (float)(*pState->var_pv_dx); + fDY = (float)(*pState->var_pv_dy); + fSX = (float)(*pState->var_pv_sx); + fSY = (float)(*pState->var_pv_sy); + } + + float fZoom2 = powf(fZoom, powf(fZoomExp, m_vertinfo[n].rad*2.0f - 1.0f)); + + // initial texcoords, w/built-in zoom factor + float fZoom2Inv = 1.0f/fZoom2; + float u = m_verts[n].x*m_fAspectX*0.5f*fZoom2Inv + 0.5f; + float v = -m_verts[n].y*m_fAspectY*0.5f*fZoom2Inv + 0.5f; + //float u_orig = u; + //float v_orig = v; + //m_verts[n].tr = u_orig + texel_offset_x; + //m_verts[n].ts = v_orig + texel_offset_y; + + // stretch on X, Y: + u = (u - fCX)/fSX + fCX; + v = (v - fCY)/fSY + fCY; + + // warping: + //if (fWarp > 0.001f || fWarp < -0.001f) + //{ + u += fWarp*0.0035f*sinf(fWarpTime*0.333f + fWarpScaleInv*(m_verts[n].x*f[0] - m_verts[n].y*f[3])); + v += fWarp*0.0035f*cosf(fWarpTime*0.375f - fWarpScaleInv*(m_verts[n].x*f[2] + m_verts[n].y*f[1])); + u += fWarp*0.0035f*cosf(fWarpTime*0.753f - fWarpScaleInv*(m_verts[n].x*f[1] - m_verts[n].y*f[2])); + v += fWarp*0.0035f*sinf(fWarpTime*0.825f + fWarpScaleInv*(m_verts[n].x*f[0] + m_verts[n].y*f[3])); + //} + + // rotation: + float u2 = u - fCX; + float v2 = v - fCY; + + float cos_rot = cosf(fRot); + float sin_rot = sinf(fRot); + u = u2*cos_rot - v2*sin_rot + fCX; + v = u2*sin_rot + v2*cos_rot + fCY; + + // translation: + u -= fDX; + v -= fDY; + + // undo aspect ratio fix: + u = (u-0.5f)*m_fInvAspectX + 0.5f; + v = (v-0.5f)*m_fInvAspectY + 0.5f; + + // final half-texel-offset translation: + u += texel_offset_x; + v += texel_offset_y; + + if (rep==0) + { + // UV's for m_pState + m_verts[n].tu = u; + m_verts[n].tv = v; + m_verts[n].Diffuse = 0xFFFFFFFF; + } + else + { + // blend to UV's for m_pOldState + float mix2 = m_vertinfo[n].a*fBlend + m_vertinfo[n].c;//fCosineBlend2; + mix2 = max(0,min(1,mix2)); + // if fBlend un-flipped, then mix2 is 0 at the beginning of a blend, 1 at the end... + // and alphas are 0 at the beginning, 1 at the end. + m_verts[n].tu = m_verts[n].tu*(mix2) + u*(1-mix2); + m_verts[n].tv = m_verts[n].tv*(mix2) + v*(1-mix2); + // this sets the alpha values for blending between two presets: + m_verts[n].Diffuse = 0x00FFFFFF | (((DWORD)(mix2*255))<<24); + } + + n++; + } + } + + } +} + +void CPlugin::WarpedBlit_NoShaders(int nPass, bool bAlphaBlend, bool bFlipAlpha, bool bCullTiles, bool bFlipCulling) +{ + MungeFPCW(NULL); // puts us in single-precision mode & disables exceptions + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + if (!wcscmp(m_pState->m_szDesc, INVALID_PRESET_DESC)) + { + // if no valid preset loaded, clear the target to black, and return + lpDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0); + return; + } + + lpDevice->SetTexture(0, m_lpVS[0]); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetPixelShader( NULL ); + lpDevice->SetFVF( MYVERTEX_FORMAT ); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + // stages 0 and 1 always just use bilinear filtering. + lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + + // note: this texture stage state setup works for 0 or 1 texture. + // if you set a texture, it will be modulated with the current diffuse color. + // if you don't set a texture, it will just use the current diffuse color. + lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + DWORD texaddr = (*m_pState->var_pf_wrap > m_fSnapPoint) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP; + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, texaddr); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, texaddr); + lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSW, texaddr); + + // decay + float fDecay = (float)(*m_pState->var_pf_decay); + + //if (m_pState->m_bBlending) + // fDecay = fDecay*(fCosineBlend) + (1.0f-fCosineBlend)*((float)(*m_pOldState->var_pf_decay)); + + if (m_n16BitGamma > 0 && + (GetBackBufFormat()==D3DFMT_R5G6B5 || GetBackBufFormat()==D3DFMT_X1R5G5B5 || GetBackBufFormat()==D3DFMT_A1R5G5B5 || GetBackBufFormat()==D3DFMT_A4R4G4B4) && + fDecay < 0.9999f) + { + fDecay = min(fDecay, (32.0f - m_n16BitGamma)/32.0f); + } + + D3DCOLOR cDecay = D3DCOLOR_RGBA_01(fDecay,fDecay,fDecay,1); + + // hurl the triangle strips at the video card + int poly; + for (poly=0; poly<(m_nGridX+1)*2; poly++) + m_verts_temp[poly].Diffuse = cDecay; + + if (bAlphaBlend) + { + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + if (bFlipAlpha) + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCALPHA); + } + else + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + } + else + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + int nAlphaTestValue = 0; + if (bFlipCulling) + nAlphaTestValue = 1-nAlphaTestValue; + + // Hurl the triangles at the video card. + // We're going to un-index it, so that we don't stress any crappy (AHEM intel g33) + // drivers out there. + // If we're blending, we'll skip any polygon that is all alpha-blended out. + // This also respects the MaxPrimCount limit of the video card. + MYVERTEX tempv[1024 * 3]; + int max_prims_per_batch = min( GetCaps()->MaxPrimitiveCount, (sizeof(tempv)/sizeof(tempv[0]))/3) - 4; + int primCount = m_nGridX*m_nGridY*2; + int src_idx = 0; + int prims_sent = 0; + while (src_idx < primCount*3) + { + int prims_queued = 0; + int i=0; + while (prims_queued < max_prims_per_batch && src_idx < primCount*3) + { + // copy 3 verts + for (int j=0; j<3; j++) + { + tempv[i++] = m_verts[ m_indices_list[src_idx++] ]; + // don't forget to flip sign on Y and factor in the decay color!: + tempv[i-1].y *= -1; + tempv[i-1].Diffuse = (cDecay & 0x00FFFFFF) | (tempv[i-1].Diffuse & 0xFF000000); + } + if (bCullTiles) + { + DWORD d1 = (tempv[i-3].Diffuse >> 24); + DWORD d2 = (tempv[i-2].Diffuse >> 24); + DWORD d3 = (tempv[i-1].Diffuse >> 24); + bool bIsNeeded; + if (nAlphaTestValue) + bIsNeeded = ((d1 & d2 & d3) < 255);//(d1 < 255) || (d2 < 255) || (d3 < 255); + else + bIsNeeded = ((d1|d2|d3) > 0);//(d1 > 0) || (d2 > 0) || (d3 > 0); + if (!bIsNeeded) + i -= 3; + else + prims_queued++; + } + else + prims_queued++; + } + if (prims_queued > 0) + lpDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, prims_queued, tempv, sizeof(MYVERTEX) ); + } + + /* + if (!bCullTiles) + { + assert(!bAlphaBlend); //not handled yet + + // draw normally - just a full triangle strip for each half-row of cells + // (even if we are blending, it is between two pre-pixel-shader presets, + // so the blend all happens exclusively in the per-vertex equations.) + for (int strip=0; strip<m_nGridY*2; strip++) + { + int index = strip * (m_nGridX+2); + + for (poly=0; poly<m_nGridX+2; poly++) + { + int ref_vert = m_indices_strip[index]; + m_verts_temp[poly].x = m_verts[ref_vert].x; + m_verts_temp[poly].y = -m_verts[ref_vert].y; + m_verts_temp[poly].z = m_verts[ref_vert].z; + m_verts_temp[poly].tu = m_verts[ref_vert].tu; + m_verts_temp[poly].tv = m_verts[ref_vert].tv; + //m_verts_temp[poly].Diffuse = cDecay; this is done just once - see jsut above + index++; + } + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, m_nGridX, (void*)m_verts_temp, sizeof(MYVERTEX)); + } + } + else + { + // we're blending to/from a new pixel-shader enabled preset; + // only draw the cells needed! (an optimization) + int nAlphaTestValue = 0; + if (bFlipCulling) + nAlphaTestValue = 1-nAlphaTestValue; + + int idx[2048]; + for (int y=0; y<m_nGridY; y++) + { + // copy verts & flip sign on Y + int ref_vert = y*(m_nGridX+1); + for (int i=0; i<(m_nGridX+1)*2; i++) + { + m_verts_temp[i].x = m_verts[ref_vert].x; + m_verts_temp[i].y = -m_verts[ref_vert].y; + m_verts_temp[i].z = m_verts[ref_vert].z; + m_verts_temp[i].tu = m_verts[ref_vert].tu; + m_verts_temp[i].tv = m_verts[ref_vert].tv; + m_verts_temp[i].Diffuse = (cDecay & 0x00FFFFFF) | (m_verts[ref_vert].Diffuse & 0xFF000000); + ref_vert++; + } + + // create (smart) indices + int count = 0; + int nVert = 0; + bool bWasNeeded; + ref_vert = (y)*(m_nGridX+1); + DWORD d1 = (m_verts[ref_vert ].Diffuse >> 24); + DWORD d2 = (m_verts[ref_vert+m_nGridX+1].Diffuse >> 24); + if (nAlphaTestValue) + bWasNeeded = (d1 < 255) || (d2 < 255); + else + bWasNeeded = (d1 > 0) || (d2 > 0); + for (i=0; i<m_nGridX; i++) + { + bool bIsNeeded; + DWORD d1 = (m_verts[ref_vert+1 ].Diffuse >> 24); + DWORD d2 = (m_verts[ref_vert+1+m_nGridX+1].Diffuse >> 24); + if (nAlphaTestValue) + bIsNeeded = (d1 < 255) || (d2 < 255); + else + bIsNeeded = (d1 > 0) || (d2 > 0); + + if (bIsNeeded || bWasNeeded) + { + idx[count++] = nVert; + idx[count++] = nVert+1; + idx[count++] = nVert+m_nGridX+1; + idx[count++] = nVert+m_nGridX+1; + idx[count++] = nVert+1; + idx[count++] = nVert+m_nGridX+2; + } + bWasNeeded = bIsNeeded; + + nVert++; + ref_vert++; + } + lpDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, (m_nGridX+1)*2, count/3, (void*)idx, D3DFMT_INDEX32, (void*)m_verts_temp, sizeof(MYVERTEX)); + } + }/**/ + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); +} + +void CPlugin::WarpedBlit_Shaders(int nPass, bool bAlphaBlend, bool bFlipAlpha, bool bCullTiles, bool bFlipCulling) +{ + // if nPass==0, it draws old preset (blending 1 of 2). + // if nPass==1, it draws new preset (blending 2 of 2, OR done blending) + + MungeFPCW(NULL); // puts us in single-precision mode & disables exceptions + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + if (!wcscmp(m_pState->m_szDesc, INVALID_PRESET_DESC)) + { + // if no valid preset loaded, clear the target to black, and return + lpDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 1.0f, 0); + return; + } + + //float fBlend = m_pState->m_fBlendProgress;//max(0,min(1,(m_pState->m_fBlendProgress*1.6f - 0.3f))); + //if (nPassOverride==0) + // fBlend = 1-fBlend; // <-- THIS IS THE KEY - FLIPS THE ALPHAS AND EVERYTHING ELSE JUST WORKS. + //bool bBlending = m_pState->m_bBlending;//(fBlend >= 0.0001f && fBlend <= 0.9999f); + + //lpDevice->SetTexture(0, m_lpVS[0]); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( MYVERTEX_FORMAT ); + + // texel alignment + float texel_offset_x = 0.5f / (float)m_nTexSizeX; + float texel_offset_y = 0.5f / (float)m_nTexSizeY; + + int nAlphaTestValue = 0; + if (bFlipCulling) + nAlphaTestValue = 1-nAlphaTestValue; + + if (bAlphaBlend) + { + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + if (bFlipAlpha) + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCALPHA); + } + else + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + } + else + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + int pass = nPass; + { + // PASS 0: draw using *blended per-vertex motion vectors*, but with the OLD warp shader. + // PASS 1: draw using *blended per-vertex motion vectors*, but with the NEW warp shader. + PShaderInfo* si = (pass==0) ? &m_OldShaders.warp : &m_shaders.warp; + CState* state = (pass==0) ? m_pOldState : m_pState; + + lpDevice->SetVertexDeclaration(m_pMyVertDecl); + lpDevice->SetVertexShader(m_fallbackShaders_vs.warp.ptr); + lpDevice->SetPixelShader (si->ptr); + + ApplyShaderParams( &(si->params), si->CT, state ); + + // Hurl the triangles at the video card. + // We're going to un-index it, so that we don't stress any crappy (AHEM intel g33) + // drivers out there. + // We divide it into the two halves of the screen (top/bottom) so we can hack + // the 'ang' values along the angle-wrap seam, halfway through the draw. + // If we're blending, we'll skip any polygon that is all alpha-blended out. + // This also respects the MaxPrimCount limit of the video card. + MYVERTEX tempv[1024 * 3]; + int max_prims_per_batch = min( GetCaps()->MaxPrimitiveCount, (sizeof(tempv)/sizeof(tempv[0]))/3) - 4; + for (int half=0; half<2; half++) + { + // hack / restore the ang values along the angle-wrap [0 <-> 2pi] seam... + float new_ang = half ? 3.1415926535897932384626433832795f : -3.1415926535897932384626433832795f; + int y_offset = (m_nGridY/2) * (m_nGridX+1); + for (int x=0; x<m_nGridX/2; x++) + m_verts[y_offset + x].ang = new_ang; + + // send half of the polys + int primCount = m_nGridX*m_nGridY*2 / 2; // in this case, to draw HALF the polys + int src_idx = 0; + int src_idx_offset = half * primCount*3; + int prims_sent = 0; + while (src_idx < primCount*3) + { + int prims_queued = 0; + int i=0; + while (prims_queued < max_prims_per_batch && src_idx < primCount*3) + { + // copy 3 verts + for (int j=0; j<3; j++) + tempv[i++] = m_verts[ m_indices_list[src_idx_offset + src_idx++] ]; + if (bCullTiles) + { + DWORD d1 = (tempv[i-3].Diffuse >> 24); + DWORD d2 = (tempv[i-2].Diffuse >> 24); + DWORD d3 = (tempv[i-1].Diffuse >> 24); + bool bIsNeeded; + if (nAlphaTestValue) + bIsNeeded = ((d1 & d2 & d3) < 255);//(d1 < 255) || (d2 < 255) || (d3 < 255); + else + bIsNeeded = ((d1|d2|d3) > 0);//(d1 > 0) || (d2 > 0) || (d3 > 0); + if (!bIsNeeded) + i -= 3; + else + prims_queued++; + } + else + prims_queued++; + } + if (prims_queued > 0) + lpDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, prims_queued, tempv, sizeof(MYVERTEX) ); + } + } + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + RestoreShaderParams(); +} + +void CPlugin::DrawCustomShapes() +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + //lpDevice->SetTexture(0, m_lpVS[0]);//NULL); + //lpDevice->SetVertexShader( SPRITEVERTEX_FORMAT ); + + int num_reps = (m_pState->m_bBlending) ? 2 : 1; + for (int rep=0; rep<num_reps; rep++) + { + CState *pState = (rep==0) ? m_pState : m_pOldState; + float alpha_mult = 1; + if (num_reps==2) + alpha_mult = (rep==0) ? m_pState->m_fBlendProgress : (1-m_pState->m_fBlendProgress); + + for (int i=0; i<MAX_CUSTOM_SHAPES; i++) + { + if (pState->m_shape[i].enabled) + { + /* + int bAdditive = 0; + int nSides = 3;//3 + ((int)GetTime() % 8); + int bThickOutline = 0; + float x = 0.5f + 0.1f*cosf(GetTime()*0.8f+1); + float y = 0.5f + 0.1f*sinf(GetTime()*0.8f+1); + float rad = 0.15f + 0.07f*sinf(GetTime()*1.1f+3); + float ang = GetTime()*1.5f; + + // inside colors + float r = 1; + float g = 0; + float b = 0; + float a = 0.4f;//0.1f + 0.1f*sinf(GetTime()*0.31f); + + // outside colors + float r2 = 0; + float g2 = 1; + float b2 = 0; + float a2 = 0; + + // border colors + float border_r = 1; + float border_g = 1; + float border_b = 1; + float border_a = 0.5f; + */ + + for (int instance=0; instance<pState->m_shape[i].instances; instance++) + { + // 1. execute per-frame code + LoadCustomShapePerFrameEvallibVars(pState, i, instance); + + #ifndef _NO_EXPR_ + if (pState->m_shape[i].m_pf_codehandle) + { + NSEEL_code_execute(pState->m_shape[i].m_pf_codehandle); + } + #endif + + // save changes to t1-t8 this frame + /* + pState->m_shape[i].t_values_after_init_code[0] = *pState->m_shape[i].var_pf_t1; + pState->m_shape[i].t_values_after_init_code[1] = *pState->m_shape[i].var_pf_t2; + pState->m_shape[i].t_values_after_init_code[2] = *pState->m_shape[i].var_pf_t3; + pState->m_shape[i].t_values_after_init_code[3] = *pState->m_shape[i].var_pf_t4; + pState->m_shape[i].t_values_after_init_code[4] = *pState->m_shape[i].var_pf_t5; + pState->m_shape[i].t_values_after_init_code[5] = *pState->m_shape[i].var_pf_t6; + pState->m_shape[i].t_values_after_init_code[6] = *pState->m_shape[i].var_pf_t7; + pState->m_shape[i].t_values_after_init_code[7] = *pState->m_shape[i].var_pf_t8; + */ + + int sides = (int)(*pState->m_shape[i].var_pf_sides); + if (sides<3) sides=3; + if (sides>100) sides=100; + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, ((int)(*pState->m_shape[i].var_pf_additive) != 0) ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA); + + SPRITEVERTEX v[512]; // for textured shapes (has texcoords) + WFVERTEX v2[512]; // for untextured shapes + borders + + v[0].x = (float)(*pState->m_shape[i].var_pf_x* 2-1);// * ASPECT; + v[0].y = (float)(*pState->m_shape[i].var_pf_y*-2+1); + v[0].z = 0; + v[0].tu = 0.5f; + v[0].tv = 0.5f; + v[0].Diffuse = + ((((int)(*pState->m_shape[i].var_pf_a * 255 * alpha_mult)) & 0xFF) << 24) | + ((((int)(*pState->m_shape[i].var_pf_r * 255)) & 0xFF) << 16) | + ((((int)(*pState->m_shape[i].var_pf_g * 255)) & 0xFF) << 8) | + ((((int)(*pState->m_shape[i].var_pf_b * 255)) & 0xFF) ); + v[1].Diffuse = + ((((int)(*pState->m_shape[i].var_pf_a2 * 255 * alpha_mult)) & 0xFF) << 24) | + ((((int)(*pState->m_shape[i].var_pf_r2 * 255)) & 0xFF) << 16) | + ((((int)(*pState->m_shape[i].var_pf_g2 * 255)) & 0xFF) << 8) | + ((((int)(*pState->m_shape[i].var_pf_b2 * 255)) & 0xFF) ); + + int j = 1; + for (j=1; j<sides+1; j++) + { + float t = (j-1)/(float)sides; + v[j].x = v[0].x + (float)*pState->m_shape[i].var_pf_rad*cosf(t*3.1415927f*2 + (float)*pState->m_shape[i].var_pf_ang + 3.1415927f*0.25f)*m_fAspectY; // DON'T TOUCH! + v[j].y = v[0].y + (float)*pState->m_shape[i].var_pf_rad*sinf(t*3.1415927f*2 + (float)*pState->m_shape[i].var_pf_ang + 3.1415927f*0.25f); // DON'T TOUCH! + v[j].z = 0; + v[j].tu = 0.5f + 0.5f*cosf(t*3.1415927f*2 + (float)*pState->m_shape[i].var_pf_tex_ang + 3.1415927f*0.25f)/((float)*pState->m_shape[i].var_pf_tex_zoom) * m_fAspectY; // DON'T TOUCH! + v[j].tv = 0.5f + 0.5f*sinf(t*3.1415927f*2 + (float)*pState->m_shape[i].var_pf_tex_ang + 3.1415927f*0.25f)/((float)*pState->m_shape[i].var_pf_tex_zoom); // DON'T TOUCH! + v[j].Diffuse = v[1].Diffuse; + } + v[sides+1] = v[1]; + + if ((int)(*pState->m_shape[i].var_pf_textured) != 0) + { + // draw textured version + lpDevice->SetTexture(0, m_lpVS[0]); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( SPRITEVERTEX_FORMAT ); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, sides, (void*)v, sizeof(SPRITEVERTEX)); + } + else + { + // no texture + for (j=0; j < sides+2; j++) + { + v2[j].x = v[j].x; + v2[j].y = v[j].y; + v2[j].z = v[j].z; + v2[j].Diffuse = v[j].Diffuse; + } + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( WFVERTEX_FORMAT ); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, sides, (void*)v2, sizeof(WFVERTEX)); + } + + // DRAW BORDER + if (*pState->m_shape[i].var_pf_border_a > 0) + { + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( WFVERTEX_FORMAT ); + + v2[0].Diffuse = + ((((int)(*pState->m_shape[i].var_pf_border_a * 255 * alpha_mult)) & 0xFF) << 24) | + ((((int)(*pState->m_shape[i].var_pf_border_r * 255)) & 0xFF) << 16) | + ((((int)(*pState->m_shape[i].var_pf_border_g * 255)) & 0xFF) << 8) | + ((((int)(*pState->m_shape[i].var_pf_border_b * 255)) & 0xFF) ); + for (j=0; j<sides+2; j++) + { + v2[j].x = v[j].x; + v2[j].y = v[j].y; + v2[j].z = v[j].z; + v2[j].Diffuse = v2[0].Diffuse; + } + + int its = ((int)(*pState->m_shape[i].var_pf_thick) != 0) ? 4 : 1; + float x_inc = 2.0f / (float)m_nTexSizeX; + float y_inc = 2.0f / (float)m_nTexSizeY; + for (int it=0; it<its; it++) + { + switch(it) + { + case 0: break; + case 1: for (j=0; j<sides+2; j++) v2[j].x += x_inc; break; // draw fat dots + case 2: for (j=0; j<sides+2; j++) v2[j].y += y_inc; break; // draw fat dots + case 3: for (j=0; j<sides+2; j++) v2[j].x -= x_inc; break; // draw fat dots + } + lpDevice->DrawPrimitiveUP(D3DPT_LINESTRIP, sides, (void*)&v2[1], sizeof(WFVERTEX)); + } + } + + lpDevice->SetTexture(0, m_lpVS[0]); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( SPRITEVERTEX_FORMAT ); + } + } + } + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); +} + +void CPlugin::LoadCustomShapePerFrameEvallibVars(CState* pState, int i, int instance) +{ + *pState->m_shape[i].var_pf_time = (double)(GetTime() - m_fStartTime); + *pState->m_shape[i].var_pf_frame = (double)GetFrame(); + *pState->m_shape[i].var_pf_fps = (double)GetFps(); + *pState->m_shape[i].var_pf_progress = (GetTime() - m_fPresetStartTime) / (m_fNextPresetTime - m_fPresetStartTime); + *pState->m_shape[i].var_pf_bass = (double)mysound.imm_rel[0]; + *pState->m_shape[i].var_pf_mid = (double)mysound.imm_rel[1]; + *pState->m_shape[i].var_pf_treb = (double)mysound.imm_rel[2]; + *pState->m_shape[i].var_pf_bass_att = (double)mysound.avg_rel[0]; + *pState->m_shape[i].var_pf_mid_att = (double)mysound.avg_rel[1]; + *pState->m_shape[i].var_pf_treb_att = (double)mysound.avg_rel[2]; + int vi = 0; + for (vi=0; vi<NUM_Q_VAR; vi++) + *pState->m_shape[i].var_pf_q[vi] = *pState->var_pf_q[vi]; + for (vi=0; vi<NUM_T_VAR; vi++) + *pState->m_shape[i].var_pf_t[vi] = pState->m_shape[i].t_values_after_init_code[vi]; + *pState->m_shape[i].var_pf_x = pState->m_shape[i].x; + *pState->m_shape[i].var_pf_y = pState->m_shape[i].y; + *pState->m_shape[i].var_pf_rad = pState->m_shape[i].rad; + *pState->m_shape[i].var_pf_ang = pState->m_shape[i].ang; + *pState->m_shape[i].var_pf_tex_zoom = pState->m_shape[i].tex_zoom; + *pState->m_shape[i].var_pf_tex_ang = pState->m_shape[i].tex_ang; + *pState->m_shape[i].var_pf_sides = pState->m_shape[i].sides; + *pState->m_shape[i].var_pf_additive = pState->m_shape[i].additive; + *pState->m_shape[i].var_pf_textured = pState->m_shape[i].textured; + *pState->m_shape[i].var_pf_instances = pState->m_shape[i].instances; + *pState->m_shape[i].var_pf_instance = instance; + *pState->m_shape[i].var_pf_thick = pState->m_shape[i].thickOutline; + *pState->m_shape[i].var_pf_r = pState->m_shape[i].r; + *pState->m_shape[i].var_pf_g = pState->m_shape[i].g; + *pState->m_shape[i].var_pf_b = pState->m_shape[i].b; + *pState->m_shape[i].var_pf_a = pState->m_shape[i].a; + *pState->m_shape[i].var_pf_r2 = pState->m_shape[i].r2; + *pState->m_shape[i].var_pf_g2 = pState->m_shape[i].g2; + *pState->m_shape[i].var_pf_b2 = pState->m_shape[i].b2; + *pState->m_shape[i].var_pf_a2 = pState->m_shape[i].a2; + *pState->m_shape[i].var_pf_border_r = pState->m_shape[i].border_r; + *pState->m_shape[i].var_pf_border_g = pState->m_shape[i].border_g; + *pState->m_shape[i].var_pf_border_b = pState->m_shape[i].border_b; + *pState->m_shape[i].var_pf_border_a = pState->m_shape[i].border_a; +} + +void CPlugin::LoadCustomWavePerFrameEvallibVars(CState* pState, int i) +{ + *pState->m_wave[i].var_pf_time = (double)(GetTime() - m_fStartTime); + *pState->m_wave[i].var_pf_frame = (double)GetFrame(); + *pState->m_wave[i].var_pf_fps = (double)GetFps(); + *pState->m_wave[i].var_pf_progress = (GetTime() - m_fPresetStartTime) / (m_fNextPresetTime - m_fPresetStartTime); + *pState->m_wave[i].var_pf_bass = (double)mysound.imm_rel[0]; + *pState->m_wave[i].var_pf_mid = (double)mysound.imm_rel[1]; + *pState->m_wave[i].var_pf_treb = (double)mysound.imm_rel[2]; + *pState->m_wave[i].var_pf_bass_att = (double)mysound.avg_rel[0]; + *pState->m_wave[i].var_pf_mid_att = (double)mysound.avg_rel[1]; + *pState->m_wave[i].var_pf_treb_att = (double)mysound.avg_rel[2]; + int vi = 0; + for (vi=0; vi<NUM_Q_VAR; vi++) + *pState->m_wave[i].var_pf_q[vi] = *pState->var_pf_q[vi]; + for (vi=0; vi<NUM_T_VAR; vi++) + *pState->m_wave[i].var_pf_t[vi] = pState->m_wave[i].t_values_after_init_code[vi]; + *pState->m_wave[i].var_pf_r = pState->m_wave[i].r; + *pState->m_wave[i].var_pf_g = pState->m_wave[i].g; + *pState->m_wave[i].var_pf_b = pState->m_wave[i].b; + *pState->m_wave[i].var_pf_a = pState->m_wave[i].a; + *pState->m_wave[i].var_pf_samples = pState->m_wave[i].samples; +} + +// does a better-than-linear smooth on a wave. Roughly doubles the # of points. +int SmoothWave(WFVERTEX* vi, int nVertsIn, WFVERTEX* vo) +{ + const float c1 = -0.15f; + const float c2 = 1.15f; + const float c3 = 1.15f; + const float c4 = -0.15f; + const float inv_sum = 1.0f/(c1+c2+c3+c4); + + int j = 0; + + int i_below = 0; + int i_above; + int i_above2 = 1; + for (int i=0; i<nVertsIn-1; i++) + { + i_above = i_above2; + i_above2 = min(nVertsIn-1,i+2); + vo[j] = vi[i]; + vo[j+1].x = (c1*vi[i_below].x + c2*vi[i].x + c3*vi[i_above].x + c4*vi[i_above2].x)*inv_sum; + vo[j+1].y = (c1*vi[i_below].y + c2*vi[i].y + c3*vi[i_above].y + c4*vi[i_above2].y)*inv_sum; + vo[j+1].z = 0; + vo[j+1].Diffuse = vi[i].Diffuse;//0xFFFF0080; + i_below = i; + j += 2; + } + vo[j++] = vi[nVertsIn-1]; + + return j; +} + +void CPlugin::DrawCustomWaves() +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( WFVERTEX_FORMAT ); + + // note: read in all sound data from CPluginShell's m_sound + int num_reps = (m_pState->m_bBlending) ? 2 : 1; + for (int rep=0; rep<num_reps; rep++) + { + CState *pState = (rep==0) ? m_pState : m_pOldState; + float alpha_mult = 1; + if (num_reps==2) + alpha_mult = (rep==0) ? m_pState->m_fBlendProgress : (1-m_pState->m_fBlendProgress); + + for (int i=0; i<MAX_CUSTOM_WAVES; i++) + { + if (pState->m_wave[i].enabled) + { + int nSamples = pState->m_wave[i].samples; + int max_samples = pState->m_wave[i].bSpectrum ? 512 : NUM_WAVEFORM_SAMPLES; + if (nSamples > max_samples) + nSamples = max_samples; + nSamples -= pState->m_wave[i].sep; + + // 1. execute per-frame code + LoadCustomWavePerFrameEvallibVars(pState, i); + + // 2.a. do just a once-per-frame init for the *per-point* *READ-ONLY* variables + // (the non-read-only ones will be reset/restored at the start of each vertex) + *pState->m_wave[i].var_pp_time = *pState->m_wave[i].var_pf_time; + *pState->m_wave[i].var_pp_fps = *pState->m_wave[i].var_pf_fps; + *pState->m_wave[i].var_pp_frame = *pState->m_wave[i].var_pf_frame; + *pState->m_wave[i].var_pp_progress = *pState->m_wave[i].var_pf_progress; + *pState->m_wave[i].var_pp_bass = *pState->m_wave[i].var_pf_bass; + *pState->m_wave[i].var_pp_mid = *pState->m_wave[i].var_pf_mid; + *pState->m_wave[i].var_pp_treb = *pState->m_wave[i].var_pf_treb; + *pState->m_wave[i].var_pp_bass_att = *pState->m_wave[i].var_pf_bass_att; + *pState->m_wave[i].var_pp_mid_att = *pState->m_wave[i].var_pf_mid_att; + *pState->m_wave[i].var_pp_treb_att = *pState->m_wave[i].var_pf_treb_att; + + NSEEL_code_execute(pState->m_wave[i].m_pf_codehandle); + int vi = 0; + for (vi=0; vi<NUM_Q_VAR; vi++) + *pState->m_wave[i].var_pp_q[vi] = *pState->m_wave[i].var_pf_q[vi]; + for (vi=0; vi<NUM_T_VAR; vi++) + *pState->m_wave[i].var_pp_t[vi] = *pState->m_wave[i].var_pf_t[vi]; + + nSamples = (int)*pState->m_wave[i].var_pf_samples; + nSamples = min(512, nSamples); + + if ((nSamples >= 2) || (pState->m_wave[i].bUseDots && nSamples >= 1)) + { + int j; + float tempdata[2][512]; + float mult = ((pState->m_wave[i].bSpectrum) ? 0.15f : 0.004f) * pState->m_wave[i].scaling * pState->m_fWaveScale.eval(-1); + float *pdata1 = (pState->m_wave[i].bSpectrum) ? m_sound.fSpectrum[0] : m_sound.fWaveform[0]; + float *pdata2 = (pState->m_wave[i].bSpectrum) ? m_sound.fSpectrum[1] : m_sound.fWaveform[1]; + + // initialize tempdata[2][512] + int j0 = (pState->m_wave[i].bSpectrum) ? 0 : (max_samples - nSamples)/2/**(1-pState->m_wave[i].bSpectrum)*/ - pState->m_wave[i].sep/2; + int j1 = (pState->m_wave[i].bSpectrum) ? 0 : (max_samples - nSamples)/2/**(1-pState->m_wave[i].bSpectrum)*/ + pState->m_wave[i].sep/2; + float t = (pState->m_wave[i].bSpectrum) ? (max_samples - pState->m_wave[i].sep)/(float)nSamples : 1; + float mix1 = powf(pState->m_wave[i].smoothing*0.98f, 0.5f); // lower exponent -> more default smoothing + float mix2 = 1-mix1; + // SMOOTHING: + tempdata[0][0] = pdata1[j0]; + tempdata[1][0] = pdata2[j1]; + for (j=1; j<nSamples; j++) + { + tempdata[0][j] = pdata1[(int)(j*t)+j0]*mix2 + tempdata[0][j-1]*mix1; + tempdata[1][j] = pdata2[(int)(j*t)+j1]*mix2 + tempdata[1][j-1]*mix1; + } + // smooth again, backwards: [this fixes the asymmetry of the beginning & end..] + for (j=nSamples-2; j>=0; j--) + { + tempdata[0][j] = tempdata[0][j]*mix2 + tempdata[0][j+1]*mix1; + tempdata[1][j] = tempdata[1][j]*mix2 + tempdata[1][j+1]*mix1; + } + // finally, scale to final size: + for (j=0; j<nSamples; j++) + { + tempdata[0][j] *= mult; + tempdata[1][j] *= mult; + } + + // 2. for each point, execute per-point code + + + // to do: + // -add any of the m_wave[i].xxx menu-accessible vars to the code? + WFVERTEX v[1024]; + float j_mult = 1.0f/(float)(nSamples-1); + for (j=0; j<nSamples; j++) + { + float t = j*j_mult; + float value1 = tempdata[0][j]; + float value2 = tempdata[1][j]; + *pState->m_wave[i].var_pp_sample = t; + *pState->m_wave[i].var_pp_value1 = value1; + *pState->m_wave[i].var_pp_value2 = value2; + *pState->m_wave[i].var_pp_x = 0.5f + value1; + *pState->m_wave[i].var_pp_y = 0.5f + value2; + *pState->m_wave[i].var_pp_r = *pState->m_wave[i].var_pf_r; + *pState->m_wave[i].var_pp_g = *pState->m_wave[i].var_pf_g; + *pState->m_wave[i].var_pp_b = *pState->m_wave[i].var_pf_b; + *pState->m_wave[i].var_pp_a = *pState->m_wave[i].var_pf_a; + + #ifndef _NO_EXPR_ + NSEEL_code_execute(pState->m_wave[i].m_pp_codehandle); + #endif + + v[j].x = (float)(*pState->m_wave[i].var_pp_x* 2-1)*m_fInvAspectX; + v[j].y = (float)(*pState->m_wave[i].var_pp_y*-2+1)*m_fInvAspectY; + v[j].z = 0; + v[j].Diffuse = + ((((int)(*pState->m_wave[i].var_pp_a * 255 * alpha_mult)) & 0xFF) << 24) | + ((((int)(*pState->m_wave[i].var_pp_r * 255)) & 0xFF) << 16) | + ((((int)(*pState->m_wave[i].var_pp_g * 255)) & 0xFF) << 8) | + ((((int)(*pState->m_wave[i].var_pp_b * 255)) & 0xFF) ); + } + + + + // save changes to t1-t8 this frame + /* + pState->m_wave[i].t_values_after_init_code[0] = *pState->m_wave[i].var_pp_t1; + pState->m_wave[i].t_values_after_init_code[1] = *pState->m_wave[i].var_pp_t2; + pState->m_wave[i].t_values_after_init_code[2] = *pState->m_wave[i].var_pp_t3; + pState->m_wave[i].t_values_after_init_code[3] = *pState->m_wave[i].var_pp_t4; + pState->m_wave[i].t_values_after_init_code[4] = *pState->m_wave[i].var_pp_t5; + pState->m_wave[i].t_values_after_init_code[5] = *pState->m_wave[i].var_pp_t6; + pState->m_wave[i].t_values_after_init_code[6] = *pState->m_wave[i].var_pp_t7; + pState->m_wave[i].t_values_after_init_code[7] = *pState->m_wave[i].var_pp_t8; + */ + + // 3. smooth it + WFVERTEX v2[2048]; + WFVERTEX *pVerts = v; + if (!pState->m_wave[i].bUseDots) + { + nSamples = SmoothWave(v, nSamples, v2); + pVerts = v2; + } + + // 4. draw it + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, pState->m_wave[i].bAdditive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA); + + float ptsize = (float)((m_nTexSizeX >= 1024) ? 2 : 1) + (pState->m_wave[i].bDrawThick ? 1 : 0); + if (pState->m_wave[i].bUseDots) + lpDevice->SetRenderState(D3DRS_POINTSIZE, *((DWORD*)&ptsize) ); + + int its = (pState->m_wave[i].bDrawThick && !pState->m_wave[i].bUseDots) ? 4 : 1; + float x_inc = 2.0f / (float)m_nTexSizeX; + float y_inc = 2.0f / (float)m_nTexSizeY; + for (int it=0; it<its; it++) + { + switch(it) + { + case 0: break; + case 1: for (j=0; j<nSamples; j++) pVerts[j].x += x_inc; break; // draw fat dots + case 2: for (j=0; j<nSamples; j++) pVerts[j].y += y_inc; break; // draw fat dots + case 3: for (j=0; j<nSamples; j++) pVerts[j].x -= x_inc; break; // draw fat dots + } + lpDevice->DrawPrimitiveUP(pState->m_wave[i].bUseDots ? D3DPT_POINTLIST : D3DPT_LINESTRIP, nSamples - (pState->m_wave[i].bUseDots ? 0 : 1), (void*)pVerts, sizeof(WFVERTEX)); + } + + ptsize = 1.0f; + if (pState->m_wave[i].bUseDots) + lpDevice->SetRenderState(D3DRS_POINTSIZE, *((DWORD*)&ptsize) ); + } + } + } + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); +} + +void CPlugin::DrawWave(float *fL, float *fR) +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( WFVERTEX_FORMAT ); + + int i; + WFVERTEX v1[576+1], v2[576+1]; + + /* + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD); //D3DSHADE_FLAT + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_SPECULARENABLE, FALSE); + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_CULLMODE, D3DCULL_NONE); + if (m_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_DITHER) + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_DITHERENABLE, TRUE); + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_ZENABLE, D3DZB_FALSE); + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_LIGHTING, FALSE); + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_COLORVERTEX, TRUE); + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_FILLMODE, D3DFILL_WIREFRAME); // vs. SOLID + m_lpD3DDev->SetRenderState(D3DRENDERSTATE_AMBIENT, D3DCOLOR_RGBA_01(1,1,1,1)); + + hr = m_lpD3DDev->SetTexture(0, NULL); + if (hr != D3D_OK) + { + //dumpmsg("Draw(): ERROR: SetTexture"); + //IdentifyD3DError(hr); + } + */ + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, (*m_pState->var_pf_wave_additive) ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA); + + //float cr = m_pState->m_waveR.eval(GetTime()); + //float cg = m_pState->m_waveG.eval(GetTime()); + //float cb = m_pState->m_waveB.eval(GetTime()); + float cr = (float)(*m_pState->var_pf_wave_r); + float cg = (float)(*m_pState->var_pf_wave_g); + float cb = (float)(*m_pState->var_pf_wave_b); + float cx = (float)(*m_pState->var_pf_wave_x); + float cy = (float)(*m_pState->var_pf_wave_y); // note: it was backwards (top==1) in the original milkdrop, so we keep it that way! + float fWaveParam = (float)(*m_pState->var_pf_wave_mystery); + + /*if (m_pState->m_bBlending) + { + cr = cr*(m_pState->m_fBlendProgress) + (1.0f-m_pState->m_fBlendProgress)*((float)(*m_pOldState->var_pf_wave_r)); + cg = cg*(m_pState->m_fBlendProgress) + (1.0f-m_pState->m_fBlendProgress)*((float)(*m_pOldState->var_pf_wave_g)); + cb = cb*(m_pState->m_fBlendProgress) + (1.0f-m_pState->m_fBlendProgress)*((float)(*m_pOldState->var_pf_wave_b)); + cx = cx*(m_pState->m_fBlendProgress) + (1.0f-m_pState->m_fBlendProgress)*((float)(*m_pOldState->var_pf_wave_x)); + cy = cy*(m_pState->m_fBlendProgress) + (1.0f-m_pState->m_fBlendProgress)*((float)(*m_pOldState->var_pf_wave_y)); + fWaveParam = fWaveParam*(m_pState->m_fBlendProgress) + (1.0f-m_pState->m_fBlendProgress)*((float)(*m_pOldState->var_pf_wave_mystery)); + }*/ + + if (cr < 0) cr = 0; + if (cg < 0) cg = 0; + if (cb < 0) cb = 0; + if (cr > 1) cr = 1; + if (cg > 1) cg = 1; + if (cb > 1) cb = 1; + + // maximize color: + if (*m_pState->var_pf_wave_brighten) + { + float fMaximizeWaveColorAmount = 1.0f; + float max = cr; + if (max < cg) max = cg; + if (max < cb) max = cb; + if (max > 0.01f) + { + cr = cr/max*fMaximizeWaveColorAmount + cr*(1.0f - fMaximizeWaveColorAmount); + cg = cg/max*fMaximizeWaveColorAmount + cg*(1.0f - fMaximizeWaveColorAmount); + cb = cb/max*fMaximizeWaveColorAmount + cb*(1.0f - fMaximizeWaveColorAmount); + } + } + + float fWavePosX = cx*2.0f - 1.0f; // go from 0..1 user-range to -1..1 D3D range + float fWavePosY = cy*2.0f - 1.0f; + + float bass_rel = mysound.imm[0]; + float mid_rel = mysound.imm[1]; + float treble_rel = mysound.imm[2]; + + int sample_offset = 0; + int new_wavemode = (int)(*m_pState->var_pf_wave_mode) % NUM_WAVES; // since it can be changed from per-frame code! + + int its = (m_pState->m_bBlending && (new_wavemode != m_pState->m_nOldWaveMode)) ? 2 : 1; + int nVerts1 = 0; + int nVerts2 = 0; + int nBreak1 = -1; + int nBreak2 = -1; + float alpha1, alpha2; + + for (int it=0; it<its; it++) + { + int wave = (it==0) ? new_wavemode : m_pState->m_nOldWaveMode; + int nVerts = NUM_WAVEFORM_SAMPLES; // allowed to peek ahead 64 (i.e. left is [i], right is [i+64]) + int nBreak = -1; + + float fWaveParam2 = fWaveParam; + //std::string fWaveParam; // kill its scope + if ((wave==0 || wave==1 || wave==4) && (fWaveParam2 < -1 || fWaveParam2 > 1)) + { + //fWaveParam2 = max(fWaveParam2, -1.0f); + //fWaveParam2 = min(fWaveParam2, 1.0f); + fWaveParam2 = fWaveParam2*0.5f + 0.5f; + fWaveParam2 -= floorf(fWaveParam2); + fWaveParam2 = fabsf(fWaveParam2); + fWaveParam2 = fWaveParam2*2-1; + } + + WFVERTEX *v = (it==0) ? v1 : v2; + ZeroMemory(v, sizeof(WFVERTEX)*nVerts); + + float alpha = (float)(*m_pState->var_pf_wave_a);//m_pState->m_fWaveAlpha.eval(GetTime()); + + switch(wave) + { + case 0: + // circular wave + + nVerts /= 2; + sample_offset = (NUM_WAVEFORM_SAMPLES-nVerts)/2;//mysound.GoGoAlignatron(nVerts * 12/10); // only call this once nVerts is final! + + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + { + float inv_nverts_minus_one = 1.0f/(float)(nVerts-1); + + for (i=0; i<nVerts; i++) + { + float rad = 0.5f + 0.4f*fR[i+sample_offset] + fWaveParam2; + float ang = (i)*inv_nverts_minus_one*6.28f + GetTime()*0.2f; + if (i < nVerts/10) + { + float mix = i/(nVerts*0.1f); + mix = 0.5f - 0.5f*cosf(mix * 3.1416f); + float rad_2 = 0.5f + 0.4f*fR[i + nVerts + sample_offset] + fWaveParam2; + rad = rad_2*(1.0f-mix) + rad*(mix); + } + v[i].x = rad*cosf(ang) *m_fAspectY + fWavePosX; // 0.75 = adj. for aspect ratio + v[i].y = rad*sinf(ang) *m_fAspectX + fWavePosY; + //v[i].Diffuse = color; + } + } + + // dupe last vertex to connect the lines; skip if blending + if (!m_pState->m_bBlending) + { + nVerts++; + memcpy(&v[nVerts-1], &v[0], sizeof(WFVERTEX)); + } + + break; + + case 1: + // x-y osc. that goes around in a spiral, in time + + alpha *= 1.25f; + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + nVerts /= 2; + + for (i=0; i<nVerts; i++) + { + float rad = 0.53f + 0.43f*fR[i] + fWaveParam2; + float ang = fL[i+32] * 1.57f + GetTime()*2.3f; + v[i].x = rad*cosf(ang) *m_fAspectY + fWavePosX; // 0.75 = adj. for aspect ratio + v[i].y = rad*sinf(ang) *m_fAspectX + fWavePosY; + //v[i].Diffuse = color;//(D3DCOLOR_RGBA_01(cr, cg, cb, alpha*min(1, max(0, fL[i]))); + } + + break; + + case 2: + // centered spiro (alpha constant) + // aimed at not being so sound-responsive, but being very "nebula-like" + // difference is that alpha is constant (and faint), and waves a scaled way up + + switch(m_nTexSizeX) + { + case 256: alpha *= 0.07f; break; + case 512: alpha *= 0.09f; break; + case 1024: alpha *= 0.11f; break; + case 2048: alpha *= 0.13f; break; + } + + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + for (i=0; i<nVerts; i++) + { + v[i].x = fR[i ] *m_fAspectY + fWavePosX;//((pR[i] ^ 128) - 128)/90.0f * ASPECT; // 0.75 = adj. for aspect ratio + v[i].y = fL[i+32] *m_fAspectX + fWavePosY;//((pL[i+32] ^ 128) - 128)/90.0f; + //v[i].Diffuse = color; + } + + break; + case 3: + // centered spiro (alpha tied to volume) + // aimed at having a strong audio-visual tie-in + // colors are always bright (no darks) + + switch(m_nTexSizeX) + { + case 256: alpha = 0.075f; break; + case 512: alpha = 0.150f; break; + case 1024: alpha = 0.220f; break; + case 2048: alpha = 0.330f; break; + } + + alpha *= 1.3f; + alpha *= powf(treble_rel, 2.0f); + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + for (i=0; i<nVerts; i++) + { + v[i].x = fR[i ] *m_fAspectY + fWavePosX;//((pR[i] ^ 128) - 128)/90.0f * ASPECT; // 0.75 = adj. for aspect ratio + v[i].y = fL[i+32] *m_fAspectX + fWavePosY;//((pL[i+32] ^ 128) - 128)/90.0f; + //v[i].Diffuse = color; + } + break; + case 4: + // horizontal "script", left channel + + if (nVerts > m_nTexSizeX/3) + nVerts = m_nTexSizeX/3; + + sample_offset = (NUM_WAVEFORM_SAMPLES-nVerts)/2;//mysound.GoGoAlignatron(nVerts + 25); // only call this once nVerts is final! + + /* + if (treble_rel > treb_thresh_for_wave6) + { + //alpha = 1.0f; + treb_thresh_for_wave6 = treble_rel * 1.025f; + } + else + { + alpha *= 0.2f; + treb_thresh_for_wave6 *= 0.996f; // fixme: make this fps-independent + } + */ + + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + { + float w1 = 0.45f + 0.5f*(fWaveParam2*0.5f + 0.5f); // 0.1 - 0.9 + float w2 = 1.0f - w1; + + float inv_nverts = 1.0f/(float)(nVerts); + + for (i=0; i<nVerts; i++) + { + v[i].x = -1.0f + 2.0f*(i*inv_nverts) + fWavePosX; + v[i].y = fL[i+sample_offset]*0.47f + fWavePosY;//((pL[i] ^ 128) - 128)/270.0f; + v[i].x += fR[i+25+sample_offset]*0.44f;//((pR[i+25] ^ 128) - 128)/290.0f; + //v[i].Diffuse = color; + + // momentum + if (i>1) + { + v[i].x = v[i].x*w2 + w1*(v[i-1].x*2.0f - v[i-2].x); + v[i].y = v[i].y*w2 + w1*(v[i-1].y*2.0f - v[i-2].y); + } + } + + /* + // center on Y + float avg_y = 0; + for (i=0; i<nVerts; i++) + avg_y += v[i].y; + avg_y /= (float)nVerts; + avg_y *= 0.5f; // damp the movement + for (i=0; i<nVerts; i++) + v[i].y -= avg_y; + */ + } + + break; + + case 5: + // weird explosive complex # thingy + + switch(m_nTexSizeX) + { + case 256: alpha *= 0.07f; break; + case 512: alpha *= 0.09f; break; + case 1024: alpha *= 0.11f; break; + case 2048: alpha *= 0.13f; break; + } + + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + { + float cos_rot = cosf(GetTime()*0.3f); + float sin_rot = sinf(GetTime()*0.3f); + + for (i=0; i<nVerts; i++) + { + float x0 = (fR[i]*fL[i+32] + fL[i]*fR[i+32]); + float y0 = (fR[i]*fR[i] - fL[i+32]*fL[i+32]); + v[i].x = (x0*cos_rot - y0*sin_rot)*m_fAspectY + fWavePosX; + v[i].y = (x0*sin_rot + y0*cos_rot)*m_fAspectX + fWavePosY; + //v[i].Diffuse = color; + } + } + + break; + + case 6: + case 7: + case 8: + // 6: angle-adjustable left channel, with temporal wave alignment; + // fWaveParam2 controls the angle at which it's drawn + // fWavePosX slides the wave away from the center, transversely. + // fWavePosY does nothing + // + // 7: same, except there are two channels shown, and + // fWavePosY determines the separation distance. + // + // 8: same as 6, except using the spectrum analyzer (UNFINISHED) + // + nVerts /= 2; + + if (nVerts > m_nTexSizeX/3) + nVerts = m_nTexSizeX/3; + + if (wave==8) + nVerts = 256; + else + sample_offset = (NUM_WAVEFORM_SAMPLES-nVerts)/2;//mysound.GoGoAlignatron(nVerts); // only call this once nVerts is final! + + if (m_pState->m_bModWaveAlphaByVolume) + alpha *= ((mysound.imm_rel[0] + mysound.imm_rel[1] + mysound.imm_rel[2])*0.333f - m_pState->m_fModWaveAlphaStart.eval(GetTime()))/(m_pState->m_fModWaveAlphaEnd.eval(GetTime()) - m_pState->m_fModWaveAlphaStart.eval(GetTime())); + if (alpha < 0) alpha = 0; + if (alpha > 1) alpha = 1; + //color = D3DCOLOR_RGBA_01(cr, cg, cb, alpha); + + { + float ang = 1.57f*fWaveParam2; // from -PI/2 to PI/2 + float dx = cosf(ang); + float dy = sinf(ang); + + float edge_x[2], edge_y[2]; + + //edge_x[0] = fWavePosX - dx*3.0f; + //edge_y[0] = fWavePosY - dy*3.0f; + //edge_x[1] = fWavePosX + dx*3.0f; + //edge_y[1] = fWavePosY + dy*3.0f; + edge_x[0] = fWavePosX*cosf(ang + 1.57f) - dx*3.0f; + edge_y[0] = fWavePosX*sinf(ang + 1.57f) - dy*3.0f; + edge_x[1] = fWavePosX*cosf(ang + 1.57f) + dx*3.0f; + edge_y[1] = fWavePosX*sinf(ang + 1.57f) + dy*3.0f; + + for (i=0; i<2; i++) // for each point defining the line + { + // clip the point against 4 edges of screen + // be a bit lenient (use +/-1.1 instead of +/-1.0) + // so the dual-wave doesn't end too soon, after the channels are moved apart + for (int j=0; j<4; j++) + { + float t; + bool bClip = false; + + switch(j) + { + case 0: + if (edge_x[i] > 1.1f) + { + t = (1.1f - edge_x[1-i]) / (edge_x[i] - edge_x[1-i]); + bClip = true; + } + break; + case 1: + if (edge_x[i] < -1.1f) + { + t = (-1.1f - edge_x[1-i]) / (edge_x[i] - edge_x[1-i]); + bClip = true; + } + break; + case 2: + if (edge_y[i] > 1.1f) + { + t = (1.1f - edge_y[1-i]) / (edge_y[i] - edge_y[1-i]); + bClip = true; + } + break; + case 3: + if (edge_y[i] < -1.1f) + { + t = (-1.1f - edge_y[1-i]) / (edge_y[i] - edge_y[1-i]); + bClip = true; + } + break; + } + + if (bClip) + { + float dx = edge_x[i] - edge_x[1-i]; + float dy = edge_y[i] - edge_y[1-i]; + edge_x[i] = edge_x[1-i] + dx*t; + edge_y[i] = edge_y[1-i] + dy*t; + } + } + } + + dx = (edge_x[1] - edge_x[0]) / (float)nVerts; + dy = (edge_y[1] - edge_y[0]) / (float)nVerts; + float ang2 = atan2f(dy,dx); + float perp_dx = cosf(ang2 + 1.57f); + float perp_dy = sinf(ang2 + 1.57f); + + if (wave == 6) + for (i=0; i<nVerts; i++) + { + v[i].x = edge_x[0] + dx*i + perp_dx*0.25f*fL[i + sample_offset]; + v[i].y = edge_y[0] + dy*i + perp_dy*0.25f*fL[i + sample_offset]; + //v[i].Diffuse = color; + } + else if (wave == 8) + //256 verts + for (i=0; i<nVerts; i++) + { + float f = 0.1f*logf(mysound.fSpecLeft[i*2] + mysound.fSpecLeft[i*2+1]); + v[i].x = edge_x[0] + dx*i + perp_dx*f; + v[i].y = edge_y[0] + dy*i + perp_dy*f; + //v[i].Diffuse = color; + } + else + { + float sep = powf(fWavePosY*0.5f + 0.5f, 2.0f); + for (i=0; i<nVerts; i++) + { + v[i].x = edge_x[0] + dx*i + perp_dx*(0.25f*fL[i + sample_offset] + sep); + v[i].y = edge_y[0] + dy*i + perp_dy*(0.25f*fL[i + sample_offset] + sep); + //v[i].Diffuse = color; + } + + //D3DPRIMITIVETYPE primtype = (*m_pState->var_pf_wave_usedots) ? D3DPT_POINTLIST : D3DPT_LINESTRIP; + //m_lpD3DDev->DrawPrimitive(primtype, D3DFVF_LVERTEX, (LPVOID)v, nVerts, NULL); + + for (i=0; i<nVerts; i++) + { + v[i+nVerts].x = edge_x[0] + dx*i + perp_dx*(0.25f*fR[i + sample_offset] - sep); + v[i+nVerts].y = edge_y[0] + dy*i + perp_dy*(0.25f*fR[i + sample_offset] - sep); + //v[i+nVerts].Diffuse = color; + } + + nBreak = nVerts; + nVerts *= 2; + } + } + + break; + } + + if (it==0) + { + nVerts1 = nVerts; + nBreak1 = nBreak; + alpha1 = alpha; + } + else + { + nVerts2 = nVerts; + nBreak2 = nBreak; + alpha2 = alpha; + } + } + + // v1[] is for the current waveform + // v2[] is for the old waveform (from prev. preset - only used if blending) + // nVerts1 is the # of vertices in v1 + // nVerts2 is the # of vertices in v2 + // nBreak1 is the index of the point at which to break the solid line in v1[] (-1 if no break) + // nBreak2 is the index of the point at which to break the solid line in v2[] (-1 if no break) + + float mix = CosineInterp(m_pState->m_fBlendProgress); + float mix2 = 1.0f - mix; + + // blend 2 waveforms + if (nVerts2 > 0) + { + // note: this won't yet handle the case where (nBreak1 > 0 && nBreak2 > 0) + // in this case, code must break wave into THREE segments + float m = (nVerts2-1)/(float)nVerts1; + float x,y; + for (int i=0; i<nVerts1; i++) + { + float fIdx = i*m; + int nIdx = (int)fIdx; + float t = fIdx - nIdx; + if (nIdx == nBreak2-1) + { + x = v2[nIdx].x; + y = v2[nIdx].y; + nBreak1 = i+1; + } + else + { + x = v2[nIdx].x*(1-t) + v2[nIdx+1].x*(t); + y = v2[nIdx].y*(1-t) + v2[nIdx+1].y*(t); + } + v1[i].x = v1[i].x*(mix) + x*(mix2); + v1[i].y = v1[i].y*(mix) + y*(mix2); + } + } + + // determine alpha + if (nVerts2 > 0) + { + alpha1 = alpha1*(mix) + alpha2*(1.0f-mix); + } + + // apply color & alpha + // ALSO reverse all y values, to stay consistent with the pre-VMS milkdrop, + // which DIDN'T: + v1[0].Diffuse = D3DCOLOR_RGBA_01(cr, cg, cb, alpha1); + for (i=0; i<nVerts1; i++) + { + v1[i].Diffuse = v1[0].Diffuse; + v1[i].y = -v1[i].y; + } + + // don't draw wave if (possibly blended) alpha is less than zero. + if (alpha1 < 0.004f) + goto SKIP_DRAW_WAVE; + + // TESSELLATE - smooth the wave, one time. + WFVERTEX* pVerts = v1; + WFVERTEX vTess[(576+3)*2]; + if (1) + { + if (nBreak1==-1) + { + nVerts1 = SmoothWave(v1, nVerts1, vTess); + } + else + { + int oldBreak = nBreak1; + nBreak1 = SmoothWave(v1, nBreak1, vTess); + nVerts1 = SmoothWave(&v1[oldBreak], nVerts1-oldBreak, &vTess[nBreak1]) + nBreak1; + } + pVerts = vTess; + } + + // draw primitives + { + //D3DPRIMITIVETYPE primtype = (*m_pState->var_pf_wave_usedots) ? D3DPT_POINTLIST : D3DPT_LINESTRIP; + float x_inc = 2.0f / (float)m_nTexSizeX; + float y_inc = 2.0f / (float)m_nTexSizeY; + int drawing_its = ((*m_pState->var_pf_wave_thick || *m_pState->var_pf_wave_usedots) && (m_nTexSizeX >= 512)) ? 4 : 1; + + for (int it=0; it<drawing_its; it++) + { + int j; + + switch(it) + { + case 0: break; + case 1: for (j=0; j<nVerts1; j++) pVerts[j].x += x_inc; break; // draw fat dots + case 2: for (j=0; j<nVerts1; j++) pVerts[j].y += y_inc; break; // draw fat dots + case 3: for (j=0; j<nVerts1; j++) pVerts[j].x -= x_inc; break; // draw fat dots + } + + if (nBreak1 == -1) + { + if (*m_pState->var_pf_wave_usedots) + lpDevice->DrawPrimitiveUP(D3DPT_POINTLIST, nVerts1, (void*)pVerts, sizeof(WFVERTEX)); + else + lpDevice->DrawPrimitiveUP(D3DPT_LINESTRIP, nVerts1-1, (void*)pVerts, sizeof(WFVERTEX)); + } + else + { + if (*m_pState->var_pf_wave_usedots) + { + lpDevice->DrawPrimitiveUP(D3DPT_POINTLIST, nBreak1, (void*)pVerts, sizeof(WFVERTEX)); + lpDevice->DrawPrimitiveUP(D3DPT_POINTLIST, nVerts1-nBreak1, (void*)&pVerts[nBreak1], sizeof(WFVERTEX)); + } + else + { + lpDevice->DrawPrimitiveUP(D3DPT_LINESTRIP, nBreak1-1, (void*)pVerts, sizeof(WFVERTEX)); + lpDevice->DrawPrimitiveUP(D3DPT_LINESTRIP, nVerts1-nBreak1-1, (void*)&pVerts[nBreak1], sizeof(WFVERTEX)); + } + } + } + } + +SKIP_DRAW_WAVE: + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); +} + +void CPlugin::DrawSprites() +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( WFVERTEX_FORMAT ); + + if (*m_pState->var_pf_darken_center) + { + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);//SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + WFVERTEX v3[6]; + ZeroMemory(v3, sizeof(WFVERTEX)*6); + + // colors: + v3[0].Diffuse = D3DCOLOR_RGBA_01(0, 0, 0, 3.0f/32.0f); + v3[1].Diffuse = D3DCOLOR_RGBA_01(0, 0, 0, 0.0f/32.0f); + v3[2].Diffuse = v3[1].Diffuse; + v3[3].Diffuse = v3[1].Diffuse; + v3[4].Diffuse = v3[1].Diffuse; + v3[5].Diffuse = v3[1].Diffuse; + + // positioning: + float fHalfSize = 0.05f; + v3[0].x = 0.0f; + v3[1].x = 0.0f - fHalfSize*m_fAspectY; + v3[2].x = 0.0f; + v3[3].x = 0.0f + fHalfSize*m_fAspectY; + v3[4].x = 0.0f; + v3[5].x = v3[1].x; + v3[0].y = 0.0f; + v3[1].y = 0.0f; + v3[2].y = 0.0f - fHalfSize; + v3[3].y = 0.0f; + v3[4].y = 0.0f + fHalfSize; + v3[5].y = v3[1].y; + //v3[0].tu = 0; v3[1].tu = 1; v3[2].tu = 0; v3[3].tu = 1; + //v3[0].tv = 1; v3[1].tv = 1; v3[2].tv = 0; v3[3].tv = 0; + + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 4, (LPVOID)v3, sizeof(WFVERTEX)); + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } + + // do borders + { + float fOuterBorderSize = (float)*m_pState->var_pf_ob_size; + float fInnerBorderSize = (float)*m_pState->var_pf_ib_size; + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + for (int it=0; it<2; it++) + { + WFVERTEX v3[4]; + ZeroMemory(v3, sizeof(WFVERTEX)*4); + + // colors: + float r = (it==0) ? (float)*m_pState->var_pf_ob_r : (float)*m_pState->var_pf_ib_r; + float g = (it==0) ? (float)*m_pState->var_pf_ob_g : (float)*m_pState->var_pf_ib_g; + float b = (it==0) ? (float)*m_pState->var_pf_ob_b : (float)*m_pState->var_pf_ib_b; + float a = (it==0) ? (float)*m_pState->var_pf_ob_a : (float)*m_pState->var_pf_ib_a; + if (a > 0.001f) + { + v3[0].Diffuse = D3DCOLOR_RGBA_01(r,g,b,a); + v3[1].Diffuse = v3[0].Diffuse; + v3[2].Diffuse = v3[0].Diffuse; + v3[3].Diffuse = v3[0].Diffuse; + + // positioning: + float fInnerRad = (it==0) ? 1.0f - fOuterBorderSize : 1.0f - fOuterBorderSize - fInnerBorderSize; + float fOuterRad = (it==0) ? 1.0f : 1.0f - fOuterBorderSize; + v3[0].x = fInnerRad; + v3[1].x = fOuterRad; + v3[2].x = fOuterRad; + v3[3].x = fInnerRad; + v3[0].y = fInnerRad; + v3[1].y = fOuterRad; + v3[2].y = -fOuterRad; + v3[3].y = -fInnerRad; + + for (int rot=0; rot<4; rot++) + { + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, (LPVOID)v3, sizeof(WFVERTEX)); + + // rotate by 90 degrees + for (int v=0; v<4; v++) + { + float t = 1.570796327f; + float x = v3[v].x; + float y = v3[v].y; + v3[v].x = x*cosf(t) - y*sinf(t); + v3[v].y = x*sinf(t) + y*cosf(t); + } + } + } + } + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } +} + +/* +bool CPlugin::SetMilkdropRenderTarget(LPDIRECTDRAWSURFACE7 lpSurf, int w, int h, char *szErrorMsg) +{ + HRESULT hr = m_lpD3DDev->SetRenderTarget(0, lpSurf, 0); + if (hr != D3D_OK) + { + //if (szErrorMsg && szErrorMsg[0]) dumpmsg(szErrorMsg); + //IdentifyD3DError(hr); + return false; + } + + //DDSURFACEDESC2 ddsd; + //ddsd.dwSize = sizeof(ddsd); + //lpSurf->GetSurfaceDesc(&ddsd); + + D3DVIEWPORT7 viewData; + ZeroMemory(&viewData, sizeof(D3DVIEWPORT7)); + viewData.dwWidth = w; // not: in windowed mode, when lpSurf is the back buffer, chances are good that w,h are smaller than the full surface size (since real size is fullscreen, but we're only using a portion of it as big as the window). + viewData.dwHeight = h; + hr = m_lpD3DDev->SetViewport(&viewData); + + return true; +} +*/ + +void CPlugin::DrawUserSprites() // from system memory, to back buffer. +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, NULL); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( SPRITEVERTEX_FORMAT ); + + //lpDevice->SetRenderState(D3DRS_WRAP0, 0); + //lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); + //lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); + //lpDevice->SetSamplerState(0, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP); + + // reset these to the standard safe mode: + lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + /* + lpDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); //D3DSHADE_GOURAUD + lpDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE); + lpDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + if (m_D3DDevDesc.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_DITHER) + lpDevice->SetRenderState(D3DRS_DITHERENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + lpDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + lpDevice->SetRenderState(D3DRS_COLORVERTEX, TRUE); + lpDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); // vs. wireframe + lpDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_RGBA_01(1,1,1,1)); + lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTFG_LINEAR ); + lpDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTFN_LINEAR ); + lpDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTFP_LINEAR ); + */ + + for (int iSlot=0; iSlot < NUM_TEX; iSlot++) + { + if (m_texmgr.m_tex[iSlot].pSurface) + { + int k; + + // set values of input variables: + *(m_texmgr.m_tex[iSlot].var_time) = (double)(GetTime() - m_texmgr.m_tex[iSlot].fStartTime); + *(m_texmgr.m_tex[iSlot].var_frame) = (double)(GetFrame() - m_texmgr.m_tex[iSlot].nStartFrame); + *(m_texmgr.m_tex[iSlot].var_fps) = (double)GetFps(); + *(m_texmgr.m_tex[iSlot].var_progress) = (double)m_pState->m_fBlendProgress; + *(m_texmgr.m_tex[iSlot].var_bass) = (double)mysound.imm_rel[0]; + *(m_texmgr.m_tex[iSlot].var_mid) = (double)mysound.imm_rel[1]; + *(m_texmgr.m_tex[iSlot].var_treb) = (double)mysound.imm_rel[2]; + *(m_texmgr.m_tex[iSlot].var_bass_att) = (double)mysound.avg_rel[0]; + *(m_texmgr.m_tex[iSlot].var_mid_att) = (double)mysound.avg_rel[1]; + *(m_texmgr.m_tex[iSlot].var_treb_att) = (double)mysound.avg_rel[2]; + + // evaluate expressions + #ifndef _NO_EXPR_ + if (m_texmgr.m_tex[iSlot].m_codehandle) + { + NSEEL_code_execute(m_texmgr.m_tex[iSlot].m_codehandle); + } + #endif + + bool bKillSprite = (*m_texmgr.m_tex[iSlot].var_done != 0.0); + bool bBurnIn = (*m_texmgr.m_tex[iSlot].var_burn != 0.0); + + // Remember the original backbuffer and zbuffer + LPDIRECT3DSURFACE9 pBackBuffer=NULL;//, pZBuffer=NULL; + lpDevice->GetRenderTarget( 0, &pBackBuffer ); + //lpDevice->GetDepthStencilSurface( &pZBuffer ); + + if (/*bKillSprite &&*/ bBurnIn) + { + // set up to render [from NULL] to VS1 (for burn-in). + + lpDevice->SetTexture(0, NULL); + + IDirect3DSurface9* pNewTarget = NULL; + if (m_lpVS[1]->GetSurfaceLevel(0, &pNewTarget) != D3D_OK) + return; + lpDevice->SetRenderTarget(0, pNewTarget ); + //lpDevice->SetDepthStencilSurface( NULL ); + pNewTarget->Release(); + + lpDevice->SetTexture(0, NULL); + } + + // finally, use the results to draw the sprite. + if (lpDevice->SetTexture(0, m_texmgr.m_tex[iSlot].pSurface) != D3D_OK) + return; + + SPRITEVERTEX v3[4]; + ZeroMemory(v3, sizeof(SPRITEVERTEX)*4); + + /* + int dest_w, dest_h; + { + LPDIRECT3DSURFACE9 pRT; + lpDevice->GetRenderTarget( 0, &pRT ); + + D3DSURFACE_DESC desc; + pRT->GetDesc(&desc); + dest_w = desc.Width; + dest_h = desc.Height; + pRT->Release(); + }*/ + + float x = min(1000.0f, max(-1000.0f, (float)(*m_texmgr.m_tex[iSlot].var_x) * 2.0f - 1.0f )); + float y = min(1000.0f, max(-1000.0f, (float)(*m_texmgr.m_tex[iSlot].var_y) * 2.0f - 1.0f )); + float sx = min(1000.0f, max(-1000.0f, (float)(*m_texmgr.m_tex[iSlot].var_sx) )); + float sy = min(1000.0f, max(-1000.0f, (float)(*m_texmgr.m_tex[iSlot].var_sy) )); + float rot = (float)(*m_texmgr.m_tex[iSlot].var_rot); + int flipx = (*m_texmgr.m_tex[iSlot].var_flipx == 0.0) ? 0 : 1; + int flipy = (*m_texmgr.m_tex[iSlot].var_flipy == 0.0) ? 0 : 1; + float repeatx = min(100.0f, max(0.01f, (float)(*m_texmgr.m_tex[iSlot].var_repeatx) )); + float repeaty = min(100.0f, max(0.01f, (float)(*m_texmgr.m_tex[iSlot].var_repeaty) )); + + int blendmode = min(4, max(0, ((int)(*m_texmgr.m_tex[iSlot].var_blendmode)))); + float r = min(1.0f, max(0.0f, ((float)(*m_texmgr.m_tex[iSlot].var_r)))); + float g = min(1.0f, max(0.0f, ((float)(*m_texmgr.m_tex[iSlot].var_g)))); + float b = min(1.0f, max(0.0f, ((float)(*m_texmgr.m_tex[iSlot].var_b)))); + float a = min(1.0f, max(0.0f, ((float)(*m_texmgr.m_tex[iSlot].var_a)))); + + // set x,y coords + v3[0+flipx].x = -sx; + v3[1-flipx].x = sx; + v3[2+flipx].x = -sx; + v3[3-flipx].x = sx; + v3[0+flipy*2].y = -sy; + v3[1+flipy*2].y = -sy; + v3[2-flipy*2].y = sy; + v3[3-flipy*2].y = sy; + + // first aspect ratio: adjust for non-1:1 images + { + float aspect = m_texmgr.m_tex[iSlot].img_h / (float)m_texmgr.m_tex[iSlot].img_w; + + if (aspect < 1) + for (k=0; k<4; k++) v3[k].y *= aspect; // wide image + else + for (k=0; k<4; k++) v3[k].x /= aspect; // tall image + } + + // 2D rotation + { + float cos_rot = cosf(rot); + float sin_rot = sinf(rot); + for (k=0; k<4; k++) + { + float x2 = v3[k].x*cos_rot - v3[k].y*sin_rot; + float y2 = v3[k].x*sin_rot + v3[k].y*cos_rot; + v3[k].x = x2; + v3[k].y = y2; + } + } + + // translation + for (k=0; k<4; k++) + { + v3[k].x += x; + v3[k].y += y; + } + + // second aspect ratio: normalize to width of screen + { + float aspect = GetWidth() / (float)(GetHeight()); + + if (aspect > 1) + for (k=0; k<4; k++) v3[k].y *= aspect; + else + for (k=0; k<4; k++) v3[k].x /= aspect; + } + + // third aspect ratio: adjust for burn-in + if (bKillSprite && bBurnIn) // final render-to-VS1 + { + float aspect = GetWidth()/(float)(GetHeight()*4.0f/3.0f); + if (aspect < 1.0f) + for (k=0; k<4; k++) v3[k].x *= aspect; + else + for (k=0; k<4; k++) v3[k].y /= aspect; + } + + // finally, flip 'y' for annoying DirectX + //for (k=0; k<4; k++) v3[k].y *= -1.0f; + + // set u,v coords + { + float dtu = 0.5f;// / (float)m_texmgr.m_tex[iSlot].tex_w; + float dtv = 0.5f;// / (float)m_texmgr.m_tex[iSlot].tex_h; + v3[0].tu = -dtu; + v3[1].tu = dtu;///*m_texmgr.m_tex[iSlot].img_w / (float)m_texmgr.m_tex[iSlot].tex_w*/ - dtu; + v3[2].tu = -dtu; + v3[3].tu = dtu;///*m_texmgr.m_tex[iSlot].img_w / (float)m_texmgr.m_tex[iSlot].tex_w*/ - dtu; + v3[0].tv = -dtv; + v3[1].tv = -dtv; + v3[2].tv = dtv;///*m_texmgr.m_tex[iSlot].img_h / (float)m_texmgr.m_tex[iSlot].tex_h*/ - dtv; + v3[3].tv = dtv;///*m_texmgr.m_tex[iSlot].img_h / (float)m_texmgr.m_tex[iSlot].tex_h*/ - dtv; + + // repeat on x,y + for (k=0; k<4; k++) + { + v3[k].tu = (v3[k].tu - 0.0f)*repeatx + 0.5f; + v3[k].tv = (v3[k].tv - 0.0f)*repeaty + 0.5f; + } + } + + // blendmodes src alpha: dest alpha: + // 0 blend r,g,b=modulate a=opacity SRCALPHA INVSRCALPHA + // 1 decal r,g,b=modulate a=modulate D3DBLEND_ONE D3DBLEND_ZERO + // 2 additive r,g,b=modulate a=modulate D3DBLEND_ONE D3DBLEND_ONE + // 3 srccolor r,g,b=no effect a=no effect SRCCOLOR INVSRCCOLOR + // 4 colorkey r,g,b=modulate a=no effect + switch(blendmode) + { + case 0: + default: + // alpha blend + + /* + Q. I am rendering with alpha blending and setting the alpha + of the diffuse vertex component to determine the opacity. + It works when there is no texture set, but as soon as I set + a texture the alpha that I set is no longer applied. Why? + + The problem originates in the texture blending stages, rather + than in the subsequent alpha blending. Alpha can come from + several possible sources. If this has not been specified, + then the alpha will be taken from the texture, if one is selected. + If no texture is selected, then the default will use the alpha + channel of the diffuse vertex component. + + Explicitly specifying the diffuse vertex component as the source + for alpha will insure that the alpha is drawn from the alpha value + you set, whether a texture is selected or not: + + pDevice->SetSamplerState(D3DSAMP_ALPHAOP,D3DTOP_SELECTARG1); + pDevice->SetSamplerState(D3DSAMP_ALPHAARG1,D3DTA_DIFFUSE); + + If you later need to use the texture alpha as the source, set + D3DSAMP_ALPHAARG1 to D3DTA_TEXTURE. + */ + + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + for (k=0; k<4; k++) v3[k].Diffuse = D3DCOLOR_RGBA_01(r,g,b,a); + break; + case 1: + // decal + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + //lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + //lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + for (k=0; k<4; k++) v3[k].Diffuse = D3DCOLOR_RGBA_01(r*a,g*a,b*a,1); + break; + case 2: + // additive + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + for (k=0; k<4; k++) v3[k].Diffuse = D3DCOLOR_RGBA_01(r*a,g*a,b*a,1); + break; + case 3: + // srccolor + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCCOLOR); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); + for (k=0; k<4; k++) v3[k].Diffuse = D3DCOLOR_RGBA_01(1,1,1,1); + break; + case 4: + // color keyed texture: use the alpha value in the texture to + // determine which texels get drawn. + /*lpDevice->SetRenderState(D3DRS_ALPHAREF, 0); + lpDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL); + lpDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); + */ + + lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + // also, smoothly blend this in-between texels: + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + for (k=0; k<4; k++) v3[k].Diffuse = D3DCOLOR_RGBA_01(r,g,b,a); + break; + } + + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (LPVOID)v3, sizeof(SPRITEVERTEX)); + + if (/*bKillSprite &&*/ bBurnIn) // final render-to-VS1 + { + // Change the rendertarget back to the original setup + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderTarget( 0, pBackBuffer ); + //lpDevice->SetDepthStencilSurface( pZBuffer ); + lpDevice->SetTexture(0, m_texmgr.m_tex[iSlot].pSurface); + + // undo aspect ratio changes (that were used to fit it to VS1): + { + float aspect = GetWidth()/(float)(GetHeight()*4.0f/3.0f); + if (aspect < 1.0f) + for (k=0; k<4; k++) v3[k].x /= aspect; + else + for (k=0; k<4; k++) v3[k].y *= aspect; + } + + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (LPVOID)v3, sizeof(SPRITEVERTEX)); + } + + SafeRelease(pBackBuffer); + //SafeRelease(pZBuffer); + + if (bKillSprite) + { + KillSprite(iSlot); + } + + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + } + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + // reset these to the standard safe mode: + lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); +} + +void CPlugin::UvToMathSpace(float u, float v, float* rad, float* ang) +{ + // (screen space = -1..1 on both axes; corresponds to UV space) + // uv space = [0..1] on both axes + // "math" space = what the preset authors are used to: + // upper left = [0,0] + // bottom right = [1,1] + // rad == 1 at corners of screen + // ang == 0 at three o'clock, and increases counter-clockwise (to 6.28). + float px = (u*2-1) * m_fAspectX; // probably 1.0 + float py = (v*2-1) * m_fAspectY; // probably <1 + + *rad = sqrtf(px*px + py*py) / sqrtf(m_fAspectX*m_fAspectX + m_fAspectY*m_fAspectY); + *ang = atan2f(py, px); + if (*ang < 0) + *ang += 6.2831853071796f; +} + +void CPlugin::RestoreShaderParams() +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + int i = 0; + for (i=0; i<2; i++) + { + lpDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);//texaddr); + lpDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);//texaddr); + lpDevice->SetSamplerState(i, D3DSAMP_ADDRESSW, D3DTADDRESS_WRAP);//texaddr); + lpDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(i, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(i, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + } + + for (i=0; i<4; i++) + lpDevice->SetTexture( i, NULL ); + + lpDevice->SetVertexShader(NULL); + //lpDevice->SetVertexDeclaration(NULL); -directx debug runtime complains heavily about this + lpDevice->SetPixelShader(NULL); + +} + +void CPlugin::ApplyShaderParams(CShaderParams* p, LPD3DXCONSTANTTABLE pCT, CState* pState) +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + + //if (p->texbind_vs >= 0) lpDevice->SetTexture( p->texbind_vs , m_lpVS[0] ); + //if (p->texbind_noise >= 0) lpDevice->SetTexture( p->texbind_noise, m_pTexNoise ); + + // bind textures + int i = 0; + for (i=0; i<sizeof(p->m_texture_bindings)/sizeof(p->m_texture_bindings[0]); i++) + { + if (p->m_texcode[i] == TEX_VS) + lpDevice->SetTexture(i, m_lpVS[0]); + else + lpDevice->SetTexture(i, p->m_texture_bindings[i].texptr); + + // also set up sampler stage, if anything is bound here... + if (p->m_texcode[i]==TEX_VS || p->m_texture_bindings[i].texptr) + { + bool bAniso = false; + DWORD HQFilter = bAniso ? D3DTEXF_ANISOTROPIC : D3DTEXF_LINEAR; + DWORD wrap = p->m_texture_bindings[i].bWrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP; + DWORD filter = p->m_texture_bindings[i].bBilinear ? HQFilter : D3DTEXF_POINT; + lpDevice->SetSamplerState(i, D3DSAMP_ADDRESSU, wrap); + lpDevice->SetSamplerState(i, D3DSAMP_ADDRESSV, wrap); + lpDevice->SetSamplerState(i, D3DSAMP_ADDRESSW, wrap); + lpDevice->SetSamplerState(i, D3DSAMP_MAGFILTER, filter); + lpDevice->SetSamplerState(i, D3DSAMP_MINFILTER, filter); + lpDevice->SetSamplerState(i, D3DSAMP_MIPFILTER, filter); + //lpDevice->SetSamplerState(i, D3DSAMP_MAXANISOTROPY, bAniso ? 4 : 1); //FIXME:ANISO + } + + // finally, if it was a blur texture, note that + if (p->m_texcode[i] >= TEX_BLUR1 && p->m_texcode[i] <= TEX_BLUR_LAST) + m_nHighestBlurTexUsedThisFrame = max(m_nHighestBlurTexUsedThisFrame, ((int)p->m_texcode[i] - (int)TEX_BLUR1) + 1); + } + + // bind "texsize_XYZ" params + int N = p->texsize_params.size(); + for (i=0; i<N; i++) + { + TexSizeParamInfo* q = &(p->texsize_params[i]); + pCT->SetVector( lpDevice, q->texsize_param, &D3DXVECTOR4((float)q->w,(float)q->h,1.0f/q->w,1.0f/q->h)); + } + + float time_since_preset_start = GetTime() - pState->GetPresetStartTime(); + float time_since_preset_start_wrapped = time_since_preset_start - (int)(time_since_preset_start/10000)*10000; + float time = GetTime() - m_fStartTime; + float progress = (GetTime() - m_fPresetStartTime) / (m_fNextPresetTime - m_fPresetStartTime); + float mip_x = logf((float)GetWidth())/logf(2.0f); + float mip_y = logf((float)GetWidth())/logf(2.0f); + float mip_avg = 0.5f*(mip_x + mip_y); + float aspect_x = 1; + float aspect_y = 1; + if (GetWidth() > GetHeight()) + aspect_y = GetHeight()/(float)GetWidth(); + else + aspect_x = GetWidth()/(float)GetHeight(); + + float blur_min[3], blur_max[3]; + GetSafeBlurMinMax(pState, blur_min, blur_max); + + // bind float4's + if (p->rand_frame ) pCT->SetVector( lpDevice, p->rand_frame , &m_rand_frame ); + if (p->rand_preset) pCT->SetVector( lpDevice, p->rand_preset, &pState->m_rand_preset ); + D3DXHANDLE* h = p->const_handles; + if (h[0]) pCT->SetVector( lpDevice, h[0], &D3DXVECTOR4( aspect_x, aspect_y, 1.0f/aspect_x, 1.0f/aspect_y )); + if (h[1]) pCT->SetVector( lpDevice, h[1], &D3DXVECTOR4(0, 0, 0, 0 )); + if (h[2]) pCT->SetVector( lpDevice, h[2], &D3DXVECTOR4(time_since_preset_start_wrapped, GetFps(), (float)GetFrame(), progress)); + if (h[3]) pCT->SetVector( lpDevice, h[3], &D3DXVECTOR4(mysound.imm_rel[0], mysound.imm_rel[1], mysound.imm_rel[2], 0.3333f*(mysound.imm_rel[0], mysound.imm_rel[1], mysound.imm_rel[2]) )); + if (h[4]) pCT->SetVector( lpDevice, h[4], &D3DXVECTOR4(mysound.avg_rel[0], mysound.avg_rel[1], mysound.avg_rel[2], 0.3333f*(mysound.avg_rel[0], mysound.avg_rel[1], mysound.avg_rel[2]) )); + if (h[5]) pCT->SetVector( lpDevice, h[5], &D3DXVECTOR4( blur_max[0]-blur_min[0], blur_min[0], blur_max[1]-blur_min[1], blur_min[1] )); + if (h[6]) pCT->SetVector( lpDevice, h[6], &D3DXVECTOR4( blur_max[2]-blur_min[2], blur_min[2], blur_min[0], blur_max[0] )); + if (h[7]) pCT->SetVector( lpDevice, h[7], &D3DXVECTOR4((float)m_nTexSizeX, (float)m_nTexSizeY, 1.0f/(float)m_nTexSizeX, 1.0f/(float)m_nTexSizeY )); + if (h[8]) pCT->SetVector( lpDevice, h[8], &D3DXVECTOR4( 0.5f+0.5f*cosf(time* 0.329f+1.2f), + 0.5f+0.5f*cosf(time* 1.293f+3.9f), + 0.5f+0.5f*cosf(time* 5.070f+2.5f), + 0.5f+0.5f*cosf(time*20.051f+5.4f) + )); + if (h[9]) pCT->SetVector( lpDevice, h[9], &D3DXVECTOR4( 0.5f+0.5f*sinf(time* 0.329f+1.2f), + 0.5f+0.5f*sinf(time* 1.293f+3.9f), + 0.5f+0.5f*sinf(time* 5.070f+2.5f), + 0.5f+0.5f*sinf(time*20.051f+5.4f) + )); + if (h[10]) pCT->SetVector( lpDevice, h[10], &D3DXVECTOR4( 0.5f+0.5f*cosf(time*0.0050f+2.7f), + 0.5f+0.5f*cosf(time*0.0085f+5.3f), + 0.5f+0.5f*cosf(time*0.0133f+4.5f), + 0.5f+0.5f*cosf(time*0.0217f+3.8f) + )); + if (h[11]) pCT->SetVector( lpDevice, h[11], &D3DXVECTOR4( 0.5f+0.5f*sinf(time*0.0050f+2.7f), + 0.5f+0.5f*sinf(time*0.0085f+5.3f), + 0.5f+0.5f*sinf(time*0.0133f+4.5f), + 0.5f+0.5f*sinf(time*0.0217f+3.8f) + )); + if (h[12]) pCT->SetVector( lpDevice, h[12], &D3DXVECTOR4( mip_x, mip_y, mip_avg, 0 )); + if (h[13]) pCT->SetVector( lpDevice, h[13], &D3DXVECTOR4( blur_min[1], blur_max[1], blur_min[2], blur_max[2] )); + + // write q vars + int num_q_float4s = sizeof(p->q_const_handles)/sizeof(p->q_const_handles[0]); + for (i=0; i<num_q_float4s; i++) + { + if (p->q_const_handles[i]) + pCT->SetVector( lpDevice, p->q_const_handles[i], &D3DXVECTOR4( + (float)*pState->var_pf_q[i*4+0], + (float)*pState->var_pf_q[i*4+1], + (float)*pState->var_pf_q[i*4+2], + (float)*pState->var_pf_q[i*4+3] )); + } + + // write matrices + for (i=0; i<20; i++) + { + if (p->rot_mat[i]) + { + D3DXMATRIX mx,my,mz,mxlate,temp; + + pMatrixRotationX(&mx, pState->m_rot_base[i].x + pState->m_rot_speed[i].x*time); + pMatrixRotationY(&my, pState->m_rot_base[i].y + pState->m_rot_speed[i].y*time); + pMatrixRotationZ(&mz, pState->m_rot_base[i].z + pState->m_rot_speed[i].z*time); + pMatrixTranslation(&mxlate, pState->m_xlate[i].x, pState->m_xlate[i].y, pState->m_xlate[i].z); + + pMatrixMultiply(&temp, &mx, &mxlate); + pMatrixMultiply(&temp, &temp, &mz); + pMatrixMultiply(&temp, &temp, &my); + + pCT->SetMatrix(lpDevice, p->rot_mat[i], &temp); + } + } + // the last 4 are totally random, each frame + for (i=20; i<24; i++) + { + if (p->rot_mat[i]) + { + D3DXMATRIX mx,my,mz,mxlate,temp; + + pMatrixRotationX(&mx, FRAND * 6.28f); + pMatrixRotationY(&my, FRAND * 6.28f); + pMatrixRotationZ(&mz, FRAND * 6.28f); + pMatrixTranslation(&mxlate, FRAND, FRAND, FRAND); + + pMatrixMultiply(&temp, &mx, &mxlate); + pMatrixMultiply(&temp, &temp, &mz); + pMatrixMultiply(&temp, &temp, &my); + + pCT->SetMatrix(lpDevice, p->rot_mat[i], &temp); + } + } +} + +void CPlugin::ShowToUser_NoShaders()//int bRedraw, int nPassOverride) +{ + // note: this one has to draw the whole screen! (one big quad) + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, m_lpVS[1]); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetPixelShader( NULL ); + lpDevice->SetFVF( SPRITEVERTEX_FORMAT ); + + // stages 0 and 1 always just use bilinear filtering. + lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + lpDevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); + + // note: this texture stage state setup works for 0 or 1 texture. + // if you set a texture, it will be modulated with the current diffuse color. + // if you don't set a texture, it will just use the current diffuse color. + lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE); + lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE); + lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 ); + lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE ); + lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE); + + float fZoom = 1.0f; + SPRITEVERTEX v3[4]; + ZeroMemory(v3, sizeof(SPRITEVERTEX)*4); + + // extend the poly we draw by 1 pixel around the viewable image area, + // in case the video card wraps u/v coords with a +0.5-texel offset + // (otherwise, a 1-pixel-wide line of the image would wrap at the top and left edges). + float fOnePlusInvWidth = 1.0f + 1.0f/(float)GetWidth(); + float fOnePlusInvHeight = 1.0f + 1.0f/(float)GetHeight(); + v3[0].x = -fOnePlusInvWidth; + v3[1].x = fOnePlusInvWidth; + v3[2].x = -fOnePlusInvWidth; + v3[3].x = fOnePlusInvWidth; + v3[0].y = fOnePlusInvHeight; + v3[1].y = fOnePlusInvHeight; + v3[2].y = -fOnePlusInvHeight; + v3[3].y = -fOnePlusInvHeight; + + //float aspect = GetWidth() / (float)(GetHeight()/(ASPECT)/**4.0f/3.0f*/); + float aspect = GetWidth() / (float)(GetHeight()*m_fInvAspectY/**4.0f/3.0f*/); + float x_aspect_mult = 1.0f; + float y_aspect_mult = 1.0f; + + if (aspect>1) + y_aspect_mult = aspect; + else + x_aspect_mult = 1.0f/aspect; + + for (int n=0; n<4; n++) + { + v3[n].x *= x_aspect_mult; + v3[n].y *= y_aspect_mult; + } + + { + float shade[4][3] = { + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f } }; // for each vertex, then each comp. + + float fShaderAmount = m_pState->m_fShader.eval(GetTime()); + + if (fShaderAmount > 0.001f) + { + for (int i=0; i<4; i++) + { + shade[i][0] = 0.6f + 0.3f*sinf(GetTime()*30.0f*0.0143f + 3 + i*21 + m_fRandStart[3]); + shade[i][1] = 0.6f + 0.3f*sinf(GetTime()*30.0f*0.0107f + 1 + i*13 + m_fRandStart[1]); + shade[i][2] = 0.6f + 0.3f*sinf(GetTime()*30.0f*0.0129f + 6 + i*9 + m_fRandStart[2]); + float max = ((shade[i][0] > shade[i][1]) ? shade[i][0] : shade[i][1]); + if (shade[i][2] > max) max = shade[i][2]; + for (int k=0; k<3; k++) + { + shade[i][k] /= max; + shade[i][k] = 0.5f + 0.5f*shade[i][k]; + } + for (int k=0; k<3; k++) + { + shade[i][k] = shade[i][k]*(fShaderAmount) + 1.0f*(1.0f - fShaderAmount); + } + v3[i].Diffuse = D3DCOLOR_RGBA_01(shade[i][0],shade[i][1],shade[i][2],1); + } + } + + float fVideoEchoZoom = (float)(*m_pState->var_pf_echo_zoom);//m_pState->m_fVideoEchoZoom.eval(GetTime()); + float fVideoEchoAlpha = (float)(*m_pState->var_pf_echo_alpha);//m_pState->m_fVideoEchoAlpha.eval(GetTime()); + int nVideoEchoOrientation = (int) (*m_pState->var_pf_echo_orient) % 4;//m_pState->m_nVideoEchoOrientation; + float fGammaAdj = (float)(*m_pState->var_pf_gamma);//m_pState->m_fGammaAdj.eval(GetTime()); + + if (m_pState->m_bBlending && + m_pState->m_fVideoEchoAlpha.eval(GetTime()) > 0.01f && + m_pState->m_fVideoEchoAlphaOld > 0.01f && + m_pState->m_nVideoEchoOrientation != m_pState->m_nVideoEchoOrientationOld) + { + if (m_pState->m_fBlendProgress < m_fSnapPoint) + { + nVideoEchoOrientation = m_pState->m_nVideoEchoOrientationOld; + fVideoEchoAlpha *= 1.0f - 2.0f*CosineInterp(m_pState->m_fBlendProgress); + } + else + { + fVideoEchoAlpha *= 2.0f*CosineInterp(m_pState->m_fBlendProgress) - 1.0f; + } + } + + if (fVideoEchoAlpha > 0.001f) + { + // video echo + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + + for (int i=0; i<2; i++) + { + fZoom = (i==0) ? 1.0f : fVideoEchoZoom; + + float temp_lo = 0.5f - 0.5f/fZoom; + float temp_hi = 0.5f + 0.5f/fZoom; + v3[0].tu = temp_lo; + v3[0].tv = temp_hi; + v3[1].tu = temp_hi; + v3[1].tv = temp_hi; + v3[2].tu = temp_lo; + v3[2].tv = temp_lo; + v3[3].tu = temp_hi; + v3[3].tv = temp_lo; + + // flipping + if (i==1) + { + for (int j=0; j<4; j++) + { + if (nVideoEchoOrientation % 2) + v3[j].tu = 1.0f - v3[j].tu; + if (nVideoEchoOrientation >= 2) + v3[j].tv = 1.0f - v3[j].tv; + } + } + + float mix = (i==1) ? fVideoEchoAlpha : 1.0f - fVideoEchoAlpha; + for (int k=0; k<4; k++) + v3[k].Diffuse = D3DCOLOR_RGBA_01(mix*shade[k][0],mix*shade[k][1],mix*shade[k][2],1); + + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + if (i==0) + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + } + + if (fGammaAdj > 0.001f) + { + // draw layer 'i' a 2nd (or 3rd, or 4th...) time, additively + int nRedraws = (int)(fGammaAdj - 0.0001f); + float gamma; + + for (int nRedraw=0; nRedraw < nRedraws; nRedraw++) + { + if (nRedraw == nRedraws-1) + gamma = fGammaAdj - (int)(fGammaAdj - 0.0001f); + else + gamma = 1.0f; + + for (int k=0; k<4; k++) + v3[k].Diffuse = D3DCOLOR_RGBA_01(gamma*mix*shade[k][0],gamma*mix*shade[k][1],gamma*mix*shade[k][2],1); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + } + } + } + } + else + { + // no video echo + v3[0].tu = 0; v3[1].tu = 1; v3[2].tu = 0; v3[3].tu = 1; + v3[0].tv = 1; v3[1].tv = 1; v3[2].tv = 0; v3[3].tv = 0; + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + + // draw it iteratively, solid the first time, and additively after that + int nPasses = (int)(fGammaAdj - 0.001f) + 1; + float gamma; + + for (int nPass=0; nPass < nPasses; nPass++) + { + if (nPass == nPasses - 1) + gamma = fGammaAdj - (float)nPass; + else + gamma = 1.0f; + + for (int k=0; k<4; k++) + v3[k].Diffuse = D3DCOLOR_RGBA_01(gamma*shade[k][0],gamma*shade[k][1],gamma*shade[k][2],1); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + if (nPass==0) + { + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + } + } + } + + SPRITEVERTEX v3[4]; + ZeroMemory(v3, sizeof(SPRITEVERTEX)*4); + float fOnePlusInvWidth = 1.0f + 1.0f/(float)GetWidth(); + float fOnePlusInvHeight = 1.0f + 1.0f/(float)GetHeight(); + v3[0].x = -fOnePlusInvWidth; + v3[1].x = fOnePlusInvWidth; + v3[2].x = -fOnePlusInvWidth; + v3[3].x = fOnePlusInvWidth; + v3[0].y = fOnePlusInvHeight; + v3[1].y = fOnePlusInvHeight; + v3[2].y = -fOnePlusInvHeight; + v3[3].y = -fOnePlusInvHeight; + for (int i=0; i<4; i++) v3[i].Diffuse = D3DCOLOR_RGBA_01(1,1,1,1); + + if (*m_pState->var_pf_brighten && + (GetCaps()->SrcBlendCaps & D3DPBLENDCAPS_INVDESTCOLOR ) && + (GetCaps()->DestBlendCaps & D3DPBLENDCAPS_DESTCOLOR) + ) + { + // square root filter + + //lpDevice->SetRenderState(D3DRS_COLORVERTEX, FALSE); //? + //lpDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); //? + + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + + // first, a perfect invert + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + // then modulate by self (square it) + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + // then another perfect invert + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + } + + if (*m_pState->var_pf_darken && + (GetCaps()->DestBlendCaps & D3DPBLENDCAPS_DESTCOLOR) + ) + { + // squaring filter + + //lpDevice->SetRenderState(D3DRS_COLORVERTEX, FALSE); //? + //lpDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); //? + + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_DESTCOLOR); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + //lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR); + //lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + //lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + } + + if (*m_pState->var_pf_solarize && + (GetCaps()->SrcBlendCaps & D3DPBLENDCAPS_DESTCOLOR ) && + (GetCaps()->DestBlendCaps & D3DPBLENDCAPS_INVDESTCOLOR) + ) + { + //lpDevice->SetRenderState(D3DRS_COLORVERTEX, FALSE); //? + //lpDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); //? + + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVDESTCOLOR); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + } + + if (*m_pState->var_pf_invert && + (GetCaps()->SrcBlendCaps & D3DPBLENDCAPS_INVDESTCOLOR ) + ) + { + //lpDevice->SetRenderState(D3DRS_COLORVERTEX, FALSE); //? + //lpDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT); //? + + lpDevice->SetTexture(0, NULL); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + + lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (void*)v3, sizeof(SPRITEVERTEX)); + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + } +} + +void CPlugin::ShowToUser_Shaders(int nPass, bool bAlphaBlend, bool bFlipAlpha, bool bCullTiles, bool bFlipCulling)//int bRedraw, int nPassOverride, bool bFlipAlpha) +{ + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + //lpDevice->SetTexture(0, m_lpVS[1]); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( MYVERTEX_FORMAT ); + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + float fZoom = 1.0f; + + float aspect = GetWidth() / (float)(GetHeight()*m_fInvAspectY/**4.0f/3.0f*/); + float x_aspect_mult = 1.0f; + float y_aspect_mult = 1.0f; + + if (aspect>1) + y_aspect_mult = aspect; + else + x_aspect_mult = 1.0f/aspect; + + // hue shader + float shade[4][3] = { + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f } }; // for each vertex, then each comp. + + float fShaderAmount = 1;//since we don't know if shader uses it or not! m_pState->m_fShader.eval(GetTime()); + + if (fShaderAmount > 0.001f || m_pState->m_bBlending) + { + // pick 4 colors for the 4 corners + for (int i=0; i<4; i++) + { + shade[i][0] = 0.6f + 0.3f*sinf(GetTime()*30.0f*0.0143f + 3 + i*21 + m_fRandStart[3]); + shade[i][1] = 0.6f + 0.3f*sinf(GetTime()*30.0f*0.0107f + 1 + i*13 + m_fRandStart[1]); + shade[i][2] = 0.6f + 0.3f*sinf(GetTime()*30.0f*0.0129f + 6 + i*9 + m_fRandStart[2]); + float max = ((shade[i][0] > shade[i][1]) ? shade[i][0] : shade[i][1]); + if (shade[i][2] > max) max = shade[i][2]; + for (int k=0; k<3; k++) + { + shade[i][k] /= max; + shade[i][k] = 0.5f + 0.5f*shade[i][k]; + } + // note: we now pass the raw hue shader colors down; the shader can only use a certain % if it wants. + //for (k=0; k<3; k++) + // shade[i][k] = shade[i][k]*(fShaderAmount) + 1.0f*(1.0f - fShaderAmount); + //m_comp_verts[i].Diffuse = D3DCOLOR_RGBA_01(shade[i][0],shade[i][1],shade[i][2],1); + } + + // interpolate the 4 colors & apply to all the verts + for (int j=0; j<FCGSY; j++) + { + for (int i=0; i<FCGSX; i++) + { + MYVERTEX* p = &m_comp_verts[i + j*FCGSX]; + float x = p->x*0.5f + 0.5f; + float y = p->y*0.5f + 0.5f; + + float col[3] = { 1, 1, 1 }; + if (fShaderAmount > 0.001f) + { + for (int c=0; c<3; c++) + col[c] = shade[0][c]*( x)*( y) + + shade[1][c]*(1-x)*( y) + + shade[2][c]*( x)*(1-y) + + shade[3][c]*(1-x)*(1-y); + } + + // TO DO: improve interp here? + // TO DO: during blend, only send the triangles needed + + // if blending, also set up the alpha values - pull them from the alphas used for the Warped Blit + double alpha = 1; + if (m_pState->m_bBlending) + { + x *= (m_nGridX + 1); + y *= (m_nGridY + 1); + x = max(min(x,m_nGridX-1),0); + y = max(min(y,m_nGridY-1),0); + int nx = (int)x; + int ny = (int)y; + double dx = x - nx; + double dy = y - ny; + double alpha00 = (m_verts[(ny )*(m_nGridX+1) + (nx )].Diffuse >> 24); + double alpha01 = (m_verts[(ny )*(m_nGridX+1) + (nx+1)].Diffuse >> 24); + double alpha10 = (m_verts[(ny+1)*(m_nGridX+1) + (nx )].Diffuse >> 24); + double alpha11 = (m_verts[(ny+1)*(m_nGridX+1) + (nx+1)].Diffuse >> 24); + alpha = alpha00*(1-dx)*(1-dy) + + alpha01*( dx)*(1-dy) + + alpha10*(1-dx)*( dy) + + alpha11*( dx)*( dy); + alpha /= 255.0f; + //if (bFlipAlpha) + // alpha = 1-alpha; + + //alpha = (m_verts[y*(m_nGridX+1) + x].Diffuse >> 24) / 255.0f; + } + p->Diffuse = D3DCOLOR_RGBA_01(col[0],col[1],col[2],alpha); + } + } + } + + int nAlphaTestValue = 0; + if (bFlipCulling) + nAlphaTestValue = 1-nAlphaTestValue; + + if (bAlphaBlend) + { + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + if (bFlipAlpha) + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVSRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCALPHA); + } + else + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + } + } + else + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + // Now do the final composite blit, fullscreen; + // or do it twice, alpha-blending, if we're blending between two sets of shaders. + + int pass = nPass; + { + // PASS 0: draw using *blended per-vertex motion vectors*, but with the OLD comp shader. + // PASS 1: draw using *blended per-vertex motion vectors*, but with the NEW comp shader. + PShaderInfo* si = (pass==0) ? &m_OldShaders.comp : &m_shaders.comp; + CState* state = (pass==0) ? m_pOldState : m_pState; + + lpDevice->SetVertexDeclaration(m_pMyVertDecl); + lpDevice->SetVertexShader(m_fallbackShaders_vs.comp.ptr); + lpDevice->SetPixelShader (si->ptr); + + ApplyShaderParams( &(si->params), si->CT, state ); + + // Hurl the triangles at the video card. + // We're going to un-index it, so that we don't stress any crappy (AHEM intel g33) + // drivers out there. Not a big deal - only ~800 polys / 24kb of data. + // If we're blending, we'll skip any polygon that is all alpha-blended out. + // This also respects the MaxPrimCount limit of the video card. + MYVERTEX tempv[1024 * 3]; + int primCount = (FCGSX-2)*(FCGSY-2)*2; // although, some might not be drawn! + int max_prims_per_batch = min( GetCaps()->MaxPrimitiveCount, (sizeof(tempv)/sizeof(tempv[0]))/3) - 4; + int src_idx = 0; + while (src_idx < primCount*3) + { + int prims_queued = 0; + int i=0; + while (prims_queued < max_prims_per_batch && src_idx < primCount*3) + { + // copy 3 verts + for (int j=0; j<3; j++) + tempv[i++] = m_comp_verts[ m_comp_indices[src_idx++] ]; + if (bCullTiles) + { + DWORD d1 = (tempv[i-3].Diffuse >> 24); + DWORD d2 = (tempv[i-2].Diffuse >> 24); + DWORD d3 = (tempv[i-1].Diffuse >> 24); + bool bIsNeeded; + if (nAlphaTestValue) + bIsNeeded = ((d1 & d2 & d3) < 255);//(d1 < 255) || (d2 < 255) || (d3 < 255); + else + bIsNeeded = ((d1|d2|d3) > 0);//(d1 > 0) || (d2 > 0) || (d3 > 0); + if (!bIsNeeded) + i -= 3; + else + prims_queued++; + } + else + prims_queued++; + } + if (prims_queued > 0) + lpDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, prims_queued, tempv, sizeof(MYVERTEX) ); + } + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + RestoreShaderParams(); +} + +void CPlugin::ShowSongTitleAnim(int w, int h, float fProgress) +{ + int i,x,y; + + if (!m_lpDDSTitle) // this *can* be NULL, if not much video mem! + return; + + LPDIRECT3DDEVICE9 lpDevice = GetDevice(); + if (!lpDevice) + return; + + lpDevice->SetTexture(0, m_lpDDSTitle); + lpDevice->SetVertexShader( NULL ); + lpDevice->SetFVF( SPRITEVERTEX_FORMAT ); + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + + SPRITEVERTEX v3[128]; + ZeroMemory(v3, sizeof(SPRITEVERTEX)*128); + + if (m_supertext.bIsSongTitle) + { + // positioning: + float fSizeX = 50.0f / (float)m_supertext.nFontSizeUsed * powf(1.5f, m_supertext.fFontSize - 2.0f); + float fSizeY = fSizeX * m_nTitleTexSizeY/(float)m_nTitleTexSizeX;// * m_nWidth/(float)m_nHeight; + + if (fSizeX > 0.88f) + { + fSizeY *= 0.88f/fSizeX; + fSizeX = 0.88f; + } + + //fixme + if (fProgress < 1.0f)//(w!=h) // regular render-to-backbuffer + { + //float aspect = w/(float)(h*4.0f/3.0f); + //fSizeY *= aspect; + } + else // final render-to-VS0 + { + //float aspect = GetWidth()/(float)(GetHeight()*4.0f/3.0f); + //if (aspect < 1.0f) + //{ + // fSizeX *= aspect; + // fSizeY *= aspect; + //} + + //fSizeY *= -1; + } + + //if (fSizeX > 0.92f) fSizeX = 0.92f; + //if (fSizeY > 0.92f) fSizeY = 0.92f; + i = 0; + float vert_clip = VERT_CLIP;//1.0f;//0.45f; // warning: visible clipping has been observed at 0.4! + for (y=0; y<8; y++) + { + for (x=0; x<16; x++) + { + v3[i].tu = x/15.0f; + v3[i].tv = (y/7.0f - 0.5f)*vert_clip + 0.5f; + v3[i].x = (v3[i].tu*2.0f - 1.0f)*fSizeX; + v3[i].y = (v3[i].tv*2.0f - 1.0f)*fSizeY; + if (fProgress >= 1.0f) + v3[i].y += 1.0f/(float)m_nTexSizeY; //this is a pretty hacky guess @ getting it to align... + i++; + } + } + + // warping + float ramped_progress = max(0.0f, 1-fProgress*1.5f); + float t2 = powf(ramped_progress, 1.8f)*1.3f; + for (y=0; y<8; y++) + { + for (x=0; x<16; x++) + { + i = y*16+x; + v3[i].x += t2*0.070f*sinf(GetTime()*0.31f + v3[i].x*0.39f - v3[i].y*1.94f); + v3[i].x += t2*0.044f*sinf(GetTime()*0.81f - v3[i].x*1.91f + v3[i].y*0.27f); + v3[i].x += t2*0.061f*sinf(GetTime()*1.31f + v3[i].x*0.61f + v3[i].y*0.74f); + v3[i].y += t2*0.061f*sinf(GetTime()*0.37f + v3[i].x*1.83f + v3[i].y*0.69f); + v3[i].y += t2*0.070f*sinf(GetTime()*0.67f + v3[i].x*0.42f - v3[i].y*1.39f); + v3[i].y += t2*0.087f*sinf(GetTime()*1.07f + v3[i].x*3.55f + v3[i].y*0.89f); + } + } + + // scale down over time + float scale = 1.01f/(powf(fProgress, 0.21f) + 0.01f); + for (i=0; i<128; i++) + { + v3[i].x *= scale; + v3[i].y *= scale; + } + } + else + { + // positioning: + float fSizeX = (float)m_nTexSizeX/1024.0f * 100.0f / (float)m_supertext.nFontSizeUsed * powf(1.033f, m_supertext.fFontSize - 50.0f); + float fSizeY = fSizeX * m_nTitleTexSizeY/(float)m_nTitleTexSizeX; + + //fixme + if (fProgress < 1.0f)//w!=h) // regular render-to-backbuffer + { + //float aspect = w/(float)(h*4.0f/3.0f); + //fSizeY *= aspect; + } + else // final render-to-VS0 + { + //float aspect = GetWidth()/(float)(GetHeight()*4.0f/3.0f); + //if (aspect < 1.0f) + //{ + // fSizeX *= aspect; + // fSizeY *= aspect; + //} + //fSizeY *= -1; + } + + //if (fSize > 0.92f) fSize = 0.92f; + i = 0; + float vert_clip = VERT_CLIP;//0.67f; // warning: visible clipping has been observed at 0.5 (for very short strings) and even 0.6 (for wingdings)! + for (y=0; y<8; y++) + { + for (x=0; x<16; x++) + { + v3[i].tu = x/15.0f; + v3[i].tv = (y/7.0f - 0.5f)*vert_clip + 0.5f; + v3[i].x = (v3[i].tu*2.0f - 1.0f)*fSizeX; + v3[i].y = (v3[i].tv*2.0f - 1.0f)*fSizeY; + if (fProgress >= 1.0f) + v3[i].y += 1.0f/(float)m_nTexSizeY; //this is a pretty hacky guess @ getting it to align... + i++; + } + } + + // apply 'growth' factor and move to user-specified (x,y) + //if (fabsf(m_supertext.fGrowth-1.0f) > 0.001f) + { + float t = (1.0f)*(1-fProgress) + (fProgress)*(m_supertext.fGrowth); + float dx = (m_supertext.fX*2-1); + float dy = (m_supertext.fY*2-1); + if (w!=h) // regular render-to-backbuffer + { + float aspect = w/(float)(h*4.0f/3.0f); + if (aspect < 1) + dx /= aspect; + else + dy *= aspect; + } + + for (i=0; i<128; i++) + { + // note: (x,y) are in (-1,1) range, but m_supertext.f{X|Y} are in (0..1) range + v3[i].x = (v3[i].x)*t + dx; + v3[i].y = (v3[i].y)*t + dy; + } + } + } + + WORD indices[7*15*6]; + i = 0; + for (y=0; y<7; y++) + { + for (x=0; x<15; x++) + { + indices[i++] = y*16 + x; + indices[i++] = y*16 + x + 1; + indices[i++] = y*16 + x + 16; + indices[i++] = y*16 + x + 1; + indices[i++] = y*16 + x + 16; + indices[i++] = y*16 + x + 17; + } + } + + // final flip on y + //for (i=0; i<128; i++) + // v3[i].y *= -1.0f; + for (i=0; i<128; i++) + //v3[i].y /= ASPECT_Y; + v3[i].y *= m_fInvAspectY; + + for (int it=0; it<2; it++) + { + // colors + { + float t; + + if (m_supertext.bIsSongTitle) + t = powf(fProgress, 0.3f)*1.0f; + else + t = CosineInterp(min(1.0f, (fProgress/m_supertext.fFadeTime))); + + if (it==0) + v3[0].Diffuse = D3DCOLOR_RGBA_01(t,t,t,t); + else + v3[0].Diffuse = D3DCOLOR_RGBA_01(t*m_supertext.nColorR/255.0f,t*m_supertext.nColorG/255.0f,t*m_supertext.nColorB/255.0f,t); + + for (i=1; i<128; i++) + v3[i].Diffuse = v3[0].Diffuse; + } + + // nudge down & right for shadow, up & left for solid text + float offset_x = 0, offset_y = 0; + switch(it) + { + case 0: + offset_x = 2.0f/(float)m_nTitleTexSizeX; + offset_y = 2.0f/(float)m_nTitleTexSizeY; + break; + case 1: + offset_x = -4.0f/(float)m_nTitleTexSizeX; + offset_y = -4.0f/(float)m_nTitleTexSizeY; + break; + } + + for (i=0; i<128; i++) + { + v3[i].x += offset_x; + v3[i].y += offset_y; + } + + if (it == 0) + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);//SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR); + } + else + { + lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);//SRCALPHA); + lpDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + } + + lpDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, 128, 15*7*6/3, indices, D3DFMT_INDEX16, v3, sizeof(SPRITEVERTEX)); + } + + lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); +}
\ No newline at end of file |