#include "stdafx.h" #include "ShaderManagerCG.h" #include "CKGL15RasterizerContext.h" #include "ShaderDescriptorCG.h" #include "default_fxCG.cpp" #include #if defined(MULTIPLESHADERMANAGER) unsigned int mCppLib_PreProcess(const XString& xsIn,const char *opt,XString& xsOut,XString& PreProcErr); BOOL DoWinExec(const XString& command); #else #ifndef macintosh #include "mCppLib.h" #endif #endif /*************************************************************************** ____ _ _ __ __ / ___|| |__ __ _ __| | ___ _ __ | \/ | __ _ _ __ __ _ __ _ ___ _ __ \___ \| '_ \ / _` |/ _` |/ _ \ '__| | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| ___) | | | | (_| | (_| | __/ | | | | | (_| | | | | (_| | (_| | __/ | |____/|_| |_|\__,_|\__,_|\___|_| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| |___/ ***************************************************************************/ XArray ShaderManagerCG::m_CGContextToRasterizer; #ifdef _DEBUG static void errorCallback() { static bool already_called = false; if (already_called) { return; } already_called = true; theShaderManager->m_Context->OutputToConsole((char*)cgGetErrorString(cgGetError())); if (theShaderManager->m_CgContext.Size()) { const char* listing = cgGetLastListing(theShaderManager->m_CgContext[0]); if (listing) theShaderManager->m_Context->OutputToConsole((char*)listing); } already_called = false; } #endif //----------------------------------------------------------------------------- ShaderManagerCG::ShaderManagerCG(CKContext *Context) : #if defined(MULTIPLESHADERMANAGER) ShaderManagerInterface(Context), m_NextCompID(NULL), #else CKShaderManager(Context,ShaderManagerGUID,"Shader Manager"), m_NextCompID(1), #endif m_ReplacementMode( ShaderReplacementMode_RenameNew ), m_NMOSavingMode( ShaderNMOSavingMode_SaveOnlyUsedShaders ), m_TangentSpaceCreationMode( ShaderTSCreationMode_DontWeldTangentSpaces ) { #if !defined(MULTIPLESHADERMANAGER) Context->RegisterNewManager(this); #endif #ifdef macintosh #warning mac must compile external file ? #else char filename[512]; GetModuleFileName(NULL, filename, 512); int c = strlen(filename); while (c >= 0 && filename[c] != '\\') --c; filename[c] = '\0'; m_FXCCommand = filename; m_FXCCommand += "\\cgc.exe"; #endif m_CurrentPassCount = 0; m_CurrentPassIndex = 0; for (int i = 0 ; iGetRasterizerContext()->m_ShaderInUse) { return; } rc->GetRasterizerContext()->DisableAllTextureStages(); m_UsedTextureStageBits = 0; #ifdef _DEBUG for (int domain = 0; domain < NUMBER_OF_CG_DOMAIN; ++domain) { XASSERT(m_CurrentCGProgram[domain] == NULL); XASSERT(m_CurrentProfile[domain] == CG_PROFILE_UNKNOWN); } #endif CKRasterizerContext *rstc = rc->GetRasterizerContext(); rstc->SetTextureStageState(0, CKRST_TSS_OP, CKRST_TOP_MODULATE); rstc->SetTextureStageState(0, CKRST_TSS_ARG1, CKRST_TA_TEXTURE); rstc->SetTextureStageState(0, CKRST_TSS_ARG2, CKRST_TA_CURRENT); rstc->SetTextureStageState(0, CKRST_TSS_AOP, CKRST_TOP_SELECTARG1); rstc->SetTextureStageState(0, CKRST_TSS_AARG1, CKRST_TA_TEXTURE); rstc->SetTextureStageState(0, CKRST_TSS_AARG2, CKRST_TA_CURRENT); ((VCKGL15Rasterizer::CKGLRasterizerContext*) rstc)->BackupLightSetup(); // Tell the rasterizer context we're in shader mode rstc->m_ShaderInUse = TRUE; m_DefaultShader = GetDefaultShaderI(); m_DefaultShader->Begin( rc ); m_DefaultShader->BeginPass( 0, rc ); //rc->GetRasterizerContext()->DisableAllTextureStages(); // m_DefaultShader->EndPass( rc ); } //----------------------------------------------------------------------------- void ShaderManagerCG::EndShaders( CKRenderContext* rc ) { //--- Flush Rasterizer Cache to make sure all states are reset CKRasterizerContext* rstc = rc->GetRasterizerContext(); if (!rstc) { return; } if (!rstc->m_ShaderInUse) { return; } m_DefaultShader->EndPass( rc ); m_DefaultShader->End( rc ); ClearModifiedRenderStates(); // for safety DeactivateProgram(CG_VERTEX_DOMAIN); DeactivateProgram(CG_FRAGMENT_DOMAIN); DeactivateProgram(CG_GEOMETRY_DOMAIN); // Tell the rasterizer context we're in not shader mode rstc->m_ShaderInUse = FALSE; DisableTextureStages(rstc, 0); rstc->FlushCaches(); rstc->DisableAllTextureStages(); ((VCKGL15Rasterizer::CKGLRasterizerContext*) rstc)->RestoreLightSetup(); } //----------------------------------------------------------------------------- int ShaderManagerCG::GetSemanticIndexFromString( XString& iStr ) { return m_ShaderDescriptorManager.GetSemanticIndexFromString( iStr ); } //----------------------------------------------------------------------------- const XClassArray& ShaderManagerCG::GetSemanticOriginalNames() { return m_ShaderDescriptorManager.GetSemanticOriginalNames(); } //----------------------------------------------------------------------------- void ShaderManagerCG::GetSemanticDesc( int iSemIndex, XString*& oSemDesc ) { m_ShaderDescriptorManager.GetSemanticDesc( iSemIndex, oSemDesc ); } //----------------------------------------------------------------------------- const XClassArray& ShaderManagerCG::GetAnnotationOriginalNames() { return m_ShaderDescriptorManager.GetAnnotationOriginalNames(); } //----------------------------------------------------------------------------- CKERROR ShaderManagerCG::OnCKEnd() { CKVariableManager* vm = m_Context->GetVariableManager(); if( vm ){ vm->UnBind( "Shader/When Loading Shader With Same Name" ); vm->UnBind( "Shader/When Saving a NMO" ); vm->UnBind( "Shader/When Building Tangent Spaces" ); } return CK_OK; } //----------------------------------------------------------------------------- CKERROR ShaderManagerCG::OnCKInit() { #if !defined(MULTIPLESHADERMANAGER) //--- Register Shader Global Variables to the Variable Manager CKVariableManager* vm = m_Context->GetVariableManager(); vm->Bind( "Shader/When Loading Shader With Same Name", &m_ReplacementMode, ShaderReplacementMode_RenameNew, VxVar::COMPOSITIONBOUND, "enum:0=Rename New;1=Replace Old By New;2=Keep Old And Dont Use New", "When loading a shader which name matches an existing one but content don't, then this variable is used to tell the Shader Manager what to do."); vm->Bind( "Shader/When Saving a NMO", &m_NMOSavingMode, ShaderNMOSavingMode_SaveOnlyUsedShaders, VxVar::COMPOSITIONBOUND, "enum:0=Save All Shaders;1=Save Only Used Shaders;2=Don't Save Any Shader", "When saving a NMO file this variable tells the Shader Manager what to do with existing shaders.\nNote: a shader is considered as being used if it is used by a material, or referenced by a parameter.\nNote: When saving the composition (CMO) all shaders are saved."); vm->Bind( "Shader/When Building Tangent Spaces", &m_TangentSpaceCreationMode, ShaderTSCreationMode_DontWeldTangentSpaces, VxVar::COMPOSITIONBOUND, "enum:0=Dont Weld Tangent Spaces;1=Weld Tangent Spaces;", "When tangent spaces are built on a mesh this variable tells the Shader Manager how tangent spaces should be generated\nNote: if one is using Normal Maps generated with 3dsmax 7 then Welding Tangent Spaces should be used."); #endif // MULTIPLESHADERMANAGER //--- Render Options Used in PostClearAll m_RenderOptionsBeforePlay = CK_RENDER_USECURRENTSETTINGS; //--- Web player special case ///// If there's already a render context, it means ///// the sm->OnRenderContextCreated(this) hasn't been called ///// as the ShaderManagerCG was not created yet. ///// That's why we must call it here, if it already exist. ///// Note: that is the case when first downloading the shader.dll ///// in the web player CKRenderManager* rm = m_Context->GetRenderManager(); if( !rm ) return CK_OK; CKRenderContext* rc = rm->GetRenderContext(0); if( rc ){ OnRenderContextCreated( rc ); OnRasterizerEvent( CKRST_EVENT_CREATE, rc ); } //--- Register All Stuffs Needed for ShaderDescriptorCG m_ShaderDescriptorManager.Init( m_Context ); return CK_OK; } //----------------------------------------------------------------------------- CKERROR ShaderManagerCG::OnRasterizerEvent(CKRST_EVENTS Event, CKRenderContext* rc) { switch (Event) { case CKRST_EVENT_CREATE: OnCreateDevice(rc); break; case CKRST_EVENT_DESTROY: OnDestroyDevice(rc); break; case CKRST_EVENT_RESIZING: case CKRST_EVENT_LOST: OnLostDevice(rc); break; case CKRST_EVENT_RESET: OnResetDevice(rc); break; }; return CK_OK; } CGbool CGFixFrontFaceCallback(CGstateassignment sa) { // TODO : use state cache int valueCount; const int *values = cgGetIntStateAssignmentValues(sa, &valueCount); XASSERT(values); CKBOOL ccw = (*values == GL_CCW) ? 1 : 0; VCKGL15Rasterizer::CKGLRasterizerContext* ctx = ShaderManagerCG::GetRasterizerFromCGContext(cgGetEffectContext(cgGetTechniqueEffect(cgGetPassTechnique(cgGetStateAssignmentPass(sa))))); XASSERT(ctx); ccw = ccw ^ ctx->m_InverseWinding ^ ctx->m_TextureTarget; glFrontFace(ccw ? GL_CCW : GL_CW); return CG_TRUE; } //----------------------------------------------------------------------------- void ShaderManagerCG::OnCreateDevice(CKRenderContext* rc) { //--- Compile all shaders for all devices int rcIndex = _GetRenderContextIndex(rc); assert( rcIndex>=0 ); #pragma todo ("Test if render context has been created") CGcontext context = cgCreateContext(); XASSERT(context); RegisterCGContext(context, (VCKGL15Rasterizer::CKGLRasterizerContext *) rc->GetRasterizerContext()); m_CgContext.Insert(rcIndex, context); //cgGLSetManageTextureParameters(context, CG_FALSE); // we setup textures ourselves cgGLRegisterStates(context); CGstate frontFaceState = cgGetNamedState(context, "FrontFace"); if (frontFaceState) { cgSetStateCallbacks(frontFaceState, CGFixFrontFaceCallback, CGFixFrontFaceCallback, NULL); } const int shaderCount = m_AllShaders.Size(); for( int a=0 ; a=0 ); const int shaderCount = m_AllShaders.Size(); for( int a=0 ; am_Effect[rcIndex]); if( !*effect ) continue; cgDestroyEffect( *effect ); *effect = NULL; shader->m_CurrentTechnique[rcIndex] = NULL; } if (m_CgContext.Size()) { if (m_CgContext[rcIndex]) { cgDestroyContext(m_CgContext[rcIndex]); UnregisterCGContext(m_CgContext[rcIndex]); } } m_CgContext[rcIndex] = NULL; } //----------------------------------------------------------------------------- void ShaderManagerCG::OnLostDevice(CKRenderContext* rc) { } //----------------------------------------------------------------------------- void ShaderManagerCG::OnResetDevice(CKRenderContext* rc) { } //----------------------------------------------------------------------------- CKERROR ShaderManagerCG::OnCKPlay() { if( !m_Context->IsReseted() ) return CK_OK; //--- Store rendering options CKRenderContext* rc = m_Context->GetRenderManager()->GetRenderContext(0); if( !rc ) return CK_OK; m_RenderOptionsBeforePlay = rc->GetCurrentRenderOptions(); return CK_OK; } //----------------------------------------------------------------------------- CKERROR ShaderManagerCG::PreClearAll() { #if !defined(MULTIPLESHADERMANAGER) CKShaderManager::PreClearAll(); for (int i = 0; i < m_AllShaders.Size(); ++i) delete m_AllShaders[i]; #endif m_AllShaders.Clear(); m_DefaultShader = NULL; m_ShadowShader = NULL; //--- Clear Exposed Parameters Management Arrays m_hashParamIndex.Clear(); const int rcCount = m_paramMeaningLinker.Size(); for( int rcIndex=0 ; rcIndexSetName( wantedName ); //--- Set default text fx->SetText(text ? *text : "technique tech\r\n{\r\n\tpass p\r\n\t{\r\n\t}\r\n}\r\n"); m_AllShaders.PushBack(fx); return fx; } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::CreateShaderFromFiles(const CKSTRING HLSLfilename, const CKSTRING CgFXfilename) { if (CgFXfilename != NULL) return CreateShaderFromFile(CgFXfilename); else return CreateShaderFromFile(HLSLfilename); } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::CreateShaderFromFile(const CKSTRING filename) { VxFile f; if (f.Open(filename, VxFile::READONLY)) { int size = f.Size(); XString s; s.Resize( size ); int readCount = f.Read( s.Str(), size ); // Extract Shader name int end = strlen(filename); while (end > 0 && filename[end] != '.') --end; int start = strlen(filename); while (start > 0 && filename[start] != '\\') --start; XString name; if (start < end && end != 0) { if (start) start++; name.Resize(end - start); strncpy(name.Str(), &filename[start], end - start); } else name = filename; // Creates the Shader. CKShader* fx = CreateShader(&name, &s); return fx; } return NULL; } //----------------------------------------------------------------------------- bool ShaderManagerCG::SaveShaderToFile(const XString& filename, CKShader* fx) { return _WriteStringToFile(filename, fx->GetText()) ? true:false; } //----------------------------------------------------------------------------- CKBOOL ShaderManagerCG::IsSupported() const { CKRenderManager* rm = m_Context->GetRenderManager(); if( rm ){ CKRenderContext* rc = rm->GetRenderContext(0); if( rc ){ CKRasterizerContext* rCtx = rc->GetRasterizerContext(); // Check rasterizer capability if (rCtx->m_Driver->m_3DCaps.CKRasterizerSpecificCaps & CKRST_SPECIFICCAPS_SUPPORTSHADERS && rCtx->m_Driver->m_2DCaps.Family == CKRST_OPENGL) return TRUE; } } return FALSE; } CKBOOL ShaderManagerCG::IsSupportedAndWarnIfNot() { const CKBOOL isSupported = IsSupported(); if( !isSupported ){ m_Output.PushBack("Rasterizer does not support Shaders (use OpenGL Shader Compatible Card)"); } return isSupported; } //----------------------------------------------------------------------------- void ShaderManagerCG::GetVSPSVersion(float& vs, float& ps) const { CGprofile vsProf = cgGLGetLatestProfile(CG_GL_VERTEX); CGprofile psProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); //CG_PROFILE_GLSLC //GLSLCombined switch(vsProf) { case CG_PROFILE_ARBVP1: vs = 2.1f; break; case CG_PROFILE_GLSLV: vs = 2.2f; break; case CG_PROFILE_VP20: vs = 1.1f; break; case CG_PROFILE_VP30: vs = 2.0f; break; case CG_PROFILE_VP40: vs = 3.0f; break; case CG_PROFILE_UNKNOWN: vs = 0.0f; break; default: vs = 1.1f; XASSERT(vsProf == CG_PROFILE_ARBVP1); break; } switch(psProf) { case CG_PROFILE_ARBFP1: ps = 2.1f; break; case CG_PROFILE_GLSLF: ps = 2.2f; break; case CG_PROFILE_FP20: ps = 1.1f; break; case CG_PROFILE_FP30: ps = 2.0f; break; case CG_PROFILE_FP40: ps = 3.0f; break; case CG_PROFILE_UNKNOWN: ps = 0.0f; break; default: ps = 1.1f; XASSERT(psProf == CG_PROFILE_ARBFP1); break; } } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::GetShaderByName(const XBaseString& name) { for (int i = 0; i < m_AllShaders.Size(); ++i) if (m_AllShaders[i]->GetName() == name) return m_AllShaders[i]; return NULL; } //----------------------------------------------------------------------------- int ShaderManagerCG::GetNumShaders() const { return m_AllShaders.Size(); } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::GetShader(int pos) { return m_AllShaders[pos]; } //----------------------------------------------------------------------------- void ShaderManagerCG::DeleteShader(CKShader* fx) { m_AllShaders.Remove(fx); if (fx == m_DefaultShader) m_DefaultShader = NULL; delete fx; } //----------------------------------------------------------------------------- bool ShaderManagerCG::CompileShader(CKShader* fx, XClassArray &output) { RCKShaderCG& rfx = *(RCKShaderCG*)fx; //--- We must compile for all created render context const int rcCount = m_rcList.Size(); for( int rcIndex=0 ; rcIndexGetRasterizerContext(); // NB : nicov : CG supports fixed pipeline setup as well, so permit it (same behavior than with Direct3D) if (!(/*rCtx->m_Driver->m_3DCaps.CKRasterizerSpecificCaps & CKRST_SPECIFICCAPS_SUPPORTSHADERS && */ rCtx->m_Driver->m_2DCaps.Family == CKRST_OPENGL)) { return FALSE; } rfx.m_EffectStateCachePerContext[rcIndex]->Clear(); const XString& Text = rfx.GetText(); if (!Text.Length()) { output.PushBack(rfx.GetName() + ": Empty shader : shader compilation failed."); rfx.RecordEffectStates(rfx.m_Effect[rcIndex], rfx.m_ShaderDescriptor[rcIndex], *rfx.m_EffectStateCachePerContext[rcIndex]); continue; } CGeffect oldfx = rfx.m_Effect[rcIndex]; // Creates and compile a simple Shader rfx.m_Effect[rcIndex] = cgCreateEffect(m_CgContext[rcIndex],Text.CStr(),NULL); //--- Retrieves compilation errors CGerror err = cgGetError(); if ( err != CG_NO_ERROR) { XStringTokenizer tokizer(cgGetErrorString(err), "\n"); const char *tok = NULL; while ((tok = tokizer.NextToken(tok)) != NULL) output.PushBack(rfx.GetName() + ": "+ XString(tok)); } const char* listing = cgGetLastListing(m_CgContext[rcIndex]); if (listing) { XStringTokenizer tokizer(listing, "\n"); const char *tok = NULL; while ((tok = tokizer.NextToken(tok)) != NULL) output.PushBack(rfx.GetName() + ": " + XString(tok)); } //--- Find Valid Technique in effect file CGtechnique tech = cgGetFirstTechnique(rfx.m_Effect[rcIndex]); BOOL valideTechFind = FALSE; while (tech) { if (cgValidateTechnique(tech) == CG_FALSE) { output.PushBack(rfx.GetName() + ": Technique "+ cgGetTechniqueName(tech) +" not validated."); listing = cgGetLastListing(m_CgContext[rcIndex]); if (listing) { XStringTokenizer tokizer(listing, "\n"); const char *tok = NULL; while ((tok = tokizer.NextToken(tok)) != NULL) output.PushBack(rfx.GetName() + ": " + XString(tok)); } } else valideTechFind = TRUE; tech = cgGetNextTechnique(tech); } if (!valideTechFind) output.PushBack(rfx.GetName() + ": No valid technique found."); #if (defined(macintosh) && defined(__i386__)) // Intel GMA 950 Hack. // this graphical chipset does not support pixel shader longger than 62 instructions. if (strstr((char *) glGetString(GL_VENDOR),"intel")!=NULL) { CGprogram program; for (program=cgGetFirstProgram(m_CgContext[rcIndex]);program;program=cgGetNextProgram(program)) { if (cgIsProgramCompiled(program)) { XString shader(cgGetProgramString(program,CG_COMPILED_PROGRAM)); int indice= shader.Find("END"); char* ptr = shader.Str()+indice+strlen("END\\n#"); int nbinstruction = atoi(ptr); if (nbinstruction>62) { rfx.m_Effect[rcIndex] = NULL; output.PushBack(rfx.GetName() + ": Shader compilation failed."); return TRUE; } } } } #endif //--- Replace the old Shader with the new one if (!rfx.m_Effect[rcIndex] || (listing!=NULL && strstr(listing, "error")) && oldfx){ rfx.m_Effect[rcIndex] = oldfx; output.PushBack(rfx.GetName() + ": Shader compilation failed."); rfx.RecordEffectStates(rfx.m_Effect[rcIndex], rfx.m_ShaderDescriptor[rcIndex], *rfx.m_EffectStateCachePerContext[rcIndex]); } else { //--- Init the Shader Descriptor for this Shader if( fx != m_DefaultShader ){ rfx.m_ShaderDescriptor[rcIndex]->Init( &m_ShaderDescriptorManager, rfx.m_Effect[rcIndex], rc, &rfx ); } if (oldfx) { cgDestroyEffect(oldfx); rfx.m_CurrentTechnique[rcIndex]=NULL; } #if defined(MULTIPLESHADERMANAGER) rfx.m_CompID = (*m_NextCompID)++; #else rfx.m_CompID = m_NextCompID++; #endif //--- Output "Compilation successful" message output.PushBack(rfx.GetName() + ": Shader compilation successful."); rfx.RecordEffectStates(rfx.m_Effect[rcIndex], rfx.m_ShaderDescriptor[rcIndex], *rfx.m_EffectStateCachePerContext[rcIndex]); } } return TRUE; } //----------------------------------------------------------------------------- bool ShaderManagerCG::CompileShaderOutput(CKShader* fx, const CKSTRING funcname, const CKSTRING target, XClassArray &output, XArray& text) { assert(fx != NULL); if (!(VxFile::GetFileMode(m_FXCCommand.CStr()) & VxFile::REGULAR_FILE)) { output.PushBack("No path to cgc.exe CGFX Compiler."); return FALSE; } // Check Direct X Version // if (dx && dx->DxVersion >= 0x0900) { #ifdef macintosh char *tmp=(char *)VxGetTempPath().CStr(); #else char tmp[1024]; GetTempPath(1024, tmp); #endif // Create an input file. XString infile; infile.Format("%sfxcin.fx", tmp); // Write XString to file. if (!_WriteStringToFile(infile, fx->GetText())) { output.PushBack("Cannot write temporary Shader file."); return FALSE; } XString outfile; outfile.Format("%sfxcout.txt", tmp); XString cmd; cmd.Reserve( 1024+strlen(funcname) ); cmd.Format( "\"%s\" -profile %s -entry %s -o %s %s", m_FXCCommand.CStr(), target, funcname, outfile.CStr(), infile.CStr()); if (!_WinExec(cmd.CStr())) { output.PushBack(XString("Command execution has failed : ") + cmd); return FALSE; } if (!_ReadStringFromFile(outfile, text)) { output.PushBack("Cannot read compiled output file."); return FALSE; } } return TRUE; } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::GetDefaultShader() { return GetDefaultShaderI(); } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::GetDefaultShaderI() { if( !m_DefaultShader ) { XString defaultShader("- default -"); XString g_defaultShader(g_default); m_DefaultShader = ShaderManagerCG::CreateShader(&defaultShader, &g_defaultShader); CompileShader(m_DefaultShader, m_Output); } return m_DefaultShader; } //----------------------------------------------------------------------------- CKShader* ShaderManagerCG::GetShadowShader() { if( !m_ShadowShader) { XString shadowShader("- builtin shadows -"); XString g_shadowShader(g_default); // TMP : not implemented, return default shader m_ShadowShader = ShaderManagerCG::CreateShader(&shadowShader, &g_shadowShader); CompileShader(m_ShadowShader, m_Output); } return m_ShadowShader; } //----------------------------------------------------------------------------- CKERROR ShaderManagerCG::LoadData(CKStateChunk *chunk,CKFile* LoadedFile) { //--- If the Default Shader doesn't exist GetDefaultShader will create it m_DefaultShader = GetDefaultShaderI(); assert(LoadedFile != 0); if (!chunk) return CKERR_INVALIDPARAMETER; chunk->StartRead(); if (chunk->SeekIdentifier(SHADERMANAGER_CHUNKID)) { XArray toBeCompiledShaders; // Check the version int version = chunk->ReadInt(); // Check the flags int flags = chunk->ReadInt(); // Read Shaders int num_Shaders = chunk->ReadInt(); int chunkid = FIRSTSHADER_CHUNKID; for (int i = 0; i < num_Shaders; ++i) { CKBOOL notEndOfChunk = chunk->SeekIdentifier(chunkid); ++chunkid; if( !notEndOfChunk ){ continue; } //--- If Version < 1 there's a BOOL "isDefaultFx" if( version < 2 ){ //--- Is it the default effect then don't read content CKBOOL isDefaultFx = chunk->ReadInt(); if( isDefaultFx ) continue; } //--- Read Shader Name and Text XString fxName = chunk->ReadString(); XString fxText;// = chunk->ReadString(); //--- Retrieves CgFX text version of a shader bool needToExit = false; int* marksBuffer = NULL; int marksSize = 0; do{ fxText = chunk->ReadString(); if (fxText.Compare("HLSL") == 0) { chunk->ReadString(); fxText = ""; } else if (fxText.Compare("CGFX") == 0){ fxText = chunk->ReadString(); needToExit = true; } else if (fxText.Compare("_END_") == 0){ fxText = ""; needToExit = true; break; } else { //--- pre Dev 4.0 (only HLSL) fxText = ""; needToExit = true; break; } // Read Marks if (version>=3) { if (marksBuffer != NULL) { CKDeletePointer(marksBuffer); marksBuffer = NULL; } marksSize = chunk->ReadBuffer((void**)&marksBuffer) / sizeof(int); } } while (needToExit == false); CKShader* sameNameEffect; BOOL shaderMustBeCreated = TRUE; int extCount = 0; while( (sameNameEffect=GetShaderByName( fxName )) && shaderMustBeCreated ){ //--- Skip this effect if text is the same if( !fxText.Compare( sameNameEffect->GetText() ) ){ shaderMustBeCreated = FALSE; break; } //--- Otherwise ... switch( m_ReplacementMode ){ case ShaderReplacementMode_ReplaceOldByNew: //--- Replace Old Shader By New Shader { sameNameEffect->SetText( fxText ); shaderMustBeCreated = FALSE; toBeCompiledShaders.PushBack( sameNameEffect ); } break; case ShaderReplacementMode_KeepOldAndDontUseNew: //--- Keep Old Shader And Dont Use New Shader { shaderMustBeCreated = FALSE; } break; case ShaderReplacementMode_RenameNew: default: //--- Rename New Shader { //--- Find Existing Extension Number XString originalFxName = fxName; int fxNameLength = fxName.Length(); if( fxNameLength > 4 ){ if( fxName[ fxNameLength-4 ] == '_' ){ XString extStr = fxName.Substring( fxNameLength-3 ); extCount = extStr.ToInt(); originalFxName = fxName.Substring( 0, fxNameLength-4 ); } } //--- Replace extension number by Incremented Extension Number ++extCount; fxName.Format( "%s_%.3d", originalFxName.CStr(), extCount ); } break; } } if( !shaderMustBeCreated ) continue; //--- Create new shader RCKShaderCG* fx = new RCKShaderCG(this); fx->SetName( fxName ); fx->SetText( fxText ); m_AllShaders.PushBack( fx ); toBeCompiledShaders.PushBack( fx ); // Read Marks if (version>=3) { #ifndef VIRTOOLS_RUNTIME_VERSION fx->m_F2Marks.Resize(marksSize); for (int index=0;indexm_F2Marks[index]=marksBuffer[index]; #endif CKDeletePointer(marksBuffer); } } //--- Compile all loaded shaders if( IsSupportedAndWarnIfNot() ){ //--- Compile every loaded Shader except the default Shader ///// as it has already been compiled (cf: just above) const int loadedShaderCount = toBeCompiledShaders.Size(); for( int i=0 ; iCStr()); OutputDebugString("\n"); } m_Output.Clear(); #endif } } chunk->CloseChunk(); return CK_OK; } //----------------------------------------------------------------------------- #define VIRTOOLS_SCRIPT_DOT_EXTENSION ".nms" #define VIRTOOLS_OBJECT_DOT_EXTENSION ".nmo" CKStateChunk* ShaderManagerCG::SaveData(CKFile* SavedFile) { assert(SavedFile != 0); //--- If there's only one shader it's certainly the default shader Shader ///// and so it's not even needed to save any ShaderManagerCG Chunk int toSaveCount = 0; for (int shIndex = 0; shIndex < m_AllShaders.Size(); ++shIndex) { if (m_AllShaders[shIndex] != m_DefaultShader && m_AllShaders[shIndex] != m_ShadowShader) { ++ toSaveCount; } } if (toSaveCount == 0) return NULL; XHashTable shaderThatCanBeSavedFromName; bool checkIfShaderMustBeSaved = false; //--- Check file extension CKSTRING fileName = SavedFile->m_FileName; const int fileNameLength = strlen(fileName); if( fileNameLength >= 4 ){ char* extension = fileName + fileNameLength - 4; //--- Saving .NMS if( stricmp( extension, VIRTOOLS_SCRIPT_DOT_EXTENSION ) == 0 ){ //--- Don't save any Shaders with an .NMS return NULL; } //--- Saving .NMO if( stricmp( extension, VIRTOOLS_OBJECT_DOT_EXTENSION ) == 0 ){ //--- If User asked not to save any Shader with NMO, then Exit if( m_NMOSavingMode == ShaderNMOSavingMode_DontSaveAnyShader ) return NULL; //--- If User asked to save Only Used Shaders, then find references to shaders if( m_NMOSavingMode == ShaderNMOSavingMode_SaveOnlyUsedShaders ){ checkIfShaderMustBeSaved = true; toSaveCount = 0; CKParameterManager* pm = m_Context->GetParameterManager(); CKParameterType shaderParamType = pm->ParameterGuidToType( CKPGUID_SHADER ); const int count = SavedFile->m_FileObjects.Size(); for( int a=0 ; am_FileObjects[a]; //--- If it's a material.. if( CKIsChildClassOf(objSaved.ObjectCid, CKCID_MATERIAL) ){ CKMaterial* mat = (CKMaterial*)objSaved.ObjPtr; if( !mat ) continue; CKMaterialShader* mfx = mat->GetMaterialShader(); if( !mfx ) continue; //--- If there's a Shader on this Material CKShader* shader = mfx->m_Shader; if( !shader ) continue; XASSERT(shader!=m_DefaultShader); // this shouldn't happen XASSERT(shader!=m_ShadowShader); // this shouldn't happen //--- If this ShaderName isn't yet supposed to be saved const XString& shaderName = shader->GetName(); if( shaderThatCanBeSavedFromName.FindPtr( shaderName ) ) continue; //--- Then add it to the list of ShaderNames to be saved shaderThatCanBeSavedFromName.Insert( shaderName, true ); ++toSaveCount; } //--- If it's a Parameter else if( CKIsChildClassOf(objSaved.ObjectCid, CKCID_PARAMETERLOCAL) ){ CKParameter* param = (CKParameter*)objSaved.ObjPtr; if( !param ) continue; //--- if this parameter is a CKPGUID_SHADER if( param->GetType() != shaderParamType ) continue; const char* shaderName = (char*)param->GetReadDataPtr(FALSE); if( !shaderName ) continue; //--- We shouldn't, but if we ever parse a shader param string == None then do nothing if( strcmp( shaderName, "-- None --" ) == 0 ) continue; //--- If this ShaderName isn't yet supposed to be saved if( shaderThatCanBeSavedFromName.FindPtr( shaderName ) ) continue; //--- Then add it to the list of ShaderNames to be saved shaderThatCanBeSavedFromName.Insert( shaderName, true ); ++toSaveCount; } } } } } // Writes chunk information CKStateChunk *chunk = CreateCKStateChunk(SHADERMANAGER_CHUNKID, SavedFile); if (!chunk) return NULL; chunk->StartWrite(); chunk->WriteIdentifier(SHADERMANAGER_CHUNKID); chunk->WriteInt(SHADERMANAGER_SAVE_VERSION); chunk->WriteInt(SHADERMANAGER_FLAGS); // Writes all Shaders we want to save. chunk->WriteInt( toSaveCount ); //--- Minus the default Shader int chunkid = FIRSTSHADER_CHUNKID; const int shaderCount = m_AllShaders.Size(); for (int i = 0; i < shaderCount; ++i) { CKShader* shader = m_AllShaders[i]; if( !shader ) continue; //--- Is it the default effect then don't write it (same for builtin shadows) if( shader == m_DefaultShader ) continue; if( shader == m_ShadowShader ) continue; //--- If extension is .NMO, filter Only Used Shaders if( checkIfShaderMustBeSaved ){ if( !shaderThatCanBeSavedFromName.FindPtr( shader->GetName() ) ) continue; } chunk->WriteIdentifier(chunkid); ++chunkid; // store name chunk->WriteString( shader->GetName().CStr() ); // store text #ifndef _XBOX #ifndef macintosh if(m_SavePreProcessed){ XString PreProcessed; XString PreProcErr; HRESULT hr = mCppLib_PreProcess( shader->GetText(),m_SavePreprocessedOptions.CStr(),PreProcessed,PreProcErr); if(S_OK == hr){ chunk->WriteString(PreProcessed.CStr()); }else{ m_Context->OutputToConsoleExBeep("Error Preprocessing Shader : %s",PreProcErr.CStr()); chunk->WriteString("CGFX"); chunk->WriteString( shader->GetText().CStr()); } }else #endif // macintosh #endif // _XBOX { chunk->WriteString("CGFX"); chunk->WriteString( shader->GetText().CStr()); } // Write F2 Marks #ifdef VIRTOOLS_RUNTIME_VERSION chunk->WriteBuffer(0,0); #else chunk->WriteBuffer(shader->GetF2Marks()->Size()*sizeof(int),shader->GetF2Marks()->Begin()); #endif chunk->WriteString("_END_"); } chunk->CloseChunk(); return chunk; } //----------------------------------------------------------------------------- #ifndef _XBOX //----------------------------------------------------------------------------- BOOL ShaderManagerCG::_ReadStringFromFile(const XString& filename, XArray &s) const { //--- Read XString from file. VxFile f; if( !f.Open(filename.CStr()) ) return FALSE; int size = f.Size(); s.Resize( size+1); f.Read( s.Begin(), size ); s[size] = '\0'; f.Close(); return TRUE; } //----------------------------------------------------------------------------- BOOL ShaderManagerCG::_WriteStringToFile(const XString& filename, const XString &s) const { VxFile f; if (!f.Open(filename.CStr(), VxFile::WRITEONLY)) return FALSE; f.Write(s.CStr(), s.Length()); f.Close(); return TRUE; } //----------------------------------------------------------------------------- BOOL ShaderManagerCG::_WinExec(const XString& command) { #ifdef macintosh return (system(command.CStr())!=-1); #else STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); // Start the child process. if( !CreateProcess( NULL, // No module name (use command line). const_cast(command.CStr()), // Command line. NULL, // Process handle not inheritable. NULL, // Thread handle not inheritable. FALSE, // Set handle inheritance to FALSE. CREATE_NO_WINDOW, // No creation flags. NULL, // Use parent's environment block. NULL, // Use parent's starting directory. &si, // Pointer to STARTUPINFO structure. &pi ) // Pointer to PROCESS_INFORMATION structure. ) return FALSE; // Wait until child process exits. WaitForSingleObject( pi.hProcess, INFINITE ); DWORD code = 0; GetExitCodeProcess( pi.hProcess, &code ); // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); return code==0; #endif } #endif //-----------------------------------------------------------------------// // Exposed Parameters Management // // m_hashParamIndex: a hash table for retrieving the index of store CKParams // m_paramMeaningLinker: array of ExposedLoclalParamsPerRC indexed per RC index. // ExposedLocalParamsPerRC: //-----------------------------------------------------------------------// //----------------------------------------------------------------------------- void ShaderManagerCG::_RegisterExposedParameterMeaning( CKParameterLocal* p, ShaderDescriptorCG::ExposedParamMeaning* paramMeaning, int rcIndex ) { //--- Check if parameter has already been registered int* paramIndexPtr = m_hashParamIndex.FindPtr(p); int registeredParamCount = m_hashParamIndex.Size(); int paramIndex = registeredParamCount; //--- If the paramater has not yet been registered then add it if( !paramIndexPtr ){ m_hashParamIndex.Insert( p, paramIndex ); m_paramMeaningLinker[rcIndex].PushBack( paramMeaning ); //--- Otherwise use the param's index } else { //--- If parameter has already been registered for another rc, but not for this one if( *paramIndexPtr >= m_paramMeaningLinker[rcIndex].Size() ){ m_paramMeaningLinker[rcIndex].Resize( *paramIndexPtr+1 ); } m_paramMeaningLinker[rcIndex][*paramIndexPtr] = paramMeaning; } } //----------------------------------------------------------------------------- void ShaderManagerCG::_UnregisterExposedParameter( CKParameterLocal* p ) { //--- Check if parameter is registered int* paramIndexPtr = m_hashParamIndex.FindPtr(p); if( !paramIndexPtr ) return; int paramIndex = *paramIndexPtr; //--- Parse all rc and remove d3dhandle for the found param index const int rcCount = m_paramMeaningLinker.Size(); for( int rcIndex=0 ; rcIndex::Iterator it = m_hashParamIndex.Begin(); while (it != m_hashParamIndex.End()) { if( *it > paramIndex ){ --(*it); } ++it; } m_hashParamIndex.Remove( p ); } //----------------------------------------------------------------------------- void ShaderManagerCG::_AddRCForExposedParameters() { const int endIndex = m_paramMeaningLinker.Size(); //--- Add one rc m_paramMeaningLinker.Expand(1); //--- Set as many params for this rc as there's params defined in the hash table const int registeredParamCount = m_hashParamIndex.Size(); m_paramMeaningLinker[endIndex].Resize( registeredParamCount ); m_paramMeaningLinker[endIndex].Fill( (ShaderDescriptorCG::ExposedParamMeaning*)0 ); } //----------------------------------------------------------------------------- void ShaderManagerCG::_RemoveRCForExposedParameters( int rcIndex ) { //--- Remove the rc ///// Note: as it's a class array, it will remove also all d3dhandles for the rc m_paramMeaningLinker.RemoveAt( rcIndex ); } //----------------------------------------------------------------------------- ShaderDescriptorCG::ExposedParamMeaning* ShaderManagerCG::_GetRegisteredParamMeaning( CKParameterLocal* p, int rcIndex ) { //--- Check if parameter is registered int* paramIndexPtr = m_hashParamIndex.FindPtr(p); if( !paramIndexPtr ) return (ShaderDescriptorCG::ExposedParamMeaning*)(-1); int paramIndex = *paramIndexPtr; return m_paramMeaningLinker[rcIndex][paramIndex]; } //----------------------------------------------------------------------------- int ShaderManagerCG::_GetRenderContextIndex( CKRenderContext* rc ) { const int rcCount = m_rcList.Size(); for( int a=0 ; a_AddRenderContextSlot(); } } //----------------------------------------------------------------------------- void ShaderManagerCG::OnRenderContextDestroyed( CKRenderContext* rc ) { int rcIndex = _GetRenderContextIndex(rc); if( rcIndex >= 0 ){ //--- Remove a RenderContext Slot for all Exposed Parameters _RemoveRCForExposedParameters( rcIndex ); //--- Remove a RenderContext Slot for all RCKShaders for(int a=0; a_RemoveRenderContextSlot( rcIndex ); } //--- Remove the RC from our managed list of RCs m_rcList.Remove( rc ); } } //----------------------------------------------------------------------------- void ShaderManagerCG::EnableProgram(CKRasterizerContext* rc, ShaderConstantStore *cstStore) { XASSERT(cstStore); CGprofile profile = cstStore->GetProfile(); CGdomain domain = cstStore->GetDomain(); EnableProfile(domain, profile); CGprogram cgProg = cstStore->GetCGProgram(); XASSERT(cgProg != 0); XASSERT(cstStore->GetGLProgramID()); // If program failed to compile, then we shouldn't have built a ShaderConstantStore from it if (cgProg != m_CurrentCGProgram[domain - CG_FIRST_DOMAIN]) { cstStore->BindProgramIntoGL(rc); m_CurrentCGProgram[domain - CG_FIRST_DOMAIN] = cgProg; } cstStore->Flush(rc); } //----------------------------------------------------------------------------- void ShaderManagerCG::EnableProgram(CGprogram prog) { #ifndef STATIC_FOR_MAYA // Maya export dll links with a cg that don't have // cgGetProfileDomain -> link would fail GLCHECK(); XASSERT(prog); // CGprofile profile = cgGetProgramProfile(prog); CGdomain domain = cgGetProfileDomain(profile); XASSERT(domain < CG_FIRST_DOMAIN + NUMBER_OF_CG_DOMAIN); if (prog != m_CurrentCGProgram[domain - CG_FIRST_DOMAIN]) { EnableProfile(domain, profile); cgGLBindProgram(prog); m_CurrentCGProgram[domain - CG_FIRST_DOMAIN] = prog; if (profile == CG_PROFILE_FP20) { cgSetPassProgramParameters(prog); // no local parameters for that profile // (GeForce 3 pixel shaders via NV_TEXTURE_SHADER ... must set them up) } } GLCHECK(); #else XASSERT(0); // not supposed to be called #endif } //----------------------------------------------------------------------------- void ShaderManagerCG::EnableProfile(CGdomain domain, CGprofile profile) { XASSERT(domain < CG_FIRST_DOMAIN + NUMBER_OF_CG_DOMAIN); GLCHECK(); if (m_CurrentProfile[domain - CG_FIRST_DOMAIN] != profile) { if (m_CurrentProfile[domain - CG_FIRST_DOMAIN] != CG_PROFILE_UNKNOWN) { cgGLDisableProfile(m_CurrentProfile[domain - CG_FIRST_DOMAIN]); } if (profile != CG_PROFILE_UNKNOWN) { cgGLEnableProfile(profile); } m_CurrentProfile[domain - CG_FIRST_DOMAIN] = profile; } GLCHECK(); } //----------------------------------------------------------------------------- void ShaderManagerCG::DeactivateProgram(CGdomain domain) { XASSERT(domain < CG_FIRST_DOMAIN + NUMBER_OF_CG_DOMAIN); GLCHECK(); if (m_CurrentCGProgram[domain - CG_FIRST_DOMAIN]) { XASSERT(m_CurrentProfile[domain - CG_FIRST_DOMAIN] != CG_PROFILE_UNKNOWN); cgGLUnbindProgram(m_CurrentProfile[domain - CG_FIRST_DOMAIN]); EnableProfile(domain, CG_PROFILE_UNKNOWN); m_CurrentCGProgram[domain - CG_FIRST_DOMAIN] = NULL; } #ifdef _DEBUG XASSERT(m_CurrentProfile[domain - CG_FIRST_DOMAIN] == CG_PROFILE_UNKNOWN); #endif GLCHECK(); } //----------------------------------------------------------------------------- void ShaderManagerCG::FlushModifiedRenderStates(int passIndex, CKRasterizerContext* rc) { if (passIndex >= m_PerPassModifiedRenderStates.Size()) { m_PerPassModifiedRenderStates.Resize(passIndex + 1); } PerPassModifiedRenderStates &flushedPass = m_PerPassModifiedRenderStates[passIndex]; int stateCount = flushedPass.RenderStates.Size(); for (int stateIndex =0 ; stateIndex < stateCount; ++stateIndex) { flushedPass.RenderStates[stateIndex]->Apply(rc, false /* do reset */); } flushedPass.RenderStates.Resize(0); // don't do a clear here : want to keep memory for future reuse } //----------------------------------------------------------------------------- void ShaderManagerCG::ClearModifiedRenderStates() { int passCount = m_PerPassModifiedRenderStates.Size(); for (int passIndex = 0; passIndex < passCount; ++passIndex) { m_PerPassModifiedRenderStates[passIndex].RenderStates.Resize(0); } } //----------------------------------------------------------------------------- void ShaderManagerCG::SignalTextureStageUsed(int stage, GLenum textureTarget) { XASSERT(stage >= 0 && stage < 32); m_UsedTextureStageBits |= (1 << stage); m_TextureStageTargets[stage] = textureTarget; } //----------------------------------------------------------------------------- void ShaderManagerCG::DisableTextureStages(CKRasterizerContext *rc, CKDWORD stagesMask) { GLCHECK(); CKDWORD toDisableBits = (~stagesMask) & m_UsedTextureStageBits; int currentStage = 0; VCKGL15Rasterizer::CKGLRasterizerContext *ogRC = (VCKGL15Rasterizer::CKGLRasterizerContext *) rc; XASSERT(ogRC->m_glActiveTextureARB); while (toDisableBits) { if (toDisableBits & 1) { ogRC->m_glActiveTextureARB(GL_TEXTURE0_ARB + currentStage); ogRC->DisableGLCurrentStageTexturing(); } toDisableBits >>= 1; ++ currentStage; } ogRC->m_glActiveTextureARB(GL_TEXTURE0_ARB); m_UsedTextureStageBits &= stagesMask; GLCHECK(); } //----------------------------------------------------------------------------- int ShaderManagerCG::GetCGEffectTechniqueCount(CGeffect effect) { if (!effect) return NULL; int techCount = 0; for (CGtechnique currTech = cgGetFirstTechnique(effect); currTech != NULL; currTech = cgGetNextTechnique(currTech), ++ techCount) { } return techCount; } /*************************************************************************** ___ _ _ __ __ |_ _|_ __ ___| |_ _ __| | ___ | \/ | __ _ _ __ __ _ __ _ ___ _ __ | || '_ \ / __| | | | |/ _` |/ _ \ | |\/| |/ _` | '_ \ / _` |/ _` |/ _ \ '__| | || | | | (__| | |_| | (_| | __/ | | | | (_| | | | | (_| | (_| | __/ | |___|_| |_|\___|_|\__,_|\__,_|\___| |_| |_|\__,_|_| |_|\__,_|\__, |\___|_| |___/ ***************************************************************************/ #if DIRECT3D_VERSION>=0x0900 //----------------------------------------------------------------------------- //HRESULT //ShaderManagerCG::IncludeManager //::Open(D3DXINCLUDE_TYPE IncludeType, LPCSTR pName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) //{ // // bool foundError = TRUE; // do { // // XString includeFileName( pName ); // CKERROR err = m_ShaderManager->m_Context->GetPathManager()->ResolveFileName( includeFileName, DATA_PATH_IDX ); // if( err!=CK_OK ){ // m_ShaderManager->m_Context->GetPathManager()->ResolveFileName( includeFileName, BITMAP_PATH_IDX ); // if( err!=CK_OK ) break; // } // VxFile file; // if( !file.Open( includeFileName.CStr() ) ) break; // // DWORD size = file.Size(); // // BYTE* pData = new BYTE[size]; // if( pData == NULL ) return E_OUTOFMEMORY; // // file.Read( pData, size ); // // *ppData = pData; // *pBytes = size; // // foundError = FALSE; // // } while(0); // // //--- If an Error Occured // if( foundError ){ // XString errStr = "Warning: Can't include file \""; // errStr += pName; // errStr += "\""; // m_ShaderManager->m_Output.PushBack( errStr ); // return E_FAIL; // } // return S_OK; //} // ////----------------------------------------------------------------------------- //HRESULT //ShaderManagerCG::IncludeManager //::Close(LPCVOID pData) //{ // if( pData ) delete[] (BYTE*)pData; // return S_OK; //} #endif