diff options
Diffstat (limited to 'src/extras/custompipes_d3d9.cpp')
-rw-r--r-- | src/extras/custompipes_d3d9.cpp | 450 |
1 files changed, 345 insertions, 105 deletions
diff --git a/src/extras/custompipes_d3d9.cpp b/src/extras/custompipes_d3d9.cpp index d7634e99..68337fb6 100644 --- a/src/extras/custompipes_d3d9.cpp +++ b/src/extras/custompipes_d3d9.cpp @@ -2,6 +2,10 @@ #include "common.h" #ifdef RW_D3D9 +#ifdef EXTENDED_PIPELINES + +#include "rpmatfx.h" + #include "main.h" #include "RwHelper.h" #include "Lights.h" @@ -14,8 +18,6 @@ #include "World.h" #include "custompipes.h" -#ifdef EXTENDED_PIPELINES - #ifndef LIBRW #error "Need librw for EXTENDED_PIPELINES" #endif @@ -36,13 +38,212 @@ enum { VSLOC_eye = rw::d3d::VSLOC_afterLights, VSLOC_reflProps, - VSLOC_specLights + VSLOC_specLights, + + // Leeds building, Leeds vehicle mobile + VSLOC_emissive = rw::d3d::VSLOC_afterLights, + VSLOC_ambient, + VSLOC_viewMat, // only vehicle + + // Leeds vehicle PS2 + VSLOC_texMat = rw::d3d::VSLOC_afterLights, + + PSLOC_colorscale = 1, + PSLOC_shininess, + PSLOC_skyTop, + PSLOC_skyBot }; /* - * Neo Vehicle pipe + * Leeds & Neo Vehicle pipe */ +static void *leedsVehicle_VS; +static void *leedsVehicle_mobile_VS; +static void *leedsVehicle_blend_PS; +static void *leedsVehicle_add_PS; +static void *leedsVehicle_mobile_PS; + +static rw::RawMatrix normal2texcoord_flipU = { + { -0.5f, 0.0f, 0.0f }, 0.0f, + { 0.0f, -0.5f, 0.0f }, 0.0f, + { 0.0f, 0.0f, 1.0f }, 0.0f, + { 0.5f, 0.5f, 0.0f }, 1.0f +}; + +void +uploadEnvMatrix(rw::Frame *frame) +{ + using namespace rw; + Matrix invMat; + if(frame == nil) + frame = engine->currentCamera->getFrame(); + + RawMatrix envMtx, invMtx; + Matrix tmp = *frame->getLTM(); + // Now the weird part: we remove the camera pitch + tmp.at.z = 0.0f; + tmp.at = normalize(tmp.at); + tmp.right.x = -tmp.at.y; + tmp.right.y = tmp.at.x; + tmp.right.z = 0.0f;; + tmp.up.set(0.0f, 0.0f, 1.0f); + tmp.pos.set(0.0f, 0.0f, 0.0f); + tmp.flags = Matrix::TYPEORTHONORMAL; + + Matrix::invert(&invMat, &tmp); + convMatrix(&invMtx, &invMat); + RawMatrix::mult(&envMtx, &invMtx, &normal2texcoord_flipU); + d3d::d3ddevice->SetVertexShaderConstantF(VSLOC_texMat, (float*)&envMtx, 4); +} + +void +leedsVehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + int vsBits; + setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); + setIndices(header->indexBuffer); + setVertexDeclaration(header->vertexDeclaration); + + vsBits = lightingCB_Shader(atomic); + uploadMatrices(atomic->getFrame()->getLTM()); + + setVertexShader(leedsVehicle_VS); + if(gGlassCarsCheat) + setPixelShader(leedsVehicle_blend_PS); + else + setPixelShader(leedsVehicle_add_PS); + + d3d::setTexture(1, EnvMapTex); + uploadEnvMatrix(nil); + + SetRenderState(SRCBLEND, BLENDONE); + + float colorscale[4]; + colorscale[3] = 1.0f; + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); + + float coef = 0.0f; + if(RpMatFXMaterialGetEffects(m) == rpMATFXEFFECTENVMAP){ + coef = CClock::ms_EnvMapTimeMultiplicator * RpMatFXMaterialGetEnvMapCoefficient(m)*0.5f; + if(gGlassCarsCheat) + coef = 1.0f; + } + d3ddevice->SetPixelShaderConstantF(PSLOC_shininess, (float*)&coef, 1); + + setMaterial(m->color, m->surfaceProps); + + float cs = 1.0f; + // how does the PS2 handle this actually? probably scaled material color? + if(VehiclePipeSwitch == VEHICLEPIPE_PSP && m->texture) + cs = 2.0f; + colorscale[0] = colorscale[1] = colorscale[2] = cs; + d3ddevice->SetPixelShaderConstantF(PSLOC_colorscale, colorscale, 1); + + if(m->texture) + d3d::setTexture(0, m->texture); + else + d3d::setTexture(0, gpWhiteTexture); + + drawInst(header, inst); + inst++; + } + + d3d::setTexture(1, nil); + + SetRenderState(SRCBLEND, BLENDSRCALPHA); +} + +void +uploadWorldLights(void) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + RGBAf amb, emiss; + amb.red = CTimeCycle::GetAmbientRed(); + amb.green = CTimeCycle::GetAmbientGreen(); + amb.blue = CTimeCycle::GetAmbientBlue(); + amb.alpha = 1.0f; + emiss = pAmbient->color; + + d3ddevice->SetVertexShaderConstantF(VSLOC_ambient, (float*)&amb, 1); + d3ddevice->SetVertexShaderConstantF(VSLOC_emissive, (float*)&emiss, 1); +} + +void +leedsVehicleRenderCB_mobile(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) +{ + using namespace rw; + using namespace rw::d3d; + using namespace rw::d3d9; + + int vsBits; + setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); + setIndices(header->indexBuffer); + setVertexDeclaration(header->vertexDeclaration); + + vsBits = lightingCB_Shader(atomic); + uploadMatrices(atomic->getFrame()->getLTM()); + + setVertexShader(leedsVehicle_mobile_VS); + setPixelShader(leedsVehicle_mobile_PS); + + uploadWorldLights(); + + RGBAf skyTop, skyBot; + skyTop.red = CTimeCycle::GetSkyTopRed()/255.0f; + skyTop.green = CTimeCycle::GetSkyTopGreen()/255.0f; + skyTop.blue = CTimeCycle::GetSkyTopBlue()/255.0f; + skyBot.red = CTimeCycle::GetSkyBottomRed()/255.0f; + skyBot.green = CTimeCycle::GetSkyBottomGreen()/255.0f; + skyBot.blue = CTimeCycle::GetSkyBottomBlue()/255.0f; + + d3ddevice->SetPixelShaderConstantF(PSLOC_skyTop, (float*)&skyTop, 1); + d3ddevice->SetPixelShaderConstantF(PSLOC_skyBot, (float*)&skyBot, 1); + + d3ddevice->SetVertexShaderConstantF(VSLOC_viewMat, (float*)&rw::engine->currentCamera->devView, 4); + + d3d::setTexture(1, EnvMapTex); + + InstanceData *inst = header->inst; + for(rw::uint32 i = 0; i < header->numMeshes; i++){ + Material *m = inst->material; + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); + + float coef = 0.0f; + if(RpMatFXMaterialGetEffects(m) == rpMATFXEFFECTENVMAP){ + coef = CClock::ms_EnvMapTimeMultiplicator * RpMatFXMaterialGetEnvMapCoefficient(m)*0.5f; + if(gGlassCarsCheat) + coef = 1.0f; + } + d3ddevice->SetPixelShaderConstantF(PSLOC_shininess, (float*)&coef, 1); + + setMaterial(m->color, m->surfaceProps); + + if(m->texture) + d3d::setTexture(0, m->texture); + else + d3d::setTexture(0, gpWhiteTexture); + + drawInst(header, inst); + inst++; + } + + d3d::setTexture(1, nil); +} + static void *neoVehicle_VS; static void *neoVehicle_PS; @@ -83,8 +284,13 @@ vehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) using namespace rw::d3d9; // TODO: make this less of a kludge - if(VehiclePipeSwitch == VEHICLEPIPE_MATFX){ - matFXGlobals.pipelines[rw::platform]->render(atomic); + if(VehiclePipeSwitch == VEHICLEPIPE_PSP || VehiclePipeSwitch == VEHICLEPIPE_PS2){ + leedsVehicleRenderCB(atomic, header); + // matFXGlobals.pipelines[rw::platform]->render(atomic); + return; + } + if(VehiclePipeSwitch == VEHICLEPIPE_MOBILE){ + leedsVehicleRenderCB_mobile(atomic, header); return; } @@ -140,24 +346,44 @@ vehicleRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) void CreateVehiclePipe(void) { - if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) - printf("Error: couldn't open 'neo/carTweakingTable.dat'\n"); - else{ - char *fp = (char*)work_buff; - fp = ReadTweakValueTable(fp, Fresnel); - fp = ReadTweakValueTable(fp, Power); - fp = ReadTweakValueTable(fp, DiffColor); - fp = ReadTweakValueTable(fp, SpecColor); - } - -#include "shaders/neoVehicle_VS.inc" +// if(CFileMgr::LoadFile("neo/carTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) +// printf("Error: couldn't open 'neo/carTweakingTable.dat'\n"); +// else{ +// char *fp = (char*)work_buff; +// fp = ReadTweakValueTable(fp, Fresnel); +// fp = ReadTweakValueTable(fp, Power); +// fp = ReadTweakValueTable(fp, DiffColor); +// fp = ReadTweakValueTable(fp, SpecColor); +// } + +#include "shaders/obj/neoVehicle_VS.inc" neoVehicle_VS = rw::d3d::createVertexShader(neoVehicle_VS_cso); assert(neoVehicle_VS); -#include "shaders/neoVehicle_PS.inc" +#include "shaders/obj/neoVehicle_PS.inc" neoVehicle_PS = rw::d3d::createPixelShader(neoVehicle_PS_cso); assert(neoVehicle_PS); +#include "shaders/obj/leedsDefault_ENV_VS.inc" + leedsVehicle_VS = rw::d3d::createVertexShader(leedsDefault_ENV_VS_cso); + assert(leedsVehicle_VS); + +#include "shaders/obj/leedsVehicle_mobile_VS.inc" + leedsVehicle_mobile_VS = rw::d3d::createVertexShader(leedsVehicle_mobile_VS_cso); + assert(leedsVehicle_mobile_VS); + +#include "shaders/obj/leedsDefault_BLEND_PS.inc" + leedsVehicle_blend_PS = rw::d3d::createPixelShader(leedsDefault_BLEND_PS_cso); + assert(leedsVehicle_blend_PS); + +#include "shaders/obj/leedsDefault_ADD_PS.inc" + leedsVehicle_add_PS = rw::d3d::createPixelShader(leedsDefault_ADD_PS_cso); + assert(leedsVehicle_add_PS); + +#include "shaders/obj/leedsVehicle_mobile_PS.inc" + leedsVehicle_mobile_PS = rw::d3d::createPixelShader(leedsVehicle_mobile_PS_cso); + assert(leedsVehicle_mobile_PS); + rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::defaultInstanceCB; @@ -175,6 +401,15 @@ DestroyVehiclePipe(void) rw::d3d::destroyPixelShader(neoVehicle_PS); neoVehicle_PS = nil; + rw::d3d::destroyVertexShader(leedsVehicle_VS); + leedsVehicle_VS = nil; + + rw::d3d::destroyPixelShader(leedsVehicle_blend_PS); + leedsVehicle_blend_PS = nil; + + rw::d3d::destroyPixelShader(leedsVehicle_add_PS); + leedsVehicle_add_PS = nil; + ((rw::d3d9::ObjPipeline*)vehiclePipe)->destroy(); vehiclePipe = nil; } @@ -182,11 +417,12 @@ DestroyVehiclePipe(void) /* - * Neo World pipe + * Leeds World pipe */ -static void *neoWorld_VS; -static void *neoWorldVC_PS; +static void *leedsBuilding_VS; +static void *leedsBuilding_mobile_VS; +static void *scale_PS; static void worldRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) @@ -195,79 +431,64 @@ worldRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) using namespace rw::d3d; using namespace rw::d3d9; - if(!LightmapEnable){ - defaultRenderCB_Shader(atomic, header); - return; - } - - int vsBits; setStreamSource(0, header->vertexStream[0].vertexBuffer, 0, header->vertexStream[0].stride); setIndices(header->indexBuffer); setVertexDeclaration(header->vertexDeclaration); - vsBits = lightingCB_Shader(atomic); + if(CustomPipes::WorldPipeSwitch == CustomPipes::WORLDPIPE_MOBILE) + setVertexShader(CustomPipes::leedsBuilding_mobile_VS); + else + setVertexShader(CustomPipes::leedsBuilding_VS); + setPixelShader(scale_PS); + uploadMatrices(atomic->getFrame()->getLTM()); + uploadWorldLights(); - float lightfactor[4]; + float colorscale[4]; + colorscale[3] = 1.0f; InstanceData *inst = header->inst; for(rw::uint32 i = 0; i < header->numMeshes; i++){ Material *m = inst->material; - if(MatFX::getEffects(m) == MatFX::DUAL){ - setVertexShader(neoWorld_VS); - - MatFX *matfx = MatFX::get(m); - Texture *dualtex = matfx->getDualTexture(); - if(dualtex == nil) - goto notex; - d3d::setTexture(1, dualtex); - lightfactor[0] = lightfactor[1] = lightfactor[2] = WorldLightmapBlend.Get()*LightmapMult; - }else{ - notex: - setVertexShader(default_amb_VS); - - d3d::setTexture(1, nil); - lightfactor[0] = lightfactor[1] = lightfactor[2] = 0.0f; - } - lightfactor[3] = m->color.alpha/255.0f; - d3d::setTexture(0, m->texture); - d3ddevice->SetPixelShaderConstantF(1, lightfactor, 1); - - SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); - - RGBA color = { 255, 255, 255, m->color.alpha }; - setMaterial(color, m->surfaceProps); + float cs = 1.0f; + if(WorldPipeSwitch != WORLDPIPE_MOBILE && m->texture) + cs = 255/128.0f; + colorscale[0] = colorscale[1] = colorscale[2] = cs; + d3ddevice->SetPixelShaderConstantF(PSLOC_colorscale, colorscale, 1); if(m->texture) d3d::setTexture(0, m->texture); else - d3d::setTexture(0, gpWhiteTexture); - setPixelShader(neoWorldVC_PS); + d3d::setTexture(0, gpWhiteTexture); // actually we don't even render this + + setMaterial(m->color, m->surfaceProps, WorldPipeSwitch == WORLDPIPE_PS2 ? 0.5f : 1.0f); + + SetRenderState(VERTEXALPHA, inst->vertexAlpha || m->color.alpha != 255); drawInst(header, inst); inst++; } - d3d::setTexture(1, nil); } void CreateWorldPipe(void) { - if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) - printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n"); - else - ReadTweakValueTable((char*)work_buff, WorldLightmapBlend); - -#include "shaders/default_UV2_VS.inc" - neoWorld_VS = rw::d3d::createVertexShader(default_UV2_VS_cso); - assert(neoWorld_VS); - -#include "shaders/neoWorldVC_PS.inc" - neoWorldVC_PS = rw::d3d::createPixelShader(neoWorldVC_PS_cso); - assert(neoWorldVC_PS); - +// if(CFileMgr::LoadFile("neo/worldTweakingTable.dat", work_buff, sizeof(work_buff), "r") <= 0) +// printf("Error: couldn't open 'neo/worldTweakingTable.dat'\n"); +// else +// ReadTweakValueTable((char*)work_buff, WorldLightmapBlend); + +#include "shaders/obj/leedsBuilding_VS.inc" + leedsBuilding_VS = rw::d3d::createVertexShader(leedsBuilding_VS_cso); + assert(leedsBuilding_VS); +#include "shaders/obj/leedsBuilding_mobile_VS.inc" + leedsBuilding_mobile_VS = rw::d3d::createVertexShader(leedsBuilding_mobile_VS_cso); + assert(leedsBuilding_mobile_VS); +#include "shaders/obj/scale_PS.inc" + scale_PS = rw::d3d::createPixelShader(scale_PS_cso); + assert(scale_PS); rw::d3d9::ObjPipeline *pipe = rw::d3d9::ObjPipeline::create(); pipe->instanceCB = rw::d3d9::defaultInstanceCB; @@ -279,10 +500,12 @@ CreateWorldPipe(void) void DestroyWorldPipe(void) { - rw::d3d::destroyVertexShader(neoWorld_VS); - neoWorld_VS = nil; - rw::d3d::destroyPixelShader(neoWorldVC_PS); - neoWorldVC_PS = nil; + rw::d3d::destroyVertexShader(leedsBuilding_VS); + leedsBuilding_VS = nil; + rw::d3d::destroyVertexShader(leedsBuilding_mobile_VS); + leedsBuilding_mobile_VS = nil; + rw::d3d::destroyPixelShader(scale_PS); + scale_PS = nil; ((rw::d3d9::ObjPipeline*)worldPipe)->destroy(); @@ -347,11 +570,11 @@ glossRenderCB(rw::Atomic *atomic, rw::d3d9::InstanceDataHeader *header) void CreateGlossPipe(void) { -#include "shaders/neoGloss_VS.inc" +#include "shaders/obj/neoGloss_VS.inc" neoGloss_VS = rw::d3d::createVertexShader(neoGloss_VS_cso); assert(neoGloss_VS); -#include "shaders/neoGloss_PS.inc" +#include "shaders/obj/neoGloss_PS.inc" neoGloss_PS = rw::d3d::createPixelShader(neoGloss_PS_cso); assert(neoGloss_PS); @@ -513,11 +736,11 @@ CreateRimLightPipes(void) } -#include "shaders/neoRim_VS.inc" +#include "shaders/obj/neoRim_VS.inc" neoRim_VS = rw::d3d::createVertexShader(neoRim_VS_cso); assert(neoRim_VS); -#include "shaders/neoRimSkin_VS.inc" +#include "shaders/obj/neoRimSkin_VS.inc" neoRimSkin_VS = rw::d3d::createVertexShader(neoRimSkin_VS_cso); assert(neoRimSkin_VS); @@ -553,12 +776,7 @@ DestroyRimLightPipes(void) } -#endif - #ifdef NEW_RENDERER -#ifndef LIBRW -#error "Need librw for NEW_PIPELINES" -#endif namespace WorldRender { @@ -616,10 +834,15 @@ AtomicFirstPass(RpAtomic *atomic, int pass) bool defer = false; SetMatrix(building, atomic->getFrame()->getLTM()); + float colorscale[4]; + InstanceData *inst = building->instHeader->inst; for(rw::uint32 i = 0; i < building->instHeader->numMeshes; i++, inst++){ Material *m = inst->material; + if(m->texture == nil) + continue; + if(inst->vertexAlpha || m->color.alpha != 255 || IsTextureTransparent(m->texture)){ defer = true; @@ -631,22 +854,29 @@ AtomicFirstPass(RpAtomic *atomic, int pass) setStreamSource(0, building->instHeader->vertexStream[0].vertexBuffer, 0, building->instHeader->vertexStream[0].stride); setIndices(building->instHeader->indexBuffer); setVertexDeclaration(building->instHeader->vertexDeclaration); - setVertexShader(default_amb_VS); - d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); - if(building->lighting) - setAmbient(pAmbient->color); + if(CustomPipes::WorldPipeSwitch == CustomPipes::WORLDPIPE_MOBILE) + setVertexShader(CustomPipes::leedsBuilding_mobile_VS); else - setAmbient(black); + setVertexShader(CustomPipes::leedsBuilding_VS); + setPixelShader(CustomPipes::scale_PS); + d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); + + CustomPipes::uploadWorldLights(); + + colorscale[3] = 1.0f; + setupDone = true; } - setMaterial(m->color, m->surfaceProps); + float cs = 1.0f; + if(CustomPipes::WorldPipeSwitch != CustomPipes::WORLDPIPE_MOBILE && m->texture) + cs = 255/128.0f; + colorscale[0] = colorscale[1] = colorscale[2] = cs; + d3ddevice->SetPixelShaderConstantF(CustomPipes::PSLOC_colorscale, colorscale, 1); - if(m->texture){ - d3d::setTexture(0, m->texture); - setPixelShader(default_tex_PS); - }else - setPixelShader(default_PS); + d3d::setTexture(0, m->texture); + + setMaterial(m->color, m->surfaceProps, CustomPipes::WorldPipeSwitch == CustomPipes::WORLDPIPE_PS2 ? 0.5f : 1.0f); drawInst(building->instHeader, inst); } @@ -680,7 +910,16 @@ RenderBlendPass(int pass) using namespace rw::d3d; using namespace rw::d3d9; - setVertexShader(default_amb_VS); + if(CustomPipes::WorldPipeSwitch == CustomPipes::WORLDPIPE_MOBILE) + setVertexShader(CustomPipes::leedsBuilding_mobile_VS); + else + setVertexShader(CustomPipes::leedsBuilding_VS); + setPixelShader(CustomPipes::scale_PS); + + CustomPipes::uploadWorldLights(); + + float colorscale[4]; + colorscale[3] = 1.0f; int i; for(i = 0; i < numBlendInsts[pass]; i++){ @@ -690,26 +929,26 @@ RenderBlendPass(int pass) setIndices(building->instHeader->indexBuffer); setVertexDeclaration(building->instHeader->vertexDeclaration); d3ddevice->SetVertexShaderConstantF(VSLOC_combined, (float*)&building->combinedMat, 4); - if(building->lighting) - setAmbient(pAmbient->color); - else - setAmbient(black); InstanceData *inst = building->instHeader->inst; for(rw::uint32 j = 0; j < building->instHeader->numMeshes; j++, inst++){ Material *m = inst->material; + if(m->texture == nil) + continue; if(!inst->vertexAlpha && m->color.alpha == 255 && !IsTextureTransparent(m->texture) && building->fadeAlpha == 255) continue; // already done this one + float cs = 1.0f; + if(CustomPipes::WorldPipeSwitch != CustomPipes::WORLDPIPE_MOBILE && m->texture) + cs = 255/128.0f; + colorscale[0] = colorscale[1] = colorscale[2] = cs; + d3ddevice->SetPixelShaderConstantF(CustomPipes::PSLOC_colorscale, colorscale, 1); + + d3d::setTexture(0, m->texture); + rw::RGBA color = m->color; color.alpha = (color.alpha * building->fadeAlpha)/255; - setMaterial(color, m->surfaceProps); - - if(m->texture){ - d3d::setTexture(0, m->texture); - setPixelShader(default_tex_PS); - }else - setPixelShader(default_PS); + setMaterial(color, m->surfaceProps, CustomPipes::WorldPipeSwitch == CustomPipes::WORLDPIPE_PS2 ? 0.5f : 1.0f); drawInst(building->instHeader, inst); } @@ -719,3 +958,4 @@ RenderBlendPass(int pass) #endif #endif +#endif |