diff --git a/src/core.h b/src/core.h index aaafaf3d..ceba8041 100644 --- a/src/core.h +++ b/src/core.h @@ -66,6 +66,11 @@ #undef OS_FILEIO_CACHE extern int WEBGL_VERSION; +#elif _3DS + #define _OS_3DS 1 + #define _GAPI_C3D 1 + + #undef OS_PTHREAD_MT #elif _PSP #define _OS_PSP 1 #define _GAPI_GU 1 @@ -82,10 +87,10 @@ #undef OS_PTHREAD_MT #elif __SWITCH__ - #define _OS_NX 1 - #define _GAPI_GL 1 + #define _OS_SWITCH 1 + #define _GAPI_GL 1 - #undef OS_PTHREAD_MT + #undef OS_PTHREAD_MT #endif #ifndef _OS_PSP @@ -117,7 +122,7 @@ extern void osMutexFree (void *obj); extern void osMutexLock (void *obj); extern void osMutexUnlock (void *obj); -extern int osGetTime (); +extern int osGetTimeMS (); extern bool osJoyReady (int index); extern void osJoyVibrate (int index, float L, float R); @@ -190,6 +195,7 @@ namespace Core { struct Support { int maxVectors; int maxAniso; + int texMinSize; bool shaderBinary; bool VAO; bool depthTexture; @@ -291,7 +297,7 @@ namespace Core { bool isQuit; int getTime() { - return osGetTime(); + return osGetTimeMS(); } void resetTime() { @@ -304,6 +310,24 @@ namespace Core { } } +#ifdef PROFILE + struct TimingCPU { + int &result; + + TimingCPU(int &result) : result(result) { + result = Core::getTime(); + } + + ~TimingCPU() { + result = Core::getTime() - result; + } + }; + + #define PROFILE_CPU_TIMING(result) TimingCPU timingCPU(result) +#else + #define PROFILE_CPU_TIMING(result) +#endif + #include "input.h" #include "sound.h" @@ -556,6 +580,10 @@ namespace Core { Vertex *vBuffer; #endif + #ifdef _GAPI_C3D + void *VAO; + #endif + int32 basisCount; Basis *basis; } active; @@ -571,6 +599,7 @@ namespace Core { int fpsTime; #ifdef PROFILE int tFrame; + int video; #endif Stats() : frame(0), frameIndex(0), fps(0), fpsTime(0) {} @@ -584,6 +613,8 @@ namespace Core { LOG("FPS: %d DIP: %d TRI: %d RT: %d CB: %d\n", fps, dips, tris, rt, cb); #ifdef PROFILE LOG("frame time: %d mcs\n", tFrame / 1000); + LOG("sound: mix %d rev %d ren %d/%d ogg %d\n", Sound::stats.mixer, Sound::stats.reverb, Sound::stats.render[0], Sound::stats.render[1], Sound::stats.ogg); + LOG("video: %d\n", video); #endif fps = frame; frame = 0; @@ -600,8 +631,8 @@ namespace Core { #include "gapi_gl.h" #elif _GAPI_D3D9 #include "gapi_d3d9.h" -#elif _GAPI_GX - #include "gapi_gx.h" +#elif _OS_3DS + #include "gapi_c3d.h" #elif _GAPI_GU #include "gapi_gu.h" #elif _GAPI_GXM @@ -654,10 +685,13 @@ namespace Core { } void init() { - memset(&support, 0, sizeof(support)); LOG("OpenLara (%s)\n", version); + x = y = 0; + memset(&support, 0, sizeof(support)); + support.texMinSize = 1; + #ifdef USE_INFLATE tinf_init(); #endif @@ -697,12 +731,14 @@ namespace Core { } eye = 0.0f; - { // init 1x1 textures - uint32 data = 0xFFFFFFFF; - whiteTex = new Texture(1, 1, 1, FMT_RGBA, OPT_NEAREST, &data); - whiteCube = new Texture(1, 1, 1, FMT_RGBA, OPT_CUBEMAP, &data); - data = 0; - blackTex = new Texture(1, 1, 1, FMT_RGBA, OPT_NEAREST, &data); + { // init dummy textures + uint32 *data = new uint32[SQR(support.texMinSize)]; + memset(data, 0xFF, SQR(support.texMinSize) * sizeof(data[0])); + whiteTex = new Texture(support.texMinSize, support.texMinSize, 1, FMT_RGBA, OPT_NEAREST, data); + whiteCube = new Texture(support.texMinSize, support.texMinSize, 1, FMT_RGBA, OPT_CUBEMAP, data); + memset(data, 0x00, SQR(support.texMinSize) * sizeof(data[0])); + blackTex = new Texture(support.texMinSize, support.texMinSize, 1, FMT_RGBA, OPT_NEAREST, data); + delete[] data; } { // generate dithering texture @@ -813,6 +849,15 @@ namespace Core { settings.detail.setLighting (Core::Settings::MEDIUM); #endif + #ifdef _OS_3DS + settings.detail.setFilter (Core::Settings::MEDIUM); + settings.detail.setLighting (Core::Settings::LOW); + settings.detail.setShadows (Core::Settings::LOW); + settings.detail.setWater (Core::Settings::LOW); + + settings.audio.reverb = false; + #endif + #ifdef FFP settings.detail.setFilter (Core::Settings::MEDIUM); settings.detail.setLighting (Core::Settings::LOW); @@ -1006,7 +1051,7 @@ namespace Core { Core::active.basis = basis; Core::active.basisCount = count; - Core::active.shader->setParam(uBasis, basis[0], count); + Core::active.shader->setParam(uBasis, *(vec4*)basis, count * 2); } void setMaterial(float diffuse, float ambient, float specular, float alpha) { diff --git a/src/format.h b/src/format.h index f279aa67..096415ab 100644 --- a/src/format.h +++ b/src/format.h @@ -3010,7 +3010,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) { - #ifndef _OS_PSP + #if !defined(_OS_PSP) && !defined(_OS_3DS) Room *room = NULL; while (stream.pos < stream.size) { @@ -3843,6 +3843,7 @@ namespace TR { initAnimTex(); initExtra(); initCutscene(); + initTextureTypes(); gObjectTextures = objectTextures; gSpriteTextures = spriteTextures; @@ -3986,6 +3987,46 @@ namespace TR { } } + void initTextureTypes() { + // rooms geometry + for (int roomIndex = 0; roomIndex < roomsCount; roomIndex++) { + TR::Room &room = rooms[roomIndex]; + TR::Room::Data &data = room.data; + for (int i = 0; i < data.fCount; i++) { + Face &f = data.faces[i]; + ASSERT(!f.colored); + ASSERT(f.flags.texture < objectTexturesCount); + TR::TextureInfo &t = objectTextures[f.flags.texture]; + t.type = TEX_TYPE_ROOM; + } + } + + // rooms static meshes + for (int staticMeshIndex = 0; staticMeshIndex < staticMeshesCount; staticMeshIndex++) { + TR::StaticMesh *staticMesh = &staticMeshes[staticMeshIndex]; + if (!meshOffsets[staticMesh->mesh]) continue; + TR::Mesh &mesh = meshes[meshOffsets[staticMesh->mesh]]; + + for (int i = 0; i < mesh.fCount; i++) { + TR::Face &f = mesh.faces[i]; + ASSERT(f.colored || f.flags.texture < objectTexturesCount); + if (f.colored) continue; + TR::TextureInfo &t = objectTextures[f.flags.texture]; + t.type = TEX_TYPE_ROOM; + } + } + + // animated textures + for (int animTextureIndex = 0; animTextureIndex < animTexturesCount; animTextureIndex++) { + AnimTexture &animTex = animTextures[animTextureIndex]; + + for (int j = 0; j < animTex.count; j++) { + TextureInfo &t = objectTextures[animTex.textures[j]]; + t.type = TEX_TYPE_ROOM; + } + } + } + static Entity::Type convToInv(Entity::Type type) { for (int j = 0; j < COUNT(ITEM_TO_INV); j++) if (type == ITEM_TO_INV[j].src) { diff --git a/src/game.h b/src/game.h index b19273fc..4cfe2f3f 100644 --- a/src/game.h +++ b/src/game.h @@ -289,11 +289,7 @@ namespace Game { void frameEnd() { if (Core::settings.version == SETTINGS_READING) return; - if (level) { - level->setupBinding(); - level->glyphs->bind(sDiffuse); - UI::renderTouch(); - } + UI::renderTouch(); Core::endFrame(); } diff --git a/src/gapi_c3d.h b/src/gapi_c3d.h new file mode 100644 index 00000000..e2f728ff --- /dev/null +++ b/src/gapi_c3d.h @@ -0,0 +1,667 @@ +#ifndef H_GAPI_C3D +#define H_GAPI_C3D + +#include <3ds.h> +#include + +#include "core.h" + +#define PROFILE_MARKER(title) +#define PROFILE_LABEL(id, name, label) +#define PROFILE_TIMING(time) + +#define DISPLAY_BUFFER_COUNT 2 + +#define DISPLAY_TRANSFER_FLAGS (\ + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_XY) \ + | GX_TRANSFER_FLIP_VERT(0) \ + | GX_TRANSFER_OUT_TILED(0) \ + | GX_TRANSFER_RAW_COPY(0) \ + | GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) \ + | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB8) ) + +namespace GAPI { + + using namespace Core; + + typedef ::Vertex Vertex; + +// Shader + extern "C" { + #include "compose_shbin.h" + #include "gui_shbin.h" + #include "filter_upscale_shbin.h" + } + + static const int bindings[uMAX] = { + 94, // uFlags + 0, // uParam + 1, // uTexParam + 2, // uViewProj + 6, // uBasis + 70, // uLightProj + 74, // uMaterial + 75, // uAmbient + 81, // uFogParams + 82, // uViewPos + 83, // uLightPos + 87, // uLightColor + 91, // uRoomSize + 92, // uPosScale + 98, // uContacts + }; + + DVLB_s* compose_dvlb; + DVLB_s* filter_upscale_dvlb; + DVLB_s* gui_dvlb; + + struct Shader { + shaderProgram_s program; + C3D_TexEnv env; + + int32 uID[uMAX]; + + vec4 cbMem[98 + MAX_CONTACTS]; + int cbCount[uMAX]; + + bool rebind; + + void init(Pass pass, int type, int *def, int defCount) { + shaderProgramInit(&program); + + DVLB_s* src = NULL; + + switch (pass) { + case Core::passFilter : src = filter_upscale_dvlb; break; + case Core::passGUI : src = gui_dvlb; break; + default : src = compose_dvlb; + } + + shaderProgramSetVsh(&program, &src->DVLE[0]); + + for (int ut = 0; ut < uMAX; ut++) { + uID[ut] = shaderInstanceGetUniformLocation(program.vertexShader, UniformName[ut]); + } + + rebind = true; + + C3D_TexEnvInit(&env); + C3D_TexEnvSrc(&env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(&env, C3D_Both, GPU_MODULATE); + if (Core::pass == Core::passCompose && type == 2) { // rooms + C3D_TexEnvScale(&env, C3D_RGB, GPU_TEVSCALE_2); + } + } + + void deinit() { + shaderProgramFree(&program); + } + + void bind() { + if (active.shader != this) { + active.shader = this; + memset(cbCount, 0, sizeof(cbCount)); + rebind = true; + } + } + + void validate() { + if (rebind) { + C3D_BindProgram(&program); + C3D_SetTexEnv(0, &env); + rebind = false; + } + + for (int uType = 0; uType < uMAX; uType++) { + if (!cbCount[uType]) continue; + + vec4 *src = cbMem + bindings[uType]; + vec4 *dst = (vec4*)C3D_FVUnifWritePtr(GPU_VERTEX_SHADER, uID[uType], cbCount[uType]); + + for (int i = 0; i < cbCount[uType]; i++) { + dst->x = src->w; + dst->y = src->z; + dst->z = src->y; + dst->w = src->x; + dst++; + src++; + } + + cbCount[uType] = 0; + Core::stats.cb++; + } + } + + void setParam(UniformType uType, const vec4 &value, int count = 1) { + if (uID[uType] == -1) return; + cbCount[uType] = count; + memcpy(cbMem + bindings[uType], &value, count * 16); + } + + void setParam(UniformType uType, const mat4 &value, int count = 1) { + if (uID[uType] == -1) return; + cbCount[uType] = count * 4; + + ASSERT(count == 1); + mat4 m = value.transpose(); + memcpy(cbMem + bindings[uType], &m, sizeof(m)); + } + }; + +// Texture + static const struct FormatDesc { + uint8 bpp; + GPU_TEXCOLOR format; + } formats[FMT_MAX] = { + { 8 , GPU_L8 }, // LUMINANCE + { 32 , GPU_RGBA8 }, // RGBA + { 16 , GPU_RGB565 }, // RGB16 + { 16 , GPU_RGBA5551 }, // RGBA16 + { 32 , GPU_RGBA8 }, // RG_FLOAT // not supported + { 32 , GPU_RGBA8 }, // RG_HALF // not supported + { 16 , GPU_RGBA8 }, // DEPTH + { 16 , GPU_RGBA8 }, // SHADOW + }; + + static const uint8 tileSwizzle[64] = { + 0, 1, 8, 9, 2, 3, 10, 11, + 16, 17, 24, 25, 18, 19, 26, 27, + 4, 5, 12, 13, 6, 7, 14, 15, + 20, 21, 28, 29, 22, 23, 30, 31, + 32, 33, 40, 41, 34, 35, 42, 43, + 48, 49, 56, 57, 50, 51, 58, 59, + 36, 37, 44, 45, 38, 39, 46, 47, + 52, 53, 60, 61, 54, 55, 62, 63 + }; + + struct Texture { + int width, height, origWidth, origHeight; + TexFormat fmt; + uint32 opt; + int mipCount; + + C3D_Tex tex; + C3D_TexCube texCube; + + void convertImage(uint32 *dst, uint32 *src, int dstWidth, int dstHeight, int srcWidth, int srcHeight, int bpp) { + // 8x8 tiles swizzling + // vertical flip + // swap RGBA channels to ABGR + dst += dstWidth * (dstHeight - srcHeight); + for (int y = 0; y < srcHeight; y += 8) { + for (int x = 0; x < srcWidth; x += 8) { + for (int i = 0; i < COUNT(tileSwizzle); i++) { + int sx = tileSwizzle[i] % 8; + int sy = (tileSwizzle[i] - sx) / 8; + int index = (srcHeight - (y + sy) - 1) * srcWidth + (x + sx); + + *dst++ = swap32(src[index]); + } + } + dst += (dstWidth - srcWidth) * 8; + } + } + + Texture(int width, int height, int depth, uint32 opt) : width(width), height(height), origWidth(width), origHeight(height), fmt(FMT_RGBA), opt(opt) { opt |= OPT_NEAREST; } + + void init(void *data) { + ASSERT((opt & OPT_PROXY) == 0); + + FormatDesc desc = formats[fmt]; + + if (width < 8 || height < 8) { + LOG("texture too small %dx%d [%d %d]!\n", width, height, fmt, opt); + width = 8; + height = 8; + data = NULL; + } + + if (width > 1024 || height > 1024) { + LOG("texture too large %dx%d [%d %d]!\n", width, height, fmt, opt); + width = 8; + height = 8; + data = NULL; + } + + int size = width * height * desc.bpp / 8; + + bool isCube = (opt & OPT_CUBEMAP) != 0; + bool isShadow = fmt == FMT_SHADOW; + + C3D_TexInitParams params; + memset(¶ms, 0, sizeof(params)); + params.width = width; + params.height = height; + params.maxLevel = ((opt & OPT_MIPMAPS) != 0) ? min(3, C3D_TexCalcMaxLevel(width, height)) : 0; + params.format = desc.format; + params.onVram = false; + + if (isCube && isShadow) + params.type = GPU_TEX_SHADOW_CUBE; + else if (isCube) + params.type = GPU_TEX_CUBE_MAP; + else if (isShadow) + params.type = GPU_TEX_SHADOW_2D; + else + params.type = GPU_TEX_2D; + + C3D_TexInitWithParams(&tex, &texCube, params); + + if (data && !isCube) { + update(data); + } + + GPU_TEXTURE_FILTER_PARAM filter = ((opt & OPT_NEAREST) == 0) ? GPU_LINEAR : GPU_NEAREST; + C3D_TexSetFilter(&tex, filter, filter); + C3D_TexSetFilterMipmap(&tex, filter); + } + + void deinit() { + C3D_TexDelete(&tex); + } + + void generateMipMap() { + C3D_TexGenerateMipmap(&tex, GPU_TEXFACE_2D); + } + + void update(void *data) { + if (!data) return; + FormatDesc desc = formats[fmt]; + convertImage((uint32*)tex.data, (uint32*)data, width, height, origWidth, origHeight, desc.bpp); + C3D_TexFlush(&tex); + } + + void bind(int sampler) { + if (opt & OPT_PROXY) return; + + if (active.textures[sampler] != this) { + active.textures[sampler] = this; + C3D_TexBind(sampler, &tex); + } + } + + void unbind(int sampler) { + active.textures[sampler] = NULL; + } + + void setFilterQuality(int value) { + GPU_TEXTURE_FILTER_PARAM filter = ((opt & OPT_NEAREST) == 0 && (value > Settings::LOW)) ? GPU_LINEAR : GPU_NEAREST; + + C3D_TexSetFilter(&tex, filter, filter); + C3D_TexSetFilterMipmap(&tex, filter); + } + }; + +// Mesh + struct Mesh { + C3D_BufInfo *VAO; + + Index *iBuffer; + Vertex *vBuffer; + + int aCount; + bool dynamic; + + struct Chunk { + int frameIndex; + int iBase, iStart, iCount; + int vBase, vStart, vCount; + } chunks[DISPLAY_BUFFER_COUNT]; + + Mesh(bool dynamic) : iBuffer(NULL), vBuffer(NULL), dynamic(dynamic) {} + + void init(Index *indices, int iCount, ::Vertex *vertices, int vCount, int aCount) { + ASSERT(sizeof(GAPI::Vertex) == sizeof(::Vertex)); + ASSERT(Core::support.VAO && aCount); + + this->aCount = aCount; + + memset(chunks, 0, sizeof(chunks)); + + for (int i = 0; i < COUNT(chunks); i++) { + chunks[i].frameIndex = -1; + chunks[i].iBase = i * iCount; + chunks[i].vBase = i * vCount; + } + + if (dynamic) { + iCount *= COUNT(chunks); + vCount *= COUNT(chunks); + } + + VAO = new C3D_BufInfo[aCount + 1]; + + iBuffer = (Index*) linearAlloc(iCount * sizeof(Index)); + vBuffer = (Vertex*) linearAlloc(vCount * sizeof(Vertex)); + + if (!dynamic) { + update(indices, iCount, vertices, vCount); + } + } + + void deinit() { + delete[] VAO; + linearFree(iBuffer); + linearFree(vBuffer); + } + + Chunk& getChunk() { + return dynamic ? chunks[Core::stats.frameIndex % COUNT(chunks)] : chunks[0]; + } + + void update(Index *indices, int iCount, ::Vertex *vertices, int vCount) { + Chunk &chunk = getChunk(); + if (chunk.frameIndex != Core::stats.frameIndex) { + chunk.frameIndex = Core::stats.frameIndex; + chunk.iStart = chunk.iCount = chunk.iBase; + chunk.vStart = chunk.vCount = chunk.vBase; + } + + if (indices && iCount) { + chunk.iStart = chunk.iCount; + chunk.iCount += iCount; + memcpy(iBuffer + chunk.iStart, indices, iCount * sizeof(Index)); + GSPGPU_FlushDataCache(iBuffer + chunk.iStart, iCount * sizeof(Index)); + } + + if (vertices && vCount) { + chunk.vStart = chunk.vCount; + chunk.vCount += vCount; + memcpy(vBuffer + chunk.vStart, vertices, vCount * sizeof(Vertex)); + GSPGPU_FlushDataCache(vBuffer + chunk.vStart, vCount * sizeof(Vertex)); + } + } + + void initVAO(C3D_BufInfo *vao, Vertex *offset) { + BufInfo_Init(vao); + BufInfo_Add(vao, offset, sizeof(Vertex), 5, 0x43210); + } + + void initNextRange(MeshRange &range, int &aIndex) { + range.aIndex = aIndex++; + initVAO(VAO + range.aIndex, vBuffer + range.vStart); + } + + void bind(const MeshRange &range) { + C3D_BufInfo *vao = VAO; + + if (range.aIndex == -1) { + vao += aCount - 1; + initVAO(vao, vBuffer + range.vStart + getChunk().vStart); + // workaround for passing "info != &ctx->bufInfo" check inside C3D_SetBufInfo for the same VAO pointers + C3D_BufInfo dummyBufInfo; + C3D_SetBufInfo(&dummyBufInfo); + } else { + vao += range.aIndex; + if (Core::active.VAO == vao) { + return; + } + } + + C3D_SetBufInfo(vao); + Core::active.VAO = vao; + } + }; + + + bool depthTest; + uint32 colorMask, depthMask; + + Shader clearShader; + Mesh clearMesh(false); + uint32 clearColor; + + C3D_RenderTarget *curTarget; + C3D_RenderTarget *defTarget[2]; + C3D_AttrInfo vertexAttribs; + + void init() { + LOG("Vendor : %s\n", "Nintendo"); + LOG("Renderer : %s\n", "PICA200 citro3D"); + LOG("Version : %s\n", "1.0"); + + gfxInitDefault(); + gfxSet3D(true); + consoleInit(GFX_BOTTOM, NULL); + + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + defTarget[0] = C3D_RenderTargetCreate(240 * 2, 400 * 2, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + defTarget[1] = C3D_RenderTargetCreate(240 * 2, 400 * 2, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); + C3D_RenderTargetSetOutput(defTarget[0], GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); + C3D_RenderTargetSetOutput(defTarget[1], GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS); + curTarget = defTarget[0]; + + support.shaderBinary = true; + support.VAO = true; + support.texMinSize = 8; + + Core::width = 400; + Core::height = 240; + + // init default vertex declaration + AttrInfo_Init(&vertexAttribs); + AttrInfo_AddLoader(&vertexAttribs, aCoord , GPU_SHORT , 4); + AttrInfo_AddLoader(&vertexAttribs, aNormal , GPU_SHORT , 4); + AttrInfo_AddLoader(&vertexAttribs, aTexCoord , GPU_SHORT , 4); + AttrInfo_AddLoader(&vertexAttribs, aColor , GPU_UNSIGNED_BYTE , 4); + AttrInfo_AddLoader(&vertexAttribs, aLight , GPU_UNSIGNED_BYTE , 4); + + // default texture env params + C3D_TexEnv* env = C3D_GetTexEnv(0); + C3D_TexEnvInit(env); + C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_PRIMARY_COLOR, GPU_PRIMARY_COLOR); + C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); + C3D_TexEnvScale(env, C3D_RGB, GPU_TEVSCALE_2); + + //C3D_TexEnvSrc(env, C3D_Both, GPU_PRIMARY_COLOR); + //C3D_TexEnvFunc(env, C3D_Both, GPU_REPLACE); + + clearColor = 0x68B0D8FF; + colorMask = GPU_WRITE_COLOR; + depthMask = GPU_WRITE_DEPTH; + + // init shaders + compose_dvlb = DVLB_ParseFile((u32*)compose_shbin, compose_shbin_size); + filter_upscale_dvlb = DVLB_ParseFile((u32*)filter_upscale_shbin, filter_upscale_shbin_size); + gui_dvlb = DVLB_ParseFile((u32*)gui_shbin, gui_shbin_size); + } + + void deinit() { + DVLB_Free(compose_dvlb); + DVLB_Free(filter_upscale_dvlb); + DVLB_Free(gui_dvlb); + + C3D_Fini(); + gfxExit(); + } + + mat4 ortho(float l, float r, float b, float t, float znear, float zfar) { + mat4 m; + Mtx_OrthoTilt((C3D_Mtx*)&m, l, r, b, t, znear, zfar, false); + + mat4 res; + res.e00 = m.e30; res.e10 = m.e31; res.e20 = m.e32; res.e30 = m.e33; + res.e01 = m.e20; res.e11 = m.e21; res.e21 = m.e22; res.e31 = m.e23; + res.e02 = m.e10; res.e12 = m.e11; res.e22 = m.e12; res.e32 = m.e13; + res.e03 = m.e00; res.e13 = m.e01; res.e23 = m.e02; res.e33 = m.e03; + return res; + } + + mat4 perspective(float fov, float aspect, float znear, float zfar) { + mat4 m; + Mtx_PerspTilt((C3D_Mtx*)&m, fov * DEG2RAD, aspect, znear, zfar, false); + + mat4 res; + res.e00 = m.e30; res.e10 = m.e31; res.e20 = m.e32; res.e30 = m.e33; + res.e01 = m.e20; res.e11 = m.e21; res.e21 = m.e22; res.e31 = m.e23; + res.e02 = m.e10; res.e12 = m.e11; res.e22 = m.e12; res.e32 = m.e13; + res.e03 = m.e00; res.e13 = m.e01; res.e23 = m.e02; res.e33 = m.e03; + return res; + } + + bool beginFrame() { + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + curTarget = Core::eye <= 0.0f ? defTarget[0] : defTarget[1]; + C3D_FrameDrawOn(curTarget); + + return true; + } + + void endFrame() { + C3D_FrameEnd(0); + } + + void resetState() { + C3D_SetAttrInfo(&vertexAttribs); + } + + 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_D16, D3DMULTISAMPLE_NONE, 0, true, &item.surface, NULL); + else + device->CreateRenderTarget(width, height, D3DFMT_R5G6B5, D3DMULTISAMPLE_NONE, 0, false, &item.surface, NULL); + + return cache.count++; + */ + return 0; + } + + void bindTarget(Texture *target, int face) { + active.viewport = Viewport(0, 0, 0, 0); // forcing viewport reset + //curTarget = Core::eye <= 0.0f ? defTarget[0] : defTarget[1]; + //C3D_FrameDrawOn(curTarget); + } + + void discardTarget(bool color, bool depth) {} + + void copyTarget(Texture *dst, int xOffset, int yOffset, int x, int y, int width, int height) { + // + } + + void setVSync(bool enable) {} + + void present() { + + } + + void setViewport(const Viewport &vp) { + //int vh = active.target ? active.target->height : Core::height; + + } + + void updateWriteMask() { + C3D_DepthTest(depthTest, GPU_GREATER, GPU_WRITEMASK(colorMask | depthMask)); + } + + void setDepthTest(bool enable) { + depthTest = enable; + updateWriteMask(); + } + + void setDepthWrite(bool enable) { + depthMask = enable ? GPU_WRITE_DEPTH : 0; + updateWriteMask(); + } + + void setColorWrite(bool r, bool g, bool b, bool a) { + colorMask = 0; + if (r) colorMask |= GPU_WRITE_RED; + if (g) colorMask |= GPU_WRITE_GREEN; + if (b) colorMask |= GPU_WRITE_BLUE; + if (a) colorMask |= GPU_WRITE_ALPHA; + updateWriteMask(); + } + + void setAlphaTest(bool enable) { + C3D_AlphaTest(enable, GPU_GREATER, 128); + } + + void setCullMode(int rsMask) { + switch (rsMask) { + case RS_CULL_BACK : C3D_CullFace(GPU_CULL_BACK_CCW); break; + case RS_CULL_FRONT : C3D_CullFace(GPU_CULL_FRONT_CCW); break; + default : C3D_CullFace(GPU_CULL_NONE); + } + } + + void setBlendMode(int rsMask) { + switch (rsMask) { + case RS_BLEND_ALPHA : C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); break; + case RS_BLEND_ADD : C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE, GPU_ONE, GPU_ONE); break; + case RS_BLEND_MULT : C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_DST_COLOR, GPU_ZERO, GPU_DST_COLOR, GPU_ZERO); break; + case RS_BLEND_PREMULT : C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); break; + default : C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_ONE, GPU_ZERO, GPU_ONE, GPU_ZERO); break; + } + } + + void setViewProj(const mat4 &mView, const mat4 &mProj) {} + + void DIP(Mesh *mesh, const MeshRange &range) { + if (!active.shader) return; + + active.shader->validate(); + + C3D_DrawElements(GPU_TRIANGLES, range.iCount, C3D_UNSIGNED_SHORT, mesh->iBuffer + mesh->getChunk().iStart + range.iStart); + } + + 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 clear(bool color, bool depth) { + uint32 mask = 0; + if (color) mask |= C3D_CLEAR_COLOR; + if (depth) mask |= C3D_CLEAR_DEPTH; + if (!mask) return; + + C3D_RenderTargetClear(curTarget, C3D_ClearBits(mask), clearColor, 0); + } + + void setClearColor(const vec4 &color) { + clearColor = (uint32(color.w * 255)) + | (uint32(color.z * 255) << 8) + | (uint32(color.y * 255) << 16) + | (uint32(color.x * 255) << 24); + } + + vec4 copyPixel(int x, int y) { +// GAPI::Texture *t = Core::active.target; +// Color32 *color = (Color32*)t->data; +// return vec4(color->r, color->g, color->b, 255.0f) * (1.0f / 255.0f); + return vec4(0.0f); // TODO: read from framebuffer + } + + void initPSO(PSO *pso) { + ASSERT(pso); + ASSERT(pso && pso->data == NULL); + pso->data = &pso; + } + + void deinitPSO(PSO *pso) { + ASSERT(pso); + ASSERT(pso->data != NULL); + pso->data = NULL; + } + + void bindPSO(const PSO *pso) { + // + } +} + +#endif \ No newline at end of file diff --git a/src/gapi_d3d9.h b/src/gapi_d3d9.h index 4a63162f..02f86173 100644 --- a/src/gapi_d3d9.h +++ b/src/gapi_d3d9.h @@ -237,10 +237,6 @@ namespace GAPI { void setParam(UniformType uType, const mat4 &value, int count = 1) { setConstant(uType, (float*)&value, count * 4); } - - void setParam(UniformType uType, const Basis &value, int count = 1) { - setConstant(uType, (float*)&value, count * 2); - } }; // Texture diff --git a/src/gapi_gl.h b/src/gapi_gl.h index 9f57bdac..8acbe000 100644 --- a/src/gapi_gl.h +++ b/src/gapi_gl.h @@ -111,7 +111,7 @@ #define glProgramBinary(...) extern EGLDisplay display; -#elif _OS_NX +#elif _OS_SWITCH #define GL_GLEXT_PROTOTYPES #include #include @@ -446,7 +446,6 @@ namespace GAPI { void bind() {} void setParam(UniformType uType, const vec4 &value, int count = 1) {} void setParam(UniformType uType, const mat4 &value, int count = 1) {} - void setParam(UniformType uType, const Basis &value, int count = 1) {} #else GLuint ID; int32 uID[uMAX]; @@ -645,7 +644,7 @@ namespace GAPI { } } - void setup() { + void validate() { if (rebind) { glUseProgram(ID); rebind = false; @@ -657,31 +656,26 @@ namespace GAPI { const Binding &b = bindings[uType]; if (b.vec) - glUniform4fv(uID[uType], cbCount[uType] / 4, (GLfloat*)(cbMem + b.reg)); + glUniform4fv(uID[uType], cbCount[uType], (GLfloat*)(cbMem + b.reg)); else - glUniformMatrix4fv(uID[uType], cbCount[uType] / 16, false, (GLfloat*)(cbMem + b.reg)); + glUniformMatrix4fv(uID[uType], cbCount[uType] / 4, false, (GLfloat*)(cbMem + b.reg)); + cbCount[uType] = 0; Core::stats.cb++; } - - memset(cbCount, 0, sizeof(cbCount)); } void setParam(UniformType uType, float *value, int count) { cbCount[uType] = count; - memcpy(cbMem + bindings[uType].reg, value, count * 4); + memcpy(cbMem + bindings[uType].reg, value, count * 16); } void setParam(UniformType uType, const vec4 &value, int count = 1) { - if (uID[uType] != -1) setParam(uType, (float*)&value, count * 4); + if (uID[uType] != -1) setParam(uType, (float*)&value, count); } void setParam(UniformType uType, const mat4 &value, int count = 1) { - if (uID[uType] != -1) setParam(uType, (float*)&value, count * 16); - } - - void setParam(UniformType uType, const Basis &value, int count = 1) { - if (uID[uType] != -1) setParam(uType, (float*)&value, count * 8); + if (uID[uType] != -1) setParam(uType, (float*)&value, count * 4); } #endif }; @@ -813,7 +807,7 @@ namespace GAPI { glGenerateMipmap(target); if ((opt & (OPT_VOLUME | OPT_CUBEMAP | OPT_NEAREST)) == 0 && (Core::support.maxAniso > 0)) { glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, min(int(Core::support.maxAniso), 8)); - //glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 4); + glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, 3); } } @@ -1381,7 +1375,7 @@ namespace GAPI { if (wglSwapIntervalEXT) wglSwapIntervalEXT(enable ? 1 : 0); #elif _OS_LINUX if (glXSwapIntervalSGI) glXSwapIntervalSGI(enable ? 1 : 0); - #elif defined(_OS_RPI) || defined(_OS_CLOVER) || defined(_OS_NX) + #elif defined(_OS_RPI) || defined(_OS_CLOVER) || defined(_OS_SWITCH) eglSwapInterval(display, enable ? 1 : 0); #endif } @@ -1510,7 +1504,7 @@ namespace GAPI { glLoadMatrixf((GLfloat*)&m); #endif if (Core::active.shader) { - Core::active.shader->setup(); + Core::active.shader->validate(); } glDrawElements(GL_TRIANGLES, range.iCount, GL_UNSIGNED_SHORT, mesh->iBuffer + range.iStart); diff --git a/src/gapi_gu.h b/src/gapi_gu.h index c685e097..f07f3d88 100644 --- a/src/gapi_gu.h +++ b/src/gapi_gu.h @@ -28,7 +28,6 @@ namespace GAPI { void bind() {} void setParam(UniformType uType, const vec4 &value, int count = 1) {} void setParam(UniformType uType, const mat4 &value, int count = 1) {} - void setParam(UniformType uType, const Basis &value, int count = 1) {} }; // Texture diff --git a/src/gapi_gx.h b/src/gapi_gx.h deleted file mode 100644 index efbc3e83..00000000 --- a/src/gapi_gx.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef H_GAPI_GX -#define H_GAPI_GX - -// TODO - -#endif \ No newline at end of file diff --git a/src/gapi_gxm.h b/src/gapi_gxm.h index 54d6a948..2d5f9165 100644 --- a/src/gapi_gxm.h +++ b/src/gapi_gxm.h @@ -696,7 +696,7 @@ namespace GAPI { } } - void setup() { + void validate() { if (rebind) { sceGxmSetVertexProgram(Context::gxmContext, vp); sceGxmSetFragmentProgram(Context::gxmContext, pso[psoIndex].fp); @@ -708,30 +708,26 @@ namespace GAPI { void *buff; if (vParams[uType]) { sceGxmReserveVertexDefaultUniformBuffer(Context::gxmContext, &buff); - sceGxmSetUniformDataF(buff, vParams[uType], 0, cbCount[uType], (float*)(cbMem + bindings[uType])); + sceGxmSetUniformDataF(buff, vParams[uType], 0, cbCount[uType] * 4, (float*)(cbMem + bindings[uType])); } if (fParams[uType]) { sceGxmReserveFragmentDefaultUniformBuffer(Context::gxmContext, &buff); - sceGxmSetUniformDataF(buff, fParams[uType], 0, cbCount[uType], (float*)(cbMem + bindings[uType])); + sceGxmSetUniformDataF(buff, fParams[uType], 0, cbCount[uType] * 4, (float*)(cbMem + bindings[uType])); } } } void setParam(UniformType uType, float *value, int count) { cbCount[uType] = count; - memcpy(cbMem + bindings[uType], value, count * 4); + memcpy(cbMem + bindings[uType], value, count * 16); } void setParam(UniformType uType, const vec4 &value, int count = 1) { - setParam(uType, (float*)&value, count * 4); + setParam(uType, (float*)&value, count); } void setParam(UniformType uType, const mat4 &value, int count = 1) { - setParam(uType, (float*)&value, count * 16); - } - - void setParam(UniformType uType, const Basis &value, int count = 1) { - setParam(uType, (float*)&value, count * 8); + setParam(uType, (float*)&value, count * 4); } }; @@ -850,7 +846,7 @@ namespace GAPI { } } - generateMipMap(); + //generateMipMap(); if (isCube) { sceGxmTextureInitCube(&ID, this->data, SceGxmTextureFormat(desc.textureFormat), width, height, mipCount); @@ -1124,20 +1120,12 @@ namespace GAPI { // LOG("VRAM : %d\n", EDRAM_SIZE); // freeEDRAM(); - support.maxAniso = 0; - support.maxVectors = 0; - support.shaderBinary = false; - support.VAO = false; + support.shaderBinary = true; support.depthTexture = true; support.shadowSampler = true; - support.discardFrame = false; support.texNPOT = true; support.texRG = true; - support.texBorder = false; - support.colorFloat = false; support.colorHalf = true; - support.texFloatLinear = false; - support.texFloat = false; support.texHalfLinear = true; support.texHalf = true; support.clipDist = true; @@ -1358,7 +1346,7 @@ namespace GAPI { if (!active.shader) return; active.shader->setBlendInfo(colorMask, blendMode); - active.shader->setup(); + active.shader->validate(); sceGxmDraw(Context::gxmContext, SCE_GXM_PRIMITIVE_TRIANGLES, SCE_GXM_INDEX_FORMAT_U16, mesh->iBuffer + mesh->getChunk().iStart + range.iStart, range.iCount); } diff --git a/src/inventory.h b/src/inventory.h index 597f7e75..c3ab8c2e 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -1159,7 +1159,7 @@ struct Inventory { else if (Input::down[ikDown] || joy.down[jkDown] || joy.L.y > 0.5f) key = cDown; - #ifdef _OS_NX + #if defined(_OS_SWITCH) || defined(_OS_3DS) // swap A/B keys for Nintendo (Japanese) UX style if (Input::touchTimerVis == 0.0f) { if (key == cAction) { @@ -1668,8 +1668,6 @@ struct Inventory { Core::mModel.scale(vec3(1.0f / 32767.0f)); #endif - Core::setBlendMode(alpha < 255 ? bmAlpha : bmNone); - Index indices[6 * 3] = { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11 }; Vertex vertices[4 * 3]; @@ -1742,6 +1740,8 @@ struct Inventory { background[0]->bind(sDiffuse); } + Core::setBlendMode(alpha < 255 ? bmAlpha : bmNone); + game->setShader(Core::passFilter, Shader::FILTER_UPSCALE, false, false); Core::active.shader->setParam(uParam, vec4(float(Core::active.textures[sDiffuse]->width), float(Core::active.textures[sDiffuse]->height), Core::getTime() * 0.001f, 0.0f)); game->getMesh()->renderBuffer(indices, COUNT(indices), vertices, COUNT(vertices)); @@ -1892,8 +1892,6 @@ struct Inventory { return; // items - game->setupBinding(); - setupCamera(aspect); UI::setupInventoryShading(vec3(0.0f)); @@ -1982,7 +1980,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]; - #ifdef _OS_NX + #if defined(_OS_SWITCH) || defined(_OS_3DS) bSelect = "A"; bBack = "B"; #endif diff --git a/src/level.h b/src/level.h index 14c8b4d7..4fe1c8e7 100644 --- a/src/level.h +++ b/src/level.h @@ -33,8 +33,10 @@ extern int loadSlot; struct Level : IGame { TR::Level level; - Texture *atlas; - Texture *glyphs; + Texture *atlasRooms; + Texture *atlasObjects; + Texture *atlasSprites; + Texture *atlasGlyphs; MeshBuilder *mesh; Lara *players[2], *player; @@ -356,8 +358,11 @@ struct Level : IGame { } virtual void applySettings(const Core::Settings &settings) { - if (settings.detail.filter != Core::settings.detail.filter) - atlas->setFilterQuality(settings.detail.filter); + if (settings.detail.filter != Core::settings.detail.filter) { + atlasRooms->setFilterQuality(settings.detail.filter); + atlasObjects->setFilterQuality(settings.detail.filter); + atlasSprites->setFilterQuality(settings.detail.filter); + } bool rebuildMesh = settings.detail.water != Core::settings.detail.water; bool rebuildAmbient = settings.detail.lighting != Core::settings.detail.lighting; @@ -389,7 +394,7 @@ struct Level : IGame { if (rebuildMesh) { delete mesh; - mesh = new MeshBuilder(&level, atlas); + mesh = new MeshBuilder(&level, atlasRooms); } if (rebuildAmbient) { @@ -576,7 +581,7 @@ struct Level : IGame { } virtual void setupBinding() { - atlas->bind(sDiffuse); + atlasRooms->bind(sDiffuse); Core::whiteTex->bind(sNormal); Core::whiteTex->bind(sMask); Core::whiteTex->bind(sReflect); @@ -869,7 +874,7 @@ struct Level : IGame { } initTextures(); - mesh = new MeshBuilder(&level, atlas); + mesh = new MeshBuilder(&level, atlasRooms); initEntities(); shadow = NULL; @@ -945,8 +950,10 @@ struct Level : IGame { delete waterCache; delete zoneCache; - delete atlas; - delete glyphs; + delete atlasRooms; + delete atlasObjects; + delete atlasSprites; + delete atlasGlyphs; delete mesh; Sound::stopAll(); @@ -1214,7 +1221,9 @@ struct Level : IGame { 0x00000000, 0x80808080, 0x80808080, 0x80808080, 0x00000000, 0x00000000, 0x60606060, 0x60606060, 0x60606060, 0x00000000, 0x00000000, 0x20202020, 0x20202020, 0x20202020, 0x00000000 }, - // white tile + // white room + { 0xFFFFFFFF }, + // white object { 0xFFFFFFFF }, // white sprite { 0xFFFFFFFF }, @@ -1276,11 +1285,12 @@ struct Level : IGame { case CTEX_HEALTH : case CTEX_OXYGEN : case CTEX_OPTION : - case CTEX_WHITE_TILE : + case CTEX_WHITE_ROOM : + case CTEX_WHITE_OBJECT : case CTEX_WHITE_SPRITE : src = (Color32*)&CommonTexData[id][0]; tex = &CommonTex[id]; - if (id != CTEX_WHITE_TILE && id != CTEX_WHITE_SPRITE) { + if (id != CTEX_WHITE_ROOM && id != CTEX_WHITE_OBJECT && id != CTEX_WHITE_SPRITE) { mm.w = 4; // height - 1 if (id == CTEX_OPTION) { stride = 5; @@ -1291,7 +1301,6 @@ struct Level : IGame { default : return; } - memset(tex, 0, sizeof(*tex)); uv = tex->texCoordAtlas; uv[2].y += mm.w; uv[3].y += mm.w; @@ -1453,8 +1462,11 @@ struct Level : IGame { } // repack texture tiles - Atlas *tAtlas = new Atlas(level.objectTexturesCount + level.spriteTexturesCount + CTEX_MAX, short4(8, 8, 8, 8), this, fillCallback); - Atlas *gAtlas = new Atlas(level.spriteTexturesCount + CTEX_MAX, short4(0, 0, 1, 1), this, fillCallback); + int maxTiles = level.objectTexturesCount + level.spriteTexturesCount + CTEX_MAX; + Atlas *rAtlas = new Atlas(maxTiles, short4(4, 4, 4, 4), this, fillCallback); + Atlas *oAtlas = new Atlas(maxTiles, short4(4, 4, 4, 4), this, fillCallback); + Atlas *sAtlas = new Atlas(maxTiles, short4(4, 4, 4, 4), this, fillCallback); + Atlas *gAtlas = new Atlas(maxTiles, short4(0, 0, 1, 1), this, fillCallback); // add textures for (int i = 0; i < level.objectTexturesCount; i++) { TR::TextureInfo &t = level.objectTextures[i]; @@ -1466,7 +1478,10 @@ struct Level : IGame { uv.z = max(max(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x) + 1; uv.w = max(max(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y) + 1; - tAtlas->add(i, uv, &t); + if (t.type == TR::TEX_TYPE_ROOM) + rAtlas->add(i, uv, &t); + else + oAtlas->add(i, uv, &t); } // add sprites for (int i = 0; i < level.spriteTexturesCount; i++) { @@ -1493,22 +1508,29 @@ struct Level : IGame { continue; } } - tAtlas->add(level.objectTexturesCount + i, uv, &t); + sAtlas->add(level.objectTexturesCount + i, uv, &t); } // add common textures - const short2 CommonTexOffset[] = { short2(0, 4), short2(0, 4), short2(0, 4), short2(4, 4), short2(0, 0), short2(0, 0) }; + const short2 CommonTexOffset[] = { short2(0, 4), short2(0, 4), short2(0, 4), short2(4, 4), short2(0, 0), short2(0, 0), short2(0, 0) }; ASSERT(COUNT(CommonTexOffset) == CTEX_MAX); + memset(CommonTex, 0, sizeof(CommonTex)); for (int i = 0; i < CTEX_MAX; i++) { CommonTex[i].type = TR::TEX_TYPE_SPRITE; - Atlas *dst = (i == CTEX_FLASH || i == CTEX_WHITE_TILE) ? tAtlas : gAtlas; + Atlas *dst = (i == CTEX_FLASH || i == CTEX_WHITE_OBJECT) ? oAtlas : ((i == CTEX_WHITE_ROOM) ? rAtlas : gAtlas); dst->add(level.objectTexturesCount + level.spriteTexturesCount + i, short4(i * 32, ATLAS_PAGE_BARS, i * 32 + CommonTexOffset[i].x, ATLAS_PAGE_BARS + CommonTexOffset[i].y), &CommonTex[i]); } // get result texture tileData = new TR::Tile32(); - atlas = tAtlas->pack(true); - glyphs = gAtlas->pack(false); + atlasRooms = rAtlas->pack(true); + atlasObjects = oAtlas->pack(true); + atlasSprites = sAtlas->pack(true); + atlasGlyphs = gAtlas->pack(false); + + ASSERT(atlasRooms->width <= 1024 && atlasRooms->height <= 1024); + ASSERT(atlasObjects->width <= 1024 && atlasObjects->height <= 1024); + ASSERT(atlasSprites->width <= 1024 && atlasSprites->height <= 1024); delete[] tileData; tileData = NULL; @@ -1518,16 +1540,24 @@ struct Level : IGame { glyphsCyr = NULL; glyphsJap = NULL; - atlas->setFilterQuality(Core::settings.detail.filter); - glyphs->setFilterQuality(Core::Settings::MEDIUM); + atlasRooms->setFilterQuality(Core::settings.detail.filter); + atlasObjects->setFilterQuality(Core::settings.detail.filter); + atlasSprites->setFilterQuality(Core::settings.detail.filter); + atlasGlyphs->setFilterQuality(Core::Settings::MEDIUM); - delete tAtlas; + delete rAtlas; + delete oAtlas; + delete sAtlas; delete gAtlas; - LOG("atlas : %d x %d\n", atlas->width, atlas->height); - LOG("glyphs : %d x %d\n", glyphs->width, glyphs->height); - PROFILE_LABEL(TEXTURE, atlas->ID, "atlas"); - PROFILE_LABEL(TEXTURE, glyphs->ID, "glyphs"); + LOG("rooms : %d x %d\n", atlasRooms->width, atlasRooms->height); + LOG("objects : %d x %d\n", atlasObjects->width, atlasObjects->height); + LOG("sprites : %d x %d\n", atlasSprites->width, atlasSprites->height); + LOG("glyphs : %d x %d\n", atlasGlyphs->width, atlasGlyphs->height); + PROFILE_LABEL(TEXTURE, atlasRooms->ID, "atlas_rooms"); + PROFILE_LABEL(TEXTURE, atlasObjects->ID, "atlas_objects"); + PROFILE_LABEL(TEXTURE, atlasSprites->ID, "atlas_sprites"); + PROFILE_LABEL(TEXTURE, atlasGlyphs->ID, "atlas_glyphs"); #else ASSERT(level.tilesCount); @@ -1753,6 +1783,8 @@ struct Level : IGame { dir = -1; } + atlasRooms->bind(sDiffuse); + while (i != end) { int roomIndex = roomsList[i]; MeshBuilder::RoomRange &range = mesh->rooms[roomIndex]; @@ -1783,6 +1815,8 @@ struct Level : IGame { Core::setDepthWrite(true); if (transp == 1) { + atlasSprites->bind(sDiffuse); + Core::setBlendMode(bmPremult); #ifdef MERGE_SPRITES @@ -2087,6 +2121,8 @@ struct Level : IGame { void renderEntitiesTransp(int transp) { mesh->dynBegin(); mesh->transparent = transp; + + atlasObjects->bind(sDiffuse); for (int i = 0; i < level.entitiesCount; i++) { TR::Entity &e = level.entities[i]; if (!e.controller || e.modelIndex == 0) continue; @@ -2097,6 +2133,7 @@ struct Level : IGame { PROFILE_MARKER("ENTITY_SPRITES"); if (mesh->dynICount) { + atlasSprites->bind(sDiffuse); Core::lightPos[0] = vec4(0, 0, 0, 0); Core::lightColor[0] = vec4(0, 0, 0, 1); setRoomParams(getLara()->getRoomIndex(), Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, mesh->transparent == 1); @@ -2135,6 +2172,7 @@ struct Level : IGame { Core::whiteTex->bind(0); #endif + Core::whiteTex->bind(sDiffuse); Core::setBlendMode(bmMult); for (int i = 0; i < level.entitiesCount; i++) { TR::Entity &entity = level.entities[i]; @@ -2797,15 +2835,29 @@ struct Level : IGame { aspect *= 0.5f; } else vp = Viewport(vX, vY, vW, vH); - + + + Core::eye = float(eye); + + #ifdef _OS_3DS + Core::eye *= osGet3DSliderState() / 3.0f; + + if (eye <= 0) { + GAPI::curTarget = GAPI::defTarget[0]; + } else { + GAPI::curTarget = GAPI::defTarget[1]; + } + + C3D_FrameDrawOn(GAPI::curTarget); + #else if (Core::settings.detail.stereo != Core::Settings::STEREO_VR) { switch (eye) { case -1 : vp = Viewport(vX + vp.x - vp.x / 2, vY + vp.y, vp.width / 2, vp.height); break; case +1 : vp = Viewport(vX + vW / 2 + vp.x / 2, vY + vp.y, vp.width / 2, vp.height); break; } } + #endif - Core::eye = float(eye); Core::setViewport(vp.x, vp.y, vp.width, vp.height); if (isUI) @@ -2904,6 +2956,10 @@ struct Level : IGame { Core::viewportDef = vp; } + #ifdef _OS_3DS + Core::settings.detail.stereo = osGet3DSliderState() > 0.0f ? Core::Settings::STEREO_ON : Core::Settings::STEREO_OFF; + #endif + if (Core::settings.detail.stereo == Core::Settings::STEREO_ON) { // left/right SBS stereo float oldEye = Core::eye; @@ -2966,10 +3022,10 @@ struct Level : IGame { UI::begin(); UI::updateAspect(camera->aspect); - atlas->bind(sDiffuse); + atlasObjects->bind(sDiffuse); UI::renderPickups(); - glyphs->bind(sDiffuse); + atlasGlyphs->bind(sDiffuse); Core::resetLights(); @@ -3034,11 +3090,14 @@ struct Level : IGame { if (level.isTitle() || inventory->titleTimer > 0.0f) inventory->renderBackground(); + + setupBinding(); + atlasObjects->bind(sDiffuse); inventory->render(aspect); UI::begin(); UI::updateAspect(aspect); - glyphs->bind(sDiffuse); + atlasGlyphs->bind(sDiffuse); inventory->renderUI(); UI::end(); } @@ -3076,7 +3135,8 @@ struct Level : IGame { Core::setTarget(NULL, NULL, RT_CLEAR_COLOR | RT_STORE_COLOR); UI::begin(); UI::updateAspect(float(Core::width) / float(Core::height)); - glyphs->bind(sDiffuse); UI::textOut(vec2(0, 480 - 16), STR_LOADING, UI::aCenter, UI::width); + atlasGlyphs->bind(sDiffuse); + UI::textOut(vec2(0, 480 - 16), STR_LOADING, UI::aCenter, UI::width); UI::end(); return; } diff --git a/src/mesh.h b/src/mesh.h index 4c669b23..22d462b6 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -9,13 +9,15 @@ enum CommonTexType { CTEX_HEALTH, CTEX_OXYGEN, CTEX_OPTION, - CTEX_WHITE_TILE, + CTEX_WHITE_ROOM, + CTEX_WHITE_OBJECT, CTEX_WHITE_SPRITE, CTEX_MAX, }; TR::TextureInfo CommonTex[CTEX_MAX]; -TR::TextureInfo &whiteTile = CommonTex[CTEX_WHITE_TILE]; +TR::TextureInfo &whiteRoom = CommonTex[CTEX_WHITE_ROOM]; +TR::TextureInfo &whiteObject = CommonTex[CTEX_WHITE_OBJECT]; TR::TextureInfo &whiteSprite = CommonTex[CTEX_WHITE_SPRITE]; #define PLANE_DETAIL 48 @@ -354,7 +356,7 @@ struct MeshBuilder { int y = m.y; int z = m.z - room.info.z; int d = m.rotation.value / 0x4000; - buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartRoom, 0, x, y, z, d, m.color); + buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartRoom, 0, x, y, z, d, m.color, true, false); } geom.finish(iCount); @@ -406,7 +408,7 @@ struct MeshBuilder { #ifndef MERGE_MODELS geom.getNextRange(vStartModel, iCount, 0xFFFF, 0xFFFF); #endif - buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartModel, j, 0, 0, 0, 0, COLOR_WHITE, forceOpaque); + buildMesh(geom, blendMask, mesh, level, indices, vertices, iCount, vCount, vStartModel, j, 0, 0, 0, 0, COLOR_WHITE, false, forceOpaque); } #ifndef MERGE_MODELS @@ -452,7 +454,7 @@ struct MeshBuilder { for (int i = 0; i < 9; i++) { Vertex &v0 = vertices[vCount + i * 2 + 0]; v0.normal = short4( 0, -1, 0, 32767 ); - v0.texCoord = short4( whiteTile.texCoordAtlas[0].x, whiteTile.texCoordAtlas[0].y, 32767, 32767 ); + v0.texCoord = short4( whiteObject.texCoordAtlas[0].x, whiteObject.texCoordAtlas[0].y, 32767, 32767 ); v0.color = v0.light = ubyte4( 0, 0, 0, 0 ); if (i == 8) { @@ -980,13 +982,13 @@ struct MeshBuilder { } } - bool buildMesh(Geometry &geom, int blendMask, const TR::Mesh &mesh, TR::Level *level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir, const Color32 &light, bool forceOpaque = false) { + bool buildMesh(Geometry &geom, int blendMask, const TR::Mesh &mesh, TR::Level *level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir, const Color32 &light, bool useRoomTex, bool forceOpaque) { bool isOpaque = true; for (int j = 0; j < mesh.fCount; j++) { TR::Face &f = mesh.faces[j]; ASSERT(f.colored || f.flags.texture < level->objectTexturesCount); - TR::TextureInfo &t = f.colored ? whiteTile : level->objectTextures[f.flags.texture]; + TR::TextureInfo &t = f.colored ? (useRoomTex ? whiteRoom : whiteObject) : level->objectTextures[f.flags.texture]; int texAttrib = forceOpaque ? 0 : t.attribute; diff --git a/src/network.h b/src/network.h index 55728f20..19348af4 100644 --- a/src/network.h +++ b/src/network.h @@ -111,7 +111,7 @@ namespace Network { void start(IGame *game) { Network::game = game; NAPI::listen(NET_PORT); - syncInputTime = syncStateTime = osGetTime(); + syncInputTime = syncStateTime = Core::getTime(); } void stop() { @@ -216,7 +216,7 @@ namespace Network { NAPI::Peer from; Packet packet, response; - int time = osGetTime(); + int time = Core::getTime(); while ( (count = recvPacket(from, packet)) > 0 ) { Player *player = getPlayerByPeer(from); diff --git a/src/platform/3ds/Makefile b/src/platform/3ds/Makefile new file mode 100644 index 00000000..ee2eadd8 --- /dev/null +++ b/src/platform/3ds/Makefile @@ -0,0 +1,256 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# GRAPHICS is a list of directories containing graphics files +# GFXBUILD is the directory where converted graphics files will be placed +# If set to $(BUILD), it will statically link in the converted +# files as if they were data files. +# +# NO_SMDH: if set to anything, no SMDH file is generated. +# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional) +# APP_TITLE is the name of the app stored in the SMDH file (Optional) +# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) +# APP_AUTHOR is the author of the app stored in the SMDH file (Optional) +# ICON is the filename of the icon (.png), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .png +# - icon.png +# - /default_icon.png +#--------------------------------------------------------------------------------- +TARGET := OpenLara +BUILD := build +SOURCES := . ../../libs/stb_vorbis ../../libs/tinf +DATA := data +INCLUDES := ../.. +GRAPHICS := gfx +GFXBUILD := $(BUILD) +APP_AUTHOR := XProger +APP_DESCRIPTION := Classic Tomb Raider open-source engine +#ROMFS := romfs +#GFXBUILD := $(ROMFS)/gfx + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft + +CFLAGS := -g -w -Ofast -ffast-math -mword-relocations \ + -fomit-frame-pointer -ffunction-sections \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS -DPROFILE + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lcitro3d -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +PICAFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica))) +SHLISTFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist))) +GFXFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +ifeq ($(GFXBUILD),$(BUILD)) +#--------------------------------------------------------------------------------- +export T3XFILES := $(GFXFILES:.t3s=.t3x) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- +export ROMFS_T3XFILES := $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES)) +export T3XHFILES := $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES)) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) \ + $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \ + $(addsuffix .o,$(T3XFILES)) + +export OFILES := $(OFILES_BIN) $(OFILES_SOURCES) + +export HFILES := $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \ + $(addsuffix .h,$(subst .,_,$(BINFILES))) \ + $(GFXFILES:.t3s=.h) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export _3DSXDEPS := $(if $(NO_SMDH),,$(OUTPUT).smdh) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.png) + ifneq (,$(findstring $(TARGET).png,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).png + else + ifneq (,$(findstring icon.png,$(icons))) + export APP_ICON := $(TOPDIR)/icon.png + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_SMDH)),) + export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh +endif + +ifneq ($(ROMFS),) + export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS) +endif + +.PHONY: all clean + +#--------------------------------------------------------------------------------- +all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +$(BUILD): + @mkdir -p $@ + +ifneq ($(GFXBUILD),$(BUILD)) +$(GFXBUILD): + @mkdir -p $@ +endif + +ifneq ($(DEPSDIR),$(BUILD)) +$(DEPSDIR): + @mkdir -p $@ +endif + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD) + +#--------------------------------------------------------------------------------- +$(GFXBUILD)/%.t3x $(BUILD)/%.h : %.t3s +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x + +#--------------------------------------------------------------------------------- +else + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).3dsx : $(OUTPUT).elf $(_3DSXDEPS) + +$(OFILES_SOURCES) : $(HFILES) + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +.PRECIOUS : %.t3x +#--------------------------------------------------------------------------------- +%.t3x.o %_t3x.h : %.t3x +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# rules for assembling GPU shaders +#--------------------------------------------------------------------------------- +define shader-as + $(eval CURBIN := $*.shbin) + $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d) + echo "$(CURBIN).o: $< $1" > $(DEPSFILE) + echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h + echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h + echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h + picasso -o $(CURBIN) $1 + bin2s $(CURBIN) | $(AS) -o $*.shbin.o +endef + +%.shbin.o %_shbin.h : %.v.pica %.g.pica + @echo $(notdir $^) + @$(call shader-as,$^) + +%.shbin.o %_shbin.h : %.v.pica + @echo $(notdir $<) + @$(call shader-as,$<) + +%.shbin.o %_shbin.h : %.shlist + @echo $(notdir $<) + @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file))) + +#--------------------------------------------------------------------------------- +%.t3x %.h : %.t3s +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x + +-include $(DEPSDIR)/*.d + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/src/platform/3ds/compose.v.pica b/src/platform/3ds/compose.v.pica new file mode 100644 index 00000000..41a2d3d5 --- /dev/null +++ b/src/platform/3ds/compose.v.pica @@ -0,0 +1,67 @@ +; constants +.constf const0(2.0, 1.0, 0.5, 0.25) +.constf const1(3.05185094e-005, 0.00392156886, 0.00784313725, 0.0) + +; uniforms +.fvec uViewProj[4] +.fvec uBasis[32*2] + +; in +.alias aCoord v0 +.alias aNormal v1 +.alias aTexCoord v2 +.alias aColor v3 +.alias aLight v4 + +; out +.out vPosition position +.out vTexCoord texcoord0 +.out vColor color + +.proc main +; mulQuat + ;mul r0.xyz, uBasis[0], aCoord.zxy + ;mad r0.xyz, aCoord, uBasis[0].zxy, -r0 + ;mad r0.xyz, aCoord.yzx, uBasis[0].w, r0 + ;mul r1.xyz, uBasis[0].zxy, r0 + ;mad r0.xyz, r0.yzx, uBasis[0].yzx, -r1 + ;mad r0.xyz, r0, const0.x, aCoord + ;add r0.xyz, uBasis[1], r0 + ;mov r0.w, uBasis[1].w + + mul r0.x, const0.xxxx, aCoord.wwww + + ;frc r0.x, v0.w + ;add r0.x, -r0.x, v0.w + mova a0.x, r0.x + mul r0.xyz, uBasis[a0.x], aCoord.zxyw + mad r0.xyz, aCoord, uBasis[a0.x].zxyw, -r0 + mad r0.xyz, aCoord.yzxw, uBasis[a0.x].w, r0 + mul r1.xyz, uBasis[a0.x].zxyw, r0 + mad r0.xyz, r0.yzxw, uBasis[a0.x].yzxw, -r1 + mad r0.xyz, r0, const0.x, aCoord + add r0.xyz, uBasis[a0.x + 1], r0 + mov r0.w, uBasis[a0.x + 1].w + +; uViewProj * coord + dp4 vPosition.x, uViewProj[0], r0 + dp4 vPosition.y, uViewProj[1], r0 + dp4 vPosition.z, uViewProj[2], r0 + dp4 vPosition.w, uViewProj[3], r0 + + mul r2, const1.xxxx, aTexCoord.xyzw + mov vTexCoord, r2 + + mov r3, aColor + mul r3, const1.yyyy, r3.xyzw + + mov r4, aLight + mul r4, const1.yyyy, r4.xyzw + + mul vColor, r3, r4 + ;mov vColor, r3 + ;mov vColor.xyz, r2.xyz + ;mov vColor.w, const0.yyyy + + end +.end diff --git a/src/platform/3ds/deploy.bat b/src/platform/3ds/deploy.bat new file mode 100644 index 00000000..a64a83ee --- /dev/null +++ b/src/platform/3ds/deploy.bat @@ -0,0 +1 @@ +C:\devkitPro\tools\bin\3dslink.exe OpenLara.3dsx -a 192.168.1.68 \ No newline at end of file diff --git a/src/platform/3ds/filter_upscale.v.pica b/src/platform/3ds/filter_upscale.v.pica new file mode 100644 index 00000000..d8e05e7e --- /dev/null +++ b/src/platform/3ds/filter_upscale.v.pica @@ -0,0 +1,24 @@ +; constants +.constf const0(1.0, 3.05185094e-005, 0.00392156886, -0.5) + +; in +.alias aCoord v0 +.alias aTexCoord v2 +.alias aLight v4 + +; out +.out vPosition position +.out vTexCoord texcoord0 +.out vColor color + +.proc main + mul r0.xyzw, const0.yyyy, aCoord.yxzw + mov r0.w, const0.xxxx + mov r0.y, -r0.y + mov vPosition, r0 + + mul vTexCoord, const0.yyyy, aTexCoord + mul vColor, const0.zzzz, aLight + + end +.end diff --git a/src/platform/3ds/gui.v.pica b/src/platform/3ds/gui.v.pica new file mode 100644 index 00000000..3a68fe7d --- /dev/null +++ b/src/platform/3ds/gui.v.pica @@ -0,0 +1,39 @@ +; constants +.constf const0(2.0, 1.0, 0.5, 0.25) +.constf const1(3.05185094e-005, 0.00392156886, 0.00784313725, 0.0) + +; uniforms +.fvec uViewProj[4] +.fvec uMaterial + +; in +.alias aCoord v0 +.alias aNormal v1 +.alias aTexCoord v2 +.alias aColor v3 +.alias aLight v4 + +; out +.out vPosition position +.out vTexCoord texcoord0 +.out vColor color + +.proc main + mov r0.xyz, aCoord + mov r0.w, const0.yyyy + +; uViewProj * coord + dp4 vPosition.x, uViewProj[0], r0 + dp4 vPosition.y, uViewProj[1], r0 + dp4 vPosition.z, uViewProj[2], r0 + dp4 vPosition.w, uViewProj[3], r0 + + mul r2, const1.xxxx, aTexCoord.xyzw + mov vTexCoord, r2 + + mov r3, aLight + mul r3, const1.yyyy, r3.xyzw + mul vColor, uMaterial, r3 + + end +.end diff --git a/src/platform/3ds/icon.png b/src/platform/3ds/icon.png new file mode 100644 index 00000000..48a5d3af Binary files /dev/null and b/src/platform/3ds/icon.png differ diff --git a/src/platform/3ds/main.cpp b/src/platform/3ds/main.cpp new file mode 100644 index 00000000..4346fa02 --- /dev/null +++ b/src/platform/3ds/main.cpp @@ -0,0 +1,187 @@ +#include +#include +#include +#include + +#include "game.h" + +// multi-threading +void* osMutexInit() { + Handle *mutex = new Handle(); + svcCreateMutex(mutex, false); + return mutex; +} + +void osMutexFree(void *obj) { + svcCloseHandle(*(Handle*)obj); + delete (Handle*)obj; +} + +void osMutexLock(void *obj) { + svcWaitSynchronization(*(Handle*)obj, U64_MAX); +} + +void osMutexUnlock(void *obj) { + svcReleaseMutex(*(Handle*)obj); +} + +// timing +u64 osStartTime = 0; + +int osGetTimeMS() { + return int(osGetTime() - osStartTime); +} + +// input +bool osJoyReady(int index) { + return index == 0; +} + +void osJoyVibrate(int index, float L, float R) { + // +} + +void inputInit() { + hidInit(); +} + +void inputUpdate() { + const static u64 keys[jkMAX] = { 0, + KEY_B, KEY_A, KEY_Y, KEY_X, KEY_L, KEY_R, KEY_SELECT, KEY_START, + 0, 0, KEY_ZL, KEY_ZR, + KEY_DLEFT, KEY_DRIGHT, KEY_DUP, KEY_DDOWN, + }; + + hidScanInput(); + + u64 mask = hidKeysDown() | hidKeysHeld(); + + for (int i = 1; i < jkMAX; i++) { + Input::setJoyDown(0, JoyKey(jkNone + i), (mask & keys[i]) != 0); + } + + circlePosition circlePos; + hidCircleRead(&circlePos); + + vec2 stickL = vec2(float(circlePos.dx), float(-circlePos.dy)) / 160.0f; + + if (fabsf(stickL.x) < 0.3f && fabsf(stickL.y) < 0.3f) stickL = vec2(0.0f); + Input::setJoyPos(0, jkL, stickL); +} + +void inputFree() { + hidExit(); +} + +// sound +#define SND_FRAMES (4704/2) + +ndspWaveBuf sndWaveBuf[2]; +Sound::Frame *sndBuffer; +Thread sndThread; +int sndBufIndex; +bool sndReady; + +void sndFill(void *arg) { + LOG("thread start\n"); + memset(sndWaveBuf, 0, sizeof(sndWaveBuf)); + sndWaveBuf[0].data_vaddr = sndBuffer + 0; + sndWaveBuf[0].nsamples = SND_FRAMES; + sndWaveBuf[1].data_vaddr = sndBuffer + SND_FRAMES; + sndWaveBuf[1].nsamples = SND_FRAMES; + + Sound::fill(sndBuffer, SND_FRAMES * 2); + + sndBufIndex = 0; + ndspChnWaveBufAdd(0, sndWaveBuf + 0); + ndspChnWaveBufAdd(0, sndWaveBuf + 1); + + while (sndReady) { + ndspWaveBuf &buf = sndWaveBuf[sndBufIndex]; + + if (buf.status == NDSP_WBUF_DONE) { + Sound::fill((Sound::Frame*)buf.data_pcm16, buf.nsamples); + DSP_FlushDataCache(buf.data_pcm16, buf.nsamples); + ndspChnWaveBufAdd(0, &buf); + sndBufIndex = !sndBufIndex; + } + + svcSleepThread(1000000ULL); + } +} + +void sndInit() { + sndBuffer = (Sound::Frame*)linearAlloc(SND_FRAMES * sizeof(Sound::Frame) * 2); + + ndspInit(); + ndspSetOutputMode (NDSP_OUTPUT_STEREO); + ndspChnSetFormat (0, NDSP_FORMAT_STEREO_PCM16); + ndspChnSetInterp (0, NDSP_INTERP_LINEAR); + ndspChnSetRate (0, 44100); + + float mix[12]; + memset(mix, 0, sizeof(mix)); + mix[0] = 1.0; + mix[1] = 1.0; + ndspChnSetMix(0, mix); + + sndReady = true; + + s32 priority = 0; + svcGetThreadPriority(&priority, CUR_THREAD_HANDLE); + + sndThread = threadCreate(sndFill, NULL, 64 * 1024, priority - 1, -1, false); +} + +void sndFree() { + sndReady = false; + threadJoin(sndThread, U64_MAX); + threadFree(sndThread); + + ndspExit(); + linearFree((uint32*)sndBuffer); +} + +int main() { + { + bool isNew3DS; + APT_CheckNew3DS(&isNew3DS); + if (isNew3DS) { + osSetSpeedupEnable(true); + } + } + + strcpy(cacheDir, "sdmc:/3ds/OpenLara/"); + strcpy(saveDir, "sdmc:/3ds/OpenLara/"); + strcpy(contentDir, "sdmc:/3ds/OpenLara/"); + + if(chdir(contentDir) != 0) { + gfxExit(); + return 0; + } + + sndInit(); + inputInit(); + + osStartTime = Core::getTime(); + + Game::init("PSXDATA/LEVEL1.PSX"); + + while (aptMainLoop() && !Core::isQuit) { + inputUpdate(); + + if (Input::joy[0].down[jkStart]) + Core::quit(); + + Game::update(); + Game::render(); + + GAPI::present(); + } + + inputFree(); + sndFree(); + Game::deinit(); + + return 0; +} \ No newline at end of file diff --git a/src/platform/web/main.cpp b/src/platform/web/main.cpp index e2faea12..d1bdd0a3 100644 --- a/src/platform/web/main.cpp +++ b/src/platform/web/main.cpp @@ -9,7 +9,7 @@ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context; int WEBGL_VERSION; // timing -int osGetTime() { +int osGetTimeMS() { return (int)emscripten_get_now(); } diff --git a/src/platform/win/OpenLara.vcxproj b/src/platform/win/OpenLara.vcxproj index fd088012..96441965 100644 --- a/src/platform/win/OpenLara.vcxproj +++ b/src/platform/win/OpenLara.vcxproj @@ -200,10 +200,10 @@ + - diff --git a/src/platform/win/OpenLara.vcxproj.filters b/src/platform/win/OpenLara.vcxproj.filters index c223b5ea..bab5cfce 100644 --- a/src/platform/win/OpenLara.vcxproj.filters +++ b/src/platform/win/OpenLara.vcxproj.filters @@ -40,7 +40,6 @@ - libs\minimp3 @@ -89,6 +88,7 @@ lang + diff --git a/src/platform/win/main.cpp b/src/platform/win/main.cpp index 0f5a638b..57b34d47 100644 --- a/src/platform/win/main.cpp +++ b/src/platform/win/main.cpp @@ -60,7 +60,7 @@ void osMutexUnlock(void *obj) { // timing int osStartTime = 0; -int osGetTime() { +int osGetTimeMS() { #ifdef DEBUG LARGE_INTEGER Freq, Count; QueryPerformanceFrequency(&Freq); @@ -144,14 +144,14 @@ void osJoyVibrate(int index, float L, float R) { void joyRumble(int index) { JoyDevice &joy = joyDevice[index]; - if (XInputSetState && joy.ready && (joy.vL != joy.oL || joy.vR != joy.oR) && osGetTime() >= joy.time) { + if (XInputSetState && joy.ready && (joy.vL != joy.oL || joy.vR != joy.oR) && Core::getTime() >= joy.time) { XINPUT_VIBRATION vibration; vibration.wLeftMotorSpeed = int(joy.vL * 65535.0f); vibration.wRightMotorSpeed = int(joy.vR * 65535.0f); XInputSetState(index, &vibration); joy.oL = joy.vL; joy.oR = joy.vR; - joy.time = osGetTime() + JOY_MIN_UPDATE_FX_TIME; + joy.time = Core::getTime() + JOY_MIN_UPDATE_FX_TIME; } } @@ -861,7 +861,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi Sound::channelsCount = 0; - osStartTime = osGetTime(); + osStartTime = Core::getTime(); touchInit(hWnd); joyInit(); diff --git a/src/sound.h b/src/sound.h index 16ea3eec..c0c2f39b 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) +#if !defined(_OS_PSP) && !defined(_OS_WEB) && !defined(_OS_PSV) && !defined(_OS_3DS) #define DECODE_MP3 #endif @@ -50,31 +50,39 @@ namespace Sound { int32 L, R; }; + struct Stats { + int mixer; + int reverb; + int render[2]; + int ogg; + } stats; + namespace Filter { - #define MAX_FDN 16 - #define MAX_DELAY 1024 + #define MAX_FDN 16 + #define MAX_DELAY 954 + + #define DSP_SCALE_BIT 8 + #define DSP_SCALE (1 << DSP_SCALE_BIT) static const int16 FDN[MAX_FDN] = { 281, 331, 373, 419, 461, 503, 547, 593, 641, 683, 727, 769, 811, 853, 907, 953 }; struct Delay { int index; - float out[MAX_DELAY]; + int16 out[MAX_DELAY]; - float process(float x, int delay) { + void process(int32 &x, int16 delay) { index = (index + 1) % delay; - float y = out[index]; + int16 y = out[index]; out[index] = x; - return y; + x = y; } }; struct Absorption { - float out; + int32 out; - float process(float x, float gain, float damping) { - out = out * damping + x * gain * (1.0f - damping); - if (out < EPS) out = 0.0f; - return out; + void process(int32 &x, int32 *coeff) { // coeff[0] - gain, coeff[1] - damping + x = out = (out * coeff[1] + ((x * coeff[0] * (DSP_SCALE - coeff[1])) >> DSP_SCALE_BIT)) >> DSP_SCALE_BIT; } }; @@ -82,25 +90,24 @@ namespace Sound { Delay df[MAX_FDN]; Absorption af[MAX_FDN]; - float output[MAX_FDN]; - float panCoeff[MAX_FDN][2]; - float absCoeff[MAX_FDN][2]; // absorption gain & damping + int32 output[MAX_FDN]; + int32 panCoeff[MAX_FDN][2]; + int32 absCoeff[MAX_FDN][2]; // absorption gain & damping + int32 buffer[MAX_FDN]; Reverberation() { - float k = 1.0f / MAX_FDN; - for (int i = 0; i < MAX_FDN; i++) { - output[i] = 0.0f; - panCoeff[i][0] = (i % 2) ? -k : k; + panCoeff[i][0] = (i % 2) ? -1 : 1; } for (int i = 0; i < MAX_FDN; i += 2) { if ((i / 2) % 2) - panCoeff[i][1] = panCoeff[i + 1][1] = -k; + panCoeff[i][1] = panCoeff[i + 1][1] = -1; else - panCoeff[i][1] = panCoeff[i + 1][1] = k; + panCoeff[i][1] = panCoeff[i + 1][1] = 1; } + memset(output, 0, sizeof(output)); memset(df, 0, sizeof(df)); memset(af, 0, sizeof(af)); @@ -115,40 +122,43 @@ namespace Sound { float k = -10.0f / (44100.0f * rt60); for (int i = 0; i < MAX_FDN; i++) { - absCoeff[i][0] = powf(10.0f, FDN[i] * k); - absCoeff[i][1] = 1.0f - (2.0f / (1.0f + powf(absCoeff[i][0], 1.0f - 1.0f / 0.15f))); + float v = powf(10.0f, FDN[i] * k); + absCoeff[i][0] = int32(v * DSP_SCALE); + absCoeff[i][1] = int32((1.0f - (2.0f / (1.0f + powf(v, 1.0f - 1.0f / 0.15f)))) * DSP_SCALE); } }; void process(FrameHI *frames, int count) { - float buffer[MAX_FDN]; + PROFILE_CPU_TIMING(stats.reverb); for (int i = 0; i < count; i++) { FrameHI &frame = frames[i]; - float L = frame.L * (1.0f / 32767.0f); - float R = frame.R * (1.0f / 32767.0f); - float in = (L + R) * 0.5f; - float out = 0.0f; + int32 in = (frame.L + frame.R) / 2; + int32 out = 0; + int32 L = 0; + int32 R = 0; // apply delay & absorption filters for (int j = 0; j < MAX_FDN; j++) { - float k = in + output[j]; - k = df[j].process(k, FDN[j]); - k = af[j].process(k, absCoeff[j][0], absCoeff[j][1]); - out += k * (2.0f / MAX_FDN); + int32 k = in + output[j]; + df[j].process(k, FDN[j]); + af[j].process(k, absCoeff[j]); + out += k; buffer[j] = k; } + out = out * 2 / MAX_FDN; // apply pan + int32 buf = buffer[MAX_FDN - 1]; for (int j = 0; j < MAX_FDN; j++) { - output[j] = out - buffer[(j + MAX_FDN - 1) % MAX_FDN]; - if (output[j] < EPS) output[j] = 0.0f; - L += buffer[j] * panCoeff[j][0]; - R += buffer[j] * panCoeff[j][1]; + output[j] = max(0, out - buf); + buf = buffer[j]; + L += buf ^ panCoeff[j][0]; + R += buf ^ panCoeff[j][1]; } - frame.L = int(L * 32767.0f); - frame.R = int(R * 32767.0f); + frame.L += L / MAX_FDN; + frame.R += R / MAX_FDN; } } }; @@ -665,6 +675,7 @@ namespace Sound { } virtual int decode(Frame *frames, int count) { + PROFILE_CPU_TIMING(stats.ogg); int i = 0; while (i < count) { int res = stb_vorbis_get_samples_short_interleaved(ogg, channels, (short*)frames + i, (count - i) * 2); @@ -914,6 +925,8 @@ namespace Sound { } void renderChannels(FrameHI *result, int count, bool music) { + PROFILE_CPU_TIMING(stats.render[music]); + int bufSize = count + count / 2; if (!buffer) buffer = new Frame[bufSize]; // + 50% for pitch @@ -946,9 +959,12 @@ namespace Sound { for (int j = 0; j < count; j++, t += channels[i]->pitch) { int idxA = int(t); int idxB = (j == (count - 1)) ? idxA : (idxA + 1); - float k = t - idxA; - result[j].L += int(lerp(buffer[idxA].L, buffer[idxB].L, k)); - result[j].R += int(lerp(buffer[idxA].R, buffer[idxB].R, k)); + int st = int((t - idxA) * DSP_SCALE); + Frame &a = buffer[idxA]; + Frame &b = buffer[idxB]; + + result[j].L += a.L + ((b.L - a.L) * st >> DSP_SCALE_BIT); + result[j].R += a.R + ((b.R - a.R) * st >> DSP_SCALE_BIT); } } } @@ -963,9 +979,10 @@ namespace Sound { void fill(Frame *frames, int count) { OS_LOCK(lock); + PROFILE_CPU_TIMING(stats.mixer); if (!channelsCount) { - if (result) { + if (result && (Core::settings.audio.music != 0 || Core::settings.audio.sound != 0)) { memset(result, 0, sizeof(FrameHI) * count); if (Core::settings.audio.reverb) reverb.process(result, count); @@ -978,12 +995,16 @@ namespace Sound { if (!result) result = new FrameHI[count]; memset(result, 0, sizeof(FrameHI) * count); - renderChannels(result, count, false); + if (Core::settings.audio.sound != 0) { + renderChannels(result, count, false); - if (Core::settings.audio.reverb) - reverb.process(result, count); + if (Core::settings.audio.reverb) + reverb.process(result, count); + } - renderChannels(result, count, true); + if (Core::settings.audio.music != 0) { + renderChannels(result, count, true); + } convFrames(result, frames, count); diff --git a/src/trigger.h b/src/trigger.h index e7436d0e..1132dfa3 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -1279,7 +1279,7 @@ struct Lightning : Controller { game->setShader(Core::pass, Shader::FLASH, false, false); Core::setMaterial(0.0f, 0.0f, 0.0f, 1.0f); - Core::active.shader->setParam(uBasis, b); + Core::setBasis(&b, 1); Core::setCullMode(cmNone); Core::setBlendMode(bmAdd); diff --git a/src/ui.h b/src/ui.h index 7336064d..20eed341 100644 --- a/src/ui.h +++ b/src/ui.h @@ -565,6 +565,8 @@ namespace UI { void renderTouch() { if (Input::touchTimerVis <= 0.0f) return; + Core::whiteTex->bind(sDiffuse); + Core::setDepthTest(false); Core::setBlendMode(bmPremult); Core::setCullMode(cmNone); diff --git a/src/utils.h b/src/utils.h index 528a9b46..d8bd179f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -98,7 +98,7 @@ typedef unsigned long long uint64; #define FOURCC(str) uint32( ((uint8*)(str))[0] | (((uint8*)(str))[1] << 8) | (((uint8*)(str))[2] << 16) | (((uint8*)(str))[3] << 24) ) #define TWOCC(str) uint32( ((uint8*)(str))[0] | (((uint8*)(str))[1] << 8) ) -#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) +#define ALIGNADDR(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) #define COUNT(arr) int(sizeof(arr) / sizeof(arr[0])) #define OFFSETOF(T, E) ((size_t)&(((T*)0)->E)) #define TEST_BIT(arr, bit) ((arr[bit / 32] >> (bit % 32)) & 1) @@ -539,8 +539,7 @@ struct quat { } vec3 operator * (const vec3 &v) const { - //return v + xyz.cross(xyz.cross(v) + v * w) * 2.0f; - return (*this * quat(v.x, v.y, v.z, 0) * inverse()).xyz(); + return v + xyz().cross(xyz().cross(v) + v * w) * 2.0f; } float dot(const quat &q) const {