Skip to content

Instantly share code, notes, and snippets.

@Pokechu22
Last active May 17, 2022 06:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pokechu22/0589a46b2dea081e24470b3275766cc0 to your computer and use it in GitHub Desktop.
Save Pokechu22/0589a46b2dea081e24470b3275766cc0 to your computer and use it in GitHub Desktop.
Embossing demo (based on lesson08)
<filepath="Dolphin-height-tex.bmp" id="heighttex" colfmt=3 />
#define SUPPLY_BINORMALS true
#define USE_CYLINDER true
#define USE_LINEAR_FILTERING true
/*---------------------------------------------------------------------------------
nehe lesson 8 port to GX by shagkur
---------------------------------------------------------------------------------*/
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
#include "heighttex_tpl.h"
#include "heighttex.h"
#define DEFAULT_FIFO_SIZE (256*1024)
typedef struct tagtexdef
{
void *pal_data;
void *tex_data;
u32 sz_x;
u32 sz_y;
u32 fmt;
u32 min_lod;
u32 max_lod;
u32 min;
u32 mag;
u32 wrap_s;
u32 wrap_t;
void *nextdef;
} texdef;
static GXColor litcolors_main[] = {
{ 0x40, 0x40, 0x40, 0xFF }, // Light color 1
{ 0x80, 0x80, 0x80, 0xFF }, // Ambient 1
{ 0xFF, 0xFF, 0xFF, 0xFF } // Material 1
};
static GXColor litcolors_aux[] = {
{ 0xFF, 0xFF, 0xFF, 0xFF }, // Light color 1
{ 0x10, 0x10, 0x10, 0xFF }, // Ambient 1
{ 0xFF, 0xFF, 0xFF, 0xFF } // Material 1
};
static GXRModeObj *rmode = NULL;
static void *frameBuffer[2] = { NULL, NULL};
void setlight(Mtx view,guVector dir, GXColor litcol, GXColor ambcol,GXColor matcol)
{
guVector lpos;
GXLightObj lobj;
guVecScale(&dir, &lpos, 10000.0f);
guVecMultiply(view,&lpos,&lpos);
GX_InitLightPos(&lobj,lpos.x,lpos.y,lpos.z);
GX_InitLightColor(&lobj,litcol);
GX_LoadLightObj(&lobj,GX_LIGHT0);
// set number of rasterized color channels
GX_SetNumChans(1);
GX_SetChanCtrl(GX_COLOR0A0,GX_ENABLE,GX_SRC_REG,GX_SRC_REG,GX_LIGHT0,GX_DF_CLAMP,GX_AF_NONE);
GX_SetChanAmbColor(GX_COLOR0A0,ambcol);
GX_SetChanMatColor(GX_COLOR0A0,matcol);
}
void setnolight()
{
GX_SetNumChans(1);
GX_SetChanCtrl(GX_COLOR0A0,GX_DISABLE,GX_SRC_REG,GX_SRC_VTX,GX_LIGHTNULL,GX_DF_CLAMP,GX_AF_NONE);
}
struct Vertex
{
guVector pos;
guVector normal;
guVector tangent;
guVector binormal;
f32 u, v;
};
inline void WriteVec(guVector vec)
{
// Note that GX_Position3f32 and GX_Normal3f32 are internally the exact same thing
GX_Position3f32(vec.x, vec.y, vec.z);
}
std::vector<Vertex> get_square()
{
std::vector<Vertex> result;
for (f32 x = -1; x <= 1; x += 2)
{
for (f32 y = -1; y <= 1; y += 2)
{
auto& vtx = result.emplace_back();
vtx.pos = {x, y, 1.0f};
vtx.normal = {0.0f, 0.0f, 1.0f};
vtx.tangent = {1.0f, 0.0f, 0.0f};
vtx.binormal = {0.0f, -1.0f, 0.0f};
vtx.u = (x + 1) / 2;
vtx.v = 1 - (y + 1) / 2;
}
}
return result;
}
std::vector<Vertex> get_half_cylinder(int num_quads)
{
std::vector<Vertex> result;
for (int i = 0; i <= num_quads; i++)
{
for (f32 y = -1; y <= 1; y += 2)
{
f32 angle = M_PI * i / (f32) num_quads;
f32 x = -cosf(angle);
f32 z = sinf(angle);
auto& vtx = result.emplace_back();
vtx.pos = {x, y, z};
// Since we're using a unit radius, x^2 + z^2 is 1, so this is a unit vector.
vtx.normal = {x, 0.0f, z};
// This works because the derivative of sin(t) is cos(t) and cos(t) is -sin(t)
vtx.tangent = {z, 0.0f, -x};
// Since this is a cylinder (and not a sphere) we can use the same binormal for everything
vtx.binormal = {0.0f, -1.0f, 0.0f};
vtx.u = i / (f32)num_quads;
vtx.v = 1 - (y + 1) / 2;
}
}
return result;
}
std::vector<Vertex> get_sphere(int num_subdivisions, f32 x_orig, f32 y_orig, f32 z_orig, f32 scale)
{
std::vector<Vertex> result;
for (int y = 0; y < num_subdivisions; y++)
{
for (int x = 0; x <= num_subdivisions; x++)
{
for (int yoff = 0; yoff <= 1; yoff++)
{
f32 angle_y = M_PI * (y + yoff) / (f32) num_subdivisions;
f32 sin_y = sinf(angle_y);
f32 pos_y = cosf(angle_y);
f32 angle_x = M_PI * x / (f32) num_subdivisions;
f32 pos_x = cosf(angle_x) * sin_y;
f32 pos_z = sinf(angle_x) * sin_y;
auto& vtx = result.emplace_back();
vtx.pos = {x_orig + pos_x*scale, y_orig + pos_y*scale, z_orig + pos_z*scale};
// This is just how spheres work. pos_x^2 + pos_y^2 + pos_z^2 = 1.
vtx.normal = {pos_x, pos_y, pos_z};
// TODO: tangent and binormal - for now I'm just using this for other purposes
vtx.tangent = {0, 0, 0};
vtx.binormal = {0, 0, 0};
// TODO: uv - same deal
vtx.u = 0;
vtx.v = 0;
}
}
}
return result;
}
int main(int argc,char **argv)
{
f32 yscale,zt = 0;
u32 xfbHeight;
u32 fb = 0;
u32 first_frame = 1;
GXTexObj texture;
Mtx view,mvi; // view and perspective matrices
Mtx model, modelview;
Mtx44 perspective;
void *gpfifo = NULL;
GXColor background = {0, 0, 0, 0xff};
guVector cam = {0.0F, 0.0F, 0.0F},
up = {0.0F, 1.0F, 0.0F},
look = {0.0F, 0.0F, -1.0F};
TPLFile heightTPL;
VIDEO_Init();
WPAD_Init();
rmode = VIDEO_GetPreferredMode(NULL);
// allocate the fifo buffer
gpfifo = memalign(32,DEFAULT_FIFO_SIZE);
memset(gpfifo,0,DEFAULT_FIFO_SIZE);
// allocate 2 framebuffers for double buffering
frameBuffer[0] = SYS_AllocateFramebuffer(rmode);
frameBuffer[1] = SYS_AllocateFramebuffer(rmode);
// configure video
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(frameBuffer[fb]);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
fb ^= 1;
// init the flipper
GX_Init(gpfifo,DEFAULT_FIFO_SIZE);
// clears the bg to color and clears the z buffer
GX_SetCopyClear(background, 0x00ffffff);
// other gx setup
GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
yscale = GX_GetYScaleFactor(rmode->efbHeight,rmode->xfbHeight);
xfbHeight = GX_SetDispCopyYScale(yscale);
GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight);
GX_SetDispCopyDst(rmode->fbWidth,xfbHeight);
GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter);
GX_SetFieldMode(rmode->field_rendering,((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));
if (rmode->aa) {
GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
} else {
GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
}
GX_SetCullMode(GX_CULL_NONE);
GX_CopyDisp(frameBuffer[fb],GX_TRUE);
GX_SetDispCopyGamma(GX_GM_1_0);
GX_InvVtxCache();
// Note: since we need to change the vertex descriptor as well,
// there isn't much benefit to defining multiple VATs
// (but we do need separate ones for when we include normal+tangent+binormal
// vs when we include only normals, so we might as well do this)
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_NBT, GX_NRM_NBT, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT1, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT1, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT1, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT2, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT2, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
GX_SetVtxAttrFmt(GX_VTXFMT3, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT3, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);
// setup texture coordinate generation
// args: texcoord slot 0-7, matrix type, source to generate texture coordinates from, matrix to use
GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX3x4, GX_TG_TEX0, GX_IDENTITY);
GX_SetTexCoordGen(GX_TEXCOORD1, GX_TG_BUMP0, GX_TG_TEXCOORD0, GX_IDENTITY);
f32 w = rmode->viWidth;
f32 h = rmode->viHeight;
GX_InvalidateTexAll();
TPL_OpenTPLFromMemory(&heightTPL, (void *)heighttex_tpl,heighttex_tpl_size);
TPL_GetTexture(&heightTPL,heighttex,&texture);
GX_InitTexObjFilterMode(&texture, USE_LINEAR_FILTERING ? GX_LINEAR : GX_NEAR, USE_LINEAR_FILTERING ? GX_LINEAR : GX_NEAR);
f32 tex_width = GX_GetTexObjWidth(&texture);
// setup our camera at the origin
// looking down the -z axis with y up
guLookAt(view, &cam, &up, &look);
// setup our projection matrix
// this creates a perspective matrix with a view angle of 90,
// and aspect ratio based on the display resolution
guPerspective(perspective, 45, (f32)w/h, 0.1F, 300.0F);
GX_LoadProjectionMtx(perspective, GX_PERSPECTIVE);
int framenum = 0;
GX_SetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_SET);
GX_SetColorUpdate(GX_TRUE);
GX_SetAlphaUpdate(GX_TRUE);
while(1) {
WPAD_ScanPads();
if(WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) exit(0);
else if (WPAD_ButtonsHeld(0)&WPAD_BUTTON_UP) zt -= 0.25f;
else if (WPAD_ButtonsHeld(0)&WPAD_BUTTON_DOWN) zt += 0.25f;
framenum = (framenum + 1) % 900;
f32 angle, angle2;
if (framenum < 300)
{
angle = framenum * M_PI / 60.f;
angle2 = .5f * M_PI * sinf(framenum * M_PI / 300.f);
}
else
{
if (framenum < 450)
angle = 0;
else if (framenum < 600)
angle = M_PI;
else if (framenum < 750)
angle = .5f * M_PI;
else
angle = 1.5f * M_PI;
angle2 = .5f * M_PI * (1 - cosf(framenum * M_PI / 150.f));
}
f32 dx = cosf(angle) * sinf(angle2);
f32 dy = sinf(angle) * sinf(angle2);
f32 dz2 = 1 - dx*dx - dy*dy; // dx^2 + dy^2 + dz^2 = 1 for a unit-length vector
f32 dz = dz2 > 0 ? sqrtf(dz2) : 0; // Avoid NaN issues
guVector ldir = {dx, dy, dz};
// set number of rasterized color channels
setlight(view, ldir, litcolors_main[0],litcolors_main[1],litcolors_main[2]);
//set number of textures to generate
GX_SetNumTexGens(2);
// These TEV stages are set up to match RS2 (x-wing 1).
GX_SetNumTevStages(3);
GX_LoadTexObj(&texture, GX_TEXMAP0);
GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
GX_SetTevColorOp(GX_TEVSTAGE0, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_FALSE, GX_TEVPREV);
GX_SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXA, GX_CC_RASC, GX_CC_RASC);
GX_SetTevOrder(GX_TEVSTAGE1, GX_TEXCOORD1, GX_TEXMAP0, GX_COLOR0A0);
GX_SetTevColorOp(GX_TEVSTAGE1, GX_TEV_SUB, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVREG1);
GX_SetTevColorIn(GX_TEVSTAGE1, GX_CC_ZERO, GX_CC_TEXA, GX_CC_RASC, GX_CC_CPREV);
GX_SetTevOrder(GX_TEVSTAGE2, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
GX_SetTevColorOp(GX_TEVSTAGE2, GX_TEV_ADD, GX_TB_ZERO, GX_CS_SCALE_1, GX_TRUE, GX_TEVPREV);
GX_SetTevColorIn(GX_TEVSTAGE2, GX_CC_ZERO, GX_CC_TEXC, GX_CC_C1, GX_CC_ZERO);
guMtxIdentity(model);
//guVector cubeAxis = {1,1,1};
//guMtxRotAxisDeg(model, &cubeAxis, 45.f);
guMtxTransApply(model, model, 0.0f,0.0f,zt-4.0f);
guMtxConcat(view,model,modelview);
// load the modelview matrix into matrix memory
GX_LoadPosMtxImm(modelview, GX_PNMTX0);
guMtxInverse(modelview,mvi);
guMtxTranspose(mvi,modelview);
guMtxScaleApply(modelview, mvi, 1.f/tex_width, 1.f/tex_width, 1.f/tex_width);
GX_LoadNrmMtxImm(mvi, GX_PNMTX0);
auto vertices = USE_CYLINDER ? get_half_cylinder(4) : get_square();
guVector fake_tangent = {1.0f, 0.0f, 0.0f};
guVector fake_binormal = {0.0f, -1.0f, 0.0f};
if (!SUPPLY_BINORMALS)
{
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_NBT, GX_DIRECT);
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
// We don't want the point we draw to set the cached tangent and binormal
// to actually draw to the screen
GX_SetCullMode(GX_CULL_ALL);
GX_Begin(GX_POINTS, GX_VTXFMT0, 1);
GX_Position3f32(0, 0, 0); // Arbitrary (as long as it's not clipped/determined to be off screen)
GX_Normal3f32(0, 0, 1); // Arbitrary
GX_Normal3f32(fake_tangent.x, fake_tangent.y, fake_tangent.z);
GX_Normal3f32(fake_binormal.x, fake_binormal.y, fake_binormal.z);
GX_TexCoord2f32(0, 0); // Arbitrary
GX_End();
GX_SetCullMode(GX_CULL_NONE);
}
// Draw the actual shape...
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(SUPPLY_BINORMALS ? GX_VA_NBT : GX_VA_NRM, GX_DIRECT);
GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT);
// Don't write depth values here, so that we can draw arrows on top of it
GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_FALSE);
GX_Begin(GX_TRIANGLESTRIP, SUPPLY_BINORMALS ? GX_VTXFMT0 : GX_VTXFMT1, vertices.size());
for (auto vtx : vertices)
{
GX_Position3f32(vtx.pos.x, vtx.pos.y, vtx.pos.z);
GX_Normal3f32(vtx.normal.x, vtx.normal.y, vtx.normal.z);
if (SUPPLY_BINORMALS)
{
GX_Normal3f32(vtx.tangent.x, vtx.tangent.y, vtx.tangent.z);
GX_Normal3f32(vtx.binormal.x, vtx.binormal.y, vtx.binormal.z);
}
GX_TexCoord2f32(vtx.u, vtx.v);
}
GX_End();
// And now draw lines to indicate the various parts of the calculation
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT);
GX_SetNumTexGens(0);
GX_SetNumTevStages(1);
GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR);
setnolight();
// We want depth-testing for our lines though
GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
f32 tex_to_pos_scale = 2.f / tex_width;
guVector scaledLdir;
guVecScale(&ldir, &scaledLdir, tex_to_pos_scale);
GX_Begin(GX_LINES, GX_VTXFMT2, 2 * 5 * vertices.size());
for (auto vtx : vertices)
{
guVector tangent = SUPPLY_BINORMALS ? vtx.tangent : fake_tangent;
guVector binormal = SUPPLY_BINORMALS ? vtx.binormal : fake_binormal;
guVector scaledtangent;
guVecScale(&tangent, &scaledtangent, tex_to_pos_scale);
guVector scaledbinormal;
guVecScale(&binormal, &scaledbinormal, tex_to_pos_scale);
// guVector ldirPos;
// guVecAdd(&vtx.pos, &scaledLdir, &ldirPos);
guVector tangentPos;
guVecSub(&vtx.pos, &scaledtangent, &tangentPos);
guVector binormalPos;
guVecSub(&vtx.pos, &scaledbinormal, &binormalPos);
f32 tangentFactor = guVecDotProduct(&ldir, &tangent);
f32 binormalFactor = guVecDotProduct(&ldir, &binormal);
guVector factoredtangent;
guVecScale(&scaledtangent, &factoredtangent, tangentFactor);
guVector factoredbinormal;
guVecScale(&scaledbinormal, &factoredbinormal, binormalFactor);
guVector tangentPosFactored;
guVecSub(&vtx.pos, &factoredtangent, &tangentPosFactored);
guVector binormalPosFactored;
guVecSub(&vtx.pos, &factoredbinormal, &binormalPosFactored);
guVector embossPos;
guVecSub(&tangentPosFactored, &factoredbinormal, &embossPos);
guVector ldirPos2;
guVecAdd(&embossPos, &scaledLdir, &ldirPos2);
// Indicate the light direction in red
// Disabled as this actually makes things more confusing
/*
WriteVec(vtx.pos);
GX_Color4u8(0xff, 0, 0, 0xff);
WriteVec(ldirPos);
GX_Color4u8(0xff, 0, 0, 0xff);
*/
// Indicate the tangent green
WriteVec(vtx.pos);
GX_Color4u8(0, 0xff, 0, 0xff);
WriteVec(tangentPos);
GX_Color4u8(0, 0xff, 0, 0xff);
// ... and the binormal in blue
WriteVec(vtx.pos);
GX_Color4u8(0, 0, 0xff, 0xff);
WriteVec(binormalPos);
GX_Color4u8(0, 0, 0xff, 0xff);
// Draw lines to the embossed position
// Here we draw the light direction, ending AT the embossed position
WriteVec(ldirPos2);
GX_Color4u8(0xff, 0, 0, 0xff);
WriteVec(embossPos);
GX_Color4u8(0xff, 0, 0, 0xff);
// And now from the light direction back-calculated from the embossed position, to the vertex position
// This doesn't have any real meaning (with "real" tangent/binormal vectors, it'll match the normal vector)
// but helps with depth perception
/*
WriteVec(ldirPos2);
GX_Color4u8(0x80, 0, 0, 0xff);
WriteVec(vtx.pos);
GX_Color4u8(0x80, 0, 0, 0xff);
*/
WriteVec(tangentPosFactored);
GX_Color4u8(0xff, 0, 0xff, 0xff);
WriteVec(embossPos);
GX_Color4u8(0xff, 0, 0xff, 0xff);
WriteVec(binormalPosFactored);
GX_Color4u8(0xff, 0xff, 0, 0xff);
WriteVec(embossPos);
GX_Color4u8(0xff, 0xff, 0, 0xff);
}
GX_End();
// Draw spheres to make the light direction clearer
// (as well as lines to indicate the light direction)
for (int center_y = -1; center_y <= 1; center_y += 2)
{
for (int center_x = -1; center_x <= 1; center_x += 2)
{
GX_Begin(GX_LINES, GX_VTXFMT2, 2);
GX_Position3f32(1.5 * center_x, center_y, 0);
GX_Color4u8(0xff, 0, 0, 0xff);
f32 scale = .25f + tex_to_pos_scale;
GX_Position3f32(1.5 * center_x + scale * dx, center_y + scale * dy, scale * dz);
GX_Color4u8(0xff, 0, 0, 0xff);
GX_End();
}
}
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_DIRECT);
GX_SetVtxDesc(GX_VA_NRM, GX_DIRECT);
setlight(view, ldir, litcolors_aux[0],litcolors_aux[1],litcolors_aux[2]);
for (int center_y = -1; center_y <= 1; center_y += 2)
{
for (int center_x = -1; center_x <= 1; center_x += 2)
{
auto vertices2 = get_sphere(25, 1.5 * center_x, center_y, 0, .25);
GX_Begin(GX_TRIANGLESTRIP, GX_VTXFMT3, vertices2.size());
for (auto vtx : vertices2)
{
GX_Position3f32(vtx.pos.x, vtx.pos.y, vtx.pos.z);
GX_Normal3f32(vtx.normal.x, vtx.normal.y, vtx.normal.z);
}
GX_End();
}
}
GX_DrawDone();
GX_CopyDisp(frameBuffer[fb],GX_TRUE);
VIDEO_SetNextFramebuffer(frameBuffer[fb]);
if(first_frame) {
first_frame = 0;
VIDEO_SetBlack(FALSE);
}
VIDEO_Flush();
VIDEO_WaitVSync();
fb ^= 1;
}
}
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
.SECONDARY:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
endif
include $(DEVKITPPC)/wii_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
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := .
DATA :=
TEXTURES := .
INCLUDES :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE)
CXXFLAGS = $(CFLAGS)
LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lwiiuse -lbte -logc -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS :=
#---------------------------------------------------------------------------------
# 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 VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(foreach dir,$(TEXTURES),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
SCFFILES := $(foreach dir,$(TEXTURES),$(notdir $(wildcard $(dir)/*.scf)))
TPLFILES := $(SCFFILES:.scf=.tpl)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
export LD := $(CC)
else
export LD := $(CXX)
endif
export OFILES_BIN := $(addsuffix .o,$(BINFILES)) $(addsuffix .o,$(TPLFILES))
export OFILES_SOURCES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(sFILES:.s=.o) $(SFILES:.S=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)
export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) $(addsuffix .h,$(subst .,_,$(TPLFILES)))
#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD) \
-I$(LIBOGC_INC)
#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
-L$(LIBOGC_LIB)
export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
#---------------------------------------------------------------------------------
run:
wiiload $(OUTPUT).dol
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)
$(OFILES_SOURCES) : $(HFILES)
#---------------------------------------------------------------------------------
# This rule links in binary data with the .bin extension
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
%.tpl.o %_tpl.h : %.tpl
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment