commit 46b2ed80aed9af0b6072ad3ee7e506245938648f Author: Bubblegumdrop Date: Sun Jan 2 19:28:16 2022 -0500 Initial commit. Another nuke! This time, trying to do a client <-> server thing. Also a bit of messing with Lua. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0727321 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.d/ +.o/ +imgui/ +main_client +main_server +*.png +res/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cd3e020 --- /dev/null +++ b/Makefile @@ -0,0 +1,190 @@ +.PHONY: all clean +.POSIX: + +# {{{ Flags +SRCDIR := src +OBJDIR := .o +DEPDIR := .d +TESTDIR := test +MKDIR := mkdir -p +# }}} + +PROGS := main_client main_server + +IMGUI_INCLUDE := -Iimgui -Iimgui/backends +IMGUI_SRCS := \ + imgui/backends/imgui_impl_glfw.cpp \ + imgui/backends/imgui_impl_opengl3.cpp \ + imgui/imgui.cpp \ + imgui/imgui_demo.cpp \ + imgui/imgui_draw.cpp \ + imgui/imgui_tables.cpp \ + imgui/imgui_widgets.cpp + +SRCS := $(wildcard $(SRCDIR)/*.cpp) $(IMGUI_SRCS) +OBJS := $(patsubst %,$(OBJDIR)/%.o,$(basename $(SRCS))) +DEPS := $(patsubst %,$(DEPDIR)/%.d,$(basename $(SRCS))) + +$(shell $(MKDIR) $(dir $(OBJS)) >/dev/null) +$(shell $(MKDIR) $(dir $(DEPS)) >/dev/null) + +COMMON_OBJS := \ + $(OBJDIR)/src/lib_lua_common.o \ + $(OBJDIR)/src/lib_SDL_common.o \ + $(OBJDIR)/src/signal_common.o + +SERVER_OBJS := \ + $(OBJDIR)/src/server_main.o \ + $(OBJDIR)/src/Server.o \ + $(COMMON_OBJS) + +CLIENT_OBJS := \ + $(OBJDIR)/src/camera.o \ + $(OBJDIR)/src/client_common.o \ + $(OBJDIR)/src/client_main.o \ + $(OBJDIR)/src/Client.o \ + $(OBJDIR)/src/cube.o \ + $(OBJDIR)/src/FileIO.o \ + $(OBJDIR)/src/lib_GL_common.o \ + $(OBJDIR)/src/object.o \ + $(OBJDIR)/src/quad.o \ + $(OBJDIR)/src/shader.o \ + $(OBJDIR)/src/UDP_Write.o \ + $(COMMON_OBJS) + +IMGUI_OBJS := $(patsubst %,$(OBJDIR)/%.o,$(basename $(IMGUI_SRCS))) + +TEST_OBJ_COMMON := \ + $(OBJDIR)/src/FileIO.o \ + $(COMMON_OBJS) + + +TESTS := test_FileIO test_File_Write_UDP test_Object test_lua +TEST_PROGS := $(addprefix $(TESTDIR)/,$(TESTS)) + +# {{{ Flags +CC := gcc +CXX := g++ +LD := g++ +ifeq ($(CLANG),1) +CC := clang-12 +CXX := clang++-12 +LD := clang-12 +endif + +SRCDIR := src +OBJDIR := .o +DEPDIR := .d + +RM := rm -rf + +PKGS := SDL2_image SDL2_mixer SDL2_ttf SDL2_net sdl2 gl glew lua54 +CPPFLAGS := $(shell pkg-config --cflags $(PKGS)) +LDLIBS := $(shell pkg-config --libs $(PKGS)) -lm + +DEFS := -D_FILE_OFFSET_BITS=64 +INCS := -Iinclude $(IMGUI_INCLUDE) + +DEBUG := -g +ifeq ($(D),1) +DEBUG := -ggdb +OPT := -O0 +DEFS += -DDEBUG=1 +endif + +OPT := -O0 +ifeq ($(O),1) +OPT := -O1 -g +endif +ifeq ($(O),2) +OPT := -O2 -g +endif +ifeq ($(O),3) +OPT := -O3 -g0 +endif + +ifeq ($(LTO),1) +LDFLAGS += -flto -fwhole-program +endif + +ifeq ($(PG),1) +CFLAGS += -pg -coverage +endif + +ifeq ($(PEDANTIC),1) +CFLAGS += -pedantic +endif + +CFLAGS += -std=c99 +CXXFLAGS += -std=c++14 +CPPFLAGS += -fPIC -W -Wextra -Wall $(DEFS) $(DEBUG) $(INCS) $(OPT) + +# flags required for dependency generation; passed to compilers +DEPFLAGS = -MT $@ -MD -MP -MF $(DEPDIR)/$*.Td + +# compile C source files +COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) $(CPPFLAGS) -c -o $@ +# compile C++ source files +COMPILE.cc = $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ +# link object files to binary +LINK.o = $(LD) $(LDFLAGS) -o $@ +LINK.so = $(LD) $(LDFLAGS) -o $@ -shared +# precompile step +PRECOMPILE = +# postcompile step +POSTCOMPILE = mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d + +# }}} + +.SUFFIXES: .c .cc .cpp .cxx .o .1 + +all: $(OBJS) $(PROGS) + +main_client: $(CLIENT_OBJS) + $(LINK.o) $^ $(LDLIBS) + +main_server: $(SERVER_OBJS) + $(LINK.o) $^ $(LDLIBS) + +test: $(TEST_PROGS) + +test/test_FileIO: $(TEST_OBJ_COMMON) + +test/test_File_Write_UDP: $(TEST_OBJ_COMMON) $(OBJDIR)/src/client_common.o $(OBJDIR)/src/UDP_Write.o $(OBJDIR)/src/Client.o + +test/test_Object: $(TEST_OBJ_COMMON) $(OBJDIR)/src/object.o $(OBJDIR)/src/quad.o $(OBJDIR)/src/cube.o $(OBJDIR)/src/lib_GL_common.o + +test/test_lua: $(TEST_OBJ_COMMON) $(OBJDIR)/src/lib_lua_common.o + +$(OBJDIR)/%.o: %.c +$(OBJDIR)/%.o: %.c $(DEPDIR)/%.d + $(PRECOMPILE) + $(COMPILE.c) $< + $(POSTCOMPILE) + +$(OBJDIR)/%.o: %.cpp +$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d + $(PRECOMPILE) + $(COMPILE.cc) $< + $(POSTCOMPILE) + +$(OBJDIR)/%.o: %.cc +$(OBJDIR)/%.o: %.cc $(DEPDIR)/%.d + $(PRECOMPILE) + $(COMPILE.cc) $< + $(POSTCOMPILE) + +$(OBJDIR)/%.o: %.cxx +$(OBJDIR)/%.o: %.cxx $(DEPDIR)/%.d + $(PRECOMPILE) + $(COMPILE.cc) $< + $(POSTCOMPILE) + +clean: + @echo $(RM) $(DEPDIR) $(OBJDIR) $(PROGS) + @echo $(RM) $(TEST_PROGS) + +.PRECIOUS: $(DEPDIR)/%.d +$(DEPDIR)/%.d: ; + +-include $(DEPS) diff --git a/glsl/header.f.glsl b/glsl/header.f.glsl new file mode 100644 index 0000000..9f059a8 --- /dev/null +++ b/glsl/header.f.glsl @@ -0,0 +1,46 @@ +/* vim: set ft=c: */ + +#version 440 core + +uniform float iChannelTime[4]; +uniform float iFrameRate; +uniform float iSampleRate; +uniform float iTime; +uniform float iTimeDelta; +uniform int iFrame; +uniform vec3 iChannelResolution[4]; +uniform vec3 iResolution; +uniform vec4 iDate; +uniform vec4 iMouse; + +uniform sampler2D iChannel0; +uniform sampler2D iChannel1; +uniform sampler2D iChannel2; +uniform sampler2D iChannel3; + +/* +TODO: Generate this section +uniform sampler2D iChannel0; +uniform samplerCube iChannel0; +uniform sampler3D iChannel0; +uniform sampler2D iChannel0; + +uniform struct { + sampler2D sampler; + samplerCube sampler; + sampler3D sampler; + vec3 size; + float time; + int loaded; +} iCh0; +*/ + +out vec4 fragColor; + +in vec2 TexCoords; + +void mainImage(out vec4, in vec2); + +void main(void) { + mainImage(fragColor, gl_FragCoord.xy); +} diff --git a/glsl/new.f.glsl b/glsl/new.f.glsl new file mode 100644 index 0000000..2691092 --- /dev/null +++ b/glsl/new.f.glsl @@ -0,0 +1,15 @@ +/* vim: set ft=c: */ + +/* */ + +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + // Normalized pixel coordinates (from 0 to 1) + // vec2 uv = TexCoords; // fragCoord / iResolution.xy; + vec2 uv = fragCoord / iResolution.xy; + + // Time varying pixel color + vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4)); + + // Output to screen + fragColor = vec4(col, 1.0); +} diff --git a/include/Client.h b/include/Client.h new file mode 100644 index 0000000..31a1ff1 --- /dev/null +++ b/include/Client.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include + +class Client +{ + std::string mHost; + Uint16 mPort; + int mChannel; + std::vector UDPsocks; + public: + Client (void) + { + } + Client (const char* host, const Uint16 port, const int channel); + UDPsocket UDP_Open (const char* host, const Uint16 port, const int channel); + void UDP_CloseAll (void); +}; diff --git a/include/FileIO.h b/include/FileIO.h new file mode 100644 index 0000000..6b60b96 --- /dev/null +++ b/include/FileIO.h @@ -0,0 +1,43 @@ +#pragma once + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 /* off_t */ +#endif + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif + +#include +#include + +class FileIO +{ + FILE* fp; + std::string path; + public: + ~FileIO (void) + { + if (fp) + { + Close (); + fp = NULL; + } + } + FileIO (const std::string&, const std::string&); + FileIO (FILE* f) + { + fp = f; + } + FILE* Open (const std::string&, const std::string&); + int Close (void); + int Seek (const off_t, const int); + off_t Tell (void); + size_t Read(void*, const size_t); + size_t Write (const void*, const size_t); + void Rewind (void); + /* I added these. */ + time_t Mtime (void); + off_t Size (void); + std::string ReadToString (void); +}; diff --git a/include/SDL_clamp.h b/include/SDL_clamp.h new file mode 100644 index 0000000..eaada8a --- /dev/null +++ b/include/SDL_clamp.h @@ -0,0 +1,6 @@ +#pragma once + +/* + * + */ +#define SDL_clamp(x, a, b) ((x) < (a)) ? (a) : (((x) > (b)) ? (b) : (x)) diff --git a/include/Server.h b/include/Server.h new file mode 100644 index 0000000..bc7d05e --- /dev/null +++ b/include/Server.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include +#include + +/* MTU */ +#define MAX_PACKET_SIZE (1024*1024) + +class Server +{ + bool mProcessPacketCallbackRunning; + SDL_TimerID mProcessPacketCallbackTimerID; + static Uint32 ProcessPacketCallback (Uint32, void*); + public: + std::vector UDPsocks; + SDLNet_SocketSet SocketSet; + UDPpacket **UDPPacketV; + Server (void) + { + mProcessPacketCallbackRunning = false; + UDPsocks.clear (); + SocketSet = NULL; + UDPPacketV = NULL; + } + Server (const char* host, const Uint16 port); + UDPsocket UDP_Open (Uint16 port); + void UDP_CloseAll (void); + void ProcessPacket (UDPpacket* packet); + void Start (void); + void Stop (void); + bool ProcessPacketCallbackRunning (void) + { + return mProcessPacketCallbackRunning; + } +}; diff --git a/include/ToggleFullscreen.h b/include/ToggleFullscreen.h new file mode 100644 index 0000000..e69de29 diff --git a/include/UDP_Write.h b/include/UDP_Write.h new file mode 100644 index 0000000..ed63385 --- /dev/null +++ b/include/UDP_Write.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +int Buffer_Write_UDP (UDPsocket udpsock, const int channel, const size_t mtu, const void* buf, const size_t size); +int File_Write_UDP (UDPsocket udpsock, const int channel, const size_t mtu, const char* path); +int SendFile_UDP (const char* host, const Uint16 port, const int channel, const size_t mtu, const char* path); +int SendBuffer_UDP (const char* host, const Uint16 port, const int channel, const size_t mtu, const void* buf, const size_t size); diff --git a/include/camera.h b/include/camera.h new file mode 100644 index 0000000..b41c2e3 --- /dev/null +++ b/include/camera.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include + +/** + * Default camera values + */ +#define DEFAULT_CAMERA_YAW (-90.0f) +#define DEFAULT_CAMERA_PITCH (0.0f) +#define DEFAULT_CAMERA_ROLL (0.0f) +#define DEFAULT_CAMERA_SPEED (1.0f) +#define DEFAULT_CAMERA_SENSITIVITY (1.0f) +#define DEFAULT_CAMERA_JOYSENSITIVITY (1.0f) +#define DEFAULT_CAMERA_ZOOM (45.0f) + +/* + * Decelerate at gravity speed (9.8 m/s) / ??? + */ +#define DEFAULT_CAMERA_DECELERATIONSPEED (0.98f) +#define DEFAULT_CAMERA_MOUSEDECELERATIONSPEED (0.098f) + +class Camera +{ + /** + * An abstract camera class that processes input and calculates the corresponding + */ + glm::vec3 Position; + glm::vec3 Front; + glm::vec3 Right; + glm::vec3 Up; + glm::vec3 WorldUp; + + /** Euler Angles, Vectors and Matrices for use in OpenGL. */ + glm::vec3 Angles; + + /* XXX Maybe we shouldn't let the camera move? */ + glm::vec3 Direction; /* Position movement */ + glm::vec2 ViewDirection; /* Yaw, Pitch movement */ + + void updateCameraVectors (void); + + public: + /** + * Defines several possible options for camera movement. + * Used as abstraction to stay away from window-system specific input methods + */ + enum Movement + { + FORWARD, BACKWARD, LEFT, RIGHT, UP, DOWN, CW, CCW, NONE + }; + + /** Camera options */ + float MovementSpeed, MouseSensitivity, Zoom; + float DecelerationSpeed, ViewDecelerationSpeed, JoystickSensitivity; + float zNear, zFar; + + Camera ( + const glm::vec3 position = glm::vec3(0.0f, 0.0f, 1.0f) + , const glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f) + , const float yaw = DEFAULT_CAMERA_YAW + , const float pitch = DEFAULT_CAMERA_PITCH + , const float roll = DEFAULT_CAMERA_ROLL); + + glm::mat4 GetViewMatrix (const bool constrainPitch = true); + void ProcessJoystickMovement (const float xoffset, const float yoffset, const float deltaTime); + void ProcessKeyboard (const Camera::Movement direction, const float deltaTime); + void ProcessMouseMovement (const float xoffset, const float yoffset, const float deltaTime); + void ProcessMouseScroll (const float xoffset, const float yoffset, const float deltaTime); +}; diff --git a/include/cube.h b/include/cube.h new file mode 100644 index 0000000..69f4bc5 --- /dev/null +++ b/include/cube.h @@ -0,0 +1,12 @@ +#include "object.h" + +class Cube : Object +{ + public: + Cube (void) + { + } + Cube (const GLsizei); + + void Draw (void); +}; diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..4543ceb --- /dev/null +++ b/include/debug.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef DEBUG +#include +#include +#define DEBUG_LOG(...) debug_log (__FILE__, __LINE__, __VA_ARGS__) +static void debug_log (const char* filename, const int lineno, const char* fmt, ...) +{ + char buf[8192]; + va_list ap; + SDL_memset (buf, 0, sizeof buf); + + va_start (ap, fmt); + vsnprintf (buf, (sizeof buf) - 1, fmt, ap); + va_end (ap); + + SDL_LogMessage (SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG, "%s:%d: %s", filename, lineno, buf); +} + + +#else +#define DEBUG_LOG(...) +#endif diff --git a/include/events_common.h b/include/events_common.h new file mode 100644 index 0000000..74db800 --- /dev/null +++ b/include/events_common.h @@ -0,0 +1,5 @@ +#pragma once + +enum { + PACKET_SDL_EVENT = SDL_USEREVENT, +}; diff --git a/include/glCheckErrors.h b/include/glCheckErrors.h new file mode 100644 index 0000000..2a558db --- /dev/null +++ b/include/glCheckErrors.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#define glCheckErrors() \ + do \ + { \ + GLenum err; \ + while ((err = glGetError()) != GL_NO_ERROR) \ + { \ + fprintf (stderr, "%s:%d: %s\n", __FILE__, __LINE__, glewGetErrorString(err)); \ + } \ + } while (0) diff --git a/include/lib_GL_common.h b/include/lib_GL_common.h new file mode 100644 index 0000000..ac9f6c9 --- /dev/null +++ b/include/lib_GL_common.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include + +#include + +void common_GL_Init (SDL_Window*, SDL_GLContext*, int); + +void common_GL_TexParameters (const GLenum, const GLenum, const GLenum, const GLenum, const GLenum); +GLuint common_GL_SurfaceFormat (SDL_Surface*); +GLuint common_GL_LoadTextureSurface (SDL_Surface*); +GLuint common_GL_LoadTexturePath (const std::string&); diff --git a/include/lib_SDL_common.h b/include/lib_SDL_common.h new file mode 100644 index 0000000..67ff276 --- /dev/null +++ b/include/lib_SDL_common.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include + +void common_SDL_Init (void); +void common_SDL_Quit (void); + +void common_SDL_CreateWindow (SDL_Window**); + +int common_SDL_ToggleFullscreen (SDL_Window*); + +SDL_Surface* common_SDL_LoadSurfacePath (const std::string&); diff --git a/include/lib_lua_common.h b/include/lib_lua_common.h new file mode 100644 index 0000000..f8deafc --- /dev/null +++ b/include/lib_lua_common.h @@ -0,0 +1,17 @@ +#pragma once + +#include /* size_t */ + +#ifdef __cplusplus +extern "C" +{ +#endif +#include +#include +#include +#ifdef __cplusplus +}; +#endif + +void common_lua_stack_dump (lua_State*); +int common_lua_run (lua_State*, const char*, const char*, const size_t); diff --git a/include/object.h b/include/object.h new file mode 100644 index 0000000..ff8d6f9 --- /dev/null +++ b/include/object.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Object +{ + protected: + GLsizei mGLsizei; + GLuint VBO, EBO; + + public: + GLuint VAO; + + Object (void) : mGLsizei (0) + { + } + Object (const GLsizei); + ~Object (void); +}; diff --git a/include/quad.h b/include/quad.h new file mode 100644 index 0000000..0ef64ca --- /dev/null +++ b/include/quad.h @@ -0,0 +1,12 @@ +#include "object.h" + +class Quad : Object +{ + public: + Quad (void) + { + } + Quad (const GLsizei); + + void Draw (void); +}; diff --git a/include/shader.h b/include/shader.h new file mode 100644 index 0000000..37127f5 --- /dev/null +++ b/include/shader.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +class ShaderProgram +{ + bool mUniformLocationsNeedRefresh; + std::map UniformLocationCache; + static GLint my_checkCompileSuccess (const GLuint id, const GLenum type); + public: + GLuint ID; + ShaderProgram (void); + ShaderProgram (const std::string&, const std::string&, const std::string&); + GLint getCachedLoc (const std::string&); + void RefreshUniformLocations (void); + void Use (void); + void Bool (const std::string&, const bool&value); + void Float (const std::string&, const float&value); + void Int (const std::string&, const int&value); + void Vec2 (const std::string&, const glm::vec2& value); + void Vec3 (const std::string&, const glm::vec3& value); + void Vec4 (const std::string&, const glm::vec4& value); + void Mat2 (const std::string&, const glm::mat2& value); + void Mat3 (const std::string&, const glm::mat3& value); + void Mat4 (const std::string&, const glm::mat4& value); + + GLuint CompileShaderBuffer (const std::string&, const GLenum type); + GLuint CompileShaderPath (const std::string&, const GLenum type); + GLint LinkProgram (void); +}; diff --git a/include/signal_common.h b/include/signal_common.h new file mode 100644 index 0000000..f77aeee --- /dev/null +++ b/include/signal_common.h @@ -0,0 +1,3 @@ +#pragma once + +void common_Signal_Init (void); diff --git a/include/trim.h b/include/trim.h new file mode 100644 index 0000000..d6849cc --- /dev/null +++ b/include/trim.h @@ -0,0 +1,36 @@ +#pragma once + +/* https://stackoverflow.com/a/217605 */ + +#include +#include +#include +#include + +// trim from start +static inline +std::string& +ltrim (std::string& s) +{ + s.erase (s.begin (), std::find_if (s.begin (), s.end (), std::not1 (std::ptr_fun(std::isspace)))); + return s; +} + + +// trim from end +static inline +std::string& +rtrim (std::string& s) +{ + s.erase (std::find_if (s.rbegin (), s.rend (), std::not1 (std::ptr_fun (std::isspace))).base (), s.end ()); + return s; +} + + +// trim from both ends +static inline +std::string& +trim (std::string& s) +{ + return ltrim (rtrim (s)); +} diff --git a/include/unused.h b/include/unused.h new file mode 100644 index 0000000..2b1c345 --- /dev/null +++ b/include/unused.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef UNUSED +#undef UNUSED +#endif + +#define UNUSED(x) ((void)(x)) diff --git a/src/Client.cpp b/src/Client.cpp new file mode 100644 index 0000000..45c3ebb --- /dev/null +++ b/src/Client.cpp @@ -0,0 +1,53 @@ +#include "debug.h" +#include "Client.h" + +Client::Client (const char* host, const Uint16 port, const int channel) +{ + UDPsocket udpsock; + if (NULL != (udpsock = UDP_Open (host, SDL_SwapBE16 (port), channel))) + { + mHost = host; + mPort = port; + mChannel = channel; + UDPsocks.push_back (udpsock); + } +} + + +UDPsocket +Client::UDP_Open (const char* host, const Uint16 port, const int channel) +{ + IPaddress address; + UDPsocket udpsock; + if (!(udpsock = SDLNet_UDP_Open (0))) + { + DEBUG_LOG ("SDLNet_UDP_Open: %s", SDLNet_GetError ()); + return NULL; + } + if (SDLNet_ResolveHost (&address, host, port) < 0) + { + DEBUG_LOG ("SDLNet_ResolveHost: %s", SDLNet_GetError ()); + return NULL; + } + if (SDLNet_UDP_Bind (udpsock, channel, &address) < 0) + { + DEBUG_LOG ("SDLNet_UDP_Bind: %s", SDLNet_GetError ()); + return NULL; + } + return udpsock; +} + + +void +Client::UDP_CloseAll (void) +{ + size_t i; + const size_t nclients = UDPsocks.size (); + for (i = 0; i < nclients; i++) + { + SDLNet_UDP_Unbind (UDPsocks[i], mChannel); + SDLNet_UDP_Close (UDPsocks[i]); + UDPsocks[i] = NULL; + } + UDPsocks.clear (); +} diff --git a/src/FileIO.cpp b/src/FileIO.cpp new file mode 100644 index 0000000..00719cc --- /dev/null +++ b/src/FileIO.cpp @@ -0,0 +1,100 @@ +#include "FileIO.h" + +#include + +#include /* errno */ +#include /* FILE* */ +#include /* stat */ +#include /* stat */ +#include /* stat */ + +FileIO::FileIO (const std::string& filename, const std::string& mode) +{ + if (!(fp = Open (filename, mode))) + throw errno; + path = filename; +} + + +FILE* +FileIO::Open (const std::string& filename, const std::string& mode) +{ + return fopen (filename.c_str (), mode.c_str ()); +} + + +int +FileIO::Close (void) +{ + return fclose (fp); +} + + +int +FileIO::Seek (const off_t offset, const int whence) +{ + return fseeko (fp, offset, whence); +} + + +off_t +FileIO::Tell (void) +{ + return ftello (fp); +} + + +size_t +FileIO::Read (void *dst, const size_t size) +{ + return fread (dst, sizeof (char), size, fp); +} + + +size_t +FileIO::Write (const void *dst, const size_t size) +{ + return fwrite (dst, sizeof (char), size, fp); +} + + +void +FileIO::Rewind (void) +{ + rewind (fp); +} + + +time_t +FileIO::Mtime (void) +{ + time_t rc; + struct stat sb; + rc = 0; + if (!stat (path.c_str (),&sb)) + rc = sb.st_mtime; + else + throw errno; + return rc; +} + + +off_t +FileIO::Size (void) +{ + off_t rc; + Seek (0, SEEK_END); + rc = Tell (); + Rewind (); + return rc; +} + + +std::string +FileIO::ReadToString (void) +{ + off_t size = Size () + 1; + std::vector b (size); + off_t nread = Read (b.data (), size); + return std::string ((const char *) b.data (), nread); +} diff --git a/src/SDL_ReadWrite64.cpp b/src/SDL_ReadWrite64.cpp new file mode 100644 index 0000000..595bfc2 --- /dev/null +++ b/src/SDL_ReadWrite64.cpp @@ -0,0 +1,34 @@ +#include +#include + +/* Write a 64-bit value to network packet buffer */ +#define SDLNet_Write64(value, areap) _SDLNet_Write64 (value, areap) + +/* Read a 64-bit value from network packet buffer */ +#define SDLNet_Read64(areap) _SDLNet_Read64 (areap) + +SDL_FORCE_INLINE void +_SDLNet_Write64 (Uint64 value, void* areap) +{ + Uint8* area = (Uint8*) areap; + area[0] = (value >> 56) & 0xFF; + area[1] = (value >> 48) & 0xFF; + area[2] = (value >> 40) & 0xFF; + area[3] = (value >> 32) & 0xFF; + area[4] = (value >> 24) & 0xFF; + area[5] = (value >> 16) & 0xFF; + area[6] = (value >> 8) & 0xFF; + area[7] = value & 0xFF; +} + + +SDL_FORCE_INLINE Uint64 +_SDLNet_Read64 (const void* areap) +{ + const Uint8* area = (const Uint8*) areap; + return ( + ((Uint64) area[0]) << 56) | (((Uint64) area[1]) << 48) | + (((Uint64) area[2]) << 40) | (((Uint64) area[3]) << 32) | + (((Uint64) area[4]) << 24) | (((Uint64) area[5]) << 16) | + (((Uint64) area[6]) << 8) | (((Uint64) area[7])); +} diff --git a/src/Server.cpp b/src/Server.cpp new file mode 100644 index 0000000..3f3285e --- /dev/null +++ b/src/Server.cpp @@ -0,0 +1,179 @@ +#include "debug.h" +#include "Server.h" +#include "events_common.h" + +#include /* inet_ntop */ + +Server::Server (const char *host, const Uint16 port) +{ + IPaddress ipaddress; + UDPsocket udpsock; + char hoststring[128]; + if (SDLNet_ResolveHost (&ipaddress, host, SDL_SwapBE16 (port)) < 0) + { + DEBUG_LOG ("SDLNet_ResolveHost: %s", SDLNet_GetError ()); + return; + } + if (NULL != (udpsock = UDP_Open (ipaddress.port))) + { + UDPsocks.push_back (udpsock); + mProcessPacketCallbackRunning = false; + inet_ntop (AF_INET, &ipaddress.host, hoststring, 128); + SDL_Log ("Listening on %s:%d", hoststring, ipaddress.port); + } + else + { + SDL_Log ("Unable to bind to port %d; is it in use?", ipaddress.port); + } +} + + +void +Server::UDP_CloseAll (void) +{ + size_t i, nclients; + nclients = UDPsocks.size (); + SDL_Log ("Server::~Server %ld", nclients); + for (i = 0; i < nclients; i++) + { + SDLNet_UDP_Close (UDPsocks[i]); + UDPsocks[i] = NULL; + } + UDPsocks.clear (); +} + + +/* + * Open and keep track of a UDP socket. + */ +UDPsocket +Server::UDP_Open (const Uint16 port) +{ + UDPsocket udpsock; + if (!(udpsock = SDLNet_UDP_Open (port))) + { + DEBUG_LOG ("SDLNet_UDP_Open: %s", SDLNet_GetError ()); + return NULL; + } + return udpsock; +} + + +void +Server::ProcessPacket (UDPpacket * packet) +{ + size_t len; + SDL_Event event; + SDL_UserEvent userevent; + UDPpacket *temp; + len = packet->len; + temp = SDLNet_AllocPacket (len); + SDL_memcpy (&temp->address, &packet->address, sizeof (IPaddress)); + SDL_memcpy (temp->data, packet->data, len); + temp->len = len; + SDL_zero (event); + SDL_zero (userevent); + userevent.type = PACKET_SDL_EVENT; + userevent.code = 0; + userevent.data1 = (void *) temp; + userevent.data2 = (void *) NULL; + event.type = SDL_USEREVENT; + event.user = userevent; + SDL_PushEvent (&event); +} + + +void +Server::Start (void) +{ + size_t i; + int nclients, numused; + nclients = (int) UDPsocks.size (); + SDL_Log ("Server %d clients", nclients); + if (!(UDPPacketV = SDLNet_AllocPacketV (nclients, MAX_PACKET_SIZE))) + { + DEBUG_LOG ("SDLNet_AllocPacketV: %s", SDLNet_GetError ()); + return; + } + if (!(SocketSet = SDLNet_AllocSocketSet (nclients))) + { + DEBUG_LOG ("SDLNet_AllocSocketSet: %s", SDLNet_GetError ()); + return; + } + for (i = 0; i < (size_t) nclients; i++) + { + numused = SDLNet_UDP_AddSocket (SocketSet, UDPsocks[i]); + if (numused < 0) + { + DEBUG_LOG ("SDLNet_AddSocket: %s", SDLNet_GetError ()); + return; + } + } + mProcessPacketCallbackRunning = true; + mProcessPacketCallbackTimerID = SDL_AddTimer (10, Server::ProcessPacketCallback, (void *) this); +} + + +void +Server::Stop (void) +{ + size_t i, nclients; + mProcessPacketCallbackRunning = false; + SDL_RemoveTimer (mProcessPacketCallbackTimerID); + + nclients = UDPsocks.size (); + for (i = 0; i < nclients; i++) + { + SDLNet_UDP_DelSocket (SocketSet, UDPsocks[i]); + } + SDLNet_FreeSocketSet (SocketSet); + SocketSet = NULL; + + SDLNet_FreePacketV (UDPPacketV); + UDPPacketV = NULL; +} + + +Uint32 +Server::ProcessPacketCallback (Uint32 interval, void *param) +{ + int numrecv, numready; + size_t i, j; + + SDL_assert (NULL != param); + Server & server = *(Server *) param; + + if (!server.ProcessPacketCallbackRunning ()) + return 0; + + numready = SDLNet_CheckSockets (server.SocketSet, ~0); + if (numready < 0) + { + DEBUG_LOG ("SDLNet_CheckSockets: %s", SDLNet_GetError ()); + return 0; + } + else if (numready > 0) + { + DEBUG_LOG ("There are %d sockets with activity!", numready); + } + + /* check all sockets with SDLNet_SocketReady and handle the active ones. */ + for (i = 0; i < (size_t) numready; i++) + { + if (SDLNet_SocketReady (server.UDPsocks[i])) + { + numrecv = SDLNet_UDP_RecvV (server.UDPsocks[i], &server.UDPPacketV[i]); + if (numrecv < 0) + { + DEBUG_LOG ("SDLNet_UDP_RecvV: %s", SDLNet_GetError ()); + break; + } + for (j = 0; j < (size_t) numrecv; j++) + { + server.ProcessPacket (server.UDPPacketV[i]); + } + } + } + + return interval; +} diff --git a/src/UDP_Write.cpp b/src/UDP_Write.cpp new file mode 100644 index 0000000..dde6716 --- /dev/null +++ b/src/UDP_Write.cpp @@ -0,0 +1,123 @@ +#include "debug.h" +#include "FileIO.h" +#include "Client.h" + +#include + +#include +#include +#include + +#include +#include + +/* XXX gross */ +int +Buffer_Write_UDP (UDPsocket udpsock, const int channel, const size_t mtu, const void *buf, const size_t size) +{ + const char *bufPtr; + const char *bufPtrEnd; + int numsent, npackets; + IPaddress *address; + size_t i, our_nsent, our_mtu; + UDPpacket **packetV; + SDL_assert (mtu > 0); + SDL_assert (size > 0); + numsent = 0; + our_mtu = mtu; + if (size < our_mtu) + our_mtu = size; + npackets = ceil ((double) size / (double) our_mtu); + if (!npackets) + { + DEBUG_LOG ("File_Write_UDP: zero length packet\n"); + return 0; + } + packetV = SDLNet_AllocPacketV (npackets, our_mtu); + if (!packetV) + { + DEBUG_LOG ("SDLNet_AllocPacketV: %s\n", SDLNet_GetError ()); + return 0; + } + i = 0; + bufPtr = (const char *) buf; + bufPtrEnd = bufPtr + size; + address = SDLNet_UDP_GetPeerAddress (udpsock, channel); + size_t nsent = size; + while (i < (size_t) npackets && bufPtr < bufPtrEnd) + { + our_nsent = nsent; + if (our_nsent > our_mtu) + our_nsent = our_mtu; + packetV[i]->channel = channel; + SDL_memcpy (&packetV[i]->address, address, sizeof (IPaddress)); + SDL_memset (packetV[i]->data, 0, our_mtu); + SDL_memcpy (packetV[i]->data, bufPtr, our_nsent); + packetV[i]->len = our_nsent; + bufPtr += our_mtu; + nsent -= our_mtu; + i++; + } + numsent = SDLNet_UDP_SendV (udpsock, packetV, npackets); + if (!numsent) + { + SDL_Log ("SDLNet_UDP_SendV (%d): %s\n", npackets, SDLNet_GetError ()); + } + SDLNet_FreePacketV (packetV); + return numsent; +} + + +int +File_Write_UDP (UDPsocket udpsock, const int channel, const char *path, const size_t mtu) +{ + int numsent; + Sint64 size; + numsent = 0; + try + { + FileIO f (path, "r"); + size = f.Size (); + std::vector b (size + 1); + f.Read (b.data (), size); + numsent = Buffer_Write_UDP (udpsock, channel, mtu, b.data (), b.size ()); + } + catch (int err) + { + SDL_Log ("File_Write_UDP: %s", strerror (errno)); + } + + return numsent; +} + + +int +SendBuffer_UDP (const char *host, const Uint16 port, const int channel, const size_t mtu, const void *buf, const size_t size) +{ + int numsent; + UDPsocket udpsock; + numsent = 0; + Client client (host, port, channel); + if (udpsock) + { + numsent = Buffer_Write_UDP (udpsock, channel, mtu, buf, size); + client.UDP_CloseAll (); + } + return numsent; +} + + +int +SendFile_UDP (const char *host, const Uint16 port, const int channel, const size_t mtu, const char *path) +{ + int numsent; + UDPsocket udpsock; + numsent = 0; + Client client (host, port, channel); + if (udpsock) + { + numsent = File_Write_UDP (udpsock, channel, path, mtu); + client.UDP_CloseAll (); + } + return numsent; +} diff --git a/src/camera.cpp b/src/camera.cpp new file mode 100644 index 0000000..65ad0a4 --- /dev/null +++ b/src/camera.cpp @@ -0,0 +1,160 @@ +#include "camera.h" +#include "SDL_clamp.h" + +#include +#include +#include +#include + +/* https://graphicscompendium.com/opengl/22-interpolation */ +static inline +glm::vec2 +lerp (const glm::vec2 x, const glm::vec2 y, const float t) +{ + return x * (1.f - t) + y * t; +} + + +static inline +glm::vec3 +lerp (const glm::vec3 x, const glm::vec3 y, const float t) +{ + return x * (1.f - t) + y * t; +} + + +Camera::Camera (const glm::vec3 position, const glm::vec3 up, const float yaw, const float pitch, const float roll) +{ + Front = glm::vec3 (0.0f, 0.0f, -1.0f); + WorldUp = up; + MovementSpeed = DEFAULT_CAMERA_SPEED; + MouseSensitivity = DEFAULT_CAMERA_SENSITIVITY; + Zoom = DEFAULT_CAMERA_ZOOM; + DecelerationSpeed = DEFAULT_CAMERA_DECELERATIONSPEED; + ViewDecelerationSpeed = DEFAULT_CAMERA_MOUSEDECELERATIONSPEED; + JoystickSensitivity = DEFAULT_CAMERA_JOYSENSITIVITY; + Position = position; + Angles = glm::vec3 (yaw, pitch, roll); + Direction = glm::vec3 (0); + ViewDirection = glm::vec2 (0); + zNear = 0.1f; + zFar = 1000.0f; + updateCameraVectors (); +} + + +/** + * Calculates the front vector from the Camera's (updated) Euler Angles + */ +void +Camera::updateCameraVectors (void) +{ + const glm::mat4 roll_mat = + glm::rotate (glm::mat4 (1.0f), glm::radians (Angles.z), Front); + /** + * Calculate the new Front vector + */ + Front = glm::normalize (glm::vec3 + ( cos (glm::radians (Angles.x)) * cos (glm::radians (Angles.y)) + , sin (glm::radians (Angles.y)) + , sin (glm::radians (Angles.x)) * cos (glm::radians (Angles.y)))); + // std::cout << glm::to_string (roll_mat) << std::endl; + /** + * Also re-calculate the Right and Up vector + * normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement. + */ + Right = glm::normalize (glm::cross (Front, WorldUp)); + Up = glm::mat3 (roll_mat) * glm::normalize (glm::cross (Right, Front)); +} + + +/** + * Returns the view matrix calculated using Euler Angles and the LookAt Matrix + */ +glm::mat4 Camera::GetViewMatrix (const bool constrainPitch) +{ + /* + * XXX Where to handle acceleration? + * + * Seems like since we're probably calling this every frame, this is the place to do it! + */ + Position += Direction; + Angles.x = fmod (Angles.x + ViewDirection.x, 360.0f); + Angles.y += ViewDirection.y; + if (constrainPitch) + { + Angles.y = SDL_clamp (Angles.y, -89.9f, 89.9f); + } + Zoom = SDL_clamp (Zoom, 1.0f, 45.0f); + /** + * Update Front, Right and Up Vectors using the updated Euler angles + */ + updateCameraVectors (); + Direction = lerp (Direction, glm::vec3 (0.0f), DecelerationSpeed); + ViewDirection = + lerp (ViewDirection, glm::vec2 (0.0f), ViewDecelerationSpeed); + // std::cout << glm::to_string (Position) << std::endl; + return glm::lookAt (Position, Position + Front, Up); +} + + +/** + * Processes input received from any keyboard-like input system. + * Accepts input parameter in the form of camera defined ENUM (to abstract it from windowing systems) + */ +void +Camera::ProcessKeyboard (Camera::Movement direction, const float deltaTime) +{ + const float velocity = MovementSpeed * deltaTime; + switch (direction) + { + case FORWARD: Direction += Front * velocity; break; + case BACKWARD: Direction -= Front * velocity; break; + case LEFT: Direction -= Right * velocity; break; + case RIGHT: Direction += Right * velocity; break; + case UP: Direction += Up * velocity; break; + case DOWN: Direction -= Up * velocity; break; + case CW: Angles.z += velocity; break; + case CCW: Angles.z -= velocity; break; + default: + break; + } +} + + +// processes input received from a mouse input system. Expects the offset value in both the x and y direction. +void +Camera::ProcessJoystickMovement (const float xoffset, const float yoffset, const float deltaTime) +{ + const float velocity = JoystickSensitivity * deltaTime; + const float Xoffset = xoffset * velocity; + const float Yoffset = yoffset * velocity; + ViewDirection.x += Xoffset; + ViewDirection.y += Yoffset; +} + + +// processes input received from a mouse input system. Expects the offset value in both the x and y direction. +void +Camera::ProcessMouseMovement (const float xoffset, const float yoffset, const float deltaTime) +{ + const float velocity = MouseSensitivity * deltaTime; + const float Xoffset = xoffset * velocity; + const float Yoffset = yoffset * velocity; + ViewDirection.x += Xoffset; + ViewDirection.y += Yoffset; +} + + +/** + * Processes input received from a mouse scroll-wheel event. + * Only requires input on the vertical wheel-axis. + */ +void +Camera::ProcessMouseScroll (const float xoffset, const float yoffset, const float deltaTime) +{ + (void) xoffset; + (void) deltaTime; + // Angles.z += (float) xoffset; + Zoom += (float) yoffset; +} diff --git a/src/client_main.cpp b/src/client_main.cpp new file mode 100644 index 0000000..a002ef4 --- /dev/null +++ b/src/client_main.cpp @@ -0,0 +1,417 @@ +#include "camera.h" +#include "Client.h" +#include "cube.h" +#include "debug.h" +#include "glCheckErrors.h" +#include "lib_GL_common.h" +#include "lib_SDL_common.h" +#include "quad.h" +#include "shader.h" +#include "signal_common.h" +#include "UDP_Write.h" +#include "unused.h" + +#include +#include + +#include /* getopt */ +#include /* exit */ +#include /* memset */ +#include /* getopt */ + +#include +#include + +#define Perf_Diff(a, b) ((double) 1000 * ((double)(((double) (b) - (double) (a)) / (double) SDL_GetPerformanceFrequency ()))) + +#define MIN_FRAME_MS 7 /* 144 Hz */ + +static int my_AnalogInput (void); +static int my_Input (SDL_Event *); +static int my_Input_Key (SDL_Event *); +static int my_SDL_WindowEvent (SDL_Event *); + +static void my_GetOpt (int, char **); +static void my_Init (int, char**); +static void my_Render (void); +static void my_UpdateValues (void); +static void my_Usage (const char *); + +struct Uniform_Values +{ + int iFrame; + float iFrameRate; + float iSampleRate; + float iTime; + float iTimeDelta; + glm::vec3 iResolution; + glm::vec4 iDate; + glm::vec4 iMouse; + std::vector iChannel; + std::vector iChannelTime; + std::vector iChannelResolution; +}; + +struct Client_State +{ + int quit; + + Uint64 start, end; + double diff; + + UDPsocket udpsock; + const char* host; + Uint16 port; + int channel; + int mtu; + + SDL_GLContext GLContext; + SDL_Window *Window; + int Window_Height; + int Window_Width; + + double ShaderTimeDividend; + bool ImGuiEnabled; + bool ShaderPlaying; + bool InputCaptureMouse; + bool InputFirstMouse; + bool InputRelativeMouseMode; + bool RenderShowQuad; +}; + +static std::shared_ptr cube; +static std::shared_ptr quad; + +static Camera camera; +static ShaderProgram shader; +static struct Client_State state; +static struct Uniform_Values values; + +int +main (int argc, char **argv) +{ + SDL_Event event; + glm::mat4 model, view, projection; + + my_Init (argc, argv); + + shader = ShaderProgram ("../handmade-x/glsl/camera.v.glsl", "", "./glsl/new.f.glsl"); + if (!shader.ID) + { + SDL_Log ("Unable to load shader."); + goto _out; + } + + camera = Camera (); + cube = std::make_shared (1); + quad = std::make_shared (1); + + state.mtu = 1450; + state.quit = 0; + if (NULL == (state.udpsock = Client::UDP_Open (state.host, state.port, state.channel))) + { + SDL_Log ("Unable to open client port"); + goto _out; + } + + while (!state.quit) + { + state.end = SDL_GetPerformanceCounter (); + state.diff = Perf_Diff (state.start, state.end); + while (state.diff < MIN_FRAME_MS) + { + SDL_Delay (1); + state.end = SDL_GetPerformanceCounter (); + state.diff = Perf_Diff (state.start, state.end); + } + state.start = SDL_GetPerformanceCounter (); + + SDL_SetRelativeMouseMode ((SDL_bool) state.InputRelativeMouseMode); + SDL_CaptureMouse ((SDL_bool) state.InputCaptureMouse); + + while (!state.quit && SDL_PollEvent (&event)) + { + state.quit = my_Input (&event); + } + my_AnalogInput (); + + my_UpdateValues (); + + projection = glm::perspective (glm::radians (camera.Zoom), (float) values.iResolution.x / (float) values.iResolution.y, camera.zNear, camera.zFar); + view = camera.GetViewMatrix (); + model = glm::mat4 (1.0f); + + shader.Use (); + shader.Int ("iFrame", values.iFrame); + shader.Float ("iTime", values.iTime); + shader.Float ("iTimeDelta", values.iTimeDelta); + shader.Vec3 ("iResolution", values.iResolution); + + shader.Mat4 ("Projection", projection); + shader.Mat4 ("View", view); + shader.Mat4 ("Model", model); + + my_Render (); + SDL_GL_SwapWindow (state.Window); + } + +_out: + client.UDP_CloseAll (); + SDL_GL_DeleteContext (state.GLContext); + SDL_DestroyWindow (state.Window); + common_SDL_Quit (); + + return 0; +} + + +static void +my_Init (int argc, char** argv) +{ + common_Signal_Init (); + my_GetOpt (argc, argv); + + state.start = state.end = SDL_GetPerformanceCounter (); + + common_SDL_Init (); + common_SDL_CreateWindow (&state.Window); + common_GL_Init (state.Window, &state.GLContext, 1); + SDL_GetWindowSize (state.Window, &state.Window_Width, &state.Window_Height); + + state.ShaderPlaying = true; + state.ShaderTimeDividend = 1000; + state.InputRelativeMouseMode = false; + state.InputCaptureMouse = false; +} + + +static void +my_UpdateValues (void) +{ + values.iFrame++; + if (state.ShaderPlaying) + { + values.iTime += ((double) state.diff / (double) state.ShaderTimeDividend); + } + values.iTimeDelta = ((double) state.diff / (double) 1000); + values.iResolution.x = state.Window_Width; + values.iResolution.y = state.Window_Height; + values.iResolution.z = 1.0; +} + + +static void +my_Render (void) +{ + glClearColor (0, 0, 0, 1); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (state.RenderShowQuad) + { + // glEnable (GL_DEPTH_TEST); + cube->Draw (); + } + else + quad->Draw (); +} + + +static int +my_SDL_MouseMotion (SDL_Event * event) +{ + SDL_assert (NULL != event); + const float + xpos = event->motion.x; + const float + ypos = event->motion.y; + + if (!state.InputCaptureMouse || state.ImGuiEnabled) + return SDL_FALSE; + + if (state.InputFirstMouse) + { + values.iMouse.x = xpos; + values.iMouse.y = ypos; + state.InputFirstMouse = false; + } + + // reversed since y-coordinates go from bottom to top + const float + Xoffset = xpos - values.iMouse.x; + const float + Yoffset = values.iMouse.y - ypos; + + values.iMouse.x = xpos; + values.iMouse.y = ypos; + + camera.ProcessMouseMovement (Xoffset, Yoffset, 0.1); + + return SDL_FALSE; +} + + +static int +my_SDL_MouseButton (SDL_Event * event) +{ + int down; + int quit; + SDL_assert (NULL != event); + down = event->type == SDL_MOUSEBUTTONDOWN; + quit = SDL_FALSE; + return quit; +} + + +static int +my_Input (SDL_Event * event) +{ + int quit; + SDL_assert (NULL != event); + quit = SDL_FALSE; + switch (event->type) + { + case SDL_QUIT: + quit = SDL_TRUE; + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + quit = my_Input_Key (event); + break; + case SDL_MOUSEMOTION: + my_SDL_MouseMotion (event); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + my_SDL_MouseButton (event); + break; + case SDL_WINDOWEVENT: + my_SDL_WindowEvent (event); + break; + default: + break; + } + return quit; +} + + +static int +my_AnalogInput (void) +{ + const Uint8 * + kbd = SDL_GetKeyboardState (NULL); + const float + dt = 0.016; + (void) state; + + camera.ProcessKeyboard (kbd[SDL_SCANCODE_W] ? Camera::Movement::FORWARD : Camera::Movement::NONE, dt); + camera.ProcessKeyboard (kbd[SDL_SCANCODE_S] ? Camera::Movement::BACKWARD : Camera::Movement::NONE, dt); + camera.ProcessKeyboard (kbd[SDL_SCANCODE_A] ? Camera::Movement::LEFT : Camera::Movement::NONE, dt); + camera.ProcessKeyboard (kbd[SDL_SCANCODE_D] ? Camera::Movement::RIGHT : Camera::Movement::NONE, dt); + + camera.ProcessKeyboard (kbd[SDL_SCANCODE_LSHIFT] ? Camera::Movement::DOWN : Camera::Movement::NONE, dt); + camera.ProcessKeyboard (kbd[SDL_SCANCODE_SPACE] ? Camera::Movement::UP : Camera::Movement::NONE, dt); + camera.ProcessKeyboard (kbd[SDL_SCANCODE_Q] ? Camera::Movement::CW : Camera::Movement::NONE, dt); + camera.ProcessKeyboard (kbd[SDL_SCANCODE_E] ? Camera::Movement::CCW : Camera::Movement::NONE, dt); + + return SDL_FALSE; +} + + +static int +my_Input_Key (SDL_Event * event) +{ + int down; + int quit; + SDL_assert (NULL != event); + down = event->type == SDL_KEYDOWN; + quit = SDL_FALSE; + switch (event->key.keysym.sym) + { + case SDLK_ESCAPE: + quit = SDL_TRUE; + break; + case SDLK_F11: + { + if (down) + { + state.InputCaptureMouse = true; + state.InputFirstMouse = true; + state.InputRelativeMouseMode = true; + common_SDL_ToggleFullscreen (SDL_GetWindowFromID (event->key.windowID)); + } + } + break; + case SDLK_r: + { + if (down) + camera = Camera (); + } + break; + case SDLK_c: + { + if (down) + state.RenderShowQuad = !state.RenderShowQuad; + } + break; + default: + { + Buffer_Write_UDP (state.udpsock, state.channel, state.mtu, event, sizeof (SDL_Event)); + } + } + return quit; +} + + +static int +my_SDL_WindowEvent (SDL_Event * event) +{ + int quit; + SDL_assert (NULL != event); + quit = SDL_FALSE; + switch (event->window.event) + { + case SDL_WINDOWEVENT_RESIZED: + { + state.Window_Width = event->window.data1; + state.Window_Height = event->window.data2; + glViewport (0, 0, (float) state.Window_Width, (float) state.Window_Height); + } + break; + default: + break; + } + return quit; +} + + +static void +my_Usage (const char *argv0) +{ + fprintf (stderr, "Usage: %s [-h] [-s host] [-p port]\n", argv0); +} + + +static void +my_GetOpt (int argc, char **argv) +{ + int opt; + state.channel = 1; + state.host = "localhost"; + state.port = 6666; + while ((opt = getopt (argc, argv, "hp:s:")) != -1) + { + switch (opt) + { + case 'p': + state.port = strtol (optarg, NULL, 0); + break; + case 's': + state.host = optarg; + break; + case 'h': + default: + my_Usage (argv[0]); + exit (EXIT_FAILURE); + break; + } + } +} diff --git a/src/cube.cpp b/src/cube.cpp new file mode 100644 index 0000000..6f006cc --- /dev/null +++ b/src/cube.cpp @@ -0,0 +1,73 @@ +#include "cube.h" + +Cube::Cube (const GLsizei size) +: Object (size) +{ + const float vertices[] = { + // positions // normals // texture coords + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, + + -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, + -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, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.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, 0.0f, 0.0f, 0.0f, 1.0f, + -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.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, 0.0f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.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, 0.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.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, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.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, -1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.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, 1.0f, 0.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.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, 1.0f, 0.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + + glBindVertexArray (VAO); + + glBindBuffer (GL_ARRAY_BUFFER, VBO); + glBufferData (GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW); + + glBindVertexArray (VAO); + glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); + glEnableVertexAttribArray (0); + + // glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); + // glEnableVertexAttribArray (1); + + glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); + glEnableVertexAttribArray (1); +} + + +void +Cube::Draw (void) +{ + glBindVertexArray (VAO); + glDrawArrays (GL_TRIANGLES, 0, 36); +} diff --git a/src/lib_GL_common.cpp b/src/lib_GL_common.cpp new file mode 100644 index 0000000..f99bbc7 --- /dev/null +++ b/src/lib_GL_common.cpp @@ -0,0 +1,104 @@ +#include "FileIO.h" +#include "lib_SDL_common.h" +#include "lib_GL_common.h" +#include "glCheckErrors.h" + +#include + +#include + +#include +#include + +void +common_GL_Init (SDL_Window * wind, SDL_GLContext * ctx, int SwapInterval) +{ + GLenum err; + SDL_assert (NULL != ctx); + *ctx = SDL_GL_CreateContext (wind); + SDL_assert (NULL != *ctx); + SDL_GL_MakeCurrent (wind, *ctx); + SDL_GL_SetSwapInterval (SwapInterval); + glewExperimental = true; + if ((err = glewInit ()) != GLEW_OK) + { + SDL_Log ("glewInit: %s", glewGetErrorString (err)); + exit (EXIT_FAILURE); + } + SDL_assert (err == GLEW_OK); + glCheckErrors (); +} + + +void +common_GL_TexParameters (const GLenum id, const GLenum wrap_s, const GLenum wrap_t, const GLenum min_filter, const GLenum mag_filter) +{ + /* all upcoming GL_TEXTURE_2D operations now have effect on this texture object */ + glBindTexture (GL_TEXTURE_2D, id); + /* set the texture wrapping parameters + * set texture wrapping to GL_REPEAT (default wrapping method) */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t); + /* set texture filtering parameters */ + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); +} + + +GLenum +common_GL_SurfaceFormat (SDL_Surface * surface) +{ + SDL_assert (NULL != surface); + switch (surface->format->BytesPerPixel) + { + case 1: return GL_RED; + case 3: return GL_RGB; + case 4: return GL_RGBA; + } + return GL_RED; +} + + +GLuint +common_GL_LoadTextureSurface (SDL_Surface* surface) +{ + GLenum format; + GLuint texture; + glGenTextures (1, &texture); + if (!texture) + return 0; + if (!SDL_LockSurface (surface)) + { + common_GL_TexParameters (texture, GL_REPEAT, GL_REPEAT, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + format = common_GL_SurfaceFormat (surface); + glBindTexture (GL_TEXTURE_2D, texture); + glTexImage2D (GL_TEXTURE_2D, 0, format, surface->w, surface->h, 0, format, GL_UNSIGNED_BYTE, surface->pixels); + glGenerateMipmap (GL_TEXTURE_2D); + SDL_UnlockSurface (surface); + } + else + { + SDL_Log ("SDL_LockSurface: %s", SDL_GetError ()); + texture = 0; + } + return texture; +} + + +GLuint +common_GL_LoadTexturePath (const char *path) +{ + GLuint id; + SDL_Surface *surface; + if (NULL != (surface = common_SDL_LoadSurfacePath (path))) + { + id = common_GL_LoadTextureSurface (surface); + SDL_FreeSurface (surface); + } + else + { + SDL_Log ("common_SDL_LoadSurfacePath: error"); + id = 0; + } + return id; +} diff --git a/src/lib_SDL_common.cpp b/src/lib_SDL_common.cpp new file mode 100644 index 0000000..b5e2b9b --- /dev/null +++ b/src/lib_SDL_common.cpp @@ -0,0 +1,146 @@ +#include "debug.h" +#include "unused.h" +#include "lib_SDL_common.h" + +#include /* exit */ + +#define DEFAULT_WINDOW_TITLE "Unicorn's Horn" +#define DEFAULT_WINDOW_WIDTH 800 +#define DEFAULT_WINDOW_HEIGHT 600 + +static void +my_SDLNet_Init (void) +{ + if (SDLNet_Init () == -1) + { + SDL_Log ("SDLNet_Init: %s", SDLNet_GetError ()); + exit (EXIT_FAILURE); + } +} + + +static void +my_IMG_Init (int flags) +{ + int initted; + initted = IMG_Init (flags); + if ((initted & flags) != flags) + { + SDL_Log ("IMG_Init: %s\n", IMG_GetError ()); + exit (EXIT_FAILURE); + } + SDL_assert ((initted & flags) == flags); +} + + +static void +my_TTF_Init (void) +{ + if (TTF_Init () == -1) + { + SDL_Log ("TTF_Init: %s", TTF_GetError ()); + exit (EXIT_FAILURE); + } + SDL_assert (1 == TTF_WasInit ()); +} + + +static void +my_Mix_Init (int flags) +{ + int initted; + initted = Mix_Init (flags); + if ((initted & flags) != flags) + { + SDL_Log ("Mix_Init: %s\n", Mix_GetError ()); + exit (EXIT_FAILURE); + } + SDL_assert ((initted & flags) == flags); +} + + +static SDL_AssertState +my_SDL_AssertionHandler(const SDL_AssertData* data, void* userdata) +{ + UNUSED (data); + UNUSED (userdata); + return SDL_ASSERTION_ABORT; +} + + +static void +my_SDL_Assert_Init (void) +{ + SDL_SetAssertionHandler(my_SDL_AssertionHandler, NULL); +} + + +void +common_SDL_Init (void) +{ + Uint32 Flags; + Flags = SDL_INIT_EVERYTHING; + if (SDL_Init (Flags) < 0) + { + SDL_Log ("SDL_Init: %s", SDL_GetError ()); + exit (EXIT_FAILURE); + } + my_SDLNet_Init (); + my_IMG_Init (IMG_INIT_JPG | IMG_INIT_PNG); + my_TTF_Init (); + my_Mix_Init (MIX_INIT_OGG); + SDL_LogSetPriority (SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); + my_SDL_Assert_Init (); +} + + +void +common_SDL_Quit (void) +{ + SDL_SetAssertionHandler (SDL_GetDefaultAssertionHandler (), NULL); + IMG_Quit (); + SDLNet_Quit (); + TTF_Quit (); + SDL_Quit (); +} + + +void +common_SDL_CreateWindow (SDL_Window ** wind) +{ + Uint32 Flags; + SDL_assert (NULL != wind); + Flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL; + *wind = SDL_CreateWindow (DEFAULT_WINDOW_TITLE, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT, Flags); + SDL_assert (NULL != *wind); +} + + +int +common_SDL_ToggleFullscreen (SDL_Window * window) +{ + Uint32 Flags = SDL_GetWindowFlags (window); + if (Flags & SDL_WINDOW_FULLSCREEN_DESKTOP) + { + Flags ^= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + else + { + Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + SDL_SetWindowFullscreen (window, Flags); + return Flags & SDL_WINDOW_FULLSCREEN_DESKTOP; +} + + +SDL_Surface * +common_SDL_LoadSurfacePath (const std::string& path) +{ + SDL_Surface *image; + if (!(image = IMG_Load (path.c_str ()))) + { + DEBUG_LOG ("IMG_Load: %s", IMG_GetError ()); + exit (EXIT_FAILURE); + } + return image; +} diff --git a/src/lib_lua_common.cpp b/src/lib_lua_common.cpp new file mode 100644 index 0000000..efb3e69 --- /dev/null +++ b/src/lib_lua_common.cpp @@ -0,0 +1,197 @@ +#include "lib_lua_common.h" + +#include +#include +#include +#include + +static int l_map (lua_State*); +static int l_split (lua_State*); +static void call_va (lua_State*, const char*, const char*, ...); +static void error (lua_State*, const char*, ...); + +int +common_lua_run (lua_State* L, const char* name, const char* buff, const size_t buff_len) +{ + int error; + error = luaL_loadbuffer (L, buff, buff_len, name) || lua_pcall (L, 0, 0, 0); + if (error) + { + fprintf (stderr, "%s\n", lua_tostring (L, -1)); + lua_pop (L, 1); /* pop error message from the stack */ + } + // common_lua_stack_dump (L); + return error; +} + + +void +common_lua_stack_dump (lua_State* L) +{ + int i, t, top; + top = lua_gettop (L); + for (i = 1; i <= top; i++) + { /* repeat for each level */ + t = lua_type (L, i); + switch (t) + { + case LUA_TSTRING: /* strings */ + printf ("`%s'", lua_tostring (L, i)); + break; + case LUA_TBOOLEAN: /* booleans */ + printf (lua_toboolean (L, i) ? "true" : "false"); + break; + case LUA_TNUMBER: /* numbers */ + printf ("%g", lua_tonumber (L, i)); + break; + default: /* other values */ + printf ("%s", lua_typename (L, t)); + break; + } + printf (" "); /* put a separator */ + } + printf ("\n"); /* end the listing */ +} + + +static void +error (lua_State* L, const char* fmt, ...) +{ + va_list argp; + va_start (argp, fmt); + vfprintf (stderr, fmt, argp); + va_end (argp); + lua_close (L); + exit (EXIT_FAILURE); +} + + +static void +load (const char* filename, int* width, int* height) +{ + lua_State* L = luaL_newstate (); + luaL_openlibs (L); + if (luaL_loadfile (L, filename) || lua_pcall (L, 0, 0, 0)) + error (L, "cannot run configuration file: %s", lua_tostring (L, -1)); + lua_getglobal (L, "width"); + lua_getglobal (L, "height"); + if (!lua_isnumber (L, -2)) + error (L, "`width' should be a number\n"); + if (!lua_isnumber (L, -1)) + error (L, "`height' should be a number\n"); + *width = (int) lua_tonumber (L, -2); + *height = (int) lua_tonumber (L, -1); + lua_close (L); +} + + +static void +call_va (lua_State* L, const char* func, const char* sig, ...) +{ + va_list vl; + int narg, nres; /* number of arguments and results */ + va_start (vl, sig); + lua_getglobal (L, func); /* get function */ + /* push arguments */ + narg = 0; + while (*sig) + { /* push arguments */ + switch (*sig++) + { + case 'd': /* double argument */ + lua_pushnumber (L, va_arg (vl, double)); + break; + case 'i': /* int argument */ + lua_pushnumber (L, va_arg (vl, int)); + break; + case 's': /* string argument */ + lua_pushstring (L, va_arg (vl, char *)); + break; + case '>': + goto endwhile; + default: + error (L, "invalid option (%c)", *(sig - 1)); + } + narg++; + luaL_checkstack (L, 1, "too many arguments"); + } + endwhile: + /* do the call */ + nres = strlen (sig); /* number of expected results */ + /* do the call */ + if (lua_pcall (L, narg, nres, 0) != 0) + error (L, "error running function `%s': %s", func, lua_tostring (L, -1)); + /* retrieve results */ + nres = -nres; /* stack index of first result */ + while (*sig) + { /* get results */ + switch (*sig++) + { + case 'd': /* double result */ + if (!lua_isnumber (L, nres)) + error (L, "wrong result type"); + *va_arg (vl, double *) = lua_tonumber (L, nres); + break; + case 'i': /* int result */ + if (!lua_isnumber (L, nres)) + error (L, "wrong result type"); + *va_arg (vl, int *) = (int) lua_tonumber (L, nres); + break; + case 's': /* string result */ + if (!lua_isstring (L, nres)) + error (L, "wrong result type"); + *va_arg (vl, const char **) = lua_tostring (L, nres); + break; + default: + error (L, "invalid option (%c)", *(sig - 1)); + } + nres++; + } + va_end (vl); +} + + +static int +l_map (lua_State * L) +{ + int i, n; + /* 1st argument must be a table (t) */ + luaL_checktype (L, 1, LUA_TTABLE); + /* 2nd argument must be a function (f) */ + luaL_checktype (L, 2, LUA_TFUNCTION); + n = luaL_len (L, 1); /* get size of table */ + for (i = 1; i <= n; i++) + { + lua_pushvalue (L, 2); /* push f */ + lua_rawgeti (L, 1, i); /* push t[i] */ + lua_call (L, 1, 1); /* call f(t[i]) */ + lua_rawseti (L, 1, i); /* t[i] = result */ + } + return 0; /* no results */ +} + + +static int +l_split (lua_State* L) +{ + int i; + const char* s; + const char* e; + const char* sep; + i = 1; + s = luaL_checkstring (L, 1); + sep = luaL_checkstring (L, 2); + lua_newtable (L); /* result */ + /* repeat for each separator */ + while ((e = strchr (s, *sep)) != NULL) + { + /* push substring */ + lua_pushlstring (L, s, e - s); + lua_rawseti (L, -2, i++); + s = e + 1; /* skip separator */ + } + /* push last substring */ + lua_pushstring (L, s); + lua_rawseti (L, -2, i); + return 1; /* return the table */ +} diff --git a/src/object.cpp b/src/object.cpp new file mode 100644 index 0000000..00f0942 --- /dev/null +++ b/src/object.cpp @@ -0,0 +1,24 @@ +#include "object.h" + +Object::Object (const GLsizei size) +: mGLsizei (size) +{ + if (mGLsizei > 0) + { + glGenVertexArrays (mGLsizei, &VAO); + glGenBuffers (mGLsizei, &VBO); + glGenBuffers (mGLsizei, &EBO); + } +} + + +Object::~Object (void) +{ + if (mGLsizei > 0) + { + glDeleteVertexArrays (mGLsizei, &VAO); + glDeleteBuffers (mGLsizei, &VBO); + glDeleteBuffers (mGLsizei, &EBO); + mGLsizei = 0; + } +} diff --git a/src/quad.cpp b/src/quad.cpp new file mode 100644 index 0000000..956f26f --- /dev/null +++ b/src/quad.cpp @@ -0,0 +1,48 @@ +#include "quad.h" + +Quad::Quad (const GLsizei size) +: Object (size) +{ + const unsigned int indices[] = + { + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle + }; + + const float vertices[] = + { + // positions // texture coords + // top right + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, + // bottom right + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, + // bottom left + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, + // top left + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f + }; + + glBindVertexArray (VAO); + + glBindBuffer (GL_ARRAY_BUFFER, VBO); + glBufferData (GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW); + + glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW); + + // position attribute + glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof (float), (void *) 0); + glEnableVertexAttribArray (0); + + // texture coord attribute + glVertexAttribPointer (1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof (float), (void *) (3 * sizeof (float))); + glEnableVertexAttribArray (1); +} + + +void +Quad::Draw (void) +{ + glBindVertexArray (VAO); + glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); +} diff --git a/src/server_main.cpp b/src/server_main.cpp new file mode 100644 index 0000000..3198155 --- /dev/null +++ b/src/server_main.cpp @@ -0,0 +1,245 @@ +#include "debug.h" +#include "lib_SDL_common.h" /* SDL* */ +#include "Server.h" +#include "signal_common.h" +#include "unused.h" +#include "events_common.h" +#include "trim.h" +#include "lib_lua_common.h" + +#include + +#include /* exit */ +#include /* getopt */ +#include /* getopt */ +#include /* inet_ntop */ + +static bool my_SDL_UserEvent (SDL_Event *); +static void my_Usage (const char *); +static void my_GetOpt (int, char **); +static void my_ProcessPacket (UDPpacket *); + +static const char* host = NULL; +static Uint16 port = 6666; + +int +main (int argc, char **argv) +{ + bool done; + SDL_Event ev; + Server server; + + my_GetOpt (argc, argv); + common_Signal_Init (); + common_SDL_Init (); + + server = Server (host, port); + server.Start (); + + done = false; + while (!done) + { + SDL_Delay (1); + while (!done && SDL_PollEvent (&ev)) + { + switch (ev.type) + { + case SDL_QUIT: + done = true; + break; + case SDL_USEREVENT: + { + done = my_SDL_UserEvent (&ev); + } + break; + default: + break; + } + } + } + + server.UDP_CloseAll (); + server.Stop (); + + common_SDL_Quit (); + + return 0; +} + + +static bool +my_SDL_UserEvent (SDL_Event * ev) +{ + SDL_assert (NULL != ev); + switch (ev->user.type) + { + case PACKET_SDL_EVENT: + { + UDPpacket *packet = (UDPpacket *) ev->user.data1; + my_ProcessPacket (packet); + SDLNet_FreePacket (packet); + } + break; + default: + break; + } + return false; +} + + +/* XXX gross */ +static std::vector server_data; +static bool left_shift = false; + +/* XXX gross */ +static void +my_ProcessPacket (UDPpacket* packet) +{ + char c; + size_t len; + char hoststring[128]; + SDL_Event event; + + if (!packet) + return; + SDL_assert (NULL != packet); + if (!packet->len) + return; + SDL_assert (packet->len > 0); + + len = packet->len; + SDL_memcpy (&event, packet->data, len); + + if ((event.type != SDL_KEYDOWN) && (event.type != SDL_KEYUP)) + { + return; + } + + if (event.key.keysym.scancode != SDL_GetScancodeFromKey (event.key.keysym.sym)) + { + DEBUG_LOG ("Physical %s key acting as %s key", SDL_GetScancodeName (event.key.keysym.scancode), SDL_GetKeyName (event.key.keysym.sym)); + } + std::string str = std::string (SDL_GetKeyName (event.key.keysym.sym)); + bool down = (event.type == SDL_KEYDOWN); + + inet_ntop (AF_INET, &packet->address.host, hoststring, 128); + DEBUG_LOG ("%d bytes from %s:%d:\n%s", packet->len, hoststring, ntohs (packet->address.port), str.c_str ()); + + // SDL_Log ("%d \"%s\"", str.length(), str.c_str ()); + + /* XXX ultra gross */ + if (down) { + if (str.length () > 1) + { + if (str == "Return") + { + server_data.push_back ('\0'); + lua_State* L = luaL_newstate (); + luaL_openlibs (L); + std::string buf = std::string ((const char*)server_data.data ()); + printf ("\n => "); + fflush (stdout); + common_lua_run (L, "line", buf.c_str (), buf.length ()); + fflush (stdout); + lua_close (L); + server_data.clear (); + } + else if (str == "Left Shift") + { + left_shift = (bool) true; + } + else if (str == "Space") + { + server_data.push_back (' '); + putchar (' '); + fflush (stdout); + } + else if (str == "Backspace") + { + off_t size = server_data.size () - 1; + if (size < 0) + size = 0; + server_data.resize (size); + printf ("\x08\e[0K"); + fflush(stdout); + } + } + else + { + c = tolower (str[0]); + if (isalpha (c)) + { + if (left_shift) + c = toupper (c); + } + else if (isdigit (c)) + { + const char* sym = ")!@#$%^&*("; + if (left_shift) + c = sym[c - '0']; + } + else + { + if (left_shift) + { + switch (c) + { + case '\'': c = '"'; break; + case ',': c = '<'; break; + case '-': c = '_'; break; + case '.': c = '>'; break; + case '/': c = '?'; break; + case ';': c = ':'; break; + case '=': c = '+'; break; + case '[': c = '{'; break; + case '\\': c = '|'; break; + case ']': c = '}'; break; + case '`': c = '~'; break; + default: break; + } + } + } + server_data.push_back (c); + putchar (c); + fflush (stdout); + } + } + else + { + if (str == "Left Shift") + { + left_shift = (bool) false; + } + } +} + + +static void +my_Usage (const char *argv0) +{ + fprintf (stderr, "Usage: %s [-h] [-s server] [-p port]\n", argv0); +} + + +static void +my_GetOpt (int argc, char **argv) +{ + int opt; + while ((opt = getopt (argc, argv, "hp:s:")) != -1) + { + switch (opt) + { + case 'p': + port = strtol (optarg, NULL, 0); + break; + case 's': + host = optarg; + break; + case 'h': + default: + my_Usage (argv[0]); + exit (EXIT_FAILURE); + break; + } + } +} diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 0000000..7c1cbb9 --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,258 @@ +#include "debug.h" +#include "shader.h" +#include "FileIO.h" +#include "lib_GL_common.h" + +#include + +#include + +#include +#include + +#define DEFAULT_FRAGMENT_HEADER "./glsl/header.f.glsl" + +ShaderProgram::ShaderProgram (void) +{ +} + + +ShaderProgram::ShaderProgram (const std::string& vertexPath, const std::string& geometryPath, const std::string& fragmentPath) +{ + GLuint vertex, geometry, fragment; + + ID = glCreateProgram (); + if (ID) + { + vertex = CompileShaderPath (vertexPath, GL_VERTEX_SHADER); + + geometry = 0; + if (!geometryPath.empty ()) + geometry = CompileShaderPath (geometryPath, GL_GEOMETRY_SHADER); + + FileIO f1 (DEFAULT_FRAGMENT_HEADER, "r"); + FileIO f2 (fragmentPath, "r"); + std::string buf = f1.ReadToString () + "\n" + f2.ReadToString (); + fragment = CompileShaderBuffer (buf.c_str (), GL_FRAGMENT_SHADER); + + if (vertex) + glAttachShader (ID, vertex); + if (geometry) + glAttachShader (ID, geometry); + if (fragment) + glAttachShader (ID, fragment); + + glLinkProgram (ID); + + if (!my_checkCompileSuccess (ID, GL_PROGRAM)) + { + throw "glLinkProgram error"; + ID = 0; + } + + glDeleteShader (vertex); + glDeleteShader (geometry); + glDeleteShader (fragment); + + RefreshUniformLocations (); + } + else + { + ID = 0; + throw "ShaderProgram error"; + } +} + + +void +ShaderProgram::Use (void) +{ + glUseProgram (ID); +} + + +GLint ShaderProgram::getCachedLoc (const std::string& name) +{ + if (mUniformLocationsNeedRefresh) + RefreshUniformLocations (); + auto const& ref = UniformLocationCache.find (name); + if (ref == UniformLocationCache.end ()) + { + /* It didn't exist */ + int loc = glGetUniformLocation (ID, name.c_str ()); + if (loc == -1) + { + } + UniformLocationCache[name] = loc; + } + return UniformLocationCache[name]; +} + + +void +ShaderProgram::RefreshUniformLocations (void) +{ + for (auto& itm : UniformLocationCache) + { + itm.second = glGetUniformLocation (ID, itm.first.c_str ()); + if (itm.second == -1) + UniformLocationCache.erase (itm.first); + } + mUniformLocationsNeedRefresh = false; +} + + +void +ShaderProgram::Bool (const std::string& name, const bool&value) +{ + glUniform1i (getCachedLoc (name), (int) value); +} + + +void +ShaderProgram::Float (const std::string& name, const float&value) +{ + glUniform1f (getCachedLoc (name), (float) value); +} + + +void +ShaderProgram::Int (const std::string& name, const int&value) +{ + glUniform1i (getCachedLoc (name), (int) value); +} + + +void +ShaderProgram::Vec2 (const std::string& name, const glm::vec2& value) +{ + glUniform2fv (getCachedLoc (name), 1, glm::value_ptr (value)); +} + + +void +ShaderProgram::Vec3 (const std::string& name, const glm::vec3& value) +{ + glUniform3fv (getCachedLoc (name), 1, glm::value_ptr (value)); +} + + +void +ShaderProgram::Vec4 (const std::string& name, const glm::vec4& value) +{ + glUniform4fv (getCachedLoc (name), 1, glm::value_ptr (value)); +} + + +void +ShaderProgram::Mat2 (const std::string& name, const glm::mat2& value) +{ + glUniformMatrix2fv (getCachedLoc (name), 1, GL_FALSE, glm::value_ptr (value)); +} + + +void +ShaderProgram::Mat3 (const std::string& name, const glm::mat3& value) +{ + glUniformMatrix3fv (getCachedLoc (name), 1, GL_FALSE, glm::value_ptr (value)); +} + + +void +ShaderProgram::Mat4 (const std::string& name, const glm::mat4& value) +{ + glUniformMatrix4fv (getCachedLoc (name), 1, GL_FALSE, glm::value_ptr (value)); +} + + +GLuint ShaderProgram::CompileShaderPath (const std::string& path, const GLenum type) +{ + FileIO f (path, "r"); + std::string buf = f.ReadToString (); + + if (buf.empty ()) + return 0; + + return CompileShaderBuffer (buf.c_str (), type); +} + + +GLuint ShaderProgram::CompileShaderBuffer (const std::string& buf, const GLenum type) +{ + GLuint id; + if (buf.empty ()) + return 0; + id = glCreateShader (type); + if (id) + { + SDL_assert (0 != id); + const char* code = buf.c_str (); + glShaderSource (id, 1, &code, NULL); + glCompileShader (id); + if (!my_checkCompileSuccess (id, type)) + { + DEBUG_LOG ("CompileShaderBuffer error"); + id = 0; + } + } + + return id; +} + + +GLint +ShaderProgram::my_checkCompileSuccess (const GLuint id, const GLenum type) +{ + GLint infoLogLength, success; + std::string infoLog; + std::string typeName = "UNKNOWN"; + infoLogLength = 0; + success = GL_FALSE; + switch (type) + { + case GL_VERTEX_SHADER: + typeName = "GL_VERTEX_SHADER"; + goto HANDLE_SHADER; + case GL_GEOMETRY_SHADER: + typeName = "GL_GEOMETRY_SHADER"; + goto HANDLE_SHADER; + case GL_FRAGMENT_SHADER: + typeName = "GL_FRAGMENT_SHADER"; +HANDLE_SHADER: + glGetShaderiv (id, GL_COMPILE_STATUS,&success); + if (GL_FALSE == success) + glGetShaderiv (id, GL_INFO_LOG_LENGTH,&infoLogLength); + break; + case GL_PROGRAM: + typeName = "PROGRAM"; + glGetProgramiv (id, GL_LINK_STATUS,&success); + if (GL_FALSE == success) + glGetProgramiv (id, GL_INFO_LOG_LENGTH,&success); + break; + default: + break; + } + if (!infoLogLength) + { + return success; + } + infoLog.reserve (infoLogLength); + switch (type) + { + case GL_VERTEX_SHADER: + case GL_GEOMETRY_SHADER: + case GL_FRAGMENT_SHADER: + glGetShaderInfoLog (id, infoLogLength, NULL, &infoLog.front ()); + break; + case GL_PROGRAM: + glGetProgramInfoLog (id, infoLogLength, NULL, &infoLog.front ()); + break; + default: + break; + } + if (infoLog[0] != '\0') + { + DEBUG_LOG ("%8s ERROR ============================================================\n%s", typeName.c_str (), infoLog.c_str ()); + } + return success; +} diff --git a/src/signal_common.cpp b/src/signal_common.cpp new file mode 100644 index 0000000..4e112e9 --- /dev/null +++ b/src/signal_common.cpp @@ -0,0 +1,63 @@ +#define _POSIX_C_SOURCE + +#include "signal_common.h" /* conflict with signal.h probably */ +#include "unused.h" + +#include /* fputs */ +#include /* struct sigaction */ +#include /* exit */ + +#include + +/* TODO longjmp to cleanup handler */ +/* #include */ +/* sigjmp_buf env; */ + +static void +my_Signal_Handler (int sig, siginfo_t* si, void* uap) +{ + SDL_Event QUIT_EVENT = {SDL_QUIT}; + + UNUSED (sig); + UNUSED (uap); + UNUSED (si); + + SDL_PushEvent (&QUIT_EVENT); + exit (EXIT_FAILURE); + /* siglongjmp (env, sig); */ +} + + +void +common_Signal_Init (void) +{ + struct sigaction sa; + sa.sa_sigaction = my_Signal_Handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = 0; + + if (sigaction (SIGINT, &sa, NULL) == -1) + { + fputs ("Failed to setup SIGINT handler\n", stderr); + } + if (sigaction (SIGTERM, &sa, NULL) == -1) + { + fputs ("Failed to setup SIGTERM handler\n", stderr); + } + if (sigaction (SIGILL, &sa, NULL) == -1) + { + fputs ("Failed to setup SIGILL handler\n", stderr); + } + if (sigaction (SIGBUS, &sa, NULL) == -1) + { + fputs ("Failed to setup SIGBUS handler\n", stderr); + } + if (sigaction (SIGSEGV, &sa, NULL) == -1) + { + fputs ("Failed to setup SIGSEGV handler\n", stderr); + } + if (sigaction (SIGABRT, &sa, NULL) == -1) + { + fputs ("Failed to setup SIGABRT handler\n", stderr); + } +} diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..cfaabe7 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,4 @@ +test_FileIO +test_File_Write_UDP +test_lua +test_Object diff --git a/test/test_FileIO.cpp b/test/test_FileIO.cpp new file mode 100644 index 0000000..e99b64a --- /dev/null +++ b/test/test_FileIO.cpp @@ -0,0 +1,77 @@ +#include "FileIO.h" + +#include /* uint8_t */ +#include +#include + +#include /* errno */ +#include /* strerror */ + +static int +IsProbablyAscii (const void *buf, const size_t len) +{ + const uint8_t *bufPtr; + const uint8_t *bufPtrEnd; + size_t nAsciiCount; + nAsciiCount = 0; + bufPtr = (const uint8_t *) buf; + bufPtrEnd = bufPtr + len; + while (bufPtr < bufPtrEnd && (NULL != bufPtr) && (*bufPtr != '\0')) + { + nAsciiCount += (*bufPtr >= ' ') && (*bufPtr <= '~'); + bufPtr++; + } + return (int) round ((double) nAsciiCount / (double) len); +} + + +static void +HexDump (const void *buf, const size_t len) +{ + const uint8_t *bufPtr; + const uint8_t *bufPtrEnd; + const size_t width = 16; + size_t nout; + nout = 0; + bufPtr = (const uint8_t *) buf; + bufPtrEnd = bufPtr + len; + while (bufPtr < bufPtrEnd && (NULL != bufPtr)) + { + if (nout == width) + { + printf ("\n"); + nout = 0; + } + printf ("%02x ", *bufPtr); + bufPtr++; + nout++; + } + puts (""); +} + + +int +main (int argc, char **argv) +{ + int rc; + rc = 1; + try + { + while (argc-- > 1) + { + FileIO f (argv[argc], "r"); + std::string buf = f.ReadToString (); + if (IsProbablyAscii (buf.c_str (), buf.size ())) + printf ("%s", buf.c_str ()); + else + HexDump (buf.c_str (), buf.size ()); + rc = 0; + } + } + catch (int err) + { + puts (strerror (errno)); + } + + return rc; +} diff --git a/test/test_File_Write_UDP.cpp b/test/test_File_Write_UDP.cpp new file mode 100644 index 0000000..0b521c7 --- /dev/null +++ b/test/test_File_Write_UDP.cpp @@ -0,0 +1,89 @@ +#include "FileIO.h" +#include "UDP_Write.h" + +#include + +#include +#include +#include +#include +#include + +#include + +static void usage (const char* argv0) +{ + fprintf (stderr, "Usage: %s [-f path] [-h] [-p port] [-s server]\n", argv0); +} + +int +main (int argc, char** argv) +{ + int opt; + int numsent; + int mtu; + int channel; + const char* path; + const char* server; + Uint16 port; + + path = NULL; + server = "localhost"; + port = 6666; + channel = 1; + mtu = 1450; + numsent = 0; + + while ((opt = getopt (argc, argv, "hs:p:f:")) != -1) + { + switch (opt) + { + case 'h': + usage (argv[0]); + exit (EXIT_FAILURE); + break; + case 's': + server = optarg; + if (!strcmp (server, "NULL")) + server = NULL; + break; + case 'p': + port = strtol (optarg, NULL, 0); + break; + case 'f': + path = optarg; + break; + default: + break; + } + } + +#if 0 + if (optind < argc) { + printf("non-option ARGV-elements: "); + while (optind < argc) + printf("%s ", argv[optind++]); + printf("\n"); + } +#endif + + if ((optind < argc) && (argv[optind][0] == '-')) + { + const size_t size = 8192; + std::vector buf (size); + std::fill (buf.begin(), buf.end(), 0); + size_t nread = fread (buf.data (), sizeof (char), size - 1, stdin); + numsent = SendBuffer_UDP (server, port, channel, mtu, buf.data (), nread); + } + else + { + if (path) + { + numsent = SendFile_UDP (server, port, channel, mtu, path); + } + } + + SDL_Log ("%d", numsent); + + return 0; +} diff --git a/test/test_Object.cpp b/test/test_Object.cpp new file mode 100644 index 0000000..4df666f --- /dev/null +++ b/test/test_Object.cpp @@ -0,0 +1,70 @@ +#include "lib_SDL_common.h" +#include "lib_GL_common.h" + +#include "object.h" +#include "cube.h" +#include "quad.h" + +#include + +int main(void) +{ + int Window_Height; + int Window_Width; + SDL_GLContext GLContext; + SDL_Window *Window; + + common_SDL_Init (); + common_SDL_CreateWindow (&Window); + common_GL_Init (Window, &GLContext, 1); + SDL_GetWindowSize (Window, &Window_Width, &Window_Height); + + try { + SDL_Log ("Object o;"); + Object o; + SDL_Log ("Cube c;"); + Cube c; + SDL_Log ("Quad q;"); + Quad q; + } + catch (int e) + { + printf ("%d\n", e); + } + + try { + SDL_Log ("Object (1);"); + Object o = Object (1); + SDL_Log ("Cube (1);"); + Cube c = Cube (1); + SDL_Log ("Quad (1);"); + Quad q = Quad (1); + } + catch (int e) + { + printf ("%d\n", e); + } + + try { + SDL_Log ("Object o;"); + Object o; + SDL_Log ("Cube c;"); + Cube c; + SDL_Log ("Quad q;"); + Quad q; + SDL_Log ("Object (1);"); + o = Object (1); + SDL_Log ("Cube (1);"); + c = Cube (1); + SDL_Log ("Quad (1);"); + q = Quad (1); + } + catch (int e) + { + printf ("%d\n", e); + } + + SDL_GL_DeleteContext (GLContext); + SDL_DestroyWindow (Window); + common_SDL_Quit (); +} diff --git a/test/test_lua.cpp b/test/test_lua.cpp new file mode 100644 index 0000000..d598c41 --- /dev/null +++ b/test/test_lua.cpp @@ -0,0 +1,24 @@ +#include "lib_lua_common.h" + +#include + +int main(int argc, char** argv) +{ + char buf[8192]; + lua_State* L; + L = luaL_newstate (); + luaL_openlibs (L); + while (fgets (buf, 8191, stdin)) + { + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; + if (strlen (buf) > 0) + { + printf (" => "); + fflush (stdout); + common_lua_run (L, "line", buf, strlen (buf)); + } + } + lua_close (L); + return 0; +}