/*---------------------------------------------------------------------------* Project: Horizon File: LightingToonApple.cpp Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 47228 $ *---------------------------------------------------------------------------*/ /* *------------------------------------------------------------ * Copyright(c) 2009-2010 by Digital Media Professionals Inc. * All rights reserved. *------------------------------------------------------------ * This source code is the confidential and proprietary * of Digital Media Professionals Inc. *------------------------------------------------------------ */ /* * Comments * -------- * * VN masking means that light reflection is set to zero when VN is smaller * than some threshold value 'outline.' This method works poorly when an * object has concave places (such as a center part of the inside of a palm). */ #include #include #include #include #include #include #include "demo.h" #include "Util.h" #include "Loader.h" #include #include "Memory.h" #define APP_NAME "LightingToonApple" /* program id */ GLuint s_ProgramID; /* shader id */ GLuint s_ShaderID; /* env texture id */ GLuint s_EnvTexName; /* obj file loader class object */ dat_t sphere; /* texture collection id */ GLuint s_TexColl; /* ExpHeap for app. */ nn::fnd::ExpHeap s_AppHeap; uptr s_HeapForGx; uptr s_HeapForMalloc; const u32 s_GxHeapSize = 0x400000; const u32 s_HeapSize = 0x200000; demo::RenderSystem s_RenderSystem; /* if USE_LN is not defined, half-vector is used as the vector controlling both diffuse illumination and highlight position */ #define USE_LN /* if WINDOW is defined, a window highlight is used */ #define WINDOW static void SetLutTable(void) { GLuint luts[4]; glGenTextures(4, luts); const float outline = 0.15f; const float highlight_eps = 0.01f; float delta[] = {1.f, 0.7f, 0.5f, -1.f}; GLfloat lut[512]; int j; memset(lut, 0, sizeof(lut)); float previous = 0, LN; int i = 0; /* f(D) = D is used as the initial shading function - see "Illumination Models in Maestro" for details first specify the steps in the [0, 1) interval */ for (j = 127; j >= 0; j--) { LN = (float)j/127.9375f; if (LN > delta[i]) lut[j] = previous; else { lut[j] = LN; previous = lut[j]; i++; } } for (j = 0; j < 127; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[127 + 256] = 0.f; /* now specify the steps in the [-1, 0) interval */ for (j = 255; j >= 128; j--) { LN = (float)(j - 256) /128.f; if (LN > delta[i]) lut[j] = previous; else { lut[j] = LN; previous = lut[j]; i++; } } /* now calculate differences (some are 0, some are not) */ for (j = 128; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = lut[0] - lut[255]; glBindTexture(GL_LUT_TEXTURE0_DMP, luts[0]); glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); for (j = 0; j < 256; j++) if ((float)j/255.9375f <= 1.f - highlight_eps) lut[j] = 0.f; else lut[j] = 1.f; for (j = 0; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = 0.f; glBindTexture(GL_LUT_TEXTURE1_DMP, luts[1]); glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); for (j = 0; j < 256; j++) if ((float)j/255.9375f < outline) lut[j] = 0.f; else lut[j] = 1.f; for (j = 0; j < 255; j++) lut[j + 256] = lut[j+1] - lut[j]; lut[255 + 256] = 1.f - lut[255]; glBindTexture(GL_LUT_TEXTURE2_DMP, luts[2]); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); for (j = 0; j < 256; j++) lut[j] = 1.f; for (j = 0; j < 256; j++) lut[j + 256] = 0.f; glBindTexture(GL_LUT_TEXTURE3_DMP, luts[3]); glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); } /* set lighting render state */ static void SetRenderState(void) { glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLighting.enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].enabled"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD0"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputD1"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputFR"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.absLutInputRR"), GL_FALSE); #ifdef USE_LN glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRR"), GL_LIGHT_ENV_LN_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD0"), GL_LIGHT_ENV_LN_DMP); #else glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputRR"), GL_LIGHT_ENV_NH_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD0"), GL_LIGHT_ENV_NH_DMP); #endif glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputD1"), GL_LIGHT_ENV_NV_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutInputFR"), GL_LIGHT_ENV_NV_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].shadowed"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].twoSideDiffuse"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].geomFactor0"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].geomFactor1"), GL_FALSE); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD0"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleD1"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleSP"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleFR"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRB"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRG"), 1.f); glUniform1f(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutScaleRR"), 1.f); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledRefl"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledSP"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG6_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.bumpMode"), GL_LIGHT_ENV_BUMP_NOT_USED_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD0"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.lutEnabledD1"), GL_TRUE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.clampHighlights"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_LightEnv.fresnelSelector"), GL_LIGHT_ENV_PRI_SEC_ALPHA_FRESNEL_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerRR"), 0); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD0"), 1); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerD1"), 2); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.samplerFR"), 3); SetLutTable(); GLfloat ma[] = {0.f, 0.f, 0.f, 1.f}; /* material ambient */ GLfloat md[] = {0.f, 0.f, 0.f, 1.f}; /* material diffuse */ GLfloat ms[] = {1.f, 1.f, 1.f, 1.f}; /* material specular */ #ifdef WINDOW ms[0] = 0.f; ms[1] = 0.f; ms[2] = 0.f; #endif glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.diffuse"), 1, md); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.specular0"), 1, ms); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentMaterial.ambient"), 1, ma); GLfloat ld[] = {1.f, 1.f, 1.f, 1.f}; /* light diffuse */ GLfloat ls[] = {1.f, 1.f, 1.f, 1.f}; /* light specular */ GLfloat ls2[] = {1.f, 0.2f, 0.f, 1.f}; /* light specular2 */ GLfloat la[] = {1.f, 1.f, 1.f, 1.f}; /* light ambient */ glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].diffuse"), 1, ld); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].specular0"), 1, ls); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].specular1"), 1, ls2); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].ambient"), 1, la); nn::math::Matrix44 proj; /* set projection matrix */ nn::math::MTX44Frustum(&proj, -0.015f, 0.015f, -0.015f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.015f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.2f, 200.f); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uProjection"), 1, GL_TRUE, static_cast(proj)); /* set texture setting */ #ifdef WINDOW /* output color is EnvTexture_rgb * PriColor_alpha + SecColor_rgb EnvTexture_rgb is environment highlight specular, PriColor_alpha is frensel reflection, and SecColor_rgb is toon shading diffuse color. */ glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[0].samplerType"), GL_TEXTURE_CUBE_MAP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineRgb"), GL_MULT_ADD_DMP); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_ALPHA, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_FRAGMENT_SECONDARY_COLOR_DMP); #else glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_Texture[0].samplerType"), GL_FALSE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineRgb"), GL_REPLACE); glUniform1i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(s_ProgramID, "dmp_TexEnv[0].srcRgb"), GL_FRAGMENT_SECONDARY_COLOR_DMP, GL_CONSTANT, GL_CONSTANT); #endif } /* load objects */ static void LoadObjects(void) { /* load obj file geometry and diffuse texture */ loadDAT( "rom:/resources/sphere.dat", &sphere); /* if you environment highlight specular, load environment cube texture */ #ifdef WINDOW char *env_tex[] = { "rom:/resources/wright.tga", "rom:/resources/wleft.tga", "rom:/resources/wtop.tga", "rom:/resources/wbottom.tga", "rom:/resources/wback.tga", "rom:/resources/wfront.tga", }; glGenTextures(1, &s_EnvTexName); glBindTexture(GL_TEXTURE_CUBE_MAP, s_EnvTexName); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD, 0); for (int face = 0; face < 6; face++) { bool use_alpha; loadTexture(env_tex[face], GL_TEXTURE_CUBE_MAP_POSITIVE_X+face, 0, use_alpha, true, 0, GL_RGBA); } glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #endif } static void UnloadObjects(void) { unloadDAT(&sphere); return; } int DrawFrame(void) { static int f = 0; s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0); s_RenderSystem.Clear(); nn::math::Matrix44 id; nn::math::MTX44Identity(&id); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uWorld"), 1, GL_FALSE, static_cast(id)); /* modelview setting */ nn::math::Matrix34 mv; nn::math::Vector3 camPos(0.f, 5.75f, 22.5f); nn::math::Vector3 camUp(0.f, 1.f, 0.f); nn::math::Vector3 target(0.f, 0.f, 0.f); nn::math::MTX34LookAt(&mv, &camPos, &camUp, &target); /* light direction setting */ nn::math::Vector4 lpos0(28.5f, 9.5f, 14.f, 1.f); nn::math::Vector4 mv0(mv.m[0][0], mv.m[0][1], mv.m[0][2], mv.m[0][3]); nn::math::Vector4 mv1(mv.m[1][0], mv.m[1][1], mv.m[1][2], mv.m[1][3]); nn::math::Vector4 mv2(mv.m[2][0], mv.m[2][1], mv.m[2][2], mv.m[2][3]); nn::math::Vector4 lpos(nn::math::VEC4Dot(&mv0, &lpos0), nn::math::VEC4Dot(&mv1, &lpos0), nn::math::VEC4Dot(&mv2, &lpos0), lpos0.w); glUniform4fv(glGetUniformLocation(s_ProgramID, "dmp_FragmentLightSource[0].position"), 1, static_cast(lpos)); nn::math::Matrix34 rot; nn::math::MTX34RotXYZDeg(&rot, 0.f, static_cast(-f), 0.f); nn::math::MTX34Mult(&mv, &mv, &rot); nn::math::Matrix44 m(mv); glUniformMatrix4fv(glGetUniformLocation(s_ProgramID, "uModelView"), 1, GL_TRUE, static_cast(m)); /* draw objects */ for (int i = 0; i < sphere.obj_num; i++) { glBindBuffer(GL_ARRAY_BUFFER, sphere.posVB); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)sphere.obj[i].vtx_offset); glBindBuffer(GL_ARRAY_BUFFER, sphere.normVB); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)sphere.obj[i].nrm_offset); for (unsigned j = sphere.obj[i].patch_offset; j < sphere.obj[i].patch_size + sphere.obj[i].patch_offset; j++) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphere.idxVB); glDrawElements(GL_TRIANGLES, sphere.patch[j].elm_size, GL_UNSIGNED_SHORT, (GLvoid*)(sphere.patch[j].elm_offset + sphere.obj[i].elm_offset)); } } glFinish(); s_RenderSystem.SwapBuffers(); s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY1); s_RenderSystem.Clear(); s_RenderSystem.SwapBuffers(); s_RenderSystem.WaitVsync(NN_GX_DISPLAY_BOTH); f++; return !glGetError(); } /* initialization */ static int Initialize(void) { s_AppHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize() ); s_HeapForGx = reinterpret_cast(s_AppHeap.Allocate(s_GxHeapSize)); /* Initialize display */ s_RenderSystem.Initialize(s_HeapForGx, s_GxHeapSize); /* Create heap for malloc*/ s_HeapForMalloc = reinterpret_cast(s_AppHeap.Allocate(s_HeapSize)); setMemoryHeap(s_HeapForMalloc, s_HeapSize); glClearColor(0.36f, 0.42f, 0.5f, 1.0f); glClearDepthf(1.f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); s_ProgramID = glCreateProgram(); s_ShaderID = glCreateShader(GL_VERTEX_SHADER); nn::fs::FileReader file(L"rom:/shader.shbin"); size_t fileSize = file.GetSize(); void* buf = s_AppHeap.Allocate(fileSize); s32 read = file.Read(buf, fileSize); glShaderBinary(1, &s_ShaderID, GL_PLATFORM_BINARY_DMP, buf, read); file.Finalize(); s_AppHeap.Free(buf); glAttachShader(s_ProgramID, s_ShaderID); /* attach fixed-function fragment shader */ glAttachShader(s_ProgramID, GL_DMP_FRAGMENT_SHADER_DMP); glBindAttribLocation(s_ProgramID, 0, "aPosition"); glBindAttribLocation(s_ProgramID, 1, "aNormal"); glLinkProgram(s_ProgramID); glValidateProgram(s_ProgramID); /* set program as current one to enable setting its uniforms */ glUseProgram(s_ProgramID); /* enable arrays that will be mapped to position and normal */ glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); /* create and bind texture collection object to capture subsequent modifications in the texture state both lookup table-related one and ordinary (2D, cube) textures */ glGenTextures(1, &s_TexColl) ; glBindTexture(GL_TEXTURE_COLLECTION_DMP, s_TexColl); LoadObjects(); return 0; } void nnMain(void) { // Call only nn::applet::Enable to also allow execution from the HOME Menu // HOME Menu transitions, POWER Button presses, and sleep are all unsupported nn::applet::Enable(); // fs initialization nn::fs::Initialize(); const size_t ROMFS_BUFFER_SIZE = 1024 * 64; static char buffer[ROMFS_BUFFER_SIZE]; NN_UTIL_PANIC_IF_FAILED( nn::fs::MountRom(16, 16, buffer, ROMFS_BUFFER_SIZE)); /* initialization */ if (Initialize() >= 0) { /* set another render state */ SetRenderState(); /* Enter loop */ while (1) { (void)DrawFrame(); } } UnloadObjects(); /* shutdown_display */ s_RenderSystem.Finalize(); s_AppHeap.Free(reinterpret_cast(s_HeapForMalloc)); s_AppHeap.Free(reinterpret_cast(s_HeapForGx)); s_AppHeap.Finalize(); }