diff --git a/uniforms/Makefile b/uniforms/Makefile new file mode 100644 index 0000000..d21b996 --- /dev/null +++ b/uniforms/Makefile @@ -0,0 +1,193 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_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 +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) +# +# NO_ICON: if set to anything, do not use icon. +# NO_NACP: if set to anything, no .nacp file is generated. +# APP_TITLE is the name of the app stored in the .nacp file (Optional) +# APP_AUTHOR is the author of the app stored in the .nacp file (Optional) +# APP_VERSION is the version of the app stored in the .nacp file (Optional) +# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) +# ICON is the filename of the icon (.jpg), relative to the project folder. +# If not set, it attempts to use one of the following (in this order): +# - .jpg +# - icon.jpg +# - /default_icon.jpg +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include +EXEFS_SRC := exefs_src +#ROMFS := romfs + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lglad -lEGL -lglapi -ldrm_nouveau -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + + +#--------------------------------------------------------------------------------- +# 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,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +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 +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +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 BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(ICON)),) + icons := $(wildcard *.jpg) + ifneq (,$(findstring $(TARGET).jpg,$(icons))) + export APP_ICON := $(TOPDIR)/$(TARGET).jpg + else + ifneq (,$(findstring icon.jpg,$(icons))) + export APP_ICON := $(TOPDIR)/icon.jpg + endif + endif +else + export APP_ICON := $(TOPDIR)/$(ICON) +endif + +ifeq ($(strip $(NO_ICON)),) + export NROFLAGS += --icon=$(APP_ICON) +endif + +ifeq ($(strip $(NO_NACP)),) + export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp +endif + +ifneq ($(APP_TITLEID),) + export NACPFLAGS += --titleid=$(APP_TITLEID) +endif + +ifneq ($(ROMFS),) + export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).pfs0 $(OUTPUT).nro + +$(OUTPUT).pfs0 : $(OUTPUT).nso + +$(OUTPUT).nso : $(OUTPUT).elf + +ifeq ($(strip $(NO_NACP)),) +$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp +else +$(OUTPUT).nro : $(OUTPUT).elf +endif + +$(OUTPUT).elf : $(OFILES) + +$(OFILES_SRC) : $(HFILES_BIN) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o %_bin.h : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/uniforms/source/main.cpp b/uniforms/source/main.cpp new file mode 100644 index 0000000..9c74695 --- /dev/null +++ b/uniforms/source/main.cpp @@ -0,0 +1,331 @@ +#include +#include +#include +#include + +#include // EGL library +#include // EGL extensions +#include // glad library (OpenGL loader) + +//----------------------------------------------------------------------------- +// nxlink support +//----------------------------------------------------------------------------- + +#ifndef ENABLE_NXLINK +#define TRACE(fmt,...) ((void)0) +#else +#include +#define TRACE(fmt,...) printf("%s: " fmt "\n", __PRETTY_FUNCTION__, ## __VA_ARGS__) + +static int s_nxlinkSock = -1; + +static void initNxLink() +{ + if (R_FAILED(socketInitializeDefault())) + return; + + s_nxlinkSock = nxlinkStdio(); + if (s_nxlinkSock >= 0) + TRACE("printf output now goes to nxlink server"); + else + socketExit(); +} + +static void deinitNxLink() +{ + if (s_nxlinkSock >= 0) + { + close(s_nxlinkSock); + socketExit(); + s_nxlinkSock = -1; + } +} + +extern "C" void userAppInit() +{ + initNxLink(); +} + +extern "C" void userAppExit() +{ + deinitNxLink(); +} + +#endif + +//----------------------------------------------------------------------------- +// EGL initialization +//----------------------------------------------------------------------------- + +static EGLDisplay s_display; +static EGLContext s_context; +static EGLSurface s_surface; + +static bool initEgl() +{ + // Connect to the EGL default display + s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!s_display) + { + TRACE("Could not connect to display! error: %d", eglGetError()); + goto _fail0; + } + + // Initialize the EGL display connection + eglInitialize(s_display, nullptr, nullptr); + + // Select OpenGL (Core) as the desired graphics API + if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) + { + TRACE("Could not set API! error: %d", eglGetError()); + goto _fail1; + } + + // Get an appropriate EGL framebuffer configuration + EGLConfig config; + EGLint numConfigs; + static const EGLint framebufferAttributeList[] = + { + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE + }; + eglChooseConfig(s_display, framebufferAttributeList, &config, 1, &numConfigs); + if (numConfigs == 0) + { + TRACE("No config found! error: %d", eglGetError()); + goto _fail1; + } + + // Create an EGL window surface + s_surface = eglCreateWindowSurface(s_display, config, (char*)"", nullptr); + if (!s_surface) + { + TRACE("Surface creation failed! error: %d", eglGetError()); + goto _fail1; + } + + // Create an EGL rendering context + static const EGLint contextAttributeList[] = + { + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_CONTEXT_MAJOR_VERSION_KHR, 4, + EGL_CONTEXT_MINOR_VERSION_KHR, 3, + EGL_NONE + }; + s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList); + if (!s_context) + { + TRACE("Context creation failed! error: %d", eglGetError()); + goto _fail2; + } + + // Connect the context to the surface + eglMakeCurrent(s_display, s_surface, s_surface, s_context); + return true; + +_fail2: + eglDestroySurface(s_display, s_surface); + s_surface = nullptr; +_fail1: + eglTerminate(s_display); + s_display = nullptr; +_fail0: + return false; +} + +static void deinitEgl() +{ + if (s_display) + { + eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (s_context) + { + eglDestroyContext(s_display, s_context); + s_context = nullptr; + } + if (s_surface) + { + eglDestroySurface(s_display, s_surface); + s_surface = nullptr; + } + eglTerminate(s_display); + s_display = nullptr; + } +} + +//----------------------------------------------------------------------------- +// Main program +//----------------------------------------------------------------------------- + +static void setMesaConfig() +{ + // Uncomment below to disable error checking and save CPU time (useful for production): + //setenv("MESA_NO_ERROR", "1", 1); + + // Uncomment below to enable Mesa logging: + //setenv("EGL_LOG_LEVEL", "debug", 1); + //setenv("MESA_VERBOSE", "all", 1); + //setenv("NOUVEAU_MESA_DEBUG", "1", 1); + + // Uncomment below to enable shader debugging in Nouveau: + //setenv("NV50_PROG_OPTIMIZE", "0", 1); + //setenv("NV50_PROG_DEBUG", "1", 1); + //setenv("NV50_PROG_CHIPSET", "0x120", 1); +} + +static const char* const vertexShaderSrc = R"glsl( + #version 150 core + in vec2 pos; + + void main() + { + gl_Position = vec4(pos, 0.0, 1.0); + } +)glsl"; + +static const char* const fragmentShaderSrc = R"glsl( + #version 330 core + out vec4 FragColor; + + uniform vec4 ourColor; // we set this variable in the OpenGL code. + + void main() + { + FragColor = ourColor; + } +)glsl"; + +static GLuint createAndCompileShader(GLenum type, const char* source) +{ + GLint success; + GLchar msg[512]; + + GLuint handle = glCreateShader(type); + if (!handle) + { + TRACE("%u: cannot create shader", type); + return 0; + } + glShaderSource(handle, 1, &source, nullptr); + glCompileShader(handle); + glGetShaderiv(handle, GL_COMPILE_STATUS, &success); + + if (!success) + { + glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg); + TRACE("%u: %s\n", type, msg); + glDeleteShader(handle); + return 0; + } + + return handle; +} + +GLuint createShader(GLenum type, const GLchar* src) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &src, nullptr); + glCompileShader(shader); + return shader; +} + + +GLuint shaderProgram; +static GLuint s_vao, s_vbo; + +static void sceneInit() +{ + + GLuint vertexShader = createShader(GL_VERTEX_SHADER, vertexShaderSrc); + GLuint fragmentShader = createShader(GL_FRAGMENT_SHADER, fragmentShaderSrc); + + shaderProgram = glCreateProgram(); + glAttachShader(shaderProgram, vertexShader); + glAttachShader(shaderProgram, fragmentShader); + glLinkProgram(shaderProgram); + glUseProgram(shaderProgram); + + GLuint vbo; + glGenBuffers(1, &vbo); + + float points[] = { + -0.45f, 0.45f, + 0.45f, 0.45f, + 0.0f, -0.45f, + }; + + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW); + + // Create VAO + GLuint vao; + glGenVertexArrays(1, &vao); + glBindVertexArray(vao); + + // Specify layout of point data + GLint posAttrib = glGetAttribLocation(shaderProgram, "pos"); + glEnableVertexAttribArray(posAttrib); + glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0); +} + +static void sceneRender() +{ + int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); + glUniform4f(vertexColorLocation, 0.0f, 1.0f, 0.0f, 1.0f); + + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glDrawArrays(GL_TRIANGLES, 0, 6); +} + +static void sceneExit() +{ + glDeleteBuffers(1, &s_vbo); + glDeleteVertexArrays(1, &s_vao); +} + +int main(int argc, char* argv[]) +{ + // Set mesa configuration (useful for debugging) + setMesaConfig(); + + // Initialize EGL + if (!initEgl()) + return EXIT_FAILURE; + + // Load OpenGL routines using glad + gladLoadGL(); + + // Initialize our scene + sceneInit(); + + // Main graphics loop + while (appletMainLoop()) + { + // Get and process input + hidScanInput(); + u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO); + if (kDown & KEY_PLUS) + { + break; + } + + // Render stuff! + sceneRender(); + eglSwapBuffers(s_display, s_surface); + } + + // Deinitialize our scene + sceneExit(); + + // Deinitialize EGL + deinitEgl(); + return EXIT_SUCCESS; +}