From f0bc71af6df767fb113e940597afd4f0a7a1d090 Mon Sep 17 00:00:00 2001 From: XProger Date: Wed, 14 Oct 2020 06:32:13 +0400 Subject: [PATCH] XBOX port WIP --- src/controller.h | 2 +- src/core.h | 30 +- src/format.h | 12 +- src/gameflow.h | 1 + src/gapi/d3d8.h | 866 ++++++++++++++++++++++++++++++ src/inventory.h | 24 +- src/lang.h | 8 +- src/level.h | 29 +- src/libs/stb_vorbis/stb_vorbis.c | 2 +- src/mesh.h | 12 +- src/platform/xbox/OpenLara.sln | 30 ++ src/platform/xbox/OpenLara.vcproj | 473 ++++++++++++++++ src/platform/xbox/main.cpp | 336 ++++++++++++ src/platform/xbox/titleimage.xbx | Bin 0 -> 10240 bytes src/shader.h | 1 - src/shaders/ambient_room.asm | 72 +++ src/shaders/ambient_sprite.asm | 10 + src/shaders/common.asm | 73 +++ src/shaders/compile_d3d8.bat | 44 ++ src/shaders/compose_entity.asm | 93 ++++ src/shaders/compose_flash.asm | 37 ++ src/shaders/compose_mirror.asm | 40 ++ src/shaders/compose_room.asm | 72 +++ src/shaders/compose_sprite.asm | 94 ++++ src/shaders/filter.asm | 65 +++ src/shaders/gui.asm | 14 + src/shaders/shadow_entity.asm | 22 + src/sound.h | 2 +- src/utils.h | 48 +- 29 files changed, 2462 insertions(+), 50 deletions(-) create mode 100644 src/gapi/d3d8.h create mode 100644 src/platform/xbox/OpenLara.sln create mode 100644 src/platform/xbox/OpenLara.vcproj create mode 100644 src/platform/xbox/main.cpp create mode 100644 src/platform/xbox/titleimage.xbx create mode 100644 src/shaders/ambient_room.asm create mode 100644 src/shaders/ambient_sprite.asm create mode 100644 src/shaders/common.asm create mode 100644 src/shaders/compile_d3d8.bat create mode 100644 src/shaders/compose_entity.asm create mode 100644 src/shaders/compose_flash.asm create mode 100644 src/shaders/compose_mirror.asm create mode 100644 src/shaders/compose_room.asm create mode 100644 src/shaders/compose_sprite.asm create mode 100644 src/shaders/filter.asm create mode 100644 src/shaders/gui.asm create mode 100644 src/shaders/shadow_entity.asm diff --git a/src/controller.h b/src/controller.h index 4006a3f..bf1f56a 100644 --- a/src/controller.h +++ b/src/controller.h @@ -1523,7 +1523,7 @@ struct DummyController : Controller { DummyController(IGame *game, int entity) : Controller(game, entity) {} - virtual void update() override {}; + virtual void update() {} }; diff --git a/src/core.h b/src/core.h index d15f153..621e639 100644 --- a/src/core.h +++ b/src/core.h @@ -7,7 +7,6 @@ #include #include -#include #define OS_FILEIO_CACHE #define OS_PTHREAD_MT @@ -129,6 +128,15 @@ #define _GAPI_GL 1 #undef OS_PTHREAD_MT +#elif _XBOX + #define _OS_XBOX 1 + #define _GAPI_D3D8 1 + + #undef OS_PTHREAD_MT + + #define NOMINMAX + #include + #include #endif #ifndef _OS_PSP @@ -608,6 +616,7 @@ enum BlendMode { bmNone, bmAlpha, bmAdd, bmMult, bmPremult, bmMAX }; namespace Core { float eye; + float aspectFix = 1.0f; Texture *eyeTex[2]; short4 viewport, viewportDef, scissor; mat4 mModel, mView, mProj, mViewProj, mViewInv; @@ -617,7 +626,6 @@ namespace Core { vec4 lightPos[MAX_LIGHTS]; vec4 lightColor[MAX_LIGHTS]; vec4 params; - vec4 fogParams; vec4 contacts[MAX_CONTACTS]; struct LightStack { @@ -707,6 +715,8 @@ namespace Core { #include "gapi/sw.h" #elif _GAPI_GL #include "gapi/gl.h" +#elif _GAPI_D3D8 + #include "gapi/d3d8.h" #elif _GAPI_D3D9 #include "gapi/d3d9.h" #elif _GAPI_D3D11 @@ -978,6 +988,13 @@ namespace Core { settings.audio.reverb = false; #endif + #ifdef _OS_XBOX + settings.detail.setFilter (Core::Settings::HIGH); + settings.detail.setLighting (Core::Settings::LOW); + settings.detail.setShadows (Core::Settings::LOW); + settings.detail.setWater (Core::Settings::LOW); + #endif + memset(&active, 0, sizeof(active)); renderState = 0; @@ -1190,6 +1207,15 @@ namespace Core { Core::active.shader->setParam(uMaterial, Core::active.material); } + void setFog(const vec4 ¶ms) { + #ifdef _XBOX + GAPI::setFog(params); + #else + ASSERT(Core::active.shader); + Core::active.shader->setParam(uFogParams, params); + #endif + } + void updateLights() { GAPI::updateLights(lightPos, lightColor, MAX_LIGHTS); } diff --git a/src/format.h b/src/format.h index 2f1d8e2..0ac234a 100644 --- a/src/format.h +++ b/src/format.h @@ -3111,14 +3111,14 @@ namespace TR { int16 waterSplash; int16 glyphs; - struct { + struct Weapon { int16 items[MAX_WEAPONS]; int16& operator[] (Entity::Type type) { return items[getWeaponIndex(type)]; }; } weapons; - struct { + struct Inventory { int16 passport; int16 passport_closed; int16 map; @@ -3131,14 +3131,14 @@ namespace TR { int16 gamma; int16 explosive; - struct { + struct Weapon { int16 items[MAX_WEAPONS]; int16& operator[] (Entity::Type type) { return items[getWeaponIndex(type)]; }; } weapons; - struct { + struct Ammo { int16 items[MAX_WEAPONS]; int16& operator[] (Entity::Type type) { return items[getWeaponIndex(type)]; @@ -3910,7 +3910,7 @@ namespace TR { ((uint64)((const char*)(str))[4] << 32) | ((uint64)((const char*)(str))[5] << 40) | ((uint64)((const char*)(str))[6] << 48) | ((uint64)((const char*)(str))[7] << 56)) void readSAT(Stream &stream) { - #if !defined(_OS_PSP) && !defined(_OS_3DS) + #if !defined(_OS_PSP) && !defined(_OS_3DS) && !defined(_OS_XBOX) Room *room = NULL; while (stream.pos < stream.size) { @@ -6224,7 +6224,7 @@ namespace TR { if (animTexBlockSize) { uint16 *animTexBlock = new uint16[animTexBlockSize]; - for (int i = 0; i < animTexBlockSize; i++) { + for (uint32 i = 0; i < animTexBlockSize; i++) { animTexBlock[i] = stream.readLE16(); } diff --git a/src/gameflow.h b/src/gameflow.h index 630e673..4b63918 100644 --- a/src/gameflow.h +++ b/src/gameflow.h @@ -1695,6 +1695,7 @@ namespace TR { } #define FOG_DIST (1.0f / (18 * 1024)) + #define FOG_NONE vec4(0.0f, 0.0f, 0.0f, 0.0f) #define FOG_BLACK vec4(0.0f, 0.0f, 0.0f, FOG_DIST) #define FOG_SANDY vec4(0.2f, 0.1f, 0.0f, FOG_DIST) #define FOG_GREEN vec4(0.0f, 0.1f, 0.0f, FOG_DIST) diff --git a/src/gapi/d3d8.h b/src/gapi/d3d8.h new file mode 100644 index 0000000..fe01446 --- /dev/null +++ b/src/gapi/d3d8.h @@ -0,0 +1,866 @@ +#ifndef H_GAPI_D3D8 +#define H_GAPI_D3D8 + +#include "core.h" + +#define PROFILE_MARKER(title) +#define PROFILE_LABEL(id, name, label) +#define PROFILE_TIMING(time) + +extern LPDIRECT3D8 D3D; +extern LPDIRECT3DDEVICE8 device; +extern D3DPRESENT_PARAMETERS d3dpp; + +#ifdef _DEBUG +void D3DCHECK(HRESULT res) { + if (!FAILED(res)) return; + + LOG("! "); + switch (res) { + case D3DERR_WRONGTEXTUREFORMAT : LOG("D3DERR_WRONGTEXTUREFORMAT"); break; + case D3DERR_UNSUPPORTEDCOLOROPERATION : LOG("D3DERR_UNSUPPORTEDCOLOROPERATION"); break; + case D3DERR_UNSUPPORTEDCOLORARG : LOG("D3DERR_UNSUPPORTEDCOLORARG"); break; + case D3DERR_UNSUPPORTEDALPHAOPERATION : LOG("D3DERR_UNSUPPORTEDALPHAOPERATION"); break; + case D3DERR_UNSUPPORTEDALPHAARG : LOG("D3DERR_UNSUPPORTEDALPHAARG"); break; + case D3DERR_TOOMANYOPERATIONS : LOG("D3DERR_TOOMANYOPERATIONS"); break; + case D3DERR_CONFLICTINGTEXTUREFILTER : LOG("D3DERR_CONFLICTINGTEXTUREFILTER"); break; + case D3DERR_UNSUPPORTEDFACTORVALUE : LOG("D3DERR_UNSUPPORTEDFACTORVALUE"); break; + case D3DERR_CONFLICTINGRENDERSTATE : LOG("D3DERR_CONFLICTINGRENDERSTATE"); break; + case D3DERR_UNSUPPORTEDTEXTUREFILTER : LOG("D3DERR_UNSUPPORTEDTEXTUREFILTER"); break; + case D3DERR_CONFLICTINGTEXTUREPALETTE : LOG("D3DERR_CONFLICTINGTEXTUREPALETTE"); break; + case D3DERR_DRIVERINTERNALERROR : LOG("D3DERR_DRIVERINTERNALERROR"); break; + case D3DERR_NOTFOUND : LOG("D3DERR_NOTFOUND"); break; + case D3DERR_MOREDATA : LOG("D3DERR_MOREDATA"); break; + case D3DERR_DEVICELOST : LOG("D3DERR_DEVICELOST"); break; + case D3DERR_DEVICENOTRESET : LOG("D3DERR_DEVICENOTRESET"); break; + case D3DERR_NOTAVAILABLE : LOG("D3DERR_NOTAVAILABLE"); break; + case D3DERR_OUTOFVIDEOMEMORY : LOG("D3DERR_OUTOFVIDEOMEMORY"); break; + case D3DERR_INVALIDDEVICE : LOG("D3DERR_INVALIDDEVICE"); break; + case D3DERR_INVALIDCALL : LOG("D3DERR_INVALIDCALL"); break; + default : LOG("D3DERR_UNKNOWN"); break; + } + LOG("\n"); + ASSERT(false); +} +#else + #define D3DCHECK(res) res +#endif + +namespace GAPI { + using namespace Core; + + typedef ::Vertex Vertex; + + int cullMode, blendMode; + uint32 clearColor; + + LPDIRECT3DSURFACE8 defRT, defDS; + + const DWORD vertexDecl[] = + { + D3DVSD_STREAM(0), + D3DVSD_REG( aCoord, D3DVSDT_SHORT4 ), + D3DVSD_REG( aNormal, D3DVSDT_NORMSHORT4 ), + D3DVSD_REG( aTexCoord, D3DVSDT_NORMSHORT4 ), + D3DVSD_REG( aColor, D3DVSDT_PBYTE4 ), + D3DVSD_REG( aLight, D3DVSDT_PBYTE4 ), + D3DVSD_END() + }; + + struct Texture; + struct Mesh; + + struct Resource { + Texture *texture; + Mesh *mesh; + } resList[256]; + int resCount; + + void registerResource(Mesh *mesh) { + resList[resCount].mesh = mesh; + resList[resCount].texture = NULL; + resCount++; + } + + void registerResource(Texture *texture) { + resList[resCount].mesh = NULL; + resList[resCount].texture = texture; + resCount++; + } + + void unregisterResource(void *res) { + for (int i = 0; i < resCount; i++) + if (resList[i].mesh == res || resList[i].texture == res) { + resList[i] = resList[--resCount]; + break; + } + } + +// Shader + #include "shaders/d3d8/shaders.h" + + enum { + USAGE_VS, + USAGE_PS, + }; + + static const struct Binding { + int reg; + int usage; + } bindings[uMAX] = { + { 0, USAGE_VS | USAGE_PS }, // uParam + { 1, USAGE_VS | USAGE_PS }, // uTexParam + { 2, USAGE_VS | USAGE_PS }, // uViewProj + { 6, USAGE_VS | USAGE_PS }, // uBasis + { 70, USAGE_VS | USAGE_PS }, // uLightProj + { 74, USAGE_VS | USAGE_PS }, // uMaterial + { 75, USAGE_VS | USAGE_PS }, // uAmbient + { 81, USAGE_VS | USAGE_PS }, // uFogParams + { 82, USAGE_VS | USAGE_PS }, // uViewPos + { 83, USAGE_VS | USAGE_PS }, // uLightPos + { 87, USAGE_VS | USAGE_PS }, // uLightColor + { 91, USAGE_VS | USAGE_PS }, // uRoomSize + { 92, USAGE_VS | USAGE_PS }, // uPosScale + { 98, USAGE_VS | USAGE_PS }, // uContacts + }; + + struct Shader { + DWORD VS; + DWORD PS; + + Shader() : VS(NULL), PS(NULL) {} + + void init(Core::Pass pass, int type, int *def, int defCount) { + bool underwater = false; + + for (int i = 0; i < defCount; i++) { + if (def[i] == SD_UNDERWATER) { + underwater = true; + } + } + + #define SHADER(S,P) S##_##P + #define SHADER_U(S,P) (underwater ? SHADER(S##_u,P) : SHADER(S,P)) + + const uint8 *vSrc, *fSrc; + switch (pass) { + case passCompose : + switch (type) { + case 0 : vSrc = SHADER_U ( compose_sprite, v ); fSrc = SHADER_U ( compose_sprite, f ); break; + case 1 : vSrc = SHADER ( compose_flash, v ); fSrc = SHADER ( compose_flash, f ); break; + case 2 : vSrc = SHADER_U ( compose_room, v ); fSrc = SHADER_U ( compose_room, f ); break; + case 3 : vSrc = SHADER_U ( compose_entity, v ); fSrc = SHADER_U ( compose_entity, f ); break; + case 4 : vSrc = SHADER ( compose_mirror, v ); fSrc = SHADER ( compose_mirror, f ); break; + default : ASSERT(false); + } + break; + case passShadow : + switch (type) { + case 3 : + case 4 : vSrc = SHADER ( shadow_entity, v ); fSrc = SHADER ( shadow_entity, f ); break; + default : ASSERT(false); + } + break; + case passAmbient : + switch (type) { + case 0 : vSrc = SHADER ( ambient_sprite, v ); fSrc = SHADER ( ambient_sprite, f ); break; + case 1 : vSrc = SHADER ( ambient_room, v ); fSrc = SHADER ( ambient_room, f ); break; // TYPE_FLASH (sky) + case 2 : vSrc = SHADER ( ambient_room, v ); fSrc = SHADER ( ambient_room, f ); break; + default : ASSERT(false); + } + break; + case passSky : vSrc = SHADER ( gui, v ); fSrc = SHADER ( gui, f ); break; // TODO + /* + case passWater : + switch (type) { + case 0 : vSrc = SHADER ( water_drop, v ); fSrc = SHADER ( water_drop, f ); break; + case 1 : vSrc = SHADER ( water_simulate, v ); fSrc = SHADER ( water_simulate, f ); break; + case 2 : vSrc = SHADER ( water_caustics, v ); fSrc = SHADER ( water_caustics, f ); break; + case 3 : vSrc = SHADER ( water_rays, v ); fSrc = SHADER ( water_rays, f ); break; + case 4 : vSrc = SHADER ( water_mask, v ); fSrc = SHADER ( water_mask, f ); break; + case 5 : vSrc = SHADER ( water_compose, v ); fSrc = SHADER ( water_compose, f ); break; + default : ASSERT(false); + } + break; + */ + case passFilter : + switch (type) { + case 0 : vSrc = SHADER ( filter_upscale, v ); fSrc = SHADER ( filter_upscale, f ); break; + case 1 : vSrc = SHADER ( filter_downsample, v ); fSrc = SHADER ( filter_downsample, f ); break; + case 3 : vSrc = SHADER ( filter_grayscale, v ); fSrc = SHADER ( filter_grayscale, f ); break; + case 4 : vSrc = SHADER ( filter_blur, v ); fSrc = SHADER ( filter_blur, f ); break; + case 5 : vSrc = SHADER ( filter_blur, v ); fSrc = SHADER ( filter_blur, f ); break; // TODO anaglyph + default : ASSERT(false); + } + break; + case passGUI : vSrc = SHADER ( gui, v ); fSrc = SHADER ( gui, f ); break; + default : ASSERT(false); LOG("! wrong pass id\n"); return; + } + + #undef SHADER + #undef SHADER_U + + device->CreateVertexShader(vertexDecl, (DWORD*)vSrc, &VS, 0); + device->CreatePixelShader(&((D3DPIXELSHADERDEF_FILE*)fSrc)->Psd, &PS); + } + + void deinit() { + device->SetVertexShader(0); + device->SetPixelShader(0); + if (VS) device->DeleteVertexShader(VS); + if (PS) device->DeletePixelShader(PS); + } + + void bind() { + if (Core::active.shader != this) { + Core::active.shader = this; + device->SetVertexShader(VS); + device->SetPixelShader(PS); + } + } + + void setConstant(UniformType uType, const float *value, int vectors) { + const Binding &b = bindings[uType]; + if (b.usage | USAGE_VS) device->SetVertexShaderConstant (b.reg, value, vectors); +// if (b.usage | USAGE_PS) device->SetPixelShaderConstant (b.reg, value, vectors); + } + + void setParam(UniformType uType, const vec4 &value, int count = 1) { + setConstant(uType, (float*)&value, count); + } + + void setParam(UniformType uType, const mat4 &value, int count = 1) { + setConstant(uType, (float*)&value, count * 4); + } + }; + +// Texture + struct Texture { + LPDIRECT3DTEXTURE8 tex2D; + LPDIRECT3DCUBETEXTURE8 texCube; + + int width, height, depth, origWidth, origHeight, origDepth; + TexFormat fmt; + uint32 opt; + D3DFORMAT d3dformat; + + Texture(int width, int height, int depth, uint32 opt) : tex2D(NULL), texCube(NULL), width(width), height(height), depth(depth), origWidth(width), origHeight(height), origDepth(depth), fmt(FMT_RGBA), opt(opt) {} + + void init(void *data) { + ASSERT((opt & OPT_PROXY) == 0); + + bool isDepth = fmt == FMT_DEPTH || fmt == FMT_SHADOW; + bool mipmaps = (opt & OPT_MIPMAPS) != 0; + bool cube = (opt & OPT_CUBEMAP) != 0; + bool dynamic = (opt & OPT_DYNAMIC) != 0; + bool isTarget = (opt & OPT_TARGET) != 0 && !isDepth; + + static const struct FormatDesc { + int bpp; + D3DFORMAT format; + } formats[FMT_MAX] = { + { 8, D3DFMT_L8 }, + { 32, D3DFMT_A8R8G8B8 }, + { 16, D3DFMT_R5G6B5 }, + { 16, D3DFMT_A1R5G5B5 }, + { 64, D3DFMT_A8R8G8B8 }, // TODO + { 32, D3DFMT_A8R8G8B8 }, // TODO + { 16, D3DFMT_LIN_D16 }, + { 16, D3DFMT_LIN_D16 }, + }; + + FormatDesc desc = formats[fmt]; + + if (isTarget) { + + } + + uint32 usage = 0; + if (isDepth) usage |= D3DUSAGE_DEPTHSTENCIL; + if (isTarget) usage |= D3DUSAGE_RENDERTARGET; + + D3DPOOL pool = (isTarget || isDepth) ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + + d3dformat = desc.format; + + if (cube) { + D3DCHECK(device->CreateCubeTexture(width, 1, usage, desc.format, pool, &texCube)); + } else { + D3DCHECK(device->CreateTexture(width, height, mipmaps ? 4 : 1, usage, desc.format, pool, &tex2D)); + if (data && !isTarget) { + update(data); + } + } + + if (pool != D3DPOOL_MANAGED) + registerResource(this); + } + + void deinit() { + unregisterResource(this); + if (tex2D) tex2D->Release(); + if (texCube) texCube->Release(); + } + + VOID XBUtil_SwizzleTexture2D( D3DLOCKED_RECT* pLock, const D3DSURFACE_DESC* pDesc ) + { + DWORD dwPixelSize = XGBytesPerPixelFromFormat( pDesc->Format ); + DWORD dwTextureSize = pDesc->Width * pDesc->Height * dwPixelSize; + + BYTE* pSrcBits = new BYTE[ dwTextureSize ]; + memcpy( pSrcBits, pLock->pBits, dwTextureSize ); + + XGSwizzleRect( pSrcBits, 0, NULL, pLock->pBits, + pDesc->Width, pDesc->Height, + NULL, dwPixelSize ); + + delete[] pSrcBits; + } + + void updateLevel(int32 level, void *data) { + int32 bpp; + switch (fmt) { + case FMT_LUMINANCE : bpp = 1; break; + case FMT_RGBA : bpp = 4; break; + default : ASSERT(false); + } + + int32 w = width >> level; + int32 h = height >> level; + int32 ow = origWidth >> level; + int32 oh = origHeight >> level; + + uint8 *buffer = new uint8[w * h * bpp]; + + if (fmt == FMT_RGBA) { + uint8 *src = (uint8*)data; + uint8 *dst = buffer; + + for (int y = 0; y < oh; y++) { + uint8 *ptr = dst; + for (int x = 0; x < ow; x++) { + ptr[0] = src[2]; + ptr[1] = src[1]; + ptr[2] = src[0]; + ptr[3] = src[3]; + ptr += 4; + src += 4; + } + dst += w * 4; + } + } else { + if (w == ow && h == oh) { + memcpy(buffer, data, w * h * bpp); + } else { + uint8 *src = (uint8*)data; + uint8 *dst = buffer; + for (int y = 0; y < oh; y++) { + memcpy(dst, src, ow * bpp); + src += ow * bpp; + dst += w * bpp; + } + } + } + + D3DLOCKED_RECT rect; + D3DCHECK(tex2D->LockRect(level, &rect, NULL, 0)); + XGSwizzleRect(buffer, 0, NULL, rect.pBits, w, h, NULL, 4); + D3DCHECK(tex2D->UnlockRect(level)); + + delete[] buffer; + + if ((opt & OPT_MIPMAPS) && (level < 3)) { + ASSERT(fmt == FMT_RGBA); + + uint8 *mip = new uint8[(ow >> 1) * (oh >> 1) * 4]; + uint8 *dst = mip; + uint8 *src = (uint8*)data; + + for (int32 y = 0; y < oh; y += 2) { + for (int32 x = 0; x < ow; x += 2) { + *dst++ = (src[0] + src[4] + src[ow * 4] + src[ow * 4 + 4]) >> 2; src++; // R + *dst++ = (src[0] + src[4] + src[ow * 4] + src[ow * 4 + 4]) >> 2; src++; // G + *dst++ = (src[0] + src[4] + src[ow * 4] + src[ow * 4 + 4]) >> 2; src++; // B + *dst++ = (src[0] + src[4] + src[ow * 4] + src[ow * 4 + 4]) >> 2; src++; // A + src += 4; + } + src += ow * 4; + } + + updateLevel(level + 1, mip); + + delete[] mip; + } + } + + void update(void *data) { + updateLevel(0, data); + } + + void bind(int sampler) { + if (sampler > 3) return; // TODO + + if (opt & OPT_PROXY) return; + ASSERT(tex2D || texCube); + + if (Core::active.textures[sampler] != this) { + Core::active.textures[sampler] = this; + if (tex2D) { + device->SetTexture(sampler, tex2D); + /* TODO unsupported + if (opt & OPT_VERTEX) { + device->SetTexture(D3DVERTEXTEXTURESAMPLER0 + sampler, tex2D); + }*/ + } else if (texCube) { + device->SetTexture(sampler, texCube); + } + + bool filter = (Core::settings.detail.filter > Core::Settings::LOW) && !(opt & OPT_NEAREST); + bool mipmaps = (Core::settings.detail.filter > Core::Settings::LOW) && (opt & OPT_MIPMAPS); + bool aniso = (Core::settings.detail.filter > Core::Settings::MEDIUM) && mipmaps && (Core::support.maxAniso > 0); + + device->SetTextureStageState(sampler, D3DTSS_ADDRESSU, (opt & OPT_REPEAT) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); + device->SetTextureStageState(sampler, D3DTSS_ADDRESSV, (opt & OPT_REPEAT) ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); + + if (aniso) { + device->SetTextureStageState(sampler, D3DTSS_MINFILTER, D3DTEXF_ANISOTROPIC); + device->SetTextureStageState(sampler, D3DTSS_MAGFILTER, D3DTEXF_LINEAR); + device->SetTextureStageState(sampler, D3DTSS_MIPFILTER, D3DTEXF_NONE); + device->SetTextureStageState(sampler, D3DTSS_MAXANISOTROPY, support.maxAniso); + } else { + device->SetTextureStageState(sampler, D3DTSS_MINFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT); + device->SetTextureStageState(sampler, D3DTSS_MAGFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT); + device->SetTextureStageState(sampler, D3DTSS_MIPFILTER, mipmaps ? (filter ? D3DTEXF_LINEAR : D3DTEXF_POINT) : D3DTEXF_NONE); + device->SetTextureStageState(sampler, D3DTSS_MAXANISOTROPY, 1); + } + } + } + + void unbind(int sampler) { + if (Core::active.textures[sampler]) { + Core::active.textures[sampler] = NULL; + device->SetTexture(sampler, NULL); + } + } + + void generateMipMap() {} + + void setFilterQuality(int value) {} + }; + +// Mesh + struct Mesh { + LPDIRECT3DINDEXBUFFER8 IB; + LPDIRECT3DVERTEXBUFFER8 VB; + + int iCount; + int vCount; + bool dynamic; + + Mesh(bool dynamic) : IB(NULL), VB(NULL), dynamic(dynamic) {} + + void init(Index *indices, int iCount, ::Vertex *vertices, int vCount, int aCount) { + this->iCount = iCount; + this->vCount = vCount; + ASSERT(sizeof(GAPI::Vertex) == sizeof(::Vertex)); + + uint32 usage = D3DUSAGE_WRITEONLY | (dynamic ? D3DUSAGE_DYNAMIC : 0); + D3DPOOL pool = dynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + + D3DCHECK(device->CreateIndexBuffer (iCount * sizeof(Index), usage, D3DFMT_INDEX16, pool, &IB)); + D3DCHECK(device->CreateVertexBuffer (vCount * sizeof(Vertex), usage, D3DFMT_UNKNOWN, pool, &VB)); + + update(indices, iCount, vertices, vCount); + + if (pool != D3DPOOL_MANAGED) + registerResource(this); + } + + void deinit() { + unregisterResource(this); + IB->Release(); + VB->Release(); + } + + void update(Index *indices, int iCount, ::Vertex *vertices, int vCount) { + ASSERT(sizeof(GAPI::Vertex) == sizeof(::Vertex)); + + void* ptr; + int size; + + if (indices && iCount) { + size = iCount * sizeof(indices[0]); + D3DCHECK(IB->Lock(0, 0, (BYTE**)&ptr, 0)); + memcpy(ptr, indices, size); + D3DCHECK(IB->Unlock()); + } + + if (vertices && vCount) { + size = vCount * sizeof(vertices[0]); + D3DCHECK(VB->Lock(0, 0, (BYTE**)&ptr, 0)); + memcpy(ptr, vertices, size); + D3DCHECK(VB->Unlock()); + } + } + + void bind(const MeshRange &range) const { + device->SetIndices(IB, range.vStart); + device->SetStreamSource(0, VB, sizeof(Vertex)); + } + + void initNextRange(MeshRange &range, int &aIndex) const { + range.aIndex = -1; + } + }; + +// GLuint FBO, defaultFBO; + struct RenderTargetCache { + int count; + struct Item { + LPDIRECT3DSURFACE8 surface; + int width; + int height; + } items[MAX_RENDER_BUFFERS]; + } rtCache[2]; + + void init() { + memset(rtCache, 0, sizeof(rtCache)); + + D3DADAPTER_IDENTIFIER8 adapterInfo; + D3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &adapterInfo); + LOG("Vendor : %s\n", adapterInfo.Description); + LOG("Renderer : Direct3D 8\n"); + + support.maxAniso = 4; + support.maxVectors = 16; + support.shaderBinary = false; + support.VAO = false; // SHADOW_COLOR + support.depthTexture = false; // SHADOW_DEPTH + support.shadowSampler = false; + support.discardFrame = false; + support.texNPOT = false; + support.texRG = false; + support.texBorder = false; + support.colorFloat = false; + support.colorHalf = false; + support.texFloatLinear = false; + support.texFloat = false; + support.texHalfLinear = false; + support.texHalf = false; + + #ifdef PROFILE + support.profMarker = false; + support.profTiming = false; + #endif + + defRT = defDS = NULL; + + const float factors[] = { + 0.0f, 0.5f, 1.0f, 2.0f, + 0.6f, 0.9f, 0.9f, 32767.0f + }; + device->SetVertexShaderConstant(94, factors, 2); + + device->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_EXP); + } + + void deinit() { + if (defRT) defRT->Release(); + if (defDS) defDS->Release(); + } + + void resetDevice() { + // release dummy RTs + for (int i = 0; i < 2; i++) { + RenderTargetCache &cache = rtCache[i]; + for (int j = 0; j < cache.count; j++) + cache.items[j].surface->Release(); + cache.count = 0; + } + + // release texture RTs + int tmpCount = resCount; + Resource tmpList[256]; + memcpy(tmpList, resList, sizeof(Resource) * tmpCount); + + for (int i = 0; i < tmpCount; i++) { + Resource &res = tmpList[i]; + if (res.mesh) + res.mesh->deinit(); + else + res.texture->deinit(); + } + + if (defRT) defRT->Release(); + if (defDS) defDS->Release(); + + D3DCHECK(device->Reset(&d3dpp)); + device->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &defRT); + device->GetDepthStencilSurface(&defDS); + + // reinit texture RTs + for (int i = 0; i < tmpCount; i++) { + Resource &res = tmpList[i]; + if (res.mesh) + res.mesh->init(NULL, res.mesh->iCount, NULL, res.mesh->vCount, 0); + else + res.texture->init(NULL); + } + } + + inline mat4::ProjRange getProjRange() { + return mat4::PROJ_ZERO_POS; + } + + mat4 ortho(float l, float r, float b, float t, float znear, float zfar) { + mat4 m; + m.ortho(getProjRange(), l, r, b, t, znear, zfar); + return m; + } + + mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) { + mat4 m; + m.perspective(getProjRange(), fov, aspect, znear, zfar, eye); + return m; + } + + bool beginFrame() { + if (defRT == NULL) device->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &defRT); + if (defDS == NULL) device->GetDepthStencilSurface(&defDS); + + device->BeginScene(); + return true; + } + + void endFrame() { + device->EndScene(); + } + + void resetState() { + device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL); + device->SetRenderState(D3DRS_LIGHTING, FALSE); + } + + int cacheRenderTarget(bool depth, int width, int height) { + RenderTargetCache &cache = rtCache[depth]; + + for (int i = 0; i < cache.count; i++) + if (cache.items[i].width == width && cache.items[i].height == height) + return i; + + ASSERT(cache.count < MAX_RENDER_BUFFERS); + + RenderTargetCache::Item &item = cache.items[cache.count]; + item.width = width; + item.height = height; + + if (depth) + device->CreateDepthStencilSurface(width, height, D3DFMT_LIN_D16, D3DMULTISAMPLE_NONE, &item.surface); + else + device->CreateRenderTarget(width, height, D3DFMT_LIN_R5G6B5, D3DMULTISAMPLE_NONE, false, &item.surface); + + return cache.count++; + } + + void bindTarget(Texture *target, int face) { + if (!target) { // may be a null + D3DCHECK(device->SetRenderTarget(defRT, defDS)); + } else { + ASSERT(target->opt & OPT_TARGET); + + LPDIRECT3DSURFACE8 surface; + + bool depth = target->fmt == FMT_DEPTH || target->fmt == FMT_SHADOW; + + if (target->tex2D) { + D3DCHECK(target->tex2D->GetSurfaceLevel(0, &surface)); + } else if (target->texCube) { + D3DCHECK(target->texCube->GetCubeMapSurface(D3DCUBEMAP_FACES(D3DCUBEMAP_FACE_POSITIVE_X + face), 0, &surface)); + } + + int rtIndex = cacheRenderTarget(!depth, target->width, target->height); + + if (depth) { + D3DCHECK(device->SetRenderTarget(rtCache[false].items[rtIndex].surface, surface)); + } else { + D3DCHECK(device->SetRenderTarget(surface, rtCache[true].items[rtIndex].surface)); + } + + surface->Release(); + } + + Core::active.viewport = short4(0, 0, 0, 0); // forcing viewport reset + Core::active.scissor = short4(0, 0, 0, 0); + } + + void discardTarget(bool color, bool depth) {} + + void copyTarget(Texture *dst, int xOffset, int yOffset, int x, int y, int width, int height) { + ASSERT(dst && dst->tex2D); + + LPDIRECT3DSURFACE8 surface; + dst->tex2D->GetSurfaceLevel(0, &surface); + + RECT srcRect = { x, y, x + width, y + height }, + dstRect = { xOffset, yOffset, xOffset + width, yOffset + height }; + + //device->StretchRect(defRT, &srcRect, surface, &dstRect, D3DTEXF_POINT); TODO + + surface->Release(); + } + + void setVSync(bool enable) { + d3dpp.FullScreen_PresentationInterval = enable ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + GAPI::resetDevice(); + } + + void waitVBlank() {} + + void clear(bool color, bool depth) { + uint32 flags = (color ? D3DCLEAR_TARGET : 0) | (depth ? (D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL) : 0); + if (flags) { + device->Clear(0, NULL, flags, clearColor, 1.0f, 0); + } + } + + void setClearColor(const vec4 &color) { + clearColor = (int(color.x * 255) << 16) | + (int(color.y * 255) << 8 ) | + (int(color.z * 255) ) | + (int(color.w * 255) << 24); + } + + void setViewport(const short4 &v) { + D3DVIEWPORT8 viewport; + viewport.X = v.x; + viewport.Y = v.y; + viewport.Width = v.z; + viewport.Height = v.w; + viewport.MinZ = 0.0f; + viewport.MaxZ = 1.0f; + + device->SetViewport(&viewport); + } + + void setScissor(const short4 &s) { + D3DRECT scissor; + scissor.x1 = s.x; + scissor.y1 = active.viewport.w - (s.y + s.w); + scissor.x2 = s.x + s.z; + scissor.y2 = active.viewport.w - s.y; + + device->SetScissors(1, FALSE, &scissor); + } + + void setDepthTest(bool enable) { + device->SetRenderState(D3DRS_ZENABLE, enable ? D3DZB_TRUE : D3DZB_FALSE); + } + + void setDepthWrite(bool enable) { + device->SetRenderState(D3DRS_ZWRITEENABLE, enable ? TRUE : FALSE); + } + + void setColorWrite(bool r, bool g, bool b, bool a) { + device->SetRenderState(D3DRS_COLORWRITEENABLE, + (r ? D3DCOLORWRITEENABLE_RED : 0) | + (g ? D3DCOLORWRITEENABLE_GREEN : 0) | + (b ? D3DCOLORWRITEENABLE_BLUE : 0) | + (a ? D3DCOLORWRITEENABLE_ALPHA : 0)); + } + + void setAlphaTest(bool enable) { + device->SetTextureStageState(0, D3DTSS_ALPHAKILL, enable ? D3DTALPHAKILL_ENABLE : D3DTALPHAKILL_DISABLE); + } + + void setCullMode(int rsMask) { + cullMode = rsMask; + switch (rsMask) { + case RS_CULL_BACK : device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW); break; + case RS_CULL_FRONT : device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); break; + default : device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + } + } + + void setBlendMode(int rsMask) { + blendMode = rsMask; + switch (rsMask) { + case RS_BLEND_ALPHA : + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + break; + case RS_BLEND_ADD : + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE); + break; + case RS_BLEND_MULT : + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ZERO); + break; + case RS_BLEND_PREMULT : + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + break; + default : + device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + return; + } + device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + } + + void setViewProj(const mat4 &mView, const mat4 &mProj) { + device->SetTransform(D3DTS_PROJECTION, (D3DXMATRIX*)&mProj); + } + + void updateLights(vec4 *lightPos, vec4 *lightColor, int count) { + if (active.shader) { + active.shader->setParam(uLightColor, lightColor[0], count); + active.shader->setParam(uLightPos, lightPos[0], count); + } + } + + void setFog(const vec4 ¶ms) { + if (params.w > 0.0f) { + device->SetRenderState(D3DRS_FOGENABLE, TRUE); + + DWORD fogColor = 0xFF000000 + | (DWORD(clamp(params.z * 255.0f, 0.0f, 255.0f)) << 0) + | (DWORD(clamp(params.y * 255.0f, 0.0f, 255.0f)) << 8) + | (DWORD(clamp(params.x * 255.0f, 0.0f, 255.0f)) << 16); + device->SetRenderState(D3DRS_FOGCOLOR, fogColor); + + ASSERT(Core::active.shader); + Core::active.shader->setParam(uFogParams, params); // color.rgb, factor + } else { + device->SetRenderState(D3DRS_FOGENABLE, FALSE); + } + } + + void DIP(Mesh *mesh, const MeshRange &range) { + device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, mesh->vCount, range.iStart, range.iCount / 3); + } + + vec4 copyPixel(int x, int y) { + GAPI::Texture *t = Core::active.target; + ASSERT(t && t->tex2D); +/* + LPDIRECT3DSURFACE8 surface, texSurface; + D3DCHECK(t->tex2D->GetSurfaceLevel(0, &texSurface)); + D3DCHECK(device->CreateOffscreenPlainSurface(t->width, t->height, t->d3dformat, D3DPOOL_SYSTEMMEM, &surface, NULL)); + D3DCHECK(device->GetRenderTargetData(texSurface, surface)); + + RECT r = { x, y, x + 1, y + 1 }; + D3DLOCKED_RECT rect; + surface->LockRect(&rect, &r, D3DLOCK_READONLY); + ubyte4 c = *((ubyte4*)rect.pBits); + surface->UnlockRect(); + + texSurface->Release(); + surface->Release(); + + return vec4(float(c.z), float(c.y), float(c.x), float(c.w)) * (1.0f / 255.0f); + */ + return vec4(1.0f); + } +} + +#endif \ No newline at end of file diff --git a/src/inventory.h b/src/inventory.h index dec19cb..7debb50 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -35,16 +35,16 @@ struct OptionItem { TYPE_KEY, } type; StringID title; - intptr_t offset; + int32 offset; uint32 color; uint32 icon; uint8 maxValue; bool bar; - OptionItem(Type type = TYPE_EMPTY, int title = STR_EMPTY, intptr_t offset = 0, uint32 color = 0xFFFFFFFF, int icon = 0, uint8 maxValue = 0, bool bar = false) : type(type), title(StringID(title)), offset(offset), color(color), icon(icon), maxValue(maxValue), bar(bar) {} + OptionItem(Type type = TYPE_EMPTY, int title = STR_EMPTY, int32 offset = 0, uint32 color = 0xFFFFFFFF, int icon = 0, uint8 maxValue = 0, bool bar = false) : type(type), title(StringID(title)), offset(offset), color(color), icon(icon), maxValue(maxValue), bar(bar) {} void setValue(uint8 value, Core::Settings *settings) const { - *(uint8*)(intptr_t(settings) + offset) = value; + *((uint8*)settings + offset) = value; } bool checkValue(uint8 value) const { @@ -102,7 +102,7 @@ struct OptionItem { if (active) UI::renderBar(CTEX_OPTION, vec2(x, y - LINE_HEIGHT + 6), vec2(w, LINE_HEIGHT - 6), 1.0f, 0xFFD8377C, 0); - const uint8 &value = *(uint8*)(intptr_t(settings) + offset); + const uint8 &value = *((uint8*)settings + offset); switch (type) { case TYPE_TITLE : @@ -164,7 +164,7 @@ static const OptionItem optSound[] = { #endif }; -#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_CLOVER) || defined(_OS_PSC) +#if defined(_OS_PSP) || defined(_OS_PSV) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_CLOVER) || defined(_OS_PSC) || defined(_OS_XBOX) #define INV_GAMEPAD_ONLY #endif @@ -182,7 +182,7 @@ static const OptionItem optSound[] = { #define INV_CTRL_START_OPTION 2 #endif -#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_RPI) || defined(_OS_GCW0) +#if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_RPI) || defined(_OS_GCW0) || defined(_OS_XBOX) #define INV_VIBRATION #endif @@ -430,7 +430,7 @@ struct Inventory { case TR::Entity::INV_PASSPORT : if (value != 0) return NULL; optCount = optLoadSlots.length; - return optLoadSlots; + return optLoadSlots.items; case TR::Entity::INV_DETAIL : optCount = COUNT(optDetail); return optDetail; @@ -492,7 +492,7 @@ struct Inventory { opt = opt + slot; - uint8 &value = *(uint8*)(intptr_t(settings) + opt->offset); + uint8 &value = *((uint8*)settings + opt->offset); switch (key) { case cAction : return (opt->type == OptionItem::TYPE_BUTTON || opt->type == OptionItem::TYPE_KEY) ? opt : NULL; @@ -818,7 +818,7 @@ struct Inventory { } int pos = 0; - for (int pos = 0; pos < itemsCount; pos++) + for (; pos < itemsCount; pos++) if (items[pos]->type > type) break; @@ -1407,7 +1407,7 @@ struct Inventory { for (int i = 0; i < COUNT(background); i++) { if (!background[i]) { - background[i] = new Texture(INV_BG_SIZE, INV_BG_SIZE, 1, FMT_RGBA, OPT_TARGET); + background[i] = new Texture(INV_BG_SIZE, INV_BG_SIZE, 1, FMT_RGB16, OPT_TARGET); } } @@ -1748,7 +1748,7 @@ struct Inventory { aspectSrc = ax = ay = 1.0f; } - float aspectDst = float(Core::width) / float(Core::height); + float aspectDst = float(Core::width) / float(Core::height) * Core::aspectFix; float aspectImg = aspectSrc / aspectDst; #ifdef FFP @@ -2090,7 +2090,7 @@ struct Inventory { const char *bSelect = STR[STR_KEY_FIRST + ikEnter]; const char *bBack = STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[cInventory].key]; - #if defined(_OS_SWITCH) || defined(_OS_3DS) || defined(_OS_GCW0) + #if defined(_OS_SWITCH) || defined(_OS_3DS) || defined(_OS_GCW0) || defined(_OS_XBOX) bSelect = "A"; bBack = "B"; #endif diff --git a/src/lang.h b/src/lang.h index 69fda91..96baba1 100644 --- a/src/lang.h +++ b/src/lang.h @@ -252,6 +252,12 @@ enum StringID { , STR_MAX }; +#ifdef _XBOX // TODO: illegal escape sequence + #define STR_RUSSIAN "Russian" +#else + #define STR_RUSSIAN "Ðóññêè{è" +#endif + #define STR_LANGUAGES \ "English" \ , "Fran|cais" \ @@ -260,7 +266,7 @@ enum StringID { , "Italiano" \ , "Polski" \ , "Portugu(es" \ - , "Ðóññêè{è" \ + , STR_RUSSIAN \ , "\x11\x02\x70\x01\x97\x01\xD6\xFF\xFF" \ , "\x11\x01\x22\x01\x0F\x01\x0F\x01\x0E\x01\x06\x01\x04\x01\x0C\x01\x0B\xFF\xFF" \ , "Suomi" \ diff --git a/src/level.h b/src/level.h index 5ee5203..64c5ba4 100644 --- a/src/level.h +++ b/src/level.h @@ -551,22 +551,24 @@ struct Level : IGame { #endif vec4 material; - if (Core::pass == Core::passAmbient) { if (room.flags.water) { - Core::fogParams = underwaterFogParams; material = vec4(underwaterColor, 1.0f); } else { - Core::fogParams = levelFogParams; material = vec4(1.0f); } } else { - Core::fogParams = levelFogParams; material = vec4(diffuse, ambient, specular, alpha); } setShader(Core::pass, type, (Core::pass == Core::passAmbient) ? false : room.flags.water, alphaTest); + if (room.flags.water) { + Core::setFog(underwaterFogParams); + } else { + Core::setFog(levelFogParams); + } + #ifdef _GAPI_SW GAPI::setPalette(room.flags.water ? GAPI::swPaletteWater : GAPI::swPaletteColor); GAPI::setShading(true); @@ -2384,6 +2386,7 @@ struct Level : IGame { renderEntitiesTransp(transp); #ifndef FFP + Core::setFog(FOG_NONE); Core::whiteTex->bind(sDiffuse); Core::setBlendMode(bmMult); for (int i = 0; i < level.entitiesCount; i++) { @@ -2529,11 +2532,17 @@ struct Level : IGame { } void renderAdditive(RoomDesc *roomsList, int roomsCount) { - vec4 oldFog = Core::fogParams; - Core::fogParams = FOG_BLACK; // don't apply fog for additive + // don't apply fog for additive geometry + vec4 oldLevelFogParams = levelFogParams; + vec4 oldUnderwaterFogParams = underwaterFogParams; + levelFogParams = FOG_NONE; + underwaterFogParams = FOG_NONE; + renderRooms(roomsList, roomsCount, 2); renderEntities(2); - Core::fogParams = oldFog; + + levelFogParams = oldLevelFogParams; + underwaterFogParams = oldUnderwaterFogParams; } virtual void renderView(int roomIndex, bool water, bool showUI, int roomsCount = 0, RoomDesc *roomsList = NULL) { @@ -2612,6 +2621,8 @@ struct Level : IGame { if (camera->isUnderwater()) renderAdditive(roomsList, roomsCount); + Core::setFog(FOG_NONE); + Core::setBlendMode(bmNone); if (water && waterCache && waterCache->visible) { Core::Pass pass = Core::pass; @@ -2631,6 +2642,8 @@ struct Level : IGame { if (!camera->isUnderwater()) renderAdditive(roomsList, roomsCount); + Core::setFog(FOG_NONE); + Core::setBlendMode(bmNone); Core::Pass pass = Core::pass; @@ -3065,7 +3078,7 @@ struct Level : IGame { int vW = Core::width; int vH = Core::height; - float aspect = float(vW) / float(vH); + float aspect = float(vW) / float(vH) * Core::aspectFix; if (Core::defaultTarget) { vX = 0; diff --git a/src/libs/stb_vorbis/stb_vorbis.c b/src/libs/stb_vorbis/stb_vorbis.c index 759065b..13bb815 100644 --- a/src/libs/stb_vorbis/stb_vorbis.c +++ b/src/libs/stb_vorbis/stb_vorbis.c @@ -1296,7 +1296,7 @@ static void compute_window(int n, float *window) rotate(c, s, s, c); for (i=0; i < n2; ++i) { - window[i] = sinf((float)(0.5 * M_PI * (y * y))); + window[i] = (float)sin(0.5 * M_PI * (y * y)); rotate(x, y, s, c); } } diff --git a/src/mesh.h b/src/mesh.h index b41ed44..cf6e17e 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -302,12 +302,12 @@ struct MeshBuilder { }; const short4 boxCoords[] = { - {-1, -1, 1, 0}, { 1, -1, 1, 0}, { 1, 1, 1, 0}, {-1, 1, 1, 0}, - { 1, 1, 1, 0}, { 1, 1, -1, 0}, { 1, -1, -1, 0}, { 1, -1, 1, 0}, - {-1, -1, -1, 0}, { 1, -1, -1, 0}, { 1, 1, -1, 0}, {-1, 1, -1, 0}, - {-1, -1, -1, 0}, {-1, -1, 1, 0}, {-1, 1, 1, 0}, {-1, 1, -1, 0}, - { 1, 1, 1, 0}, {-1, 1, 1, 0}, {-1, 1, -1, 0}, { 1, 1, -1, 0}, - {-1, -1, -1, 0}, { 1, -1, -1, 0}, { 1, -1, 1, 0}, {-1, -1, 1, 0}, + short4(-1, -1, 1, 0), short4( 1, -1, 1, 0), short4( 1, 1, 1, 0), short4(-1, 1, 1, 0), + short4( 1, 1, 1, 0), short4( 1, 1, -1, 0), short4( 1, -1, -1, 0), short4( 1, -1, 1, 0), + short4(-1, -1, -1, 0), short4( 1, -1, -1, 0), short4( 1, 1, -1, 0), short4(-1, 1, -1, 0), + short4(-1, -1, -1, 0), short4(-1, -1, 1, 0), short4(-1, 1, 1, 0), short4(-1, 1, -1, 0), + short4( 1, 1, 1, 0), short4(-1, 1, 1, 0), short4(-1, 1, -1, 0), short4( 1, 1, -1, 0), + short4(-1, -1, -1, 0), short4( 1, -1, -1, 0), short4( 1, -1, 1, 0), short4(-1, -1, 1, 0), }; iCount += COUNT(boxIndices); diff --git a/src/platform/xbox/OpenLara.sln b/src/platform/xbox/OpenLara.sln new file mode 100644 index 0000000..e764153 --- /dev/null +++ b/src/platform/xbox/OpenLara.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenLara", "OpenLara.vcproj", "{9DC80A4E-C379-4FB3-994E-62C358071007}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Profile = Profile + Profile_FastCap = Profile_FastCap + Release = Release + Release_LTCG = Release_LTCG + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {9DC80A4E-C379-4FB3-994E-62C358071007}.Debug.ActiveCfg = Debug|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Debug.Build.0 = Debug|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Profile.ActiveCfg = Profile|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Profile.Build.0 = Profile|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Profile_FastCap.ActiveCfg = Profile_FastCap|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Profile_FastCap.Build.0 = Profile_FastCap|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Release.ActiveCfg = Release|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Release.Build.0 = Release|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Release_LTCG.ActiveCfg = Release_LTCG|Xbox + {9DC80A4E-C379-4FB3-994E-62C358071007}.Release_LTCG.Build.0 = Release_LTCG|Xbox + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/src/platform/xbox/OpenLara.vcproj b/src/platform/xbox/OpenLara.vcproj new file mode 100644 index 0000000..1ab81fd --- /dev/null +++ b/src/platform/xbox/OpenLara.vcproj @@ -0,0 +1,473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/platform/xbox/main.cpp b/src/platform/xbox/main.cpp new file mode 100644 index 0000000..54d1cd6 --- /dev/null +++ b/src/platform/xbox/main.cpp @@ -0,0 +1,336 @@ +#include "game.h" + +LPDIRECT3D8 D3D; +LPDIRECT3DDEVICE8 device; +D3DPRESENT_PARAMETERS d3dpp; + +// multi-threading +void* osMutexInit() { + CRITICAL_SECTION *CS = new CRITICAL_SECTION(); + InitializeCriticalSection(CS); + return CS; +} + +void osMutexFree(void *obj) { + DeleteCriticalSection((CRITICAL_SECTION*)obj); + delete (CRITICAL_SECTION*)obj; +} + +void osMutexLock(void *obj) { + EnterCriticalSection((CRITICAL_SECTION*)obj); +} + +void osMutexUnlock(void *obj) { + LeaveCriticalSection((CRITICAL_SECTION*)obj); +} + +// timing +LARGE_INTEGER timerFreq; +LARGE_INTEGER timerStart; + +int osGetTimeMS() { + LARGE_INTEGER time; + QueryPerformanceCounter(&time); + return int32((time.QuadPart - timerStart.QuadPart) * 1000L / timerFreq.QuadPart); +} + + +// input +struct JoyDevice { + HANDLE handle; + int time; + float vL, vR; + float oL, oR; + XINPUT_FEEDBACK feedback; +} joyDevice[INPUT_JOY_COUNT]; + +#define JOY_MIN_UPDATE_FX_TIME 50 +#define JOY_TRIGGER_THRESHOLD 30 + +void osJoyVibrate(int index, float L, float R) { + joyDevice[index].vL = L; + joyDevice[index].vR = R; +} + +void joyInit() { + XInitDevices(0, NULL); +} + +float joyTrigger(int32 value) { + return clamp(float(value - JOY_TRIGGER_THRESHOLD) / (255 - JOY_TRIGGER_THRESHOLD), 0.0f, 1.0f); +} + +void joyUpdate() { + static XINPUT_POLLING_PARAMETERS params = { TRUE, TRUE, 0, 8, 8, 0, }; + + DWORD dwInsertions, dwRemovals; + XGetDeviceChanges(XDEVICE_TYPE_GAMEPAD, &dwInsertions, &dwRemovals); + + for (DWORD i = 0; i < XGetPortCount(); i++) { + JoyDevice &joy = joyDevice[i]; + + if (dwInsertions & (1 << i)) { + memset(&joy, 0, sizeof(joy)); + joy.handle = XInputOpen(XDEVICE_TYPE_GAMEPAD, i, XDEVICE_NO_SLOT, ¶ms); + LOG("joy insert: %d\n", i); + } + + if (dwRemovals & (1 << i)) { + XInputClose(joy.handle); + joy.handle = NULL; + LOG("joy remove: %d\n", i); + } + + if (joy.handle) { + XINPUT_STATE state; + XInputGetState(joy.handle, &state); + + Input::setJoyPos(i, jkL, vec2(state.Gamepad.sThumbLX + 0.5f, -(state.Gamepad.sThumbLY + 0.5f)) / 32767.5f); + Input::setJoyPos(i, jkR, vec2(state.Gamepad.sThumbRX + 0.5f, -(state.Gamepad.sThumbRY + 0.5f)) / 32767.5f); + Input::setJoyPos(i, jkLT, vec2(state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE], 0.0f)); + Input::setJoyPos(i, jkRT, vec2(state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK], 0.0f)); + + Input::setJoyDown(i, jkA, state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_A] > 0); + Input::setJoyDown(i, jkB, state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_B] > 0); + Input::setJoyDown(i, jkX, state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_X] > 0); + Input::setJoyDown(i, jkY, state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_Y] > 0); + Input::setJoyDown(i, jkLB, joyTrigger(state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER]) > 0); + Input::setJoyDown(i, jkRB, joyTrigger(state.Gamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER]) > 0); + Input::setJoyDown(i, jkUp, (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) > 0); + Input::setJoyDown(i, jkDown, (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) > 0); + Input::setJoyDown(i, jkLeft, (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) > 0); + Input::setJoyDown(i, jkRight, (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) > 0); + Input::setJoyDown(i, jkStart, (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) > 0); + Input::setJoyDown(i, jkSelect, (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) > 0); + Input::setJoyDown(i, jkL, (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) > 0); + Input::setJoyDown(i, jkR, (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) > 0); + + if ((joy.vL != joy.oL || joy.vR != joy.oR) && Core::getTime() >= joy.time) { + if (joy.feedback.Header.dwStatus != ERROR_IO_PENDING) { + joy.feedback.Rumble.wLeftMotorSpeed = WORD(joy.vL * 65535.0f); + joy.feedback.Rumble.wRightMotorSpeed = WORD(joy.vR * 65535.0f); + joy.oL = joy.vL; + joy.oR = joy.vR; + joy.time = Core::getTime() + JOY_MIN_UPDATE_FX_TIME; + XInputSetState(joy.handle, &joy.feedback); + } + } + + } + } +} + + +// sound +#define SND_PACKETS 2 +#define SND_SAMPLES 2352 +#define SND_SIZE (SND_SAMPLES * sizeof(int16) * 2) + +LPDIRECTSOUND DSound; +LPDIRECTSOUNDSTREAM sndStream; +uint8* sndBuffer; + +void CALLBACK sndFill(VOID* pStreamContext, VOID* pPacketContext, DWORD dwStatus) { + if (Core::isQuit) return; + + if (dwStatus != XMEDIAPACKET_STATUS_SUCCESS) + return; + + int32 index = (int32)pPacketContext; + + XMEDIAPACKET packet = {0}; + packet.dwMaxSize = SND_SIZE; + packet.pvBuffer = sndBuffer + index * SND_SIZE; + packet.pContext = pPacketContext; + + Sound::fill((Sound::Frame*)packet.pvBuffer, SND_SIZE / 4); + + sndStream->Process(&packet, NULL); +} + +void sndInit() { + if (FAILED(DirectSoundCreate(NULL, &DSound, NULL))) + return; + + WAVEFORMATEX waveFmt = { WAVE_FORMAT_PCM, 2, 44100, 44100 * 4, 4, 16, sizeof(waveFmt) }; + + DSSTREAMDESC dssd = {0}; + dssd.dwMaxAttachedPackets = SND_PACKETS; + dssd.lpwfxFormat = &waveFmt; + dssd.lpfnCallback = sndFill; + + sndBuffer = (uint8*)XPhysicalAlloc(SND_SIZE * SND_PACKETS, MAXULONG_PTR, 0, PAGE_READWRITE | PAGE_NOCACHE); + if (!sndBuffer) + return; + memset(sndBuffer, 0, SND_SIZE * SND_PACKETS); + + if (FAILED(DirectSoundCreateStream(&dssd, &sndStream))) + return; + + XMEDIAPACKET packet = {0}; + packet.dwMaxSize = SND_SIZE; + for (int i = 0; i < SND_PACKETS; i++) { + packet.pvBuffer = sndBuffer + i * SND_SIZE; + packet.pContext = (VOID*)i; + sndStream->Process(&packet, NULL); + } +} + +void sndFree() { + if (sndStream) { + sndStream->Pause(DSSTREAMPAUSE_PAUSE); + sndStream->Discontinuity(); + sndStream->Flush(); + sndStream->Release(); + } + if (sndBuffer) { + XPhysicalFree(sndBuffer); + } +} + +void sndUpdate() { + DirectSoundDoWork(); +} + + +// context +struct { + int width; + int height; + bool prog; + bool wide; + + bool support(DWORD videoFlags) { + if (wide && !(videoFlags & XC_VIDEO_FLAGS_WIDESCREEN)) + return false; + if ((height == 480) && wide && !(videoFlags & XC_VIDEO_FLAGS_WIDESCREEN)) + return false; + if ((height == 480) && prog && !(videoFlags & XC_VIDEO_FLAGS_HDTV_480p)) + return false; + if ((height == 720) && !(videoFlags & XC_VIDEO_FLAGS_HDTV_720p)) + return false; + if ((height == 1080) && !(videoFlags & XC_VIDEO_FLAGS_HDTV_1080i)) + return false; + return true; + } +} displayModes[] = { + { 1920, 1080, false, true }, // 1920x1080 interlaced 16x9 + { 1280, 720, true, true }, // 1280x720 progressive 16x9 + { 720, 480, true, true }, // 720x480 progressive 16x9 + { 720, 480, false, true }, // 720x480 interlaced 16x9 + { 640, 480, true, true }, // 640x480 progressive 16x9 + { 640, 480, false, true }, // 640x480 interlaced 16x9 + { 720, 480, true, false }, // 720x480 progressive 4x3 + { 720, 480, false, false }, // 720x480 interlaced 4x3 + { 640, 480, true, false }, // 640x480 progressive 4x3 + { 640, 480, false, false }, // 640x480 interlaced 4x3 +}; + +HRESULT ContextCreate() { + D3D = Direct3DCreate8(D3D_SDK_VERSION); + + if (!D3D) return E_FAIL; + + memset(&d3dpp, 0, sizeof(d3dpp)); + d3dpp.BackBufferWidth = 640; + d3dpp.BackBufferHeight = 480; + + DWORD videoFlags = XGetVideoFlags(); + + for (int i = 0; i < COUNT(displayModes); i++) { + if (displayModes[i].support(videoFlags)) { + d3dpp.BackBufferWidth = displayModes[i].width; + d3dpp.BackBufferHeight = displayModes[i].height; + if (displayModes[i].wide) { + d3dpp.Flags |= D3DPRESENTFLAG_WIDESCREEN; + } + if (displayModes[i].prog) { + d3dpp.Flags |= D3DPRESENTFLAG_PROGRESSIVE; + } + break; + } + } + + float f = float(d3dpp.BackBufferWidth) / float(d3dpp.BackBufferHeight); + if (d3dpp.Flags & D3DPRESENTFLAG_WIDESCREEN) { + Core::aspectFix = (16.0f / 9.0f) / f; + } else { + Core::aspectFix = (4.0f / 3.0f) / f; + } + + d3dpp.BackBufferFormat = D3DFMT_LIN_X8R8G8B8; + d3dpp.BackBufferCount = 1; + d3dpp.EnableAutoDepthStencil = TRUE; + d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE_OR_IMMEDIATE; + + if (FAILED(D3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &device))) + return E_FAIL; + + Core::width = d3dpp.BackBufferWidth; + Core::height = d3dpp.BackBufferHeight; + + return S_OK; +} + +void ContextSwap() { + device->Present(NULL, NULL, NULL, NULL); +} + + +int checkLanguage() { + int str = STR_LANG_EN; + switch (XGetLanguage()) { + case XC_LANGUAGE_ENGLISH : str = STR_LANG_EN; break; + case XC_LANGUAGE_FRENCH : str = STR_LANG_FR; break; + case XC_LANGUAGE_GERMAN : str = STR_LANG_DE; break; + case XC_LANGUAGE_SPANISH : str = STR_LANG_ES; break; + case XC_LANGUAGE_ITALIAN : str = STR_LANG_IT; break; + case XC_LANGUAGE_PORTUGUESE : str = STR_LANG_PT; break; + case XC_LANGUAGE_JAPANESE : str = STR_LANG_JA; break; + case XC_LANGUAGE_TCHINESE : str = STR_LANG_CN; break; + } + return str - STR_LANG_EN; +} + +void main() +{ + strcpy(contentDir, "D:\\"); + + saveDir[0] = cacheDir[0] = 0; + if (XCreateSaveGame("U:\\", L"OpenLara", OPEN_ALWAYS, 0, saveDir, 256) == ERROR_SUCCESS) { + strcpy(cacheDir, saveDir); + } + + if (FAILED(ContextCreate())) + return; + + QueryPerformanceFrequency(&timerFreq); + QueryPerformanceCounter(&timerStart); + + Sound::channelsCount = 0; + + joyInit(); + sndInit(); + + Core::defLang = checkLanguage(); + + Game::init("PSXDATA\\LEVEL1.PSX"); // (char*)NULL); + + while (!Core::isQuit) { + joyUpdate(); + sndUpdate(); + + if (Game::update()) { + Game::render(); + ContextSwap(); + } + }; + + sndFree(); + Game::deinit(); + + LD_LAUNCH_DASHBOARD LaunchData = { XLD_LAUNCH_DASHBOARD_MAIN_MENU }; + XLaunchNewImage(NULL, (LAUNCH_DATA*)&LaunchData); +} diff --git a/src/platform/xbox/titleimage.xbx b/src/platform/xbox/titleimage.xbx new file mode 100644 index 0000000000000000000000000000000000000000..8598f21d288db3a2d1f48ae17596db157bf3d6a7 GIT binary patch literal 10240 zcmeHMUu;{|89%)p8osG($5WAN?P0YPjoe_In<+ze9$=2N36~|Q>B-HK;2V**7!uPc z!*UwiE71l`z#TN=shYK^igiu6nSxg|XdR(V%TTxEfxGO1DHczzN!D!IC02%W_I>yI zGM3R%1Su2by!?Frf4}eFIp?~kE7VNxCWPEc$N};d_+b{|&=X(9Uku9Je%aT+z6SO+ zu&;rA4eV>+3#9?~ubi9&rOV~y6iTUu^ByP1pl;#EweRk?>bvp0+g#T|tKZsZH~w;$ z99G@J`CaQ->+VKtoV6eJ7c@1Nl=rDuP%GI}i( zOC+Bh3M`40N-WkGaPZNdaQ7FHzr41_-kNsuR6EkpdhvYZiD)#|F=+48@4N3Vd!NlO zb-!I{5}aIr|EVtwf5Yw*)#UQH>F>{9=yc!lOil?+b_|xvH>$(H|E{~f{`&A>B=C0S z)y(0C@jlYuW6WDyt6K4b_<8!jm48qUh}&9T`)Gt~h0)KLw54Cb@p)V8rA~J~mm;rR z7#@_QL~&)bV^F7zq$BpeaJ-b7AGP(Bf`o5^3Ed*y=7RyVhr4^75m zv3UKNbVL!Zu8LOHi5qsjt$hGvG<*Q>Y2H8#L4#zq{zri|JH-9|93vM_O}q_ zNJ6<8Z>#;Y=AWN7gIIXdQqGlh$rbX z0`Tpp@qM_d{{XL84&G2^f zkM^1N{s;JZgY6)B>%z)tza+^{urJls)XJzDDZHkT&C%fqr)6&|j(7+E9E&GMB2&Tso_HMcLhy|#Z+;^L@!#DuVEWnC*5}3F z+~2&{Ft)n2Fx|}0wBBKBy|{28w#emvICpxIsvL)QmJ${4gUxX&D=Bd@9g!0;$oJPm zc+Mg64sYJu7YRZ<#BxC)AY5fq2kqNWHwA!DT_C4H~%jI4GesA1u`v2WvCQZ&@J7Dty;DJhSEw;ijK#x3pd@CqJ8}^20bGvJ#z0m~m6P=P@t-sj zp}+)aCrXvt{7w$S!H9=yb?ZG#p4-8H3=;2QBpM;xw(2Csc{JRRA%{`V;I zUMyB(_P(KDrc{Zezots@L@@BwovBh-o(lfP$$9hnDd5YiRf-67f#>hUOeIl3o(Wk6 zUbAApj*#o$c~HRjgN3}`=7(#thNxE=L;i3sq#pf&c^@$OS3kICV|}mue@vgzm&Xsg zyl{}=`BjO=;uyz3KYR`SxD<;A1Eu37uGj=h2VnDwTfZr)Y}HU3}ny6wGsS;c)Gq#LRmE z^l$AI|A$%(zdxdmp77T}SsC>X7K>5C?j^AMP;f~^J(MtBv)imOFe`FNO0ix6!4MfM zG$HT2*Ty05q`dmg2ZI~+<#?G05Z5x5VkrM>6IE>k{5ucv#zQ+dN9oE66kkv2mR#tA|aej06hvyIgagIQ-OL1?QSW~Uop?)aItm| zeA&A1?8Z-e|5@*Ka5FQltuIV>3|@cgje(BAp&8?rARQ_ z2<9F5@$vPepw{G)=zwfklFFi!Bd@N~Y$Mj2v{~`ZW6*!a*OzVd!uri8-aOX-s7`tG zTkxw-%m4AnVZODs!7ENq1;OpZxe)j}7<>uh`P8L4urJs-?*P1E1N#<{FDZs;9qb+3_VCaN$Y)ZtM;#rFW(bcKkD_)&3XGu$14e0 zDjlB{`!CyVCA3E>+K+KcwkbUX->YLadB>P0qnyasI-mC%ala4ef0RE5j)C}VK+6wq zl|QbB--+>FQm1Z=p`H6*yj%sn@?2_j&NN`0mEfn!lwtTvyMVaae^vhD`hZe_|5y zh9&pS@K)`Av-uy_Zsi#t=#5gnt`p(}KAR|Jy__;pu6-w_32Xevt$VY6YreTIz0z## zt8v!823A$YhjK_|rOSoqS>b-`|G%1_lU?gn&Go7^xoh(4s;3#g?*biS^e1ar6$q}c zsR|GK@l};4QMZpDW82wIS2M2`&Jk?iE-P6ax1%T-n9oCap+5K|{4nI+A;erCPDJFv zQh@OP9P~T*X8gX+HG}*~#>x)pU(LcesIvm_gLMQ&5xQJHUR9gwqV6sirG;GDK*mrW z1{)*(?0!L5IPu2=??XB7@$od7&5XDmfMr8&v=@$pcf2s-hVMD5q9CFih!U(b;&x)4 zjKDYs`j{6wVZSP$_H?;)#*<-B)P?P7q08u3K{AOcoby{aj_2yzV0}i2*ye+JmaMd3 zyOPEEaC;;svK^E|-Z^K=!xTR1cJN82WDWTqsmZA^1@)T~?gsfgT^f8ZLR8@0h4>D< zKS55{)q+HUud7hFa{t1^8wUS*&xm{CY+ZTY)9Gewm2V(+_x_x$2%T=YtpI|2HGZ%S z)JNP}LFG1E(jLkLf(Yg>Z?>p{T1@E6EomW~WSp}tEzqiuJK3(bXuf;Z=Jw?lm1N@0 z?av7u5%MS7oo(z~<{V=7EU&>hAsj2FHvO=!oT#LQv}dBQ4eNHgr83|2x0Vr*Cq@i8 z8SOdm?sT7fnlY50UD0UT6LlxIRUg)=%s3oJC9n%3gZC=(vE-i-$f?KDp6>4M_r|6*tTH<^OBS8D_B?l0~zCuI!k7Nmq*!7666QzFy(9?_AsXLUC2|T8E5;m;D&PQ zhW~LPvrRd~ZAmA~{(C&iT5oFE|9#J8rt^j#Y> d3d8/shaders.h + echo #include "%~1%~2_f.h" >> d3d8/shaders.h + xsasm /D VERTEX /O2 %~3 %~1.asm tmp + bin2c -n %~1%~2_v -o d3d8/%~1%~2_v.h tmp + xsasm /D PIXEL /O2 %~3 %~1.asm tmp + bin2c -n %~1%~2_f -o d3d8/%~1%~2_f.h tmp + ENDLOCAL +EXIT /B 0 + +:compile_u + SETLOCAL + call :compile %~1 + call :compile %~1 _u "/D UNDERWATER" + ENDLOCAL +EXIT /B 0 diff --git a/src/shaders/compose_entity.asm b/src/shaders/compose_entity.asm new file mode 100644 index 0000000..fc3bc22 --- /dev/null +++ b/src/shaders/compose_entity.asm @@ -0,0 +1,93 @@ +#include "common.asm" + +#ifdef VERTEX +; variables + #define lv0 r0 + #define lv1 r1 + #define lv2 r2 + #define lv3 r3 + #define normal r4 + #define att r5 + #define light r6 + #define pos r7 + + #define MAT_AMBIENT c[uMaterial].y + #define MAT_ALPHA c[uMaterial].w + +; joint = int(aCoord.w) + mov a0.x, aCoord.w + +; pos = mulQuatPos(uBasis[joint], aCoord) + mulQuatPos(pos, aCoord, a0.x) + +; vFog = length((uViewPos - pos) * uFogParams.w) + applyFog(vFog, pos) + +; vPosition = uViewProj * pos + mulMat(vPosition, pos, uViewProj) + +; lighting + ; lv[0..3] = (uLightPos[0..3].xyz - pos) * uLightColor[0..3].w; + add lv0.xyz, c[uLightPos + 0], -pos + add lv1.xyz, c[uLightPos + 1], -pos + add lv2.xyz, c[uLightPos + 2], -pos + add lv3.xyz, c[uLightPos + 3], -pos + mul lv0.xyz, c[uLightColor + 0].w, lv0 + mul lv1.xyz, c[uLightColor + 1].w, lv1 + mul lv2.xyz, c[uLightColor + 2].w, lv2 + mul lv3.xyz, c[uLightColor + 3].w, lv3 + + ; att[0..3] = dot(lv[0..3], lv[0..3]) + dp3 att.x, lv0, lv0 + dp3 att.y, lv1, lv1 + dp3 att.z, lv2, lv2 + dp3 att.w, lv3, lv3 + + ; att = max(0, 1 - att) / sqrt(att) + rsq tmp.x, att.x + rsq tmp.y, att.y + rsq tmp.z, att.z + rsq tmp.w, att.w + + add att, ONE, -att + max att, ZERO, att + mul att, tmp, att + + ; normal = mulQuat(uBasis[joint], aNormal) + mul pos.xyz, c[uBasis + a0.x], aNormal.zxyw + mad pos.xyz, aNormal, c[uBasis + a0.x].zxyw, -pos + mad pos.xyz, aNormal.yzxw, c[uBasis + a0.x].w, pos + mul tmp.xyz, c[uBasis + a0.x].zxyw, pos + mad pos.xyz, pos.yzxw, c[uBasis + a0.x].yzxw, -tmp + mad normal, pos, TWO, aNormal + + ; light = max(0, dot(normal, lv[0..3])) + dp3 light.x, lv0, normal + dp3 light.y, lv1, normal + dp3 light.z, lv2, normal + dp3 light.w, lv3, normal + + ; light = max(0, light) * att + max light, ZERO, light + mul light, light, att + +; vColor = aColor * material.alpha * (material.ambient + light[0..3] * uLightColor[0..3]) * 2 + mov pos, aColor + mul pos, MAT_ALPHA, pos + mov tmp.xyz, MAT_AMBIENT + mov tmp.w, ONE + mad tmp.xyz, light.x, c[uLightColor + 0], tmp + mad tmp.xyz, light.y, c[uLightColor + 1], tmp + mad tmp.xyz, light.z, c[uLightColor + 2], tmp + mad tmp.xyz, light.w, c[uLightColor + 3], tmp + mul tmp, pos, tmp + applyUnderwater(tmp) + add vColor, tmp, tmp + +; vTecCoord = (aTexCoord.xyz, 1) + mov vTexCoord, aTexCoord + mov vTexCoord.w, ONE +#else + tex t0 + mul r0, t0, vColor +#endif diff --git a/src/shaders/compose_flash.asm b/src/shaders/compose_flash.asm new file mode 100644 index 0000000..d6e0ca2 --- /dev/null +++ b/src/shaders/compose_flash.asm @@ -0,0 +1,37 @@ +#include "common.asm" + +#ifdef VERTEX +; variables + #define lv0 r0 + #define lv1 r1 + #define lv2 r2 + #define lv3 r3 + #define normal r4 + #define att r5 + #define light r6 + #define pos r7 + #define MAT_DIFFUSE c[uMaterial].x + #define MAT_EMISSIVE c[uMaterial].w + +; pos = mulQuatPos(uBasis[0], aCoord) + mulQuatPos(pos, aCoord, 0) + +; vFog = length((uViewPos - pos) * uFogParams.w) + applyFog(vFog, pos) + +; vPosition = uViewProj * pos + mulMat(vPosition, pos, uViewProj) + +; vColor = (material.diffuse * aColor.xyz * 2.0 + material.emissive, 1.0) + mul tmp, MAT_DIFFUSE, aColor + add tmp, tmp, tmp + add vColor, tmp, MAT_EMISSIVE + mov vColor.w, MAT_EMISSIVE + +; vTecCoord = (aTexCoord.xyz, 1) + mov vTexCoord, aTexCoord + mov vTexCoord.w, ONE +#else + tex t0 + mul r0, t0, vColor +#endif \ No newline at end of file diff --git a/src/shaders/compose_mirror.asm b/src/shaders/compose_mirror.asm new file mode 100644 index 0000000..fdd4459 --- /dev/null +++ b/src/shaders/compose_mirror.asm @@ -0,0 +1,40 @@ +#include "common.asm" + +#ifdef VERTEX +; variables + #define lv0 r0 + #define lv1 r1 + #define lv2 r2 + #define lv3 r3 + #define normal r4 + #define att r5 + #define light r6 + #define pos r7 + +; joint = int(aCoord.w) + mov a0.x, aCoord.w + +; pos = mulQuatPos(uBasis[joint], aCoord) + mulQuatPos(pos, aCoord, a0.x) + +; vFog = length((uViewPos - pos) * uFogParams.w) + applyFog(vFog, pos) + +; vPosition = uViewProj * pos + mulMat(vPosition, pos, uViewProj) + +; vColor = uMaterial + mov vColor, ONE ; TODO + +; normal = mulQuat(uBasis, aNormal) + mov normal, aNormal + mul pos.xyz, c[uBasis + 0 + a0.x], normal.zxyw + mad pos.xyz, normal, c[uBasis + 0 + a0.x].zxyw, -pos + mad pos.xyz, normal.yzxw, c[uBasis + 0 + a0.x].w, pos + mul tmp.xyz, c[uBasis + 0 + a0.x].zxyw, pos + mad pos.xyz, pos.yzxw, c[uBasis + 0 + a0.x].yzxw, -tmp + mad vTexCoord, pos, TWO, normal +#else + tex t0 + mul r0, t0, vColor +#endif \ No newline at end of file diff --git a/src/shaders/compose_room.asm b/src/shaders/compose_room.asm new file mode 100644 index 0000000..a3aef76 --- /dev/null +++ b/src/shaders/compose_room.asm @@ -0,0 +1,72 @@ +#include "common.asm" + +#ifdef VERTEX +; variables + #define lv1 r1 + #define lv2 r2 + #define lv3 r3 + #define normal r4 + #define att r5 + #define light r6 + #define pos r7 + +; pos = mulQuatPos(uBasis[0], aCoord) + mulQuatPos(pos, aCoord, 0) + +; vFog = length((uViewPos - pos) * uFogParams.w) + applyFog(vFog, pos) + +; vPosition = uViewProj * pos + mulMat(vPosition, pos, uViewProj) + +; lighting + ; lv[1..3] = (uLightPos[1..3].xyz - pos) * uLightColor[1..3].w; + add lv1.xyz, c[uLightPos + 1], -pos + add lv2.xyz, c[uLightPos + 2], -pos + add lv3.xyz, c[uLightPos + 3], -pos + mul lv1.xyz, c[uLightColor + 1].w, lv1 + mul lv2.xyz, c[uLightColor + 2].w, lv2 + mul lv3.xyz, c[uLightColor + 3].w, lv3 + + ; att[1..3] = dot(lv[1..3], lv[1..3]) + mov att.x, ONE + dp3 att.y, lv1, lv1 + dp3 att.z, lv2, lv2 + dp3 att.w, lv3, lv3 + + ; att = max(0, 1 - att) / sqrt(att) + rsq tmp.y, att.y + rsq tmp.z, att.z + rsq tmp.w, att.w + + add att, ONE, -att + max att, ZERO, att + mul att, tmp, att + + ; light = max(0, dot(aNormal, lv[1..3])) + mov light.x, ZERO + dp3 light.y, lv1, aNormal + dp3 light.z, lv2, aNormal + dp3 light.w, lv3, aNormal + + ; light = max(0, light) * att + max light, ZERO, light + mul light, light, att + +; vColor = (aLight + light[1..3] * uLightColor[1..3]) * aColor * 2 + mov pos, aColor + mov tmp, aLight + mad tmp.xyz, light.y, c[uLightColor + 1], tmp + mad tmp.xyz, light.z, c[uLightColor + 2], tmp + mad tmp.xyz, light.w, c[uLightColor + 3], tmp + mul tmp, pos, tmp + applyUnderwater(tmp) + add vColor, tmp, tmp + +; vTecCoord = (aTexCoord.xyz, 1) + mov vTexCoord, aTexCoord + mov vTexCoord.w, ONE +#else + tex t0 + mul r0, t0, vColor +#endif \ No newline at end of file diff --git a/src/shaders/compose_sprite.asm b/src/shaders/compose_sprite.asm new file mode 100644 index 0000000..7e3c8ef --- /dev/null +++ b/src/shaders/compose_sprite.asm @@ -0,0 +1,94 @@ +#include "common.asm" + +#ifdef VERTEX +; variables + #define lv0 r0 + #define lv1 r1 + #define lv2 r2 + #define lv3 r3 + #define normal r4 + #define att r5 + #define light r6 + #define pos r7 + #define size r4 + #define vv r6 + + #define MAT_AMBIENT c[uMaterial].y + #define MAT_ALPHA c[uMaterial].w + +; pos = (aTexCoord.zw * 32767, 0, 0) + mov pos, ZERO + mul pos.xy, aTexCoord.zw, MAX_SHORT + +; size = mulQuatPos(uBasis[0], pos) + mulQuatPos(size, pos, 0) + +; pos = size + aCoord + add pos, size, aCoord + mov pos.w, ONE + +; vFog = length((uViewPos - pos) * uFogParams.w) + applyFog(vFog, pos) + +; vPosition = uViewProj * pos + mulMat(vPosition, pos, uViewProj) + +; lighting + ; lv[1..3] = (uLightPos[1..3].xyz - pos) * uLightColor[1..3].w; + add lv1.xyz, c[uLightPos + 1], -pos + add lv2.xyz, c[uLightPos + 2], -pos + add lv3.xyz, c[uLightPos + 3], -pos + mul lv1.xyz, c[uLightColor + 1].w, lv1 + mul lv2.xyz, c[uLightColor + 2].w, lv2 + mul lv3.xyz, c[uLightColor + 3].w, lv3 + + ; att[1..3] = dot(lv[1..3], lv[1..3]) + mov att.x, ONE + dp3 att.y, lv1, lv1 + dp3 att.z, lv2, lv2 + dp3 att.w, lv3, lv3 + + ; att = max(0, 1 - att) / sqrt(att) + rsq tmp.y, att.y + rsq tmp.z, att.z + rsq tmp.w, att.w + + add att, ONE, -att + max att, ZERO, att + mul att, tmp, att + + ; viewVec = uViewPos - pos + add vv, c[uViewPos], -pos + + ; normal = normalize(viewVec) + dp3 tmp.x, vv, vv + rsq tmp.x, tmp.x + mul normal, vv, tmp.x + + ; light = max(0, dot(normal, lv[1..3])) + mov light.x, MAT_AMBIENT + dp3 light.y, lv1, normal + dp3 light.z, lv2, normal + dp3 light.w, lv3, normal + + ; light = max(0, light) * att + max light, ZERO, light + mul light, light, att + +; vColor = vec4(aLight.rgb * aLight.a, aLight.a) * 2 + mov tmp, aLight + mad tmp.xyz, light.x, c[uLightColor + 0], tmp + mad tmp.xyz, light.y, c[uLightColor + 1], tmp + mad tmp.xyz, light.z, c[uLightColor + 2], tmp + mad tmp.xyz, light.w, c[uLightColor + 3], tmp + mul tmp.xyz, tmp, tmp.w + applyUnderwater(tmp) + add vColor, tmp, tmp + +; vTecCoord = (aTexCoord.xyz, 1) + mov vTexCoord, aTexCoord + mov vTexCoord.w, ONE +#else + tex t0 + mul r0, t0, vColor +#endif \ No newline at end of file diff --git a/src/shaders/filter.asm b/src/shaders/filter.asm new file mode 100644 index 0000000..9349437 --- /dev/null +++ b/src/shaders/filter.asm @@ -0,0 +1,65 @@ +#include "common.asm" + +#ifdef VERTEX + mulMat(vPosition, aCoord, uViewProj) + + + #ifdef GRAYSCALE + mov vColor.xyz, c[uParam].xyz + mov vColor.w, ONE + #else + mov vColor, aLight + #endif + +; vTecCoord = (aTexCoord.xyz, 1) + #ifdef UPSCALE + mov vTexCoord, aTexCoord ; TODO + 0.5 * (1 / uParam.xy) + #endif + + #ifdef DOWNSAMPLE + mov vTexCoord, aTexCoord + #endif + + #ifdef GRAYSCALE + mov tmp, c[uParam].wwww + mad vTexCoord, tmp, HALF, aTexCoord + #endif + + #ifdef BLUR + mov tmp, c[uParam].wwww + mad vTexCoord, tmp, HALF, aTexCoord + #endif + + #ifdef ANAGLYPH + mov vTexCoord, aTexCoord + #endif + + mov vTexCoord.w, ONE +#else + def c0, 0.299f, 0.587f, 0.114f, 0.0f + + tex t0 + + #ifdef UPSCALE + mul r0, t0, vColor + #endif + + #ifdef DOWNSAMPLE + mul r0, t0, vColor + #endif + + #ifdef GRAYSCALE + dp3 r1.rgb, t0, c0 + mov r0, vColor + mul r0.rgb, r0.rgb, r1.rgb + #endif + + #ifdef BLUR ; TODO four tap blur + mul r0, t0, vColor + #endif + + #ifdef ANAGLYPH + mul r0, t0, vColor + #endif + +#endif \ No newline at end of file diff --git a/src/shaders/gui.asm b/src/shaders/gui.asm new file mode 100644 index 0000000..27ab378 --- /dev/null +++ b/src/shaders/gui.asm @@ -0,0 +1,14 @@ +#include "common.asm" + +#ifdef VERTEX + mulMat(vPosition, aCoord, uViewProj) + + mul vColor, aLight, c[uMaterial] + +; vTecCoord = (aTexCoord.xyz, 1) + mov vTexCoord, aTexCoord + mov vTexCoord.w, ONE +#else + tex t0 + mul r0, t0, vColor +#endif \ No newline at end of file diff --git a/src/shaders/shadow_entity.asm b/src/shaders/shadow_entity.asm new file mode 100644 index 0000000..d22c84a --- /dev/null +++ b/src/shaders/shadow_entity.asm @@ -0,0 +1,22 @@ +#include "common.asm" + +#ifdef VERTEX +; variables + #define pos r7 + +; joint = int(aCoord.w) + mov a0.x, aCoord.w + +; pos = mulQuatPos(uBasis[joint], aCoord) + mulQuatPos(pos, aCoord, a0.x) + +; vPosition = uViewProj * pos + mulMat(vPosition, pos, uViewProj) + +; vTecCoord = (aTexCoord.xyz, 1) TODO check AKILL + mov vTexCoord, aTexCoord + mov vTexCoord.w, ONE +#else + def c0, 0.0f, 0.0f, 0.0f, 1.0f + mov r0, c0 +#endif \ No newline at end of file diff --git a/src/sound.h b/src/sound.h index b9659fa..f8691c8 100644 --- a/src/sound.h +++ b/src/sound.h @@ -8,7 +8,7 @@ #define DECODE_OGG -#if !defined(_OS_PSP) && !defined(_OS_WEB) && !defined(_OS_PSV) && !defined(_OS_3DS) +#if !defined(_OS_PSP) && !defined(_OS_WEB) && !defined(_OS_PSV) && !defined(_OS_3DS) && !defined(_OS_XBOX) #define DECODE_MP3 #endif diff --git a/src/utils.h b/src/utils.h index 18afd7c..1a9e48e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -20,7 +20,7 @@ #define ASSERTV(expr) ASSERT(expr) #ifndef _OS_ANDROID - #define LOG(...) printf(__VA_ARGS__) + #define LOG printf #endif #else @@ -35,7 +35,7 @@ #define LOG(...) printf(__VA_ARGS__) #endif #else - #define LOG(...) printf(__VA_ARGS__) + #define LOG printf #endif #endif @@ -50,6 +50,21 @@ #define LOG(...) __android_log_print(ANDROID_LOG_INFO,"OpenLara",__VA_ARGS__) #endif +#ifdef _OS_XBOX + #define MAX_LOG_LENGTH 1024 + + #undef LOG + void LOG(const char *format, ...) { + char str[MAX_LOG_LENGTH]; + va_list arglist; + va_start(arglist, format); + _vsnprintf(str, MAX_LOG_LENGTH, format, arglist); + va_end(arglist); + OutputDebugStringA(str); + } +#endif + + #ifdef _OS_PSP extern "C" { // pspmath.h @@ -74,7 +89,7 @@ #define DECL_STR(v) #v, #define EPS FLT_EPSILON -#define INF INFINITY +#define INF FLT_MAX #define PI 3.14159265358979323846f #define PIH (PI * 0.5f) #define PI2 (PI * 2.0f) @@ -1585,14 +1600,15 @@ struct Stream { ASSERT(false); } + char path[256]; + path[0] = 0; if (contentDir[0] && (!cacheDir[0] || !strstr(name, cacheDir))) { - char path[255]; - path[0] = 0; strcat(path, contentDir); - strcat(path, name); - f = fopen(path, "rb"); - } else - f = fopen(name, "rb"); + } + strcat(path, name); + fixBackslash(path); + + f = fopen(path, "rb"); if (!f) { #ifdef _OS_WEB @@ -1650,6 +1666,17 @@ struct Stream { osCacheWrite(stream); } + static void fixBackslash(char *str) { + #ifdef _OS_XBOX + int len = strlen(str); + for (int i = 0; i < len; i++) { + if (str[i] == '/') { + str[i] = '\\'; + } + } + #endif + } + static bool exists(const char *name) { FILE *f = fopen(name, "rb"); if (!f) return false; @@ -1661,6 +1688,7 @@ struct Stream { char fileName[1024]; strcpy(fileName, contentDir); strcat(fileName, name); + fixBackslash(fileName); return exists(fileName); } @@ -2151,8 +2179,6 @@ struct Array { ASSERT(index >= 0 && index < length); return items[index]; }; - - inline operator T*() const { return items; }; };