/*---------------------------------------------------------------------------* Project: Horizon File: LightingVertex.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 * -------- * * This sample emulates the fixed vertex pipeline of OpenGL ES 1.1. * It is implemented using the vertex shader to convert vertex coordinates to clip space and calculate vertex colors from one light and point light source. * */ #include #include #include #include #include #include #include #include #include #include "demo.h" #define DMP_PI (3.1415926f) /* program id */ GLuint s_PgID; /* shader id */ GLuint s_ShID; /* OpenGLES1.1 vertex lighting specular shininess */ #define SPEC_SHININESS 32.f #define STEP 6.f /* object names */ enum { OBJECT_SPHERE, /* sphere */ OBJECT_PLANE, /* plane */ OBJECT_COUNT /* object count */ }; /* buffer object ID */ static struct tagObject { GLuint id[OBJECT_COUNT]; GLuint idxId[OBJECT_COUNT]; } s_Object; /* buffer object information */ static struct tagObjectInfo { GLushort idxcnt[OBJECT_COUNT]; GLushort vtxcnt[OBJECT_COUNT]; } s_ObjectInfo; #define ROW_NUM (50) /* NUM in ROW */ #define COL_NUM (50) /* NUM in COLUMN */ #define deltaROW (DMP_PI / (ROW_NUM - 1)) #define deltaCOL (2 * DMP_PI / (COL_NUM - 1)) struct tagVertex{ GLfloat pos[ROW_NUM * COL_NUM][3]; GLfloat nor[ROW_NUM * COL_NUM][3]; } s_Vtx; GLushort s_Idx[COL_NUM * (ROW_NUM - 1) * 2]; /* ExpHeap for app. */ nn::fnd::ExpHeap s_AppHeap; uptr s_HeapForGx; const u32 s_GxHeapSize = 0x400000; demo::RenderSystem s_RenderSystem; /* load sphere object */ static void LoadSphere(void) { /* vertex array */ for(int row = 0; row < ROW_NUM; row++) { for(int col = 0; col < COL_NUM; col++) { /* position */ s_Vtx.pos[row * COL_NUM + col][0] =(GLfloat)sin(deltaROW * row) * cos(deltaCOL * col); s_Vtx.pos[row * COL_NUM + col][1] =(GLfloat)cos(deltaROW * row); s_Vtx.pos[row * COL_NUM + col][2] =(GLfloat)sin(deltaROW * row) * sin(deltaCOL * col); /* normal */ s_Vtx.nor[row * COL_NUM + col][0] =(GLfloat)sin(deltaROW * row) * cos(deltaCOL * col); s_Vtx.nor[row * COL_NUM + col][1] =(GLfloat)cos(deltaROW * row); s_Vtx.nor[row * COL_NUM + col][2] =(GLfloat)sin(deltaROW * row) * sin(deltaCOL * col); } } /* index array */ for(int i = 0, row = 0; row < ROW_NUM - 1; row++) { #define __INDEX(ROW, COL) ((ROW) * COL_NUM + (COL)) for(int col = 0; col < COL_NUM; col++) { s_Idx[i++] = __INDEX(row + 1, col); s_Idx[i++] = __INDEX(row, col); } #undef __INDEX } /* count */ s_ObjectInfo.idxcnt[OBJECT_SPHERE] = COL_NUM * (ROW_NUM - 1) * 2; s_ObjectInfo.vtxcnt[OBJECT_SPHERE] = ROW_NUM * COL_NUM; /* load vertex array and index array */ glBindBuffer(GL_ARRAY_BUFFER, s_Object.id[OBJECT_SPHERE]); glBufferData(GL_ARRAY_BUFFER, sizeof(s_Vtx), &s_Vtx, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_Object.idxId[OBJECT_SPHERE]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(s_Idx), s_Idx, GL_STATIC_DRAW); #undef LONG_NUM #undef LATI_NUM #undef deltaROW #undef deltaCOL } /* load plane object */ static void LoadPlane(void) { struct tagVertex{ GLfloat pos[4][3]; GLfloat nor[4][3]; } vtx; GLushort idx[4] ={0, 1, 2, 3}; /* vertex array */ vtx.pos[0][0] = +3.0f; vtx.pos[0][1] = -1.0f; vtx.pos[0][2] = +3.0f; vtx.pos[1][0] = +3.0f; vtx.pos[1][1] = -1.0f; vtx.pos[1][2] = -3.0f; vtx.pos[2][0] = -3.0f; vtx.pos[2][1] = -1.0f; vtx.pos[2][2] = +3.0f; vtx.pos[3][0] = -3.0f; vtx.pos[3][1] = -1.0f; vtx.pos[3][2] = -3.0f; for(int i = 0; i < 4; i++) { vtx.nor[i][0] = 0.f; vtx.nor[i][1] = 1.f; vtx.nor[i][2] = 0.f; } /* count */ s_ObjectInfo.idxcnt[OBJECT_PLANE] = 4; s_ObjectInfo.vtxcnt[OBJECT_PLANE] = 4; /* load vertex array and index array */ glBindBuffer(GL_ARRAY_BUFFER, s_Object.id[OBJECT_PLANE]); glBufferData(GL_ARRAY_BUFFER, sizeof(vtx), vtx.pos[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_Object.idxId[OBJECT_PLANE]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(idx), idx, GL_STATIC_DRAW); } /* load objects */ static void LoadObjects(void) { glGenBuffers(OBJECT_COUNT * 2, (GLuint*)&s_Object); LoadSphere(); LoadPlane(); } int DrawFrame(void) { static int f = 0; nn::math::Matrix44 proj; nn::math::Matrix34 mv; s_RenderSystem.SetRenderTarget(NN_GX_DISPLAY0); s_RenderSystem.Clear(); /* In this application light positions are specified in object coordinates. * */ GLfloat lpos[] = {3.f, 3.f, 0.f, 1.f}; /* light position */ /* Projection settings */ nn::math::MTX44Frustum(&proj, -0.07f, 0.07f, -0.07f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.07f * nn::gx::DISPLAY0_HEIGHT / nn::gx::DISPLAY0_WIDTH, 0.2f, 200.f); glUniformMatrix4fv(glGetUniformLocation(s_PgID, "uProjection"), 1, GL_TRUE, static_cast(proj)); /* modelview setting */ /* mv indicates the view matrix.*/ nn::math::MTX34RotXYZDeg(&mv, 0.0f, 0.0f, -90.0f); nn::math::Matrix34 cam; nn::math::Vector3 camPos(0.f, 5.f, 10.f); nn::math::Vector3 camUp(0.f, 1.f, 0.f); nn::math::Vector3 target(0.f, 0.f, 0.f); nn::math::MTX34LookAt(&cam, &camPos, &camUp, &target); nn::math::MTX34Mult(&mv, &mv, &cam); nn::math::Matrix44 tmp(mv); glUniformMatrix4fv(glGetUniformLocation(s_PgID, "uModelView"), 1, GL_TRUE, static_cast(tmp)); /* set light position */ /* constant light position is transformed from object-space to eye-space */ /* The vertex shader assumes that the eye coordinate system is used for lighting calculations. * The light's position is therefore converted to the eye coordinate system. The shader does not perform conversion. * This is because lights use common values in all vertex processing. * mv is the view matrix. Multiplication by this matrix converts coordinates to the eye coordinate system.*/ nn::math::Vector4 p(lpos); nn::math::Vector4 mvv0(mv.m[0]); nn::math::Vector4 mvv1(mv.m[1]); nn::math::Vector4 mvv2(mv.m[2]); nn::math::Vector4 lpos2( nn::math::VEC4Dot(&mvv0, &p), nn::math::VEC4Dot(&mvv1, &p), nn::math::VEC4Dot(&mvv2, &p), 1.f); glUniform4fv(glGetUniformLocation(s_PgID, "uLightPos"), 1, static_cast(lpos2)); /* draw objects */ for (int i = 0; i < OBJECT_COUNT; i++) { glBindBuffer(GL_ARRAY_BUFFER, s_Object.id[i]); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(sizeof(GLfloat) * 3 * s_ObjectInfo.vtxcnt[i])); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s_Object.idxId[i]); if (i == OBJECT_SPHERE) /* for sphere */ { /* Rotation and translation are contributed to the current model view matrix (mv) only when a sphere model is used, resulting in mv2. The model view matrix returns to its original state (mv1) when rendering is complete. * * This is equivalent to the glPushMatrix and glPopMatrix operations in OpenGL ES 1.1.*/ nn::math::Matrix34 arr[2]; nn::math::Matrix34 mv2; nn::math::Vector3 trans(2.0f, 0.f, 0.f); nn::math::MTX34RotXYZDeg(&arr[0], 0.f, STEP * f / 2, 0.f); nn::math::MTX34Translate(&arr[1], &trans); nn::math::MTX34Mult(&mv2, &mv, &arr[0]); nn::math::MTX34Mult(&mv2, &mv2, &arr[1]); nn::math::Matrix44 tmp2(mv2); glUniformMatrix4fv(glGetUniformLocation(s_PgID, "uModelView"), 1, GL_TRUE, static_cast(tmp2)); } glDrawElements(GL_TRIANGLE_STRIP, s_ObjectInfo.idxcnt[i], GL_UNSIGNED_SHORT,(GLvoid*)0); if (i == OBJECT_SPHERE) /* for sphere */ { /* Returns the model view matrix to its original state. (mv) */ glUniformMatrix4fv(glGetUniformLocation(s_PgID, "uModelView"), 1, GL_TRUE, static_cast(tmp)); } } 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) { // 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)); 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); /* setup vertex shader */ /* The shader setup process with DMPGL2.0 uses the same mechanism as OpenGLES2.0. * Because in DMPGL 2.0 only the vertex shader can be user-defined, only one shader object is created. * * */ s_PgID = glCreateProgram(); s_ShID = 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_ShID, GL_PLATFORM_BINARY_DMP, buf, read); file.Finalize(); s_AppHeap.Free(buf); glAttachShader(s_PgID, s_ShID); /* The GL_DMP_FRAGMENT_SHADER_DMP shader object is the shader object of the fragment shader reserved with DMPGL2.0. * */ glAttachShader(s_PgID, GL_DMP_FRAGMENT_SHADER_DMP); /* Because the shader program performs coordinate conversions and lighting calculations, vertex coordinates and normals are required as vertex attributes, and the vertex coordinates are allocated to attribute0 and the normals are allocated to attribute1. * * */ glBindAttribLocation(s_PgID, 0, "aPosition"); glBindAttribLocation(s_PgID, 1, "aNormal"); /* Links the vertex shader and fragment shader.*/ glLinkProgram(s_PgID); glValidateProgram(s_PgID); glUseProgram(s_PgID); glClearColor(0.36f, 0.42f, 0.5f, 1.0f); glClearDepthf(1.f); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); LoadObjects(); /* The following parameters are the same as the OpenGLES1.1 vertex lighting parameters. * * Note that the result of multiplying the same light and material components together is set in the shader Uniform. * */ GLfloat ldif[] = {1.f, 1.f, 1.f, 1.f}; /* light diffuse */ GLfloat lspc[] = {1.f, 1.f, 1.f, 1.f}; /* light specular */ GLfloat lamb[] = {0.f, 0.f, 0.f, 1.f}; /* light ambient */ GLfloat lmamb[] = {0.2f, 0.2f, 0.f, 1.f}; /* light model ambient */ GLfloat mspc[] = {1.f, 1.f, 1.f, 1.f}; /* material specular */ GLfloat mdif[] = {0.8f, 0.8f, 0.f, 1.f}; /* material diffuse */ GLfloat mamb[] = {0.8f, 0.f, 0.f, 1.f}; /* material ambient */ /* Set the sum of the light ambient and global ambient as ambient. * */ nn::math::Vector4 amb; nn::math::Vector4 lamb_mamb = nn::math::Vector4(lamb[0]*mamb[0], lamb[1]*mamb[1], lamb[2]*mamb[2], lamb[3]*mamb[3]); nn::math::Vector4 lmamb_mamb = nn::math::Vector4(lmamb[0]*mamb[0], lmamb[1]*mamb[1], lmamb[2]*mamb[2], lmamb[3]*mamb[3]); nn::math::VEC4Add(&amb, &lamb_mamb, &lmamb_mamb); nn::math::Vector4 dif = nn::math::Vector4(ldif[0]*mdif[0], ldif[1]*mdif[1], ldif[2]*mdif[2], ldif[3]*mdif[3]); nn::math::Vector4 spc = nn::math::Vector4(lspc[0]*mspc[0], lspc[1]*mspc[1], lspc[2]*mspc[2], lspc[3]*mspc[3]); GLfloat a[] = {amb.x, amb.y, amb.z, amb.w}; GLfloat d[] = {dif.x, dif.y, dif.z, dif.w}; GLfloat s[] = {spc.x, spc.y, spc.z, spc.w}; glUniform4fv(glGetUniformLocation(s_PgID, "uDiff"), 1, d); glUniform4fv(glGetUniformLocation(s_PgID, "uAmb"), 1, a); glUniform4fv(glGetUniformLocation(s_PgID, "uSpec"), 1, s); glUniform1f(glGetUniformLocation(s_PgID, "uMatShiniess"), SPEC_SHININESS); glUniform3i(glGetUniformLocation(s_PgID, "dmp_TexEnv[0].srcRgb"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(s_PgID, "dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); 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(); /* initialization */ if (Initialize() >= 0) { while (1) { (void)DrawFrame(); } } /* shutdown_display */ s_RenderSystem.Finalize(); s_AppHeap.Free(reinterpret_cast(s_HeapForGx)); s_AppHeap.Finalize(); }