mirror of
https://github.com/switchbrew/switch-examples.git
synced 2025-06-21 13:22:40 +02:00
Add OpenGL examples
This commit is contained in:
parent
183d9246ec
commit
8ebbb5fdec
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
build
|
build
|
||||||
|
.*/
|
||||||
*.elf
|
*.elf
|
||||||
*.nso
|
*.nso
|
||||||
*.pfs0
|
*.pfs0
|
||||||
|
17
graphics/opengl/README.md
Normal file
17
graphics/opengl/README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# OpenGL Examples
|
||||||
|
|
||||||
|
## Necessary packages
|
||||||
|
|
||||||
|
These examples use OpenGL to render graphics with the GPU. In order to compile them, you need to install the necessary packages with the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pacman -S switch-mesa switch-glad switch-glm
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that on some systems, you may need to use `dkp-pacman` instead, and you may need to prefix the installation commands with `sudo`.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
It is not possible to use the libnx console and the GPU at the same time. For this reason, debugging output must be redirected to nxlink. All examples contain code that sets up stdout to redirect to the nxlink socket, however by default it's disabled. In order to enable it, you can `#define ENABLE_NXLINK` at the top of the file, or alternatively modify the Makefile to add `-DENABLE_NXLINK` to the `CFLAGS` variable.
|
||||||
|
|
||||||
|
Additionally, mesa and nouveau are presently configured to support debugging output. Each example has a `setMesaConfig` function that controls debugging and shader optimization flags. Please refer to the source code of this function for more details.
|
184
graphics/opengl/es2gears/Makefile
Normal file
184
graphics/opengl/es2gears/Makefile
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/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 := -lEGL -lGLESv2 -lglapi -ldrm_nouveau -lnx -lm
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# 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
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
|
||||||
|
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
|
||||||
|
#---------------------------------------------------------------------------------------
|
869
graphics/opengl/es2gears/source/main.c
Normal file
869
graphics/opengl/es2gears/source/main.c
Normal file
@ -0,0 +1,869 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included
|
||||||
|
* in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ported to GLES2.
|
||||||
|
* Kristian Høgsberg <krh@bitplanet.net>
|
||||||
|
* May 3, 2010
|
||||||
|
*
|
||||||
|
* Improve GLES2 port:
|
||||||
|
* * Refactor gear drawing.
|
||||||
|
* * Use correct normals for surfaces.
|
||||||
|
* * Improve shader.
|
||||||
|
* * Use perspective projection transformation.
|
||||||
|
* * Add FPS count.
|
||||||
|
* * Add comments.
|
||||||
|
* Alexandros Frantzis <alexandros.frantzis@linaro.org>
|
||||||
|
* Jul 13, 2010
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ported to Nintendo Switch using mesa/nouveau and EGL.
|
||||||
|
* Armada & fincs
|
||||||
|
* September 9th, 2018
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h> // EGL library
|
||||||
|
#include <GLES2/gl2.h> // OpenGL ES 2.0 library
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nxlink support
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ENABLE_NXLINK
|
||||||
|
#define TRACE(fmt,...) ((void)0)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void userAppInit()
|
||||||
|
{
|
||||||
|
initNxLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
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, NULL, NULL);
|
||||||
|
|
||||||
|
// Get an appropriate EGL framebuffer configuration
|
||||||
|
EGLConfig config;
|
||||||
|
EGLint numConfigs;
|
||||||
|
static const EGLint attributeList[] =
|
||||||
|
{
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
eglChooseConfig(s_display, attributeList, &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*)"", NULL);
|
||||||
|
if (!s_surface)
|
||||||
|
{
|
||||||
|
TRACE("Surface creation failed! error: %d", eglGetError());
|
||||||
|
goto _fail1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an EGL rendering context
|
||||||
|
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, NULL);
|
||||||
|
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 = NULL;
|
||||||
|
_fail1:
|
||||||
|
eglTerminate(s_display);
|
||||||
|
s_display = NULL;
|
||||||
|
_fail0:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deinitEgl()
|
||||||
|
{
|
||||||
|
if (s_display)
|
||||||
|
{
|
||||||
|
if (s_context)
|
||||||
|
{
|
||||||
|
eglDestroyContext(s_display, s_context);
|
||||||
|
s_context = NULL;
|
||||||
|
}
|
||||||
|
if (s_surface)
|
||||||
|
{
|
||||||
|
eglDestroySurface(s_display, s_surface);
|
||||||
|
s_surface = NULL;
|
||||||
|
}
|
||||||
|
eglTerminate(s_display);
|
||||||
|
s_display = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STRIPS_PER_TOOTH 7
|
||||||
|
#define VERTICES_PER_TOOTH 34
|
||||||
|
#define GEAR_VERTEX_STRIDE 6
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct describing the vertices in triangle strip
|
||||||
|
*/
|
||||||
|
struct vertex_strip {
|
||||||
|
/** The first vertex in the strip */
|
||||||
|
GLint first;
|
||||||
|
/** The number of consecutive vertices in the strip after the first */
|
||||||
|
GLint count;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Each vertex consist of GEAR_VERTEX_STRIDE GLfloat attributes */
|
||||||
|
typedef GLfloat GearVertex[GEAR_VERTEX_STRIDE];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Struct representing a gear.
|
||||||
|
*/
|
||||||
|
struct gear {
|
||||||
|
/** The array of vertices comprising the gear */
|
||||||
|
GearVertex *vertices;
|
||||||
|
/** The number of vertices comprising the gear */
|
||||||
|
int nvertices;
|
||||||
|
/** The array of triangle strips comprising the gear */
|
||||||
|
struct vertex_strip *strips;
|
||||||
|
/** The number of triangle strips comprising the gear */
|
||||||
|
int nstrips;
|
||||||
|
/** The Vertex Buffer Object holding the vertices in the graphics card */
|
||||||
|
GLuint vbo;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** The view rotation [x, y, z] */
|
||||||
|
static GLfloat view_rot[3] = { 20.0, 30.0, 0.0 };
|
||||||
|
/** The gears */
|
||||||
|
static struct gear *gear1, *gear2, *gear3;
|
||||||
|
/** The current gear rotation angle */
|
||||||
|
static GLfloat angle = 0.0;
|
||||||
|
/** The location of the shader uniforms */
|
||||||
|
static GLuint ModelViewProjectionMatrix_location,
|
||||||
|
NormalMatrix_location,
|
||||||
|
LightSourcePosition_location,
|
||||||
|
MaterialColor_location;
|
||||||
|
/** The projection matrix */
|
||||||
|
static GLfloat ProjectionMatrix[16];
|
||||||
|
/** The direction of the directional light for the scene */
|
||||||
|
static const GLfloat LightSourcePosition[4] = { 5.0, 5.0, 10.0, 1.0};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fills a gear vertex.
|
||||||
|
*
|
||||||
|
* @param v the vertex to fill
|
||||||
|
* @param x the x coordinate
|
||||||
|
* @param y the y coordinate
|
||||||
|
* @param z the z coortinate
|
||||||
|
* @param n pointer to the normal table
|
||||||
|
*
|
||||||
|
* @return the operation error code
|
||||||
|
*/
|
||||||
|
static GearVertex *
|
||||||
|
vert(GearVertex *v, GLfloat x, GLfloat y, GLfloat z, GLfloat n[3])
|
||||||
|
{
|
||||||
|
v[0][0] = x;
|
||||||
|
v[0][1] = y;
|
||||||
|
v[0][2] = z;
|
||||||
|
v[0][3] = n[0];
|
||||||
|
v[0][4] = n[1];
|
||||||
|
v[0][5] = n[2];
|
||||||
|
|
||||||
|
return v + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a gear wheel.
|
||||||
|
*
|
||||||
|
* @param inner_radius radius of hole at center
|
||||||
|
* @param outer_radius radius at center of teeth
|
||||||
|
* @param width width of gear
|
||||||
|
* @param teeth number of teeth
|
||||||
|
* @param tooth_depth depth of tooth
|
||||||
|
*
|
||||||
|
* @return pointer to the constructed struct gear
|
||||||
|
*/
|
||||||
|
static struct gear *
|
||||||
|
create_gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
|
||||||
|
GLint teeth, GLfloat tooth_depth)
|
||||||
|
{
|
||||||
|
GLfloat r0, r1, r2;
|
||||||
|
GLfloat da;
|
||||||
|
GearVertex *v;
|
||||||
|
struct gear *gear;
|
||||||
|
double s[5], c[5];
|
||||||
|
GLfloat normal[3];
|
||||||
|
int cur_strip = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Allocate memory for the gear */
|
||||||
|
gear = malloc(sizeof *gear);
|
||||||
|
if (gear == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Calculate the radii used in the gear */
|
||||||
|
r0 = inner_radius;
|
||||||
|
r1 = outer_radius - tooth_depth / 2.0;
|
||||||
|
r2 = outer_radius + tooth_depth / 2.0;
|
||||||
|
|
||||||
|
da = 2.0 * M_PI / teeth / 4.0;
|
||||||
|
|
||||||
|
/* Allocate memory for the triangle strip information */
|
||||||
|
gear->nstrips = STRIPS_PER_TOOTH * teeth;
|
||||||
|
gear->strips = calloc(gear->nstrips, sizeof (*gear->strips));
|
||||||
|
|
||||||
|
/* Allocate memory for the vertices */
|
||||||
|
gear->vertices = calloc(VERTICES_PER_TOOTH * teeth, sizeof(*gear->vertices));
|
||||||
|
v = gear->vertices;
|
||||||
|
|
||||||
|
for (i = 0; i < teeth; i++) {
|
||||||
|
/* Calculate needed sin/cos for varius angles */
|
||||||
|
sincos(i * 2.0 * M_PI / teeth, &s[0], &c[0]);
|
||||||
|
sincos(i * 2.0 * M_PI / teeth + da, &s[1], &c[1]);
|
||||||
|
sincos(i * 2.0 * M_PI / teeth + da * 2, &s[2], &c[2]);
|
||||||
|
sincos(i * 2.0 * M_PI / teeth + da * 3, &s[3], &c[3]);
|
||||||
|
sincos(i * 2.0 * M_PI / teeth + da * 4, &s[4], &c[4]);
|
||||||
|
|
||||||
|
/* A set of macros for making the creation of the gears easier */
|
||||||
|
#define GEAR_POINT(r, da) { (r) * c[(da)], (r) * s[(da)] }
|
||||||
|
#define SET_NORMAL(x, y, z) do { \
|
||||||
|
normal[0] = (x); normal[1] = (y); normal[2] = (z); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define GEAR_VERT(v, point, sign) vert((v), p[(point)].x, p[(point)].y, (sign) * width * 0.5, normal)
|
||||||
|
|
||||||
|
#define START_STRIP do { \
|
||||||
|
gear->strips[cur_strip].first = v - gear->vertices; \
|
||||||
|
} while(0);
|
||||||
|
|
||||||
|
#define END_STRIP do { \
|
||||||
|
int _tmp = (v - gear->vertices); \
|
||||||
|
gear->strips[cur_strip].count = _tmp - gear->strips[cur_strip].first; \
|
||||||
|
cur_strip++; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define QUAD_WITH_NORMAL(p1, p2) do { \
|
||||||
|
SET_NORMAL((p[(p1)].y - p[(p2)].y), -(p[(p1)].x - p[(p2)].x), 0); \
|
||||||
|
v = GEAR_VERT(v, (p1), -1); \
|
||||||
|
v = GEAR_VERT(v, (p1), 1); \
|
||||||
|
v = GEAR_VERT(v, (p2), -1); \
|
||||||
|
v = GEAR_VERT(v, (p2), 1); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
struct point {
|
||||||
|
GLfloat x;
|
||||||
|
GLfloat y;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Create the 7 points (only x,y coords) used to draw a tooth */
|
||||||
|
struct point p[7] = {
|
||||||
|
GEAR_POINT(r2, 1), // 0
|
||||||
|
GEAR_POINT(r2, 2), // 1
|
||||||
|
GEAR_POINT(r1, 0), // 2
|
||||||
|
GEAR_POINT(r1, 3), // 3
|
||||||
|
GEAR_POINT(r0, 0), // 4
|
||||||
|
GEAR_POINT(r1, 4), // 5
|
||||||
|
GEAR_POINT(r0, 4), // 6
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Front face */
|
||||||
|
START_STRIP;
|
||||||
|
SET_NORMAL(0, 0, 1.0);
|
||||||
|
v = GEAR_VERT(v, 0, +1);
|
||||||
|
v = GEAR_VERT(v, 1, +1);
|
||||||
|
v = GEAR_VERT(v, 2, +1);
|
||||||
|
v = GEAR_VERT(v, 3, +1);
|
||||||
|
v = GEAR_VERT(v, 4, +1);
|
||||||
|
v = GEAR_VERT(v, 5, +1);
|
||||||
|
v = GEAR_VERT(v, 6, +1);
|
||||||
|
END_STRIP;
|
||||||
|
|
||||||
|
/* Inner face */
|
||||||
|
START_STRIP;
|
||||||
|
QUAD_WITH_NORMAL(4, 6);
|
||||||
|
END_STRIP;
|
||||||
|
|
||||||
|
/* Back face */
|
||||||
|
START_STRIP;
|
||||||
|
SET_NORMAL(0, 0, -1.0);
|
||||||
|
v = GEAR_VERT(v, 6, -1);
|
||||||
|
v = GEAR_VERT(v, 5, -1);
|
||||||
|
v = GEAR_VERT(v, 4, -1);
|
||||||
|
v = GEAR_VERT(v, 3, -1);
|
||||||
|
v = GEAR_VERT(v, 2, -1);
|
||||||
|
v = GEAR_VERT(v, 1, -1);
|
||||||
|
v = GEAR_VERT(v, 0, -1);
|
||||||
|
END_STRIP;
|
||||||
|
|
||||||
|
/* Outer face */
|
||||||
|
START_STRIP;
|
||||||
|
QUAD_WITH_NORMAL(0, 2);
|
||||||
|
END_STRIP;
|
||||||
|
|
||||||
|
START_STRIP;
|
||||||
|
QUAD_WITH_NORMAL(1, 0);
|
||||||
|
END_STRIP;
|
||||||
|
|
||||||
|
START_STRIP;
|
||||||
|
QUAD_WITH_NORMAL(3, 1);
|
||||||
|
END_STRIP;
|
||||||
|
|
||||||
|
START_STRIP;
|
||||||
|
QUAD_WITH_NORMAL(5, 3);
|
||||||
|
END_STRIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
gear->nvertices = (v - gear->vertices);
|
||||||
|
|
||||||
|
/* Store the vertices in a vertex buffer object (VBO) */
|
||||||
|
glGenBuffers(1, &gear->vbo);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, gear->vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, gear->nvertices * sizeof(GearVertex),
|
||||||
|
gear->vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
return gear;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiplies two 4x4 matrices.
|
||||||
|
*
|
||||||
|
* The result is stored in matrix m.
|
||||||
|
*
|
||||||
|
* @param m the first matrix to multiply
|
||||||
|
* @param n the second matrix to multiply
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
multiply(GLfloat *m, const GLfloat *n)
|
||||||
|
{
|
||||||
|
GLfloat tmp[16];
|
||||||
|
const GLfloat *row, *column;
|
||||||
|
div_t d;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
tmp[i] = 0;
|
||||||
|
d = div(i, 4);
|
||||||
|
row = n + d.quot * 4;
|
||||||
|
column = m + d.rem;
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
tmp[i] += row[j] * column[j * 4];
|
||||||
|
}
|
||||||
|
memcpy(m, &tmp, sizeof tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates a 4x4 matrix.
|
||||||
|
*
|
||||||
|
* @param[in,out] m the matrix to rotate
|
||||||
|
* @param angle the angle to rotate
|
||||||
|
* @param x the x component of the direction to rotate to
|
||||||
|
* @param y the y component of the direction to rotate to
|
||||||
|
* @param z the z component of the direction to rotate to
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
rotate(GLfloat *m, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
|
||||||
|
{
|
||||||
|
double s, c;
|
||||||
|
|
||||||
|
sincos(angle, &s, &c);
|
||||||
|
GLfloat r[16] = {
|
||||||
|
x * x * (1 - c) + c, y * x * (1 - c) + z * s, x * z * (1 - c) - y * s, 0,
|
||||||
|
x * y * (1 - c) - z * s, y * y * (1 - c) + c, y * z * (1 - c) + x * s, 0,
|
||||||
|
x * z * (1 - c) + y * s, y * z * (1 - c) - x * s, z * z * (1 - c) + c, 0,
|
||||||
|
0, 0, 0, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
multiply(m, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates a 4x4 matrix.
|
||||||
|
*
|
||||||
|
* @param[in,out] m the matrix to translate
|
||||||
|
* @param x the x component of the direction to translate to
|
||||||
|
* @param y the y component of the direction to translate to
|
||||||
|
* @param z the z component of the direction to translate to
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
translate(GLfloat *m, GLfloat x, GLfloat y, GLfloat z)
|
||||||
|
{
|
||||||
|
GLfloat t[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 };
|
||||||
|
|
||||||
|
multiply(m, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an identity 4x4 matrix.
|
||||||
|
*
|
||||||
|
* @param m the matrix make an identity matrix
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
identity(GLfloat *m)
|
||||||
|
{
|
||||||
|
GLfloat t[16] = {
|
||||||
|
1.0, 0.0, 0.0, 0.0,
|
||||||
|
0.0, 1.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 1.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0, 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
memcpy(m, t, sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transposes a 4x4 matrix.
|
||||||
|
*
|
||||||
|
* @param m the matrix to transpose
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
transpose(GLfloat *m)
|
||||||
|
{
|
||||||
|
GLfloat t[16] = {
|
||||||
|
m[0], m[4], m[8], m[12],
|
||||||
|
m[1], m[5], m[9], m[13],
|
||||||
|
m[2], m[6], m[10], m[14],
|
||||||
|
m[3], m[7], m[11], m[15]};
|
||||||
|
|
||||||
|
memcpy(m, t, sizeof(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverts a 4x4 matrix.
|
||||||
|
*
|
||||||
|
* This function can currently handle only pure translation-rotation matrices.
|
||||||
|
* Read http://www.gamedev.net/community/forums/topic.asp?topic_id=425118
|
||||||
|
* for an explanation.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
invert(GLfloat *m)
|
||||||
|
{
|
||||||
|
GLfloat t[16];
|
||||||
|
identity(t);
|
||||||
|
|
||||||
|
// Extract and invert the translation part 't'. The inverse of a
|
||||||
|
// translation matrix can be calculated by negating the translation
|
||||||
|
// coordinates.
|
||||||
|
t[12] = -m[12]; t[13] = -m[13]; t[14] = -m[14];
|
||||||
|
|
||||||
|
// Invert the rotation part 'r'. The inverse of a rotation matrix is
|
||||||
|
// equal to its transpose.
|
||||||
|
m[12] = m[13] = m[14] = 0;
|
||||||
|
transpose(m);
|
||||||
|
|
||||||
|
// inv(m) = inv(r) * inv(t)
|
||||||
|
multiply(m, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate a perspective projection transformation.
|
||||||
|
*
|
||||||
|
* @param m the matrix to save the transformation in
|
||||||
|
* @param fovy the field of view in the y direction
|
||||||
|
* @param aspect the view aspect ratio
|
||||||
|
* @param zNear the near clipping plane
|
||||||
|
* @param zFar the far clipping plane
|
||||||
|
*/
|
||||||
|
void perspective(GLfloat *m, GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
|
||||||
|
{
|
||||||
|
GLfloat tmp[16];
|
||||||
|
identity(tmp);
|
||||||
|
|
||||||
|
double sine, cosine, cotangent, deltaZ;
|
||||||
|
GLfloat radians = fovy / 2 * M_PI / 180;
|
||||||
|
|
||||||
|
deltaZ = zFar - zNear;
|
||||||
|
sincos(radians, &sine, &cosine);
|
||||||
|
|
||||||
|
if ((deltaZ == 0) || (sine == 0) || (aspect == 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cotangent = cosine / sine;
|
||||||
|
|
||||||
|
tmp[0] = cotangent / aspect;
|
||||||
|
tmp[5] = cotangent;
|
||||||
|
tmp[10] = -(zFar + zNear) / deltaZ;
|
||||||
|
tmp[11] = -1;
|
||||||
|
tmp[14] = -2 * zNear * zFar / deltaZ;
|
||||||
|
tmp[15] = 0;
|
||||||
|
|
||||||
|
memcpy(m, tmp, sizeof(tmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a gear.
|
||||||
|
*
|
||||||
|
* @param gear the gear to draw
|
||||||
|
* @param transform the current transformation matrix
|
||||||
|
* @param x the x position to draw the gear at
|
||||||
|
* @param y the y position to draw the gear at
|
||||||
|
* @param angle the rotation angle of the gear
|
||||||
|
* @param color the color of the gear
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
draw_gear(struct gear *gear, GLfloat *transform,
|
||||||
|
GLfloat x, GLfloat y, GLfloat angle, const GLfloat color[4])
|
||||||
|
{
|
||||||
|
GLfloat model_view[16];
|
||||||
|
GLfloat normal_matrix[16];
|
||||||
|
GLfloat model_view_projection[16];
|
||||||
|
|
||||||
|
/* Translate and rotate the gear */
|
||||||
|
memcpy(model_view, transform, sizeof (model_view));
|
||||||
|
translate(model_view, x, y, 0);
|
||||||
|
rotate(model_view, 2 * M_PI * angle / 360.0, 0, 0, 1);
|
||||||
|
|
||||||
|
/* Create and set the ModelViewProjectionMatrix */
|
||||||
|
memcpy(model_view_projection, ProjectionMatrix, sizeof(model_view_projection));
|
||||||
|
multiply(model_view_projection, model_view);
|
||||||
|
|
||||||
|
glUniformMatrix4fv(ModelViewProjectionMatrix_location, 1, GL_FALSE,
|
||||||
|
model_view_projection);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create and set the NormalMatrix. It's the inverse transpose of the
|
||||||
|
* ModelView matrix.
|
||||||
|
*/
|
||||||
|
memcpy(normal_matrix, model_view, sizeof (normal_matrix));
|
||||||
|
invert(normal_matrix);
|
||||||
|
transpose(normal_matrix);
|
||||||
|
glUniformMatrix4fv(NormalMatrix_location, 1, GL_FALSE, normal_matrix);
|
||||||
|
|
||||||
|
/* Set the gear color */
|
||||||
|
glUniform4fv(MaterialColor_location, 1, color);
|
||||||
|
|
||||||
|
/* Set the vertex buffer object to use */
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, gear->vbo);
|
||||||
|
|
||||||
|
/* Set up the position of the attributes in the vertex buffer object */
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
|
||||||
|
6 * sizeof(GLfloat), NULL);
|
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
|
||||||
|
6 * sizeof(GLfloat), (GLfloat *) 0 + 3);
|
||||||
|
|
||||||
|
/* Enable the attributes */
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
/* Draw the triangle strips that comprise the gear */
|
||||||
|
int n;
|
||||||
|
for (n = 0; n < gear->nstrips; n++)
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, gear->strips[n].first, gear->strips[n].count);
|
||||||
|
|
||||||
|
/* Disable the attributes */
|
||||||
|
glDisableVertexAttribArray(1);
|
||||||
|
glDisableVertexAttribArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the gears.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
gears_draw(void)
|
||||||
|
{
|
||||||
|
const static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 };
|
||||||
|
const static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 };
|
||||||
|
const static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 };
|
||||||
|
GLfloat transform[16];
|
||||||
|
identity(transform);
|
||||||
|
|
||||||
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
/* Translate and rotate the view */
|
||||||
|
translate(transform, 0, 0, -20);
|
||||||
|
rotate(transform, 2 * M_PI * view_rot[0] / 360.0, 1, 0, 0);
|
||||||
|
rotate(transform, 2 * M_PI * view_rot[1] / 360.0, 0, 1, 0);
|
||||||
|
rotate(transform, 2 * M_PI * view_rot[2] / 360.0, 0, 0, 1);
|
||||||
|
|
||||||
|
/* Draw the gears */
|
||||||
|
draw_gear(gear1, transform, -3.0, -2.0, angle, red);
|
||||||
|
draw_gear(gear2, transform, 3.1, -2.0, -2 * angle - 9.0, green);
|
||||||
|
draw_gear(gear3, transform, -3.1, 4.2, -2 * angle - 25.0, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a new window size or exposure.
|
||||||
|
*
|
||||||
|
* @param width the window width
|
||||||
|
* @param height the window height
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
gears_reshape(int width, int height)
|
||||||
|
{
|
||||||
|
/* Update the projection matrix */
|
||||||
|
perspective(ProjectionMatrix, 60.0, width / (float)height, 1.0, 1024.0);
|
||||||
|
|
||||||
|
/* Set the viewport */
|
||||||
|
glViewport(0, 0, (GLint) width, (GLint) height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gears_idle(void)
|
||||||
|
{
|
||||||
|
static int frames = 0;
|
||||||
|
static double tRot0 = -1.0, tRate0 = -1.0;
|
||||||
|
double dt, t;
|
||||||
|
static u64 origTicks = U64_MAX;
|
||||||
|
|
||||||
|
if (origTicks == U64_MAX)
|
||||||
|
origTicks = armGetSystemTick();
|
||||||
|
|
||||||
|
u64 ticksElapsed = armGetSystemTick() - origTicks;
|
||||||
|
t = (ticksElapsed * 625 / 12) / 1000000000.0;
|
||||||
|
|
||||||
|
if (tRot0 < 0.0)
|
||||||
|
tRot0 = t;
|
||||||
|
dt = t - tRot0;
|
||||||
|
tRot0 = t;
|
||||||
|
|
||||||
|
/* advance rotation for next frame */
|
||||||
|
angle += 70.0 * dt; /* 70 degrees per second */
|
||||||
|
if (angle > 3600.0)
|
||||||
|
angle -= 3600.0;
|
||||||
|
|
||||||
|
frames++;
|
||||||
|
|
||||||
|
if (tRate0 < 0.0)
|
||||||
|
tRate0 = t;
|
||||||
|
if (t - tRate0 >= 5.0) {
|
||||||
|
GLfloat seconds = t - tRate0;
|
||||||
|
GLfloat fps = frames / seconds;
|
||||||
|
printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds,
|
||||||
|
fps);
|
||||||
|
tRate0 = t;
|
||||||
|
frames = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char vertex_shader[] =
|
||||||
|
"attribute vec3 position;\n"
|
||||||
|
"attribute vec3 normal;\n"
|
||||||
|
"\n"
|
||||||
|
"uniform mat4 ModelViewProjectionMatrix;\n"
|
||||||
|
"uniform mat4 NormalMatrix;\n"
|
||||||
|
"uniform vec4 LightSourcePosition;\n"
|
||||||
|
"uniform vec4 MaterialColor;\n"
|
||||||
|
"\n"
|
||||||
|
"varying vec4 Color;\n"
|
||||||
|
"\n"
|
||||||
|
"void main(void)\n"
|
||||||
|
"{\n"
|
||||||
|
" // Transform the normal to eye coordinates\n"
|
||||||
|
" vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n"
|
||||||
|
"\n"
|
||||||
|
" // The LightSourcePosition is actually its direction for directional light\n"
|
||||||
|
" vec3 L = normalize(LightSourcePosition.xyz);\n"
|
||||||
|
"\n"
|
||||||
|
" // Multiply the diffuse value by the vertex color (which is fixed in this case)\n"
|
||||||
|
" // to get the actual color that we will use to draw this vertex with\n"
|
||||||
|
" float diffuse = max(dot(N, L), 0.0);\n"
|
||||||
|
" Color = diffuse * MaterialColor;\n"
|
||||||
|
"\n"
|
||||||
|
" // Transform the position to clip coordinates\n"
|
||||||
|
" gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
static const char fragment_shader[] =
|
||||||
|
"precision mediump float;\n"
|
||||||
|
"varying vec4 Color;\n"
|
||||||
|
"\n"
|
||||||
|
"void main(void)\n"
|
||||||
|
"{\n"
|
||||||
|
" gl_FragColor = Color;\n"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
static void
|
||||||
|
gears_init(void)
|
||||||
|
{
|
||||||
|
GLuint v, f, program;
|
||||||
|
const char *p;
|
||||||
|
char msg[512];
|
||||||
|
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
/* Compile the vertex shader */
|
||||||
|
p = vertex_shader;
|
||||||
|
v = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(v, 1, &p, NULL);
|
||||||
|
glCompileShader(v);
|
||||||
|
glGetShaderInfoLog(v, sizeof msg, NULL, msg);
|
||||||
|
printf("vertex shader info: %s\n", msg);
|
||||||
|
|
||||||
|
/* Compile the fragment shader */
|
||||||
|
p = fragment_shader;
|
||||||
|
f = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(f, 1, &p, NULL);
|
||||||
|
glCompileShader(f);
|
||||||
|
glGetShaderInfoLog(f, sizeof msg, NULL, msg);
|
||||||
|
printf("fragment shader info: %s\n", msg);
|
||||||
|
|
||||||
|
/* Create and link the shader program */
|
||||||
|
program = glCreateProgram();
|
||||||
|
glAttachShader(program, v);
|
||||||
|
glAttachShader(program, f);
|
||||||
|
glBindAttribLocation(program, 0, "position");
|
||||||
|
glBindAttribLocation(program, 1, "normal");
|
||||||
|
|
||||||
|
glLinkProgram(program);
|
||||||
|
glGetProgramInfoLog(program, sizeof msg, NULL, msg);
|
||||||
|
printf("info: %s\n", msg);
|
||||||
|
|
||||||
|
/* Enable the shaders */
|
||||||
|
glUseProgram(program);
|
||||||
|
|
||||||
|
/* Get the locations of the uniforms so we can access them */
|
||||||
|
ModelViewProjectionMatrix_location = glGetUniformLocation(program, "ModelViewProjectionMatrix");
|
||||||
|
NormalMatrix_location = glGetUniformLocation(program, "NormalMatrix");
|
||||||
|
LightSourcePosition_location = glGetUniformLocation(program, "LightSourcePosition");
|
||||||
|
MaterialColor_location = glGetUniformLocation(program, "MaterialColor");
|
||||||
|
|
||||||
|
/* Set the LightSourcePosition uniform which is constant throught the program */
|
||||||
|
glUniform4fv(LightSourcePosition_location, 1, LightSourcePosition);
|
||||||
|
|
||||||
|
/* make the gears */
|
||||||
|
gear1 = create_gear(1.0, 4.0, 1.0, 20, 0.7);
|
||||||
|
gear2 = create_gear(0.5, 2.0, 2.0, 10, 0.7);
|
||||||
|
gear3 = create_gear(1.3, 2.0, 0.5, 10, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
// Set mesa configuration (useful for debugging)
|
||||||
|
setMesaConfig();
|
||||||
|
|
||||||
|
// Initialize EGL
|
||||||
|
if (!initEgl())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// Initialize gears demo
|
||||||
|
gears_init();
|
||||||
|
gears_reshape(1280, 720);
|
||||||
|
|
||||||
|
// Main graphics loop
|
||||||
|
while (appletMainLoop())
|
||||||
|
{
|
||||||
|
// Get and process input
|
||||||
|
hidScanInput();
|
||||||
|
u32 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
if (kDown & KEY_PLUS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Render stuff!
|
||||||
|
gears_draw();
|
||||||
|
eglSwapBuffers(s_display, s_surface);
|
||||||
|
|
||||||
|
// Update gears state
|
||||||
|
gears_idle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deinitialize EGL
|
||||||
|
deinitEgl();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
197
graphics/opengl/lenny/Makefile
Normal file
197
graphics/opengl/lenny/Makefile
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/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__
|
||||||
|
|
||||||
|
# The following line works around an issue in newlib that produces a compilation
|
||||||
|
# error in glm. It will be removed as soon as this issue is resolved.
|
||||||
|
CFLAGS += -D_GLIBCXX_USE_C99_MATH_TR1 -D_LDBL_EQ_DBL
|
||||||
|
|
||||||
|
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 -lm
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# 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
|
||||||
|
#---------------------------------------------------------------------------------------
|
3350
graphics/opengl/lenny/source/lenny.c
Normal file
3350
graphics/opengl/lenny/source/lenny.c
Normal file
File diff suppressed because it is too large
Load Diff
19
graphics/opengl/lenny/source/lenny.h
Normal file
19
graphics/opengl/lenny/source/lenny.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float x, y, z;
|
||||||
|
float nx, ny, nz;
|
||||||
|
} lennyVertex;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const lennyVertex lennyVertices[3345];
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define lennyVerticesCount (sizeof(lennyVertices)/sizeof(lennyVertices[0]))
|
427
graphics/opengl/lenny/source/main.cpp
Normal file
427
graphics/opengl/lenny/source/main.cpp
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h> // EGL library
|
||||||
|
#include <glad/glad.h> // glad library (OpenGL loader)
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_PURE
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
// ( ͡° ͜ʖ ͡°) mesh data
|
||||||
|
#include "lenny.h"
|
||||||
|
|
||||||
|
constexpr auto TAU = glm::two_pi<float>();
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nxlink support
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ENABLE_NXLINK
|
||||||
|
#define TRACE(fmt,...) ((void)0)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#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 attributeList[] =
|
||||||
|
{
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
eglChooseConfig(s_display, attributeList, &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
|
||||||
|
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, nullptr);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 vertexShaderSource = R"text(
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec3 inNormal;
|
||||||
|
|
||||||
|
out vec4 vtxColor;
|
||||||
|
out vec4 vtxNormalQuat;
|
||||||
|
out vec3 vtxView;
|
||||||
|
|
||||||
|
uniform mat4 mdlvMtx;
|
||||||
|
uniform mat4 projMtx;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Calculate position
|
||||||
|
vec4 pos = mdlvMtx * vec4(inPos, 1.0);
|
||||||
|
vtxView = -pos.xyz;
|
||||||
|
gl_Position = projMtx * pos;
|
||||||
|
|
||||||
|
// Calculate normalquat
|
||||||
|
vec3 normal = normalize(mat3(mdlvMtx) * inNormal);
|
||||||
|
float z = (1.0 + normal.z) / 2.0;
|
||||||
|
vtxNormalQuat = vec4(1.0, 0.0, 0.0, 0.0);
|
||||||
|
if (z > 0.0)
|
||||||
|
{
|
||||||
|
vtxNormalQuat.z = sqrt(z);
|
||||||
|
vtxNormalQuat.xy = normal.xy / (2.0 * vtxNormalQuat.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate color
|
||||||
|
vtxColor = vec4(1.0);
|
||||||
|
}
|
||||||
|
)text";
|
||||||
|
|
||||||
|
static const char* const fragmentShaderSource = R"text(
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec4 vtxColor;
|
||||||
|
in vec4 vtxNormalQuat;
|
||||||
|
in vec3 vtxView;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform vec4 lightPos;
|
||||||
|
uniform vec3 ambient;
|
||||||
|
uniform vec3 diffuse;
|
||||||
|
uniform vec4 specular; // w component is shininess
|
||||||
|
|
||||||
|
// Rotate the vector v by the quaternion q
|
||||||
|
vec3 quatrotate(vec4 q, vec3 v)
|
||||||
|
{
|
||||||
|
return v + 2.0*cross(q.xyz, cross(q.xyz, v) + q.w*v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Extract normal from quaternion
|
||||||
|
vec4 normquat = normalize(vtxNormalQuat);
|
||||||
|
vec3 normal = quatrotate(normquat, vec3(0.0, 0.0, 1.0));
|
||||||
|
|
||||||
|
vec3 lightVec;
|
||||||
|
if (lightPos.w != 0.0)
|
||||||
|
lightVec = normalize(lightPos.xyz + vtxView);
|
||||||
|
else
|
||||||
|
lightVec = normalize(lightPos.xyz);
|
||||||
|
|
||||||
|
vec3 viewVec = normalize(vtxView);
|
||||||
|
vec3 halfVec = normalize(viewVec + lightVec);
|
||||||
|
float diffuseFactor = max(dot(lightVec, normal), 0.0);
|
||||||
|
float specularFactor = pow(max(dot(normal, halfVec), 0.0), specular.w);
|
||||||
|
|
||||||
|
vec3 fragLightColor = ambient + diffuseFactor*diffuse + specularFactor*specular.xyz;
|
||||||
|
|
||||||
|
fragColor = vec4(min(fragLightColor, 1.0), 1.0);
|
||||||
|
}
|
||||||
|
)text";
|
||||||
|
|
||||||
|
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 == GL_FALSE)
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
|
||||||
|
TRACE("%u: %s\n", type, msg);
|
||||||
|
glDeleteShader(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint s_program;
|
||||||
|
static GLuint s_vao, s_vbo;
|
||||||
|
|
||||||
|
static GLint loc_mdlvMtx, loc_projMtx;
|
||||||
|
static GLint loc_lightPos, loc_ambient, loc_diffuse, loc_specular;
|
||||||
|
|
||||||
|
static u64 s_startTicks;
|
||||||
|
|
||||||
|
static void sceneInit()
|
||||||
|
{
|
||||||
|
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||||
|
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||||
|
|
||||||
|
s_program = glCreateProgram();
|
||||||
|
glAttachShader(s_program, vsh);
|
||||||
|
glAttachShader(s_program, fsh);
|
||||||
|
glLinkProgram(s_program);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
|
||||||
|
if (success == GL_FALSE)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
|
||||||
|
TRACE("Link error: %s", buf);
|
||||||
|
}
|
||||||
|
glDeleteShader(vsh);
|
||||||
|
glDeleteShader(fsh);
|
||||||
|
|
||||||
|
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
|
||||||
|
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
|
||||||
|
loc_lightPos = glGetUniformLocation(s_program, "lightPos");
|
||||||
|
loc_ambient = glGetUniformLocation(s_program, "ambient");
|
||||||
|
loc_diffuse = glGetUniformLocation(s_program, "diffuse");
|
||||||
|
loc_specular = glGetUniformLocation(s_program, "specular");
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &s_vao);
|
||||||
|
glGenBuffers(1, &s_vbo);
|
||||||
|
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
|
||||||
|
glBindVertexArray(s_vao);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(lennyVertices), lennyVertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(lennyVertex), (void*)offsetof(lennyVertex, x));
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(lennyVertex), (void*)offsetof(lennyVertex, nx));
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
|
||||||
|
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
|
glUseProgram(s_program);
|
||||||
|
auto projMtx = glm::perspective(40.0f*TAU/360.0f, 1280.0f/720.0f, 0.01f, 1000.0f);
|
||||||
|
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
|
||||||
|
glUniform4f(loc_lightPos, 0.0f, 0.0f, -0.5f, 1.0f);
|
||||||
|
glUniform3f(loc_ambient, 0.1f, 0.1f, 0.1f);
|
||||||
|
glUniform3f(loc_diffuse, 0.4f, 0.4f, 0.4f);
|
||||||
|
glUniform4f(loc_specular, 0.5f, 0.5f, 0.5f, 20.0f);
|
||||||
|
s_startTicks = armGetSystemTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getTime()
|
||||||
|
{
|
||||||
|
u64 elapsed = armGetSystemTick() - s_startTicks;
|
||||||
|
return (elapsed * 625 / 12) / 1000000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneUpdate()
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx{1.0};
|
||||||
|
mdlvMtx = glm::translate(mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
|
||||||
|
mdlvMtx = glm::rotate(mdlvMtx, getTime() * TAU * 0.234375f, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
mdlvMtx = glm::scale(mdlvMtx, glm::vec3{2.0f});
|
||||||
|
glUniformMatrix4fv(loc_mdlvMtx, 1, GL_FALSE, glm::value_ptr(mdlvMtx));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneRender()
|
||||||
|
{
|
||||||
|
glClearColor(0x68/255.0f, 0xB0/255.0f, 0xD8/255.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// draw our ( ͡° ͜ʖ ͡°)
|
||||||
|
glBindVertexArray(s_vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, lennyVerticesCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneExit()
|
||||||
|
{
|
||||||
|
glDeleteBuffers(1, &s_vbo);
|
||||||
|
glDeleteVertexArrays(1, &s_vao);
|
||||||
|
glDeleteProgram(s_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Update our scene
|
||||||
|
sceneUpdate();
|
||||||
|
|
||||||
|
// Render stuff!
|
||||||
|
sceneRender();
|
||||||
|
eglSwapBuffers(s_display, s_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deinitialize our scene
|
||||||
|
sceneExit();
|
||||||
|
|
||||||
|
// Deinitialize EGL
|
||||||
|
deinitEgl();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
193
graphics/opengl/simple_triangle/Makefile
Normal file
193
graphics/opengl/simple_triangle/Makefile
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/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
|
||||||
|
#---------------------------------------------------------------------------------------
|
335
graphics/opengl/simple_triangle/source/main.cpp
Normal file
335
graphics/opengl/simple_triangle/source/main.cpp
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h> // EGL library
|
||||||
|
#include <glad/glad.h> // glad library (OpenGL loader)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nxlink support
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ENABLE_NXLINK
|
||||||
|
#define TRACE(fmt,...) ((void)0)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#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 attributeList[] =
|
||||||
|
{
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
eglChooseConfig(s_display, attributeList, &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
|
||||||
|
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, nullptr);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 vertexShaderSource = R"text(
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 aPos;
|
||||||
|
layout (location = 1) in vec3 aColor;
|
||||||
|
|
||||||
|
out vec3 ourColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
|
||||||
|
ourColor = aColor;
|
||||||
|
}
|
||||||
|
)text";
|
||||||
|
|
||||||
|
static const char* const fragmentShaderSource = R"text(
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 ourColor;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fragColor = vec4(ourColor, 1.0f);
|
||||||
|
}
|
||||||
|
)text";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint s_program;
|
||||||
|
static GLuint s_vao, s_vbo;
|
||||||
|
|
||||||
|
static void sceneInit()
|
||||||
|
{
|
||||||
|
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||||
|
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||||
|
|
||||||
|
s_program = glCreateProgram();
|
||||||
|
glAttachShader(s_program, vsh);
|
||||||
|
glAttachShader(s_program, fsh);
|
||||||
|
glLinkProgram(s_program);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
|
||||||
|
TRACE("Link error: %s", buf);
|
||||||
|
}
|
||||||
|
glDeleteShader(vsh);
|
||||||
|
glDeleteShader(fsh);
|
||||||
|
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float color[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Vertex vertices[] =
|
||||||
|
{
|
||||||
|
{ { -0.5f, -0.5f, 0.0f }, { 1.0f, 0.0f, 0.0f } },
|
||||||
|
{ { 0.5f, -0.5f, 0.0f }, { 0.0f, 1.0f, 0.0f } },
|
||||||
|
{ { 0.0f, 0.5f, 0.0f }, { 0.0f, 0.0f, 1.0f } },
|
||||||
|
};
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &s_vao);
|
||||||
|
glGenBuffers(1, &s_vbo);
|
||||||
|
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
|
||||||
|
glBindVertexArray(s_vao);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color));
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
|
||||||
|
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneRender()
|
||||||
|
{
|
||||||
|
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
// draw our first triangle
|
||||||
|
glUseProgram(s_program);
|
||||||
|
glBindVertexArray(s_vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneExit()
|
||||||
|
{
|
||||||
|
glDeleteBuffers(1, &s_vbo);
|
||||||
|
glDeleteVertexArrays(1, &s_vao);
|
||||||
|
glDeleteProgram(s_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
203
graphics/opengl/textured_cube/Makefile
Normal file
203
graphics/opengl/textured_cube/Makefile
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/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):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/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__
|
||||||
|
|
||||||
|
# The following line works around an issue in newlib that produces a compilation
|
||||||
|
# error in glm. It will be removed as soon as this issue is resolved.
|
||||||
|
CFLAGS += -D_GLIBCXX_USE_C99_MATH_TR1 -D_LDBL_EQ_DBL
|
||||||
|
|
||||||
|
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 -lm
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.png.o %_png.h : %.png
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
BIN
graphics/opengl/textured_cube/data/devkitlenny.png
Normal file
BIN
graphics/opengl/textured_cube/data/devkitlenny.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
527
graphics/opengl/textured_cube/source/main.cpp
Normal file
527
graphics/opengl/textured_cube/source/main.cpp
Normal file
@ -0,0 +1,527 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h> // EGL library
|
||||||
|
#include <glad/glad.h> // glad library (OpenGL loader)
|
||||||
|
|
||||||
|
// GLM headers
|
||||||
|
#define GLM_FORCE_PURE
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec4.hpp>
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
|
#include "stb_image.h"
|
||||||
|
#include "devkitlenny_png.h"
|
||||||
|
|
||||||
|
constexpr auto TAU = glm::two_pi<float>();
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// nxlink support
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef ENABLE_NXLINK
|
||||||
|
#define TRACE(fmt,...) ((void)0)
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#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 attributeList[] =
|
||||||
|
{
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_NONE
|
||||||
|
};
|
||||||
|
eglChooseConfig(s_display, attributeList, &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
|
||||||
|
s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, nullptr);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 vertexShaderSource = R"text(
|
||||||
|
#version 320 es
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 inPos;
|
||||||
|
layout (location = 1) in vec2 inTexCoord;
|
||||||
|
layout (location = 2) in vec3 inNormal;
|
||||||
|
|
||||||
|
out vec2 vtxTexCoord;
|
||||||
|
out vec4 vtxNormalQuat;
|
||||||
|
out vec3 vtxView;
|
||||||
|
|
||||||
|
uniform mat4 mdlvMtx;
|
||||||
|
uniform mat4 projMtx;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Calculate position
|
||||||
|
vec4 pos = mdlvMtx * vec4(inPos, 1.0);
|
||||||
|
vtxView = -pos.xyz;
|
||||||
|
gl_Position = projMtx * pos;
|
||||||
|
|
||||||
|
// Calculate normalquat
|
||||||
|
vec3 normal = normalize(mat3(mdlvMtx) * inNormal);
|
||||||
|
float z = (1.0 + normal.z) / 2.0;
|
||||||
|
vtxNormalQuat = vec4(1.0, 0.0, 0.0, 0.0);
|
||||||
|
if (z > 0.0)
|
||||||
|
{
|
||||||
|
vtxNormalQuat.z = sqrt(z);
|
||||||
|
vtxNormalQuat.xy = normal.xy / (2.0 * vtxNormalQuat.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate texcoord
|
||||||
|
vtxTexCoord = inTexCoord;
|
||||||
|
}
|
||||||
|
)text";
|
||||||
|
|
||||||
|
static const char* const fragmentShaderSource = R"text(
|
||||||
|
#version 320 es
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
in vec2 vtxTexCoord;
|
||||||
|
in vec4 vtxNormalQuat;
|
||||||
|
in vec3 vtxView;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
uniform vec4 lightPos;
|
||||||
|
uniform vec3 ambient;
|
||||||
|
uniform vec3 diffuse;
|
||||||
|
uniform vec4 specular; // w component is shininess
|
||||||
|
|
||||||
|
uniform sampler2D tex_diffuse;
|
||||||
|
|
||||||
|
// Rotate the vector v by the quaternion q
|
||||||
|
vec3 quatrotate(vec4 q, vec3 v)
|
||||||
|
{
|
||||||
|
return v + 2.0*cross(q.xyz, cross(q.xyz, v) + q.w*v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Extract normal from quaternion
|
||||||
|
vec4 normquat = normalize(vtxNormalQuat);
|
||||||
|
vec3 normal = quatrotate(normquat, vec3(0.0, 0.0, 1.0));
|
||||||
|
|
||||||
|
vec3 lightVec;
|
||||||
|
if (lightPos.w != 0.0)
|
||||||
|
lightVec = normalize(lightPos.xyz + vtxView);
|
||||||
|
else
|
||||||
|
lightVec = normalize(lightPos.xyz);
|
||||||
|
|
||||||
|
vec3 viewVec = normalize(vtxView);
|
||||||
|
vec3 halfVec = normalize(viewVec + lightVec);
|
||||||
|
float diffuseFactor = max(dot(lightVec, normal), 0.0);
|
||||||
|
float specularFactor = pow(max(dot(normal, halfVec), 0.0), specular.w);
|
||||||
|
|
||||||
|
vec4 texDiffuseColor = texture(tex_diffuse, vtxTexCoord);
|
||||||
|
vec3 fragLightColor = ambient + diffuseFactor*diffuse*texDiffuseColor.rgb + specularFactor*specular.xyz;
|
||||||
|
|
||||||
|
fragColor = vec4(min(fragLightColor, 1.0), texDiffuseColor.a);
|
||||||
|
}
|
||||||
|
)text";
|
||||||
|
|
||||||
|
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 == GL_FALSE)
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(handle, sizeof(msg), nullptr, msg);
|
||||||
|
TRACE("%u: %s\n", type, msg);
|
||||||
|
glDeleteShader(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
float position[3];
|
||||||
|
float texcoord[2];
|
||||||
|
float normal[3];
|
||||||
|
} Vertex;
|
||||||
|
|
||||||
|
static const Vertex vertex_list[] =
|
||||||
|
{
|
||||||
|
// First face (PZ)
|
||||||
|
// First triangle
|
||||||
|
{ {-0.5f, -0.5f, +0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, +1.0f} },
|
||||||
|
{ {+0.5f, -0.5f, +0.5f}, {1.0f, 0.0f}, {0.0f, 0.0f, +1.0f} },
|
||||||
|
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, +1.0f} },
|
||||||
|
// Second triangle
|
||||||
|
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, +1.0f} },
|
||||||
|
{ {-0.5f, +0.5f, +0.5f}, {0.0f, 1.0f}, {0.0f, 0.0f, +1.0f} },
|
||||||
|
{ {-0.5f, -0.5f, +0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, +1.0f} },
|
||||||
|
|
||||||
|
// Second face (MZ)
|
||||||
|
// First triangle
|
||||||
|
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
|
||||||
|
{ {-0.5f, +0.5f, -0.5f}, {1.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
|
||||||
|
{ {+0.5f, +0.5f, -0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f} },
|
||||||
|
// Second triangle
|
||||||
|
{ {+0.5f, +0.5f, -0.5f}, {1.0f, 1.0f}, {0.0f, 0.0f, -1.0f} },
|
||||||
|
{ {+0.5f, -0.5f, -0.5f}, {0.0f, 1.0f}, {0.0f, 0.0f, -1.0f} },
|
||||||
|
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, 0.0f, -1.0f} },
|
||||||
|
|
||||||
|
// Third face (PX)
|
||||||
|
// First triangle
|
||||||
|
{ {+0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {+0.5f, +0.5f, -0.5f}, {1.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} },
|
||||||
|
// Second triangle
|
||||||
|
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {+0.5f, -0.5f, +0.5f}, {0.0f, 1.0f}, {+1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {+0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f} },
|
||||||
|
|
||||||
|
// Fourth face (MX)
|
||||||
|
// First triangle
|
||||||
|
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {-0.5f, -0.5f, +0.5f}, {1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {-0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} },
|
||||||
|
// Second triangle
|
||||||
|
{ {-0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {-0.5f, +0.5f, -0.5f}, {0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f} },
|
||||||
|
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f} },
|
||||||
|
|
||||||
|
// Fifth face (PY)
|
||||||
|
// First triangle
|
||||||
|
{ {-0.5f, +0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, +1.0f, 0.0f} },
|
||||||
|
{ {-0.5f, +0.5f, +0.5f}, {1.0f, 0.0f}, {0.0f, +1.0f, 0.0f} },
|
||||||
|
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, +1.0f, 0.0f} },
|
||||||
|
// Second triangle
|
||||||
|
{ {+0.5f, +0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, +1.0f, 0.0f} },
|
||||||
|
{ {+0.5f, +0.5f, -0.5f}, {0.0f, 1.0f}, {0.0f, +1.0f, 0.0f} },
|
||||||
|
{ {-0.5f, +0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, +1.0f, 0.0f} },
|
||||||
|
|
||||||
|
// Sixth face (MY)
|
||||||
|
// First triangle
|
||||||
|
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
|
||||||
|
{ {+0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
|
||||||
|
{ {+0.5f, -0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
|
||||||
|
// Second triangle
|
||||||
|
{ {+0.5f, -0.5f, +0.5f}, {1.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
|
||||||
|
{ {-0.5f, -0.5f, +0.5f}, {0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} },
|
||||||
|
{ {-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}, {0.0f, -1.0f, 0.0f} },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define vertex_list_count (sizeof(vertex_list)/sizeof(vertex_list[0]))
|
||||||
|
|
||||||
|
static GLuint s_program;
|
||||||
|
static GLuint s_vao, s_vbo;
|
||||||
|
static GLuint s_tex;
|
||||||
|
|
||||||
|
static GLint loc_mdlvMtx, loc_projMtx;
|
||||||
|
static GLint loc_lightPos, loc_ambient, loc_diffuse, loc_specular, loc_tex_diffuse;
|
||||||
|
|
||||||
|
static u64 s_startTicks;
|
||||||
|
|
||||||
|
static void sceneInit()
|
||||||
|
{
|
||||||
|
GLint vsh = createAndCompileShader(GL_VERTEX_SHADER, vertexShaderSource);
|
||||||
|
GLint fsh = createAndCompileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);
|
||||||
|
|
||||||
|
s_program = glCreateProgram();
|
||||||
|
glAttachShader(s_program, vsh);
|
||||||
|
glAttachShader(s_program, fsh);
|
||||||
|
glLinkProgram(s_program);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetProgramiv(s_program, GL_LINK_STATUS, &success);
|
||||||
|
if (success == GL_FALSE)
|
||||||
|
{
|
||||||
|
char buf[512];
|
||||||
|
glGetProgramInfoLog(s_program, sizeof(buf), nullptr, buf);
|
||||||
|
TRACE("Link error: %s", buf);
|
||||||
|
}
|
||||||
|
glDeleteShader(vsh);
|
||||||
|
glDeleteShader(fsh);
|
||||||
|
|
||||||
|
loc_mdlvMtx = glGetUniformLocation(s_program, "mdlvMtx");
|
||||||
|
loc_projMtx = glGetUniformLocation(s_program, "projMtx");
|
||||||
|
loc_lightPos = glGetUniformLocation(s_program, "lightPos");
|
||||||
|
loc_ambient = glGetUniformLocation(s_program, "ambient");
|
||||||
|
loc_diffuse = glGetUniformLocation(s_program, "diffuse");
|
||||||
|
loc_specular = glGetUniformLocation(s_program, "specular");
|
||||||
|
loc_tex_diffuse = glGetUniformLocation(s_program, "tex_diffuse");
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthFunc(GL_LESS);
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &s_vao);
|
||||||
|
glGenBuffers(1, &s_vbo);
|
||||||
|
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
|
||||||
|
glBindVertexArray(s_vao);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, s_vbo);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_list), vertex_list, GL_STATIC_DRAW);
|
||||||
|
|
||||||
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoord));
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
|
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
|
||||||
|
// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|
||||||
|
// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
|
||||||
|
// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
|
||||||
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
// Textures
|
||||||
|
glGenTextures(1, &s_tex);
|
||||||
|
glActiveTexture(GL_TEXTURE0); // activate the texture unit first before binding texture
|
||||||
|
glBindTexture(GL_TEXTURE_2D, s_tex);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
int width, height, nchan;
|
||||||
|
stbi_set_flip_vertically_on_load(true);
|
||||||
|
stbi_uc* img = stbi_load_from_memory((const stbi_uc*)devkitlenny_png, devkitlenny_png_size, &width, &height, &nchan, 4);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||||
|
stbi_image_free(img);
|
||||||
|
|
||||||
|
// Uniforms
|
||||||
|
glUseProgram(s_program);
|
||||||
|
auto projMtx = glm::perspective(40.0f*TAU/360.0f, 1280.0f/720.0f, 0.01f, 1000.0f);
|
||||||
|
glUniformMatrix4fv(loc_projMtx, 1, GL_FALSE, glm::value_ptr(projMtx));
|
||||||
|
glUniform4f(loc_lightPos, 0.0f, 0.0f, 0.5f, 1.0f);
|
||||||
|
glUniform3f(loc_ambient, 0.1f, 0.1f, 0.1f);
|
||||||
|
glUniform3f(loc_diffuse, 0.4f, 0.4f, 0.4f);
|
||||||
|
glUniform4f(loc_specular, 0.5f, 0.5f, 0.5f, 20.0f);
|
||||||
|
glUniform1i(loc_tex_diffuse, 0); // texunit 0
|
||||||
|
s_startTicks = armGetSystemTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float getTime()
|
||||||
|
{
|
||||||
|
u64 elapsed = armGetSystemTick() - s_startTicks;
|
||||||
|
return (elapsed * 625 / 12) / 1000000000.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneUpdate()
|
||||||
|
{
|
||||||
|
glm::mat4 mdlvMtx{1.0};
|
||||||
|
mdlvMtx = glm::translate(mdlvMtx, glm::vec3{0.0f, 0.0f, -3.0f});
|
||||||
|
mdlvMtx = glm::rotate(mdlvMtx, getTime() * TAU * 0.234375f, glm::vec3{1.0f, 0.0f, 0.0f});
|
||||||
|
mdlvMtx = glm::rotate(mdlvMtx, getTime() * TAU * 0.234375f / 2.0f, glm::vec3{0.0f, 1.0f, 0.0f});
|
||||||
|
glUniformMatrix4fv(loc_mdlvMtx, 1, GL_FALSE, glm::value_ptr(mdlvMtx));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneRender()
|
||||||
|
{
|
||||||
|
glClearColor(0x68/255.0f, 0xB0/255.0f, 0xD8/255.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// draw our textured cube
|
||||||
|
glBindVertexArray(s_vao); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, vertex_list_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sceneExit()
|
||||||
|
{
|
||||||
|
glDeleteTextures(1, &s_tex);
|
||||||
|
glDeleteBuffers(1, &s_vbo);
|
||||||
|
glDeleteVertexArrays(1, &s_vao);
|
||||||
|
glDeleteProgram(s_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Update our scene
|
||||||
|
sceneUpdate();
|
||||||
|
|
||||||
|
// Render stuff!
|
||||||
|
sceneRender();
|
||||||
|
eglSwapBuffers(s_display, s_surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deinitialize our scene
|
||||||
|
sceneExit();
|
||||||
|
|
||||||
|
// Deinitialize EGL
|
||||||
|
deinitEgl();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
3
graphics/opengl/textured_cube/source/stb_image.cpp
Normal file
3
graphics/opengl/textured_cube/source/stb_image.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#define STBI_ONLY_PNG
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
7462
graphics/opengl/textured_cube/source/stb_image.h
Normal file
7462
graphics/opengl/textured_cube/source/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user