Programming 2D Games

The official forum for "Programming 2D Games" the book by: Charles Kelly

It is currently Fri Feb 28, 2020 7:53 am

All times are UTC




Post new topic Reply to topic  [ 1 post ] 
Author Message
PostPosted: Thu Jan 28, 2016 8:58 am 
Offline

Joined: Fri Oct 19, 2012 5:50 pm
Posts: 32
Since the topic of 2D HLSL might make the next version of the book, I'd like to post a little of what I've learned along the way as maybe a jumping off point, and also an aspect I'm struggling with at the moment, which is doing multiple passes.

Here's my wrapper for the effect:
Code:
struct EFFECT
{
   LPD3DXEFFECT pEffect;
   UINT passes;
   CONST CHAR * filename;

   EFFECT();
   ~EFFECT();

   void OnLostDevice();
   void Release();
};


This EFFECT struct is nested in my graphics engine. It's not meant to be functional. The graphics engine does the work with it.

Here's the game engine code that creates the effect:
Code:
HRESULT GAMEENGINE::DIRECTX::LoadEffectFromFile(EFFECT * e, LPCTSTR file)
{
   HRESULT hr;
   hr = D3DXCreateEffectFromFile(
      d3ddev,
      file,
      0,
      0,
      0,
      0,
      &e->pEffect,
      &d3derrorlog);

   if(FAILED(hr)) return hr;

   e->filename = file;
   D3DXTECHNIQUE_DESC d3dtechDesc;
   e->pEffect->FindNextValidTechnique(NULL, &d3dtechnique);
   e->pEffect->GetTechniqueDesc(d3dtechnique,&d3dtechDesc);
   e->passes = d3dtechDesc.Passes;
   //e->pEffect->SetTechnique(d3dtechnique);

   return hr;
}


Storing the number of passes turned out to be unnecessary, because you can pluck that information out when you need it by passing an input parameter to Begin(), but I'm leaving it for now in case I decide I want to do it this way later. I commented out setting the technique because I think that FindNextValidTechnique() handles that itself. The d3dtechnique and d3derrorlog variables being used are members of my DirectX wrapped class.

Here's the code that toggles the effect on and off for testing. It has some oddities to it that I will try to explain.
Code:
if(pE&&pE->pEffect)
{
   pE->Release();
}
else
{
   HRESULT hr;
   int bailout = 50;
   do
   {
      hr = engine.LoadEffectFromFile(pE,pE->filename);
               
      if(hr!=S_OK)
      {
         LPVOID errors = engine.directx.d3derrorlog->GetBufferPointer();
         MessageBox(NULL, (const char*)errors, "Shader Compile Error...", MB_OK | MB_ICONEXCLAMATION);
         //always put a breakpoint here
         bailout--;
      }
   }
   while(hr!=S_OK&&bailout);
}


This bit with the "bailout" is a little weird. It should never really be a factor. It's just there to keep the loop from becoming infinite in some unforeseen situation. If a loop CAN become infinite, it will. I still live in constant fear of getting stuck in my pathfinding loop. But it hasn't happened in like a year.

The part with the errors and the MessageBox is EXTREMELY USEFUL! If you're new to shaders, that is. Because VC++ is not going to know or care when you do something illegal in your fx file. And when your shader fails, all you're going to know is that it failed unless you do something like this. This MessageBox is not for your user, it's for you, the developer! See the nice thing about this setup is that I can fix or change things in the shader code on the fly, without reloading the whole game. Very useful.

Here is the code that draws a sprite with the effect applied:
Code:
void GAMEENGINE::DIRECTX::DrawSprite(SPRITE * pSprite, INT frameIndex, float x, float y, D3DCOLOR colorFilter, BOOL cameraOffset, EFFECT * pFX)
{
   if(!pSprite) return;
   if(frameIndex<0||frameIndex>=pSprite->numFrames) return;

   SPRITE::FRAMEINFO * pFrame = &pSprite->frames[frameIndex];
   D3DXVECTOR2 offset((pFrame->innerOffsetX-pFrame->regX)*spriteScale.x,(pFrame->innerOffsetY-pFrame->regY)*spriteScale.y);

   spriteCenter = D3DXVECTOR2(pFrame->pivotX*spriteScale.x,pFrame->pivotY*spriteScale.y);
   spriteTranslation = D3DXVECTOR2(x*d3d_worldScale.x,y*d3d_worldScale.y) + offset + d3d_worldTranslation;
   if(cameraOffset) spriteTranslation -= d3d_camera;

   D3DXMatrixTransformation2D(&spriteTransformation,
                        NULL,
                        0.0,
                        &spriteScale,
                        &spriteCenter,
                        spriteRotation,
                        &spriteTranslation);

   d3dsprt->SetTransform(&spriteTransformation);

   if(!pFX||!pFX->pEffect)
   {
      d3dsprt->Draw(pSprite->tex, &pFrame->frameRect, NULL, NULL, colorFilter);
      return;
   }

   d3dsprt->Flush();
   
   pFX->pEffect->Begin(NULL, NULL);

       for(UINT i=0;i<pFX->passes;i++)
       {
         pFX->pEffect->BeginPass(i);

         d3dsprt->Draw(pSprite->tex, &pFrame->frameRect, NULL, NULL, colorFilter);

         pFX->pEffect->EndPass();
       }

   d3dsprt->Flush();

     pFX->pEffect->End();
}


Why the Flush()es? Well I draw a lot of sprites, and I don't want the effect to apply to all of them. Without the Flush(), I can draw 99 sprites with no effect, then 1 sprite with an effect, and the effect will be applied to all 100 sprites, regardless of when I Begin() and End() the effect. I want the flexibility to apply an effect to only certain sprites, or different effects to different sprites, etc, and this gives me that. Most of my drawing is done without effects, so I Flush() at the beginning and end of the effect so that the other draws can remain effect agnostic.

This code works for me fine expect for the passes. If I use multiple passes, all I ever get is the final pass. And it doesn't matter where I put the second Flush(). In the loop, out of the loop, before or after the EndPass(). No matter what I do I can't figure out how to apply multiple passes yet. As a workaround, I managed to find some optimizations that allowed me to put everything I needed in a single pass, but this is not going to be an option always. I'd like to make this work for multiple passes, but if I can't, I guess I can work around it.

I'm open to the idea that maybe I'm setting things up the wrong way in other places as well. As I have said, there is very little information available on the topic that I have found.

We have a facebook page for our little 2-person game operation where I recently posted a video demonstrating a simple shader being applied to our main character. Soon I'm planning to post a follow-up which shows the full shader in operation. https://www.facebook.com/conundrumcove/


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 1 post ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group