├── demo ├── settings.gradle ├── Resources │ ├── cube.bmp │ ├── cube.mtl │ └── cube.obj ├── app │ ├── src │ │ └── main │ │ │ ├── jni │ │ │ ├── Application.mk │ │ │ └── Android.mk │ │ │ ├── assets │ │ │ ├── cube.bmp │ │ │ ├── cube.mtl │ │ │ └── cube.obj │ │ │ ├── res │ │ │ ├── drawable-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ └── layout │ │ │ │ └── main.xml │ │ │ ├── java │ │ │ └── lv │ │ │ │ └── elviss │ │ │ │ └── softwarerenderer │ │ │ │ ├── MainActivity.java │ │ │ │ ├── DemoLibJNIWrapper.java │ │ │ │ └── BitmapView.java │ │ │ └── AndroidManifest.xml │ ├── project.properties │ ├── proguard-rules.pro │ └── build.gradle ├── ios │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Info.plist ├── tvos │ ├── Assets.xcassets │ │ ├── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ └── Info.plist ├── SoftwareRenderer.xcodeproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── windows │ ├── resource.h │ └── resource.rc ├── ApplicationWindows.hpp ├── build.gradle ├── ApplicationAndroid.hpp ├── ApplicationX11.hpp ├── ApplicationHaiku.hpp ├── gradle.properties ├── macos │ ├── Info.plist │ └── Assets.xcassets │ │ └── AppIcon.appiconset │ │ └── Contents.json ├── SoftwareRenderer.sln ├── ApplicationMacOS.hpp ├── ApplicationIOS.hpp ├── ApplicationTVOS.hpp ├── ApplicationAndroid.cpp ├── SoftwareRenderer.vcxproj.filters ├── ApplicationHaiku.cpp ├── Makefile ├── ApplicationX11.cpp ├── ApplicationWindows.cpp ├── ApplicationTVOS.mm ├── ApplicationIOS.mm ├── SoftwareRenderer.vcxproj ├── Application.hpp ├── Bmp.hpp └── ApplicationMacOS.mm ├── test ├── main.cpp ├── tests.cpp ├── test.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── Makefile ├── test.sln └── test.vcxproj ├── .gitmodules ├── sr ├── DepthState.hpp ├── Constants.hpp ├── RenderError.hpp ├── sr.hpp ├── Sampler.hpp ├── PixelFormat.hpp ├── Vertex.hpp ├── Shader.hpp ├── Color.hpp ├── BlendState.hpp ├── Size.hpp ├── Rect.hpp ├── Texture.hpp ├── Renderer.hpp └── Vector.hpp ├── sonar-project.properties ├── .gitignore ├── .travis.yml ├── README.md └── UNLICENSE /demo/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch2/catch.hpp" 3 | -------------------------------------------------------------------------------- /demo/Resources/cube.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/Resources/cube.bmp -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/Catch2"] 2 | path = external/Catch2 3 | url = https://github.com/catchorg/Catch2.git 4 | -------------------------------------------------------------------------------- /demo/app/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_PLATFORM := android-14 2 | APP_ABI := armeabi-v7a x86 3 | APP_STL := c++_shared -------------------------------------------------------------------------------- /demo/ios/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /demo/tvos/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /demo/app/src/main/assets/cube.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/app/src/main/assets/cube.bmp -------------------------------------------------------------------------------- /test/tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "catch2/catch.hpp" 4 | #include "sr.hpp" 5 | 6 | TEST_CASE("Test", "[test]") 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elnormous/SoftwareRenderer/HEAD/demo/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | SoftwareRenderer 4 | 5 | -------------------------------------------------------------------------------- /test/test.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/SoftwareRenderer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /sr/DepthState.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_DEPTHSTATE_HPP 6 | #define SR_DEPTHSTATE_HPP 7 | 8 | namespace sr 9 | { 10 | class DepthState final 11 | { 12 | public: 13 | bool read = false; 14 | bool write = false; 15 | }; 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /demo/Resources/cube.mtl: -------------------------------------------------------------------------------- 1 | newmtl cube 2 | Ns 10.0000 3 | Ni 1.5000 4 | d 1.0000 5 | Tr 0.0000 6 | Tf 1.0000 1.0000 1.0000 7 | illum 2 8 | Ka 0.0000 0.0000 0.0000 9 | Kd 0.5880 0.5880 0.5880 10 | Ks 0.0000 0.0000 0.0000 11 | Ke 0.0000 0.0000 0.0000 12 | map_Ka cube.bmp 13 | map_Kd cube.bmp 14 | -------------------------------------------------------------------------------- /sr/Constants.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_CONSTANTS_HPP 6 | #define SR_CONSTANTS_HPP 7 | 8 | namespace sr 9 | { 10 | template constexpr T tau = T(6.28318530717958647692); 11 | template constexpr T pi = T(3.14159265358979323846); 12 | } 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /demo/app/src/main/assets/cube.mtl: -------------------------------------------------------------------------------- 1 | newmtl cube 2 | Ns 10.0000 3 | Ni 1.5000 4 | d 1.0000 5 | Tr 0.0000 6 | Tf 1.0000 1.0000 1.0000 7 | illum 2 8 | Ka 0.0000 0.0000 0.0000 9 | Kd 0.5880 0.5880 0.5880 10 | Ks 0.0000 0.0000 0.0000 11 | Ke 0.0000 0.0000 0.0000 12 | map_Ka cube.bmp 13 | map_Kd cube.bmp 14 | -------------------------------------------------------------------------------- /sr/RenderError.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_RENDERERROR_HPP 6 | #define SR_RENDERERROR_HPP 7 | 8 | #include 9 | 10 | namespace sr 11 | { 12 | class RenderError final: public std::runtime_error 13 | { 14 | public: 15 | using runtime_error::runtime_error; 16 | }; 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /demo/app/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE := demo 6 | LOCAL_CFLAGS := -Wall -Wextra 7 | LOCAL_CPPFLAGS += -std=c++11 -fexceptions 8 | LOCAL_C_INCLUDES += $(LOCAL_PATH)/../../../../../sr 9 | 10 | LOCAL_SRC_FILES := ../../../../ApplicationAndroid.cpp 11 | 12 | LOCAL_LDLIBS := -llog -landroid -latomic 13 | 14 | include $(BUILD_SHARED_LIBRARY) 15 | -------------------------------------------------------------------------------- /demo/windows/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by resource.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /sr/sr.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_HPP 6 | #define SR_HPP 7 | 8 | #include "BlendState.hpp" 9 | #include "Color.hpp" 10 | #include "Constants.hpp" 11 | #include "DepthState.hpp" 12 | #include "Matrix.hpp" 13 | #include "Rect.hpp" 14 | #include "Renderer.hpp" 15 | #include "Sampler.hpp" 16 | #include "Shader.hpp" 17 | #include "Size.hpp" 18 | #include "Texture.hpp" 19 | #include "Vector.hpp" 20 | #include "Vertex.hpp" 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /demo/app/src/main/java/lv/elviss/softwarerenderer/MainActivity.java: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | package lv.elviss.softwarerenderer; 6 | 7 | import android.app.Activity; 8 | import android.os.Bundle; 9 | 10 | public class MainActivity extends Activity 11 | { 12 | @Override public void onCreate(Bundle savedInstanceState) 13 | { 14 | super.onCreate(savedInstanceState); 15 | 16 | setContentView(new BitmapView(this)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/app/src/main/java/lv/elviss/softwarerenderer/DemoLibJNIWrapper.java: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | package lv.elviss.softwarerenderer; 6 | 7 | import android.graphics.Bitmap; 8 | 9 | public class DemoLibJNIWrapper 10 | { 11 | static 12 | { 13 | System.loadLibrary("demo"); 14 | } 15 | 16 | public static native void init(int width, int height); 17 | public static native void onSizeChanged(int width, int height); 18 | public static native void onDraw(Bitmap bitmap); 19 | } 20 | -------------------------------------------------------------------------------- /demo/ApplicationWindows.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONWINDOWS_HPP 6 | #define APPLICATIONWINDOWS_HPP 7 | 8 | #include 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | class ApplicationWindows: public Application 14 | { 15 | public: 16 | ApplicationWindows(); 17 | ~ApplicationWindows(); 18 | 19 | void draw(); 20 | void didResize(); 21 | 22 | void run(); 23 | 24 | private: 25 | ATOM windowClass = 0; 26 | HWND window = 0; 27 | }; 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=elnormous_SoftwareRenderer 2 | sonar.organization=elnormous-github 3 | 4 | # This is the name and version displayed in the SonarCloud UI. 5 | #sonar.projectName=SoftwareRenderer 6 | #sonar.projectVersion=1.0 7 | 8 | # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. 9 | #sonar.sources=. 10 | sonar.sources=sr,demo 11 | 12 | # Encoding of the source code. Default is default system encoding 13 | #sonar.sourceEncoding=UTF-8 14 | 15 | sonar.exclusions=**/*.java 16 | sonar.cfamily.build-wrapper-output=bw-output 17 | sonar.cfamily.gcov.reportsPath=. -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.1' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /demo/app/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-14 15 | -------------------------------------------------------------------------------- /demo/ApplicationAndroid.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONANDROID_HPP 6 | #define APPLICATIONANDROID_HPP 7 | 8 | #include 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | class ApplicationAndroid: public Application 14 | { 15 | public: 16 | ApplicationAndroid(JavaVM* initJavaVM); 17 | ~ApplicationAndroid(); 18 | 19 | void init(jint initWidth, jint initHeight); 20 | void onDraw(jobject bitmap); 21 | void onSizeChanged(jint newWidth, jint newHeight); 22 | 23 | private: 24 | JavaVM* javaVM; 25 | }; 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /sr/Sampler.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_SAMPLER_HPP 6 | #define SR_SAMPLER_HPP 7 | 8 | #include "Color.hpp" 9 | #include "Vector.hpp" 10 | 11 | namespace sr 12 | { 13 | class Sampler final 14 | { 15 | public: 16 | enum class AddressMode 17 | { 18 | clamp, 19 | repeat, 20 | mirror 21 | }; 22 | 23 | enum class Filter 24 | { 25 | point, 26 | linear 27 | }; 28 | 29 | AddressMode addressModeX = AddressMode::clamp; 30 | AddressMode addressModeY = AddressMode::clamp; 31 | Filter filter = Filter::point; 32 | }; 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /demo/Resources/cube.obj: -------------------------------------------------------------------------------- 1 | # Apple ModelIO OBJ File: cube 2 | mtllib cube.mtl 3 | g 4 | v -50.0 -50.0 50.0 5 | v 50.0 -50.0 50.0 6 | v -50.0 50.0 50.0 7 | v 50.0 50.0 50.0 8 | v -50.0 50.0 -50.0 9 | v 50.0 50.0 -50.0 10 | v -50.0 -50.0 -50.0 11 | v 50.0 -50.0 -50.0 12 | vn 0 0 1 13 | vn 0 1 0 14 | vn 0 0 -1 15 | vn 0 -1 0 16 | vn 1 0 0 17 | vn -1 0 0 18 | vt 0 0 19 | vt 1 0 20 | vt 0 1 21 | vt 1 1 22 | usemtl cube 23 | f 1/1/1 2/2/1 3/3/1 24 | f 3/3/1 2/2/1 4/4/1 25 | f 3/1/2 4/2/2 5/3/2 26 | f 5/3/2 4/2/2 6/4/2 27 | f 5/4/3 6/3/3 7/2/3 28 | f 7/2/3 6/3/3 8/1/3 29 | f 7/1/4 8/2/4 1/3/4 30 | f 1/3/4 8/2/4 2/4/4 31 | f 2/1/5 8/2/5 4/3/5 32 | f 4/3/5 8/2/5 6/4/5 33 | f 7/1/6 1/2/6 5/3/6 34 | f 5/3/6 1/2/6 3/4/6 35 | s off 36 | -------------------------------------------------------------------------------- /demo/app/src/main/assets/cube.obj: -------------------------------------------------------------------------------- 1 | # Apple ModelIO OBJ File: cube 2 | mtllib cube.mtl 3 | g 4 | v -50.0 -50.0 50.0 5 | v 50.0 -50.0 50.0 6 | v -50.0 50.0 50.0 7 | v 50.0 50.0 50.0 8 | v -50.0 50.0 -50.0 9 | v 50.0 50.0 -50.0 10 | v -50.0 -50.0 -50.0 11 | v 50.0 -50.0 -50.0 12 | vn 0 0 1 13 | vn 0 1 0 14 | vn 0 0 -1 15 | vn 0 -1 0 16 | vn 1 0 0 17 | vn -1 0 0 18 | vt 0 0 19 | vt 1 0 20 | vt 0 1 21 | vt 1 1 22 | usemtl cube 23 | f 1/1/1 2/2/1 3/3/1 24 | f 3/3/1 2/2/1 4/4/1 25 | f 3/1/2 4/2/2 5/3/2 26 | f 5/3/2 4/2/2 6/4/2 27 | f 5/4/3 6/3/3 7/2/3 28 | f 7/2/3 6/3/3 8/1/3 29 | f 7/1/4 8/2/4 1/3/4 30 | f 1/3/4 8/2/4 2/4/4 31 | f 2/1/5 8/2/5 4/3/5 32 | f 4/3/5 8/2/5 6/4/5 33 | f 7/1/6 1/2/6 5/3/6 34 | f 5/3/6 1/2/6 3/4/6 35 | s off 36 | -------------------------------------------------------------------------------- /demo/ApplicationX11.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONX11_HPP 6 | #define APPLICATIONX11_HPP 7 | 8 | #include 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | class ApplicationX11: public Application 14 | { 15 | public: 16 | ApplicationX11(); 17 | ~ApplicationX11(); 18 | 19 | void draw(); 20 | void didResize(int newWidth, int newHeight); 21 | 22 | void run(); 23 | 24 | private: 25 | Visual* visual = nullptr; 26 | int depth; 27 | Display* display = nullptr; 28 | ::Window window; 29 | Atom protocolsAtom; 30 | Atom deleteAtom; 31 | GC gc; 32 | }; 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /demo/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/elviss/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /test/Makefile: -------------------------------------------------------------------------------- 1 | DEBUG=0 2 | CXXFLAGS=-std=c++17 -Wall -Wextra -Wshadow -Wno-c++98-compat -I../external/Catch2/single_include -I../sr 3 | SOURCES=main.cpp tests.cpp 4 | BASE_NAMES=$(basename $(SOURCES)) 5 | OBJECTS=$(BASE_NAMES:=.o) 6 | DEPENDENCIES=$(OBJECTS:.o=.d) 7 | EXECUTABLE=test 8 | 9 | all: $(EXECUTABLE) 10 | ifeq ($(DEBUG),1) 11 | all: CXXFLAGS+=-DDEBUG -g 12 | else 13 | all: CXXFLAGS+=-O3 14 | all: LDFLAGS+=-O3 15 | endif 16 | 17 | $(EXECUTABLE): $(OBJECTS) 18 | $(CXX) $(OBJECTS) $(LDFLAGS) -o $@ -fprofile-arcs -ftest-coverage 19 | 20 | -include $(DEPENDENCIES) 21 | 22 | %.o: %.cpp 23 | $(CXX) -c $(CXXFLAGS) -MMD -MP $< -o $@ -fprofile-arcs -ftest-coverage 24 | 25 | .PHONY: clean 26 | clean: 27 | $(RM) $(EXECUTABLE) $(OBJECTS) $(DEPENDENCIES) $(EXECUTABLE).exe *.gcda *.gcno -------------------------------------------------------------------------------- /demo/ApplicationHaiku.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONHAIKU_HPP 6 | #define APPLICATIONHAIKU_HPP 7 | 8 | #include 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | class AppWindow; 14 | class AppView; 15 | 16 | class ApplicationHaiku: public Application, public BApplication 17 | { 18 | public: 19 | ApplicationHaiku(); 20 | ~ApplicationHaiku(); 21 | 22 | void draw(); 23 | void didResize(float newWidth, float newHeight); 24 | 25 | void run(); 26 | 27 | virtual void Pulse() override; 28 | 29 | private: 30 | BWindow* window = nullptr; 31 | AppView* view = nullptr; 32 | BBitmap* bitmap = nullptr; 33 | }; 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | demo/SoftwareRenderer.xcodeproj/project.xcworkspace/xcshareddata 2 | demo/SoftwareRenderer.xcodeproj/project.xcworkspace/xcuserdata 3 | demo/SoftwareRenderer.xcodeproj/xcshareddata 4 | demo/SoftwareRenderer.xcodeproj/xcuserdata 5 | *.opendb 6 | *.db 7 | demo/x64 8 | demo/Win32 9 | *.d 10 | *.o 11 | demo/SoftwareRenderer 12 | demo/SoftwareRenderer.app 13 | .project 14 | *.user 15 | demo/.gradle 16 | demo/.settings 17 | demo/app/.externalNativeBuild 18 | demo/app/.idea 19 | demo/app/build 20 | local.properties 21 | test/test.xcodeproj/project.xcworkspace/xcshareddata 22 | test/test.xcodeproj/project.xcworkspace/xcuserdata/elviss.xcuserdatad 23 | test/test.xcodeproj/xcuserdata/elviss.xcuserdatad/xcdebugger 24 | test/test.xcodeproj/xcuserdata/elviss.xcuserdatad/xcschemes 25 | test/x64 26 | test/Win32 27 | *.aps 28 | -------------------------------------------------------------------------------- /sr/PixelFormat.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_PIXELFORMAT_HPP 6 | #define SR_PIXELFORMAT_HPP 7 | 8 | #include 9 | 10 | namespace sr 11 | { 12 | enum class PixelFormat 13 | { 14 | r8, 15 | a8, 16 | rgba8, 17 | float32 18 | }; 19 | 20 | [[nodiscard]] inline std::size_t getPixelSize(const PixelFormat pixelFormat) noexcept 21 | { 22 | switch (pixelFormat) 23 | { 24 | case PixelFormat::r8: 25 | case PixelFormat::a8: 26 | return sizeof(std::uint8_t) * 1; 27 | case PixelFormat::rgba8: 28 | return sizeof(std::uint8_t) * 4; 29 | case PixelFormat::float32: 30 | return sizeof(float); 31 | default: 32 | return 0; 33 | } 34 | } 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /sr/Vertex.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_VERTEX_HPP 6 | #define SR_VERTEX_HPP 7 | 8 | #include "Color.hpp" 9 | #include "Vector.hpp" 10 | 11 | namespace sr 12 | { 13 | class Vertex final 14 | { 15 | public: 16 | Vertex() noexcept = default; 17 | 18 | Vertex(const Vector& initPosition, 19 | const Color initColor, 20 | const Vector& initTexCoord, 21 | const Vector& initNormal) noexcept: 22 | position{initPosition}, color{initColor}, 23 | texCoords{initTexCoord}, normal{initNormal} 24 | { 25 | } 26 | 27 | Vector position; 28 | Color color; 29 | Vector texCoords[2]; 30 | Vector normal; 31 | }; 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /sr/Shader.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_SHADER_HPP 6 | #define SR_SHADER_HPP 7 | 8 | #include 9 | #include "Matrix.hpp" 10 | #include "Texture.hpp" 11 | #include "Vertex.hpp" 12 | 13 | namespace sr 14 | { 15 | struct VertexShaderOutput final 16 | { 17 | Vector position; 18 | Color color; 19 | std::array, 2> texCoords; 20 | Vector normal; 21 | }; 22 | 23 | using VertexShader = VertexShaderOutput(const Matrix& modelViewProjection, 24 | const Vertex& vertex); 25 | 26 | using FragmentShader = Color(const VertexShaderOutput& input, 27 | const std::array& samplers, 28 | const std::array& textures); 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /demo/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: xenial 2 | compiler: gcc 3 | os: linux 4 | 5 | addons: 6 | sonarcloud: 7 | organization: "elnormous-github" 8 | token: 9 | secure: "XK93Z+39PSnN0kpky696KCJOAC7cZimGZWwYskEsqkDf7Ufh4npK9PdnJ2DGceQDulojFs0Q6YdDwfkGzMSEu09b3ARgGxYjoGACkU82UZ6RZMtKqErWr9mXIbvwQIHFuuREK3L6kc4HKU51WMlSoQ8gkYWwKzpjbMC9uOZd14f8LaYVfnryGcvdOJPvNVpFGSWZJUbQE8BRjIVKtLMHp2Pj/oG56SmDM2wTyhrc11Ucr3LnH7V83aLzieDoPfCAvQOzy32ygKu+4vuEkfFcACReOBIeMDLtsR6hHKYZ2qkZs5CyaBHAyhhEl/OLJX/3hZ3RdVI8ryDrge+opSg+3OPMXXvoYb+BYltbCK8llulhHSeoUxTsTe1ZJmGzI02tAVnVL/52Y8T5PSVhK3cW8iXsKm2BRJRIfjQbsjckGQbWYA9VTh195ucGKfQyLXMDtq7eSODonMHDCL4dxAT11PNyitHA/AygkPZ2t1pQkfeZktNJlLbvijGUYrC/0lqsEC6XIASAHSmaru/THNzKbtoCSC+CRZCCnq8opTUPcvBf2kvDFXtR9t2XUYdOo9255zpHeCVIneugT+FTiSsrjmf/AXbcU5dBtwy0qPnXP0SQsHiJ2jVzzlpmklQE0Pb2fo6CZfTJZsAB08iYtzLWLRm8A8IYheW2nWSzAxcDdow=" 10 | 11 | script: 12 | - build-wrapper-linux-x86-64 --out-dir bw-output make -C demo 13 | - sonar-scanner -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Software Renderer 2 | With this project, I am trying to implement the GPU rendering pipeline in software to understand the steps deeper. This project can be used for learning purposes (I try to keep the code clean). 3 | 4 | # Features 5 | 6 | * Indexed triangle rasterization 7 | * Depth testing 8 | * Blending 9 | * Texture sampling with clamp, repeat, and mirror address modes 10 | * Custom shader support (by extending the Shader class) 11 | * Point and linear texture filtering 12 | 13 | # Usage 14 | 15 | Just include the headers from the sr directory in your project and build. You can check the code in the demo directory and build the demo project to see how to use the library. 16 | 17 | # Showcase 18 | 19 | The demonstration app is in the demo directory and it can be built for macOS/iOS/tvOS (Xcode project or GNU makefile), Linux/Solaris/BSD (GNU makefile), Windows (Visual Studio project or GNU makefile) and Haiku (GNU makefile). This is a sample output of the renderer (a box with one side transparent and another colored): 20 | ![SR sample](https://elviss.lv/files/sr_sample_filtered.png) -------------------------------------------------------------------------------- /demo/app/src/main/java/lv/elviss/softwarerenderer/BitmapView.java: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | package lv.elviss.softwarerenderer; 6 | 7 | import android.content.Context; 8 | import android.graphics.Bitmap; 9 | import android.graphics.Canvas; 10 | import android.graphics.Rect; 11 | import android.view.View; 12 | import android.util.Log; 13 | 14 | public class BitmapView extends View 15 | { 16 | Bitmap bitmap; 17 | 18 | public BitmapView(Context context) 19 | { 20 | super(context); 21 | } 22 | 23 | @Override public void onSizeChanged(int w, int h, int oldw, int oldh) 24 | { 25 | if (bitmap == null) 26 | DemoLibJNIWrapper.init(w, h); 27 | else 28 | DemoLibJNIWrapper.onSizeChanged(w, h); 29 | 30 | bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 31 | } 32 | 33 | @Override public void onDraw(Canvas canvas) 34 | { 35 | DemoLibJNIWrapper.onDraw(bitmap); 36 | 37 | canvas.drawBitmap(bitmap, new Rect(0, 0, getWidth(), getHeight()), new Rect(0, 0, 100, 100), null); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /demo/macos/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 Elviss Strazdins. All rights reserved. 27 | NSPrincipalClass 28 | NSApplication 29 | 30 | 31 | -------------------------------------------------------------------------------- /demo/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 14 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo/macos/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /test/test.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{614C7EC0-3262-40DF-B884-224B959A01F9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Debug|x64 = Debug|x64 12 | Release|Win32 = Release|Win32 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Debug|Win32.ActiveCfg = Debug|Win32 17 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Debug|Win32.Build.0 = Debug|Win32 18 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Debug|x64.ActiveCfg = Debug|x64 19 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Debug|x64.Build.0 = Debug|x64 20 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Release|Win32.ActiveCfg = Release|Win32 21 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Release|Win32.Build.0 = Release|Win32 22 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Release|x64.ActiveCfg = Release|x64 23 | {614C7EC0-3262-40DF-B884-224B959A01F9}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /demo/SoftwareRenderer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SoftwareRenderer", "SoftwareRenderer.vcxproj", "{4E6216F3-C039-4547-9658-378F199432B0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {4E6216F3-C039-4547-9658-378F199432B0}.Debug|x64.ActiveCfg = Debug|x64 17 | {4E6216F3-C039-4547-9658-378F199432B0}.Debug|x64.Build.0 = Debug|x64 18 | {4E6216F3-C039-4547-9658-378F199432B0}.Debug|x86.ActiveCfg = Debug|Win32 19 | {4E6216F3-C039-4547-9658-378F199432B0}.Debug|x86.Build.0 = Debug|Win32 20 | {4E6216F3-C039-4547-9658-378F199432B0}.Release|x64.ActiveCfg = Release|x64 21 | {4E6216F3-C039-4547-9658-378F199432B0}.Release|x64.Build.0 = Release|x64 22 | {4E6216F3-C039-4547-9658-378F199432B0}.Release|x86.ActiveCfg = Release|Win32 23 | {4E6216F3-C039-4547-9658-378F199432B0}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /demo/ios/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | UIInterfaceOrientationPortraitUpsideDown 35 | 36 | UISupportedInterfaceOrientations~ipad 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationPortraitUpsideDown 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /demo/tvos/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | UIInterfaceOrientationPortraitUpsideDown 35 | 36 | UISupportedInterfaceOrientations~ipad 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationPortraitUpsideDown 40 | UIInterfaceOrientationLandscapeLeft 41 | UIInterfaceOrientationLandscapeRight 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /demo/app/build.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.tools.ant.taskdefs.condition.Os 2 | apply plugin: 'com.android.application' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | 8 | defaultConfig { 9 | applicationId "lv.elviss.softwarerenderer" 10 | minSdkVersion 14 11 | targetSdkVersion 17 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | ndk { 16 | abiFilters 'armeabi-v7a', 'x86' 17 | } 18 | 19 | externalNativeBuild { 20 | ndkBuild { 21 | arguments '-j' + Runtime.runtime.availableProcessors() 22 | } 23 | } 24 | } 25 | buildTypes { 26 | release { 27 | debuggable false 28 | jniDebuggable false 29 | renderscriptDebuggable false 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 | 33 | externalNativeBuild { 34 | ndkBuild { 35 | arguments 'NDK_DEBUG=0' 36 | } 37 | } 38 | } 39 | 40 | debug { 41 | debuggable true 42 | jniDebuggable true 43 | renderscriptDebuggable true 44 | externalNativeBuild { 45 | ndkBuild { 46 | arguments 'NDK_DEBUG=1' 47 | } 48 | } 49 | } 50 | } 51 | externalNativeBuild { 52 | ndkBuild { 53 | path 'src/main/jni/Android.mk' 54 | } 55 | } 56 | } 57 | 58 | dependencies { 59 | compile fileTree(dir: 'libs', include: ['*.jar']) 60 | testCompile 'junit:junit:4.12' 61 | compile 'com.android.support:appcompat-v7:23.1.1' 62 | compile 'com.android.support:design:23.1.1' 63 | } 64 | -------------------------------------------------------------------------------- /demo/ios/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /demo/tvos/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /sr/Color.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_COLOR_HPP 6 | #define SR_COLOR_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "Vector.hpp" 13 | 14 | namespace sr 15 | { 16 | class Color final 17 | { 18 | public: 19 | float r = 0.0F; 20 | float g = 0.0F; 21 | float b = 0.0F; 22 | float a = 0.0F; 23 | 24 | constexpr Color() noexcept 25 | { 26 | } 27 | 28 | explicit constexpr Color(std::uint32_t color) noexcept: 29 | r{static_cast((color & 0xFF000000U) >> 24) / 255.0F}, 30 | g{static_cast((color & 0x00FF0000U) >> 16) / 255.0F}, 31 | b{static_cast((color & 0x0000FF00U) >> 8) / 255.0F}, 32 | a{static_cast(color & 0x000000FFU) / 255.0F} 33 | { 34 | } 35 | 36 | template ::value>::type* = nullptr> 37 | constexpr Color(T red, T green, T blue, T alpha = 0xFFU) noexcept: 38 | r{red / 255.0F}, 39 | g{green / 255.0F}, 40 | b{blue / 255.0F}, 41 | a{alpha / 255.0F} 42 | { 43 | } 44 | 45 | constexpr Color(float red, float green, float blue, float alpha = 1.0F) noexcept: 46 | r{red}, 47 | g{green}, 48 | b{blue}, 49 | a{alpha} 50 | { 51 | } 52 | 53 | [[nodiscard]] constexpr std::uint32_t getIntValue() const noexcept 54 | { 55 | return (static_cast(r * 255.0F) << 24) | 56 | (static_cast(g * 255.0F) << 16) | 57 | (static_cast(b * 255.0F) << 8) | 58 | static_cast(a * 255.0F); 59 | } 60 | 61 | [[nodiscard]] std::uint32_t getIntValueRaw() const noexcept 62 | { 63 | const std::array result{ 64 | static_cast(r * 255.0F), 65 | static_cast(g * 255.0F), 66 | static_cast(b * 255.0F), 67 | static_cast(a * 255.0F) 68 | }; 69 | 70 | return *reinterpret_cast(result.data()); 71 | } 72 | }; 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /demo/windows/resource.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE 9, 1 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "resource.h\0" 30 | END 31 | 32 | 2 TEXTINCLUDE 33 | BEGIN 34 | "#include ""winres.h""\r\n" 35 | "\0" 36 | END 37 | 38 | 3 TEXTINCLUDE 39 | BEGIN 40 | "\r\n" 41 | "\0" 42 | END 43 | 44 | #endif // APSTUDIO_INVOKED 45 | 46 | 47 | ///////////////////////////////////////////////////////////////////////////// 48 | // 49 | // Version 50 | // 51 | 52 | VS_VERSION_INFO VERSIONINFO 53 | FILEVERSION 1,0,0,0 54 | PRODUCTVERSION 1,0,0,0 55 | FILEFLAGSMASK 0x3fL 56 | #ifdef _DEBUG 57 | FILEFLAGS 0x1L 58 | #else 59 | FILEFLAGS 0x0L 60 | #endif 61 | FILEOS 0x40004L 62 | FILETYPE 0x0L 63 | FILESUBTYPE 0x0L 64 | BEGIN 65 | BLOCK "StringFileInfo" 66 | BEGIN 67 | BLOCK "040904b0" 68 | BEGIN 69 | VALUE "CompanyName", "Elviss Strazdins" 70 | VALUE "FileDescription", "SoftwareRenderer demo" 71 | VALUE "FileVersion", "1.0.0.0" 72 | VALUE "InternalName", "samples.rc" 73 | VALUE "LegalCopyright", "Copyright (C) 2022" 74 | VALUE "OriginalFilename", "samples.rc" 75 | VALUE "ProductName", "SoftwareRenderer" 76 | VALUE "ProductVersion", "1.0.0.0" 77 | END 78 | END 79 | BLOCK "VarFileInfo" 80 | BEGIN 81 | VALUE "Translation", 0x409, 1200 82 | END 83 | END 84 | 85 | #endif // English (United States) resources 86 | ///////////////////////////////////////////////////////////////////////////// 87 | 88 | 89 | 90 | #ifndef APSTUDIO_INVOKED 91 | ///////////////////////////////////////////////////////////////////////////// 92 | // 93 | // Generated from the TEXTINCLUDE 3 resource. 94 | // 95 | 96 | 97 | ///////////////////////////////////////////////////////////////////////////// 98 | #endif // not APSTUDIO_INVOKED 99 | -------------------------------------------------------------------------------- /demo/ApplicationMacOS.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONMACOS_HPP 6 | #define APPLICATIONMACOS_HPP 7 | 8 | #import 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | namespace objc 14 | { 15 | template 16 | class Pointer final 17 | { 18 | public: 19 | Pointer() noexcept = default; 20 | 21 | ~Pointer() 22 | { 23 | [p release]; 24 | } 25 | 26 | Pointer(T* a) noexcept: p{a} {} 27 | Pointer& operator=(T* a) noexcept 28 | { 29 | [p release]; 30 | p = a; 31 | return *this; 32 | } 33 | 34 | operator T*() const noexcept { return p; } 35 | bool operator!() const noexcept { return p == nil; } 36 | 37 | private: 38 | T* p = nil; 39 | }; 40 | } 41 | 42 | namespace cf 43 | { 44 | template >* = nullptr> 45 | class Pointer final 46 | { 47 | public: 48 | Pointer() noexcept = default; 49 | 50 | ~Pointer() 51 | { 52 | if (p) CFRelease(p); 53 | } 54 | 55 | Pointer(T a) noexcept: p{a} {} 56 | Pointer& operator=(T a) noexcept 57 | { 58 | if (p) CFRelease(p); 59 | p = a; 60 | return *this; 61 | } 62 | 63 | operator T() const noexcept { return p; } 64 | bool operator!() const noexcept { return p == nullptr; } 65 | 66 | private: 67 | T p = nullptr; 68 | }; 69 | } 70 | 71 | class ApplicationMacOS: public Application 72 | { 73 | public: 74 | ApplicationMacOS(); 75 | ~ApplicationMacOS(); 76 | 77 | void draw(); 78 | void didResize(); 79 | 80 | void run(); 81 | 82 | private: 83 | objc::Pointer pool; 84 | 85 | objc::Pointer> appDelegate; 86 | objc::Pointer window; 87 | objc::Pointer content; 88 | objc::Pointer> windowDelegate; 89 | objc::Pointer timer; 90 | 91 | std::size_t componentsPerPixel; 92 | std::size_t bitsPerComponent; 93 | cf::Pointer colorSpace; 94 | cf::Pointer provider; 95 | }; 96 | } 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /demo/ApplicationIOS.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONIOS_HPP 6 | #define APPLICATIONIOS_HPP 7 | 8 | #import 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | namespace objc 14 | { 15 | template 16 | class Pointer final 17 | { 18 | public: 19 | Pointer() noexcept = default; 20 | 21 | ~Pointer() 22 | { 23 | [p release]; 24 | } 25 | 26 | Pointer(T* a) noexcept: p{a} {} 27 | Pointer& operator=(T* a) noexcept 28 | { 29 | [p release]; 30 | p = a; 31 | return *this; 32 | } 33 | 34 | operator T*() const noexcept { return p; } 35 | bool operator!() const noexcept { return p == nil; } 36 | 37 | private: 38 | T* p = nil; 39 | }; 40 | } 41 | 42 | namespace cf 43 | { 44 | template >* = nullptr> 45 | class Pointer final 46 | { 47 | public: 48 | Pointer() noexcept = default; 49 | 50 | ~Pointer() 51 | { 52 | if (p) CFRelease(p); 53 | } 54 | 55 | Pointer(T a) noexcept: p{a} {} 56 | Pointer& operator=(T a) noexcept 57 | { 58 | if (p) CFRelease(p); 59 | p = a; 60 | return *this; 61 | } 62 | 63 | operator T() const noexcept { return p; } 64 | bool operator!() const noexcept { return p == nullptr; } 65 | 66 | private: 67 | T p = nullptr; 68 | }; 69 | } 70 | 71 | class ApplicationIOS: public Application 72 | { 73 | public: 74 | ApplicationIOS(); 75 | ~ApplicationIOS(); 76 | 77 | void createWindow(); 78 | 79 | void draw(); 80 | void didResize(CGFloat newWidth, CGFloat newHeight); 81 | 82 | void run(int argc, char* argv[]); 83 | 84 | private: 85 | objc::Pointer pool; 86 | 87 | UIScreen* screen = nil; 88 | objc::Pointer window; 89 | objc::Pointer content; 90 | objc::Pointer viewController; 91 | objc::Pointer timer; 92 | 93 | std::size_t componentsPerPixel; 94 | std::size_t bitsPerComponent; 95 | cf::Pointer colorSpace; 96 | cf::Pointer provider; 97 | }; 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /demo/ApplicationTVOS.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATIONIOS_HPP 6 | #define APPLICATIONIOS_HPP 7 | 8 | #import 9 | #include "Application.hpp" 10 | 11 | namespace demo 12 | { 13 | namespace objc 14 | { 15 | template 16 | class Pointer final 17 | { 18 | public: 19 | Pointer() noexcept = default; 20 | 21 | ~Pointer() 22 | { 23 | [p release]; 24 | } 25 | 26 | Pointer(T* a) noexcept: p{a} {} 27 | Pointer& operator=(T* a) noexcept 28 | { 29 | [p release]; 30 | p = a; 31 | return *this; 32 | } 33 | 34 | operator T*() const noexcept { return p; } 35 | bool operator!() const noexcept { return p == nil; } 36 | 37 | private: 38 | T* p = nil; 39 | }; 40 | } 41 | 42 | namespace cf 43 | { 44 | template >* = nullptr> 45 | class Pointer final 46 | { 47 | public: 48 | Pointer() noexcept = default; 49 | 50 | ~Pointer() 51 | { 52 | if (p) CFRelease(p); 53 | } 54 | 55 | Pointer(T a) noexcept: p{a} {} 56 | Pointer& operator=(T a) noexcept 57 | { 58 | if (p) CFRelease(p); 59 | p = a; 60 | return *this; 61 | } 62 | 63 | operator T() const noexcept { return p; } 64 | bool operator!() const noexcept { return p == nullptr; } 65 | 66 | private: 67 | T p = nullptr; 68 | }; 69 | } 70 | 71 | class ApplicationTVOS: public Application 72 | { 73 | public: 74 | ApplicationTVOS(); 75 | ~ApplicationTVOS(); 76 | 77 | void createWindow(); 78 | 79 | void draw(); 80 | void didResize(CGFloat newWidth, CGFloat newHeight); 81 | 82 | void run(int argc, char* argv[]); 83 | 84 | private: 85 | objc::Pointer pool; 86 | 87 | UIScreen* screen = nil; 88 | objc::Pointer window; 89 | objc::Pointer content; 90 | objc::Pointer viewController; 91 | objc::Pointer timer; 92 | 93 | std::size_t componentsPerPixel; 94 | std::size_t bitsPerComponent; 95 | cf::Pointer colorSpace; 96 | cf::Pointer provider; 97 | }; 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /demo/ApplicationAndroid.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ApplicationAndroid.hpp" 10 | 11 | std::unique_ptr application; 12 | 13 | extern "C" JNIEXPORT jint JNIEXPORT JNI_OnLoad(JavaVM* javaVM, void*) 14 | { 15 | try 16 | { 17 | application.reset(new demo::ApplicationAndroid(javaVM)); 18 | } 19 | catch (const std::exception& e) 20 | { 21 | std::cerr << e.what() << std::endl; 22 | } 23 | 24 | return JNI_VERSION_1_6; 25 | } 26 | 27 | extern "C" JNIEXPORT void JNIEXPORT JNI_OnUnload(JavaVM*, void*) 28 | { 29 | application.reset(); 30 | } 31 | 32 | extern "C" JNIEXPORT void JNICALL Java_lv_elviss_softwarerenderer_DemoLibJNIWrapper_init(JNIEnv*, jclass, jint width, jint height) 33 | { 34 | try 35 | { 36 | application->init(width, height); 37 | } 38 | catch (const std::exception& e) 39 | { 40 | std::cerr << e.what() << std::endl; 41 | } 42 | } 43 | 44 | extern "C" JNIEXPORT void JNICALL Java_lv_elviss_softwarerenderer_DemoLibJNIWrapper_onSizeChanged(JNIEnv*, jclass, jint width, jint height) 45 | { 46 | try 47 | { 48 | application->onSizeChanged(width, height); 49 | } 50 | catch (const std::exception& e) 51 | { 52 | std::cerr << e.what() << std::endl; 53 | } 54 | } 55 | 56 | extern "C" JNIEXPORT void JNICALL Java_lv_elviss_softwarerenderer_DemoLibJNIWrapper_onDraw(JNIEnv*, jclass, jobject bitmap) 57 | { 58 | try 59 | { 60 | application->onDraw(bitmap); 61 | } 62 | catch (const std::exception& e) 63 | { 64 | std::cerr << e.what() << std::endl; 65 | } 66 | } 67 | 68 | namespace demo 69 | { 70 | std::string getResourcePath() 71 | { 72 | return "Resources"; 73 | } 74 | 75 | ApplicationAndroid::ApplicationAndroid(JavaVM* initJavaVM): 76 | javaVM(initJavaVM) 77 | { 78 | } 79 | 80 | ApplicationAndroid::~ApplicationAndroid() 81 | { 82 | } 83 | 84 | void ApplicationAndroid::init(jint initWidth, jint initHeight) 85 | { 86 | // TODO: implement file loading from apk 87 | setup(static_cast(newWidth), 88 | static_cast(newHeight)); 89 | } 90 | 91 | void ApplicationAndroid::onDraw(jobject bitmap) 92 | { 93 | render(); 94 | 95 | // TODO: copy pixels to the bitmap 96 | } 97 | 98 | void ApplicationAndroid::onSizeChanged(jint newWidth, jint newHeight) 99 | { 100 | onResize(static_cast(newWidth), 101 | static_cast(newHeight)); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /demo/SoftwareRenderer.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {eab7caca-519d-4590-b637-9b8f8b31efa2} 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | sr 26 | 27 | 28 | sr 29 | 30 | 31 | sr 32 | 33 | 34 | sr 35 | 36 | 37 | sr 38 | 39 | 40 | sr 41 | 42 | 43 | sr 44 | 45 | 46 | sr 47 | 48 | 49 | sr 50 | 51 | 52 | sr 53 | 54 | 55 | sr 56 | 57 | 58 | sr 59 | 60 | 61 | sr 62 | 63 | 64 | sr 65 | 66 | 67 | sr 68 | 69 | 70 | sr 71 | 72 | 73 | Header Files 74 | 75 | 76 | Header Files 77 | 78 | 79 | Header Files 80 | 81 | 82 | 83 | 84 | Source Files 85 | 86 | 87 | 88 | 89 | Resource Files 90 | 91 | 92 | -------------------------------------------------------------------------------- /demo/ApplicationHaiku.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "ApplicationHaiku.hpp" 11 | 12 | namespace demo 13 | { 14 | std::string getResourcePath() 15 | { 16 | return "Resources"; 17 | } 18 | 19 | class AppView: public BView 20 | { 21 | public: 22 | AppView(ApplicationHaiku& initApplication, const BRect& frame, const std::string& title): 23 | BView{frame, title.c_str(), B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS}, 24 | application{initApplication} 25 | { 26 | } 27 | 28 | virtual void FrameResized(float newWidth, float newHeight) override 29 | { 30 | application.didResize(newWidth, newHeight); 31 | } 32 | 33 | virtual void Draw(BRect) override 34 | { 35 | application.draw(); 36 | } 37 | 38 | private: 39 | ApplicationHaiku& application; 40 | }; 41 | 42 | ApplicationHaiku::ApplicationHaiku(): 43 | BApplication{"application/x-vnd.SoftwareRenderer"} 44 | { 45 | const BRect frame{100, 100, 100 + 640, 100 + 480}; 46 | window = new BWindow(frame, "SoftwareRenderer", B_TITLED_WINDOW, 47 | B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE); 48 | 49 | const BRect bounds = window->Bounds(); 50 | 51 | view = new AppView(*this, bounds, "render"); 52 | window->AddChild(view); 53 | 54 | bitmap = new BBitmap(bounds, 0, B_RGB32); 55 | 56 | const auto w = static_cast(bounds.Width()); 57 | const auto h = static_cast(bounds.Height()); 58 | 59 | setup(w, h); 60 | window->Show(); 61 | SetPulseRate(100000); 62 | } 63 | 64 | ApplicationHaiku::~ApplicationHaiku() 65 | { 66 | if (bitmap) delete bitmap; 67 | } 68 | 69 | void ApplicationHaiku::draw() 70 | { 71 | render(); 72 | 73 | const auto& frameBuffer = getFrameBuffer(); 74 | bitmap->ImportBits(frameBuffer.getData().data(), frameBuffer.getWidth() * frameBuffer.getHeight() * 4, 75 | frameBuffer.getWidth() * 4, 0, B_RGB32); 76 | 77 | view->DrawBitmap(bitmap, bitmap->Bounds(), view->Bounds(), 0); 78 | } 79 | 80 | void ApplicationHaiku::didResize(float newWidth, float newHeight) 81 | { 82 | if (bitmap) delete bitmap; 83 | 84 | bitmap = new BBitmap(BRect{0, 0, newWidth, newHeight}, 0, B_RGB32); 85 | 86 | onResize(static_cast(newWidth), 87 | static_cast(newHeight)); 88 | } 89 | 90 | void ApplicationHaiku::run() 91 | { 92 | Run(); 93 | } 94 | 95 | void ApplicationHaiku::Pulse() 96 | { 97 | if (window->Lock()) 98 | { 99 | view->Invalidate(); 100 | window->Unlock(); 101 | } 102 | } 103 | } 104 | 105 | int main() 106 | { 107 | try 108 | { 109 | demo::ApplicationHaiku application; 110 | application.run(); 111 | return EXIT_SUCCESS; 112 | } 113 | catch (const std::exception& e) 114 | { 115 | std::cerr << e.what() << std::endl; 116 | return EXIT_FAILURE; 117 | } 118 | catch (...) 119 | { 120 | return EXIT_FAILURE; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /sr/BlendState.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_BLENDSTATE_HPP 6 | #define SR_BLENDSTATE_HPP 7 | 8 | #include "Color.hpp" 9 | #include "RenderError.hpp" 10 | 11 | namespace sr 12 | { 13 | class BlendState final 14 | { 15 | public: 16 | enum class Factor 17 | { 18 | zero, 19 | one, 20 | srcColor, 21 | invSrcColor, 22 | srcAlpha, 23 | invSrcAlpha, 24 | destAlpha, 25 | invDestAlpha, 26 | destColor, 27 | invDestColor, 28 | srcAlphaSat, 29 | blendFactor, 30 | invBlendFactor 31 | }; 32 | 33 | enum class Operation 34 | { 35 | add, 36 | subtract, 37 | reverseSubtract, 38 | min, 39 | max 40 | }; 41 | 42 | BlendState::Factor colorBlendSource = BlendState::Factor::one; 43 | BlendState::Factor colorBlendDest = BlendState::Factor::zero; 44 | BlendState::Operation colorOperation = BlendState::Operation::add; 45 | BlendState::Factor alphaBlendSource = BlendState::Factor::one; 46 | BlendState::Factor alphaBlendDest = BlendState::Factor::zero; 47 | BlendState::Operation alphaOperation = BlendState::Operation::add; 48 | bool enabled = false; 49 | Color blendFactor; 50 | }; 51 | 52 | [[nodiscard]] inline float getValue(const BlendState::Factor factor, 53 | const float srcColor, 54 | const float srcAlpha, 55 | const float destColor, 56 | const float destAlpha, 57 | const float blendFactor) 58 | { 59 | switch (factor) 60 | { 61 | case BlendState::Factor::zero: return 0.0F; 62 | case BlendState::Factor::one: return 1.0F; 63 | case BlendState::Factor::srcColor: return srcColor; 64 | case BlendState::Factor::invSrcColor: return 1.0F - srcColor; 65 | case BlendState::Factor::srcAlpha: return srcAlpha; 66 | case BlendState::Factor::invSrcAlpha: return 1.0F - srcAlpha; 67 | case BlendState::Factor::destAlpha: return destAlpha; 68 | case BlendState::Factor::invDestAlpha: return 1.0F - destAlpha; 69 | case BlendState::Factor::destColor: return destColor; 70 | case BlendState::Factor::invDestColor: return 1.0F - destColor; 71 | case BlendState::Factor::srcAlphaSat: return std::min(srcAlpha, 1.0F - destAlpha); 72 | case BlendState::Factor::blendFactor: return blendFactor; 73 | case BlendState::Factor::invBlendFactor: return 1.0F - blendFactor; 74 | default: throw RenderError{"Invalid blend factor"}; 75 | } 76 | } 77 | 78 | [[nodiscard]] inline float getValue(const BlendState::Operation operation, 79 | const float a, 80 | const float b) 81 | { 82 | switch (operation) 83 | { 84 | case BlendState::Operation::add: return std::clamp(a + b, 0.0F, 1.0F); 85 | case BlendState::Operation::subtract: return std::clamp(a - b, 0.0F, 1.0F); 86 | case BlendState::Operation::reverseSubtract: return std::clamp(b - a, 0.0F, 1.0F); 87 | case BlendState::Operation::min: return std::min(a, b); 88 | case BlendState::Operation::max: return std::max(a, b); 89 | default: throw RenderError{"Invalid blend operation"}; 90 | } 91 | } 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /demo/Makefile: -------------------------------------------------------------------------------- 1 | DEBUG=0 2 | ifeq ($(OS),Windows_NT) 3 | PLATFORM=windows 4 | else 5 | architecture=$(shell uname -m) 6 | os=$(shell uname -s) 7 | 8 | ifeq ($(os),Linux) 9 | PLATFORM=linux 10 | else ifeq ($(os),SunOS) 11 | PLATFORM=sunos 12 | else ifeq ($(os),FreeBSD) 13 | PLATFORM=bsd 14 | else ifeq ($(os),DragonFly) 15 | PLATFORM=bsd 16 | else ifeq ($(os),NetBSD) 17 | PLATFORM=bsd 18 | else ifeq ($(os),Darwin) 19 | PLATFORM=macos 20 | else ifeq ($(os),Haiku) 21 | PLATFORM=haiku 22 | endif 23 | endif 24 | ifeq ($(PLATFORM),emscripten) 25 | CC=emcc 26 | CXX=em++ 27 | endif 28 | CXXFLAGS=-std=c++17 -Wall -Wextra -Wshadow -I../sr 29 | ifeq ($(PLATFORM),windows) 30 | LDFLAGS+=-u WinMain 31 | SOURCES=ApplicationWindows.cpp 32 | else ifeq ($(PLATFORM),linux) 33 | LDFLAGS+=-lX11 34 | SOURCES=ApplicationX11.cpp 35 | else ifeq ($(PLATFORM),sunos) 36 | LDFLAGS+=-lX11 37 | SOURCES=ApplicationX11.cpp 38 | else ifeq ($(PLATFORM),bsd) 39 | CXXFLAGS+=-I/usr/local/include 40 | LDFLAGS+=-lX11 -L/usr/local/lib 41 | SOURCES=ApplicationX11.cpp 42 | else ifeq ($(PLATFORM),macos) 43 | LDFLAGS+=-framework Cocoa 44 | SOURCES=ApplicationMacOS.mm 45 | else ifeq ($(PLATFORM),haiku) 46 | LDFLAGS+=-lbe 47 | SOURCES=ApplicationHaiku.cpp 48 | else ifeq ($(PLATFORM),ios) 49 | CFLAGS+=-arch arm64 -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=8.0 50 | CXXFLAGS+=-arch arm64 -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=8.0 51 | LDFLAGS+=-arch arm64 -isysroot $(shell xcrun --sdk iphoneos --show-sdk-path) -miphoneos-version-min=8.0 \ 52 | -framework CoreGraphics -framework Foundation -framework UIKit 53 | SOURCES=ApplicationIOS.mm 54 | else ifeq ($(PLATFORM),tvos) 55 | CFLAGS+=-arch arm64 -isysroot $(shell xcrun --sdk appletvos --show-sdk-path) -mtvos-version-min=9.0 56 | CXXFLAGS+=-arch arm64 -isysroot $(shell xcrun --sdk appletvos --show-sdk-path) -mtvos-version-min=9.0 57 | LDFLAGS+=-arch arm64 -isysroot $(shell xcrun --sdk appletvos --show-sdk-path) -mtvos-version-min=9.0 \ 58 | -framework CoreGraphics -framework Foundation -framework UIKit 59 | SOURCES=ApplicationTVOS.mm 60 | else ifeq ($(PLATFORM),emscripten) 61 | LDFLAGS+=--embed-file Resources -s TOTAL_MEMORY=134217728 62 | endif 63 | BASE_NAMES=$(basename $(SOURCES)) 64 | OBJECTS=$(BASE_NAMES:=.o) 65 | DEPENDENCIES=$(OBJECTS:.o=.d) 66 | ifeq ($(PLATFORM),emscripten) 67 | EXECUTABLE=SoftwareRenderer.js 68 | else 69 | EXECUTABLE=SoftwareRenderer 70 | endif 71 | 72 | .PHONY: all 73 | ifeq ($(DEBUG),1) 74 | all: CXXFLAGS+=-DDEBUG=1 -g 75 | else 76 | all: CXXFLAGS+=-O3 77 | all: LDFLAGS+=-O3 78 | endif 79 | all: bundle 80 | 81 | .PHONY: bundle 82 | bundle: $(EXECUTABLE) 83 | ifeq ($(PLATFORM),macos) 84 | bundle: 85 | mkdir -p $(EXECUTABLE).app 86 | mkdir -p $(EXECUTABLE).app/Contents 87 | sed -e s/'$$(DEVELOPMENT_LANGUAGE)'/en/ \ 88 | -e s/'$$(EXECUTABLE_NAME)'/SoftwareRenderer/ \ 89 | -e s/'$$(PRODUCT_BUNDLE_IDENTIFIER)'/lv.elviss.softwarerenderer/ \ 90 | -e s/'$$(PRODUCT_NAME)'/SoftwareRenderer/ \ 91 | -e s/'$$(MACOSX_DEPLOYMENT_TARGET)'/10.8/ \ 92 | macos/Info.plist > $(EXECUTABLE).app/Contents/Info.plist 93 | mkdir -p $(EXECUTABLE).app/Contents/MacOS 94 | cp -f $(EXECUTABLE) $(EXECUTABLE).app/Contents/MacOS 95 | mkdir -p $(EXECUTABLE).app/Contents/Resources 96 | cp -f Resources/* $(EXECUTABLE).app/Contents/Resources/ 97 | else ifeq ($(PLATFORM),ios) 98 | mkdir -p $(EXECUTABLE).app 99 | sed -e s/'$$(EXECUTABLE_NAME)'/SoftwareRenderer/ \ 100 | -e s/'$$(PRODUCT_BUNDLE_IDENTIFIER)'/lv.elviss.softwarerenderer/ \ 101 | -e s/'$$(PRODUCT_NAME)'/SoftwareRenderer/ \ 102 | ios/Info.plist > $(EXECUTABLE).app/Info.plist 103 | cp -f $(EXECUTABLE) $(EXECUTABLE).app 104 | cp -f Resources/* $(EXECUTABLE).app 105 | else ifeq ($(PLATFORM),tvos) 106 | mkdir -p $(EXECUTABLE).app 107 | sed -e s/'$$(EXECUTABLE_NAME)'/SoftwareRenderer/ \ 108 | -e s/'$$(PRODUCT_BUNDLE_IDENTIFIER)'/lv.elviss.softwarerenderer/ \ 109 | -e s/'$$(PRODUCT_NAME)'/SoftwareRenderer/ \ 110 | tvos/Info.plist > $(EXECUTABLE).app/Info.plist 111 | cp -f $(EXECUTABLE) $(EXECUTABLE).app 112 | cp -f Resources/* $(EXECUTABLE).app 113 | endif 114 | 115 | $(EXECUTABLE): $(OBJECTS) 116 | $(CXX) $(OBJECTS) $(LDFLAGS) -o $@ 117 | 118 | -include $(DEPENDENCIES) 119 | 120 | %.o: %.cpp 121 | $(CXX) -c $(CXXFLAGS) -MMD -MP $< -o $@ 122 | 123 | %.o: %.mm 124 | $(CXX) -c -fno-objc-arc $(CXXFLAGS) -MMD -MP $< -o $@ 125 | 126 | .PHONY: clean 127 | clean: 128 | $(RM) $(EXECUTABLE) $(OBJECTS) $(DEPENDENCIES) *.js.mem *.js *.hpp.gch $(EXECUTABLE).exe assetcatalog_generated_info.plist assetcatalog_dependencies 129 | $(RM) -r $(EXECUTABLE).app -------------------------------------------------------------------------------- /demo/ApplicationX11.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "ApplicationX11.hpp" 9 | 10 | namespace demo 11 | { 12 | std::string getResourcePath() 13 | { 14 | return "Resources"; 15 | } 16 | 17 | ApplicationX11::ApplicationX11() 18 | { 19 | if (!XInitThreads()) 20 | throw std::runtime_error{"Failed to initialize thread support"}; 21 | 22 | display = XOpenDisplay(nullptr); 23 | 24 | if (!display) 25 | throw std::runtime_error{"Failed to open display"}; 26 | 27 | Screen* screen = XDefaultScreenOfDisplay(display); 28 | const int screenIndex = XScreenNumberOfScreen(screen); 29 | visual = DefaultVisual(display, screenIndex); 30 | depth = DefaultDepth(display, screenIndex); 31 | 32 | const auto w = static_cast(XWidthOfScreen(screen) * 0.6F); 33 | const auto h = static_cast(XHeightOfScreen(screen) * 0.6F); 34 | 35 | XSetWindowAttributes swa; 36 | swa.background_pixel = XWhitePixel(display, screenIndex); 37 | swa.border_pixel = 0; 38 | swa.event_mask = KeyPressMask | ExposureMask | StructureNotifyMask; 39 | 40 | window = XCreateWindow(display, 41 | RootWindow(display, screenIndex), 42 | 0, 0, w, h, 43 | 0, depth, InputOutput, visual, 44 | CWBorderPixel | CWBackPixel | CWEventMask, &swa); 45 | 46 | XSetStandardProperties(display, 47 | window, "SoftwareRenderer", "SoftwareRenderer", None, 48 | nullptr, 0, nullptr); 49 | 50 | XMapWindow(display, window); 51 | 52 | protocolsAtom = XInternAtom(display, "WM_PROTOCOLS", False); 53 | deleteAtom = XInternAtom(display, "WM_DELETE_WINDOW", False); 54 | XSetWMProtocols(display, window, &deleteAtom, 1); 55 | 56 | gc = XCreateGC(display, window, 0, 0); 57 | XSetForeground(display, gc, 0); 58 | 59 | setup(w, h); 60 | } 61 | 62 | ApplicationX11::~ApplicationX11() 63 | { 64 | if (display) 65 | { 66 | if (gc) XFreeGC(display, gc); 67 | if (window) XDestroyWindow(display, window); 68 | 69 | XCloseDisplay(display); 70 | } 71 | } 72 | 73 | void ApplicationX11::draw() 74 | { 75 | render(); 76 | 77 | const auto& frameBuffer = getFrameBuffer(); 78 | 79 | const auto data = frameBuffer.getData().data(); 80 | XImage* image = XCreateImage(display, visual, depth, ZPixmap, 0, 81 | const_cast(reinterpret_cast(data)), 82 | frameBuffer.getWidth(), frameBuffer.getHeight(), 32, 0); 83 | 84 | XPutImage(display, window, gc, image, 0, 0, 0, 0, 85 | frameBuffer.getWidth(), frameBuffer.getHeight()); 86 | XFlush(display); 87 | XFree(image); 88 | } 89 | 90 | void ApplicationX11::didResize(int newWidth, int newHeight) 91 | { 92 | onResize(static_cast(newWidth), 93 | static_cast(newHeight)); 94 | } 95 | 96 | void ApplicationX11::run() 97 | { 98 | bool running = true; 99 | 100 | while (running) 101 | { 102 | while (XPending(display)) 103 | { 104 | XEvent event; 105 | XNextEvent(display, &event); 106 | 107 | switch (event.type) 108 | { 109 | case ClientMessage: 110 | if (event.xclient.message_type == protocolsAtom && 111 | static_cast(event.xclient.data.l[0]) == deleteAtom) 112 | running = false; 113 | break; 114 | case KeyPress: 115 | break; 116 | case Expose: 117 | draw(); 118 | break; 119 | case ConfigureNotify: 120 | didResize(event.xconfigure.width, event.xconfigure.height); 121 | break; 122 | } 123 | } 124 | 125 | draw(); 126 | } 127 | } 128 | } 129 | 130 | int main() 131 | { 132 | try 133 | { 134 | demo::ApplicationX11 application; 135 | application.run(); 136 | return EXIT_SUCCESS; 137 | } 138 | catch (const std::exception& e) 139 | { 140 | std::cerr << e.what() << std::endl; 141 | return EXIT_FAILURE; 142 | } 143 | catch (...) 144 | { 145 | return EXIT_FAILURE; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /demo/ApplicationWindows.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include "ApplicationWindows.hpp" 9 | 10 | namespace 11 | { 12 | LRESULT CALLBACK windowProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) 13 | { 14 | auto applicationWindows = reinterpret_cast(GetWindowLongPtr(window, GWLP_USERDATA)); 15 | if (!applicationWindows) return DefWindowProcW(window, msg, wParam, lParam); 16 | 17 | switch (msg) 18 | { 19 | case WM_PAINT: 20 | { 21 | applicationWindows->draw(); 22 | break; 23 | } 24 | 25 | case WM_SIZE: 26 | { 27 | applicationWindows->didResize(); 28 | break; 29 | } 30 | 31 | case WM_DESTROY: 32 | { 33 | PostQuitMessage(0); 34 | break; 35 | } 36 | } 37 | 38 | return DefWindowProcW(window, msg, wParam, lParam); 39 | } 40 | } 41 | 42 | namespace demo 43 | { 44 | std::string getResourcePath() 45 | { 46 | return "Resources"; 47 | } 48 | 49 | ApplicationWindows::ApplicationWindows() 50 | { 51 | HINSTANCE instance = GetModuleHandleW(nullptr); 52 | 53 | WNDCLASSEXW wc; 54 | wc.cbSize = sizeof(wc); 55 | wc.style = CS_HREDRAW | CS_VREDRAW; 56 | wc.lpfnWndProc = windowProc; 57 | wc.cbClsExtra = 0; 58 | wc.cbWndExtra = 0; 59 | wc.hInstance = instance; 60 | // Application icon should be the first resource 61 | //wc.hIcon = LoadIconW(instance, MAKEINTRESOURCEW(101)); 62 | wc.hIcon = nullptr; 63 | wc.hCursor = LoadCursor(nullptr, IDC_ARROW); 64 | wc.hbrBackground = static_cast(GetStockObject(COLOR_WINDOW)); 65 | wc.lpszMenuName = nullptr; 66 | wc.lpszClassName = L"SoftwareRenderer"; 67 | wc.hIconSm = nullptr; 68 | 69 | windowClass = RegisterClassExW(&wc); 70 | if (!windowClass) 71 | throw std::system_error{static_cast(GetLastError()), std::system_category(), "Failed to register window class"}; 72 | 73 | const DWORD windowStyle = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPSIBLINGS | WS_BORDER | WS_DLGFRAME | WS_THICKFRAME | WS_GROUP | WS_TABSTOP | WS_SIZEBOX | WS_MAXIMIZEBOX; 74 | const DWORD windowExStyle = WS_EX_APPWINDOW; 75 | 76 | window = CreateWindowExW(windowExStyle, MAKEINTATOM(windowClass), L"SoftwareRenderer", windowStyle, 77 | CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, instance, nullptr); 78 | 79 | if (!window) 80 | throw std::system_error{static_cast(GetLastError()), std::system_category(), "Failed to create window"}; 81 | 82 | RECT clientRect; 83 | GetClientRect(window, &clientRect); 84 | 85 | const auto w = static_cast(clientRect.right - clientRect.left); 86 | const auto h = static_cast(clientRect.bottom - clientRect.top); 87 | 88 | ShowWindow(window, SW_SHOW); 89 | SetWindowLongPtr(window, GWLP_USERDATA, (LONG_PTR)this); 90 | 91 | setup(w, h); 92 | } 93 | 94 | ApplicationWindows::~ApplicationWindows() 95 | { 96 | if (window) DestroyWindow(window); 97 | if (windowClass) UnregisterClassW(MAKEINTATOM(windowClass), GetModuleHandleW(nullptr)); 98 | } 99 | 100 | void ApplicationWindows::draw() 101 | { 102 | render(); 103 | 104 | const auto& frameBuffer = getFrameBuffer(); 105 | 106 | BITMAPINFO info = {}; 107 | info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 108 | info.bmiHeader.biWidth = static_cast(frameBuffer.getWidth()); 109 | info.bmiHeader.biHeight = -static_cast(frameBuffer.getHeight()); 110 | info.bmiHeader.biPlanes = 1; 111 | info.bmiHeader.biBitCount = 32; 112 | info.bmiHeader.biCompression = BI_RGB; 113 | 114 | PAINTSTRUCT ps; 115 | HDC hdc = BeginPaint(window, &ps); 116 | 117 | SetDIBitsToDevice(hdc, 0, 0, 118 | static_cast(frameBuffer.getWidth()), 119 | static_cast(frameBuffer.getHeight()), 120 | 0, 0, 0, 121 | static_cast(frameBuffer.getHeight()), 122 | frameBuffer.getData().data(), 123 | &info, DIB_RGB_COLORS); 124 | 125 | EndPaint(window, &ps); 126 | } 127 | 128 | void ApplicationWindows::didResize() 129 | { 130 | RECT clientRect; 131 | GetClientRect(window, &clientRect); 132 | 133 | onResize(static_cast(clientRect.right - clientRect.left), 134 | static_cast(clientRect.bottom - clientRect.top)); 135 | } 136 | 137 | void ApplicationWindows::run() 138 | { 139 | MSG msg; 140 | for (;;) 141 | { 142 | while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) > 0) 143 | { 144 | TranslateMessage(&msg); 145 | DispatchMessage(&msg); 146 | 147 | if (msg.message == WM_QUIT) return; 148 | } 149 | 150 | InvalidateRect(window, nullptr, FALSE); 151 | } 152 | } 153 | } 154 | 155 | int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) 156 | { 157 | try 158 | { 159 | demo::ApplicationWindows application; 160 | application.run(); 161 | return EXIT_SUCCESS; 162 | } 163 | catch (const std::exception& e) 164 | { 165 | std::cerr << e.what() << std::endl; 166 | return EXIT_FAILURE; 167 | } 168 | catch (...) 169 | { 170 | return EXIT_FAILURE; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /sr/Size.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_SIZE_HPP 6 | #define SR_SIZE_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include "Vector.hpp" 12 | 13 | namespace sr 14 | { 15 | template class Size final 16 | { 17 | public: 18 | std::array v{}; 19 | 20 | constexpr Size() noexcept {} 21 | 22 | template 23 | explicit constexpr Size(A... args) noexcept: 24 | v{args...} 25 | { 26 | } 27 | 28 | template ::type* = nullptr> 29 | explicit Size(const Size& size) noexcept 30 | { 31 | for (std::size_t i = 0; i < N && i < N2; ++i) 32 | v[i] = size.v[i]; 33 | } 34 | 35 | explicit constexpr Size(const Vector& vec) noexcept: 36 | v{vec.v} 37 | { 38 | } 39 | 40 | T& operator[](std::size_t index) noexcept { return v[index]; } 41 | [[nodiscard]] constexpr T operator[](std::size_t index) const noexcept { return v[index]; } 42 | 43 | template = 1)>::type* = nullptr> 44 | T& width() noexcept { return v[0]; } 45 | 46 | template = 1)>::type* = nullptr> 47 | [[nodiscard]] constexpr T width() const noexcept { return v[0]; } 48 | 49 | template = 2)>::type* = nullptr> 50 | T& height() noexcept { return v[1]; } 51 | 52 | template = 2)>::type* = nullptr> 53 | [[nodiscard]] constexpr T height() const noexcept { return v[1]; } 54 | 55 | template = 3)>::type* = nullptr> 56 | T& depth() noexcept { return v[2]; } 57 | 58 | template = 3)>::type* = nullptr> 59 | [[nodiscard]] constexpr T depth() const noexcept { return v[2]; } 60 | 61 | void scale(const Vector& scale) noexcept 62 | { 63 | for (std::size_t i = 0; i < N; ++i) 64 | v[i] *= scale.v[i]; 65 | } 66 | 67 | [[nodiscard]] const Size operator+(const Size& size) const noexcept 68 | { 69 | Size result = *this; 70 | for (std::size_t i = 0; i < N; ++i) 71 | result.v[i] += size.v[i]; 72 | return result; 73 | } 74 | 75 | Size& operator+=(const Size& size) noexcept 76 | { 77 | for (std::size_t i = 0; i < N; ++i) 78 | v[i] += size.v[i]; 79 | return *this; 80 | } 81 | 82 | [[nodiscard]] const Size operator-(const Size& size) const noexcept 83 | { 84 | Size result = *this; 85 | for (std::size_t i = 0; i < N; ++i) 86 | result.v[i] -= size.v[i]; 87 | return result; 88 | } 89 | 90 | Size& operator-=(const Size& size) noexcept 91 | { 92 | for (std::size_t i = 0; i < N; ++i) 93 | v[i] -= size.v[i]; 94 | return *this; 95 | } 96 | 97 | [[nodiscard]] const Size operator-() const noexcept 98 | { 99 | Size result = *this; 100 | for (auto& c : result.v) 101 | c = -c; 102 | return result; 103 | } 104 | 105 | [[nodiscard]] const Size operator*(const T scalar) const noexcept 106 | { 107 | Size result = *this; 108 | for (auto& c : result.v) 109 | c *= scalar; 110 | return result; 111 | } 112 | 113 | Size& operator*=(const T scalar) noexcept 114 | { 115 | for (auto& c : v) 116 | c *= scalar; 117 | return *this; 118 | } 119 | 120 | [[nodiscard]] const Size operator/(const T scalar) const noexcept 121 | { 122 | Size result = *this; 123 | for (auto& c : result.v) 124 | c /= scalar; 125 | return result; 126 | } 127 | 128 | Size& operator/=(const T scalar) noexcept 129 | { 130 | for (auto& c : v) 131 | c /= scalar; 132 | return *this; 133 | } 134 | 135 | [[nodiscard]] bool operator<(const Size& size) const noexcept 136 | { 137 | for (std::size_t i = 0; i < N; ++i) 138 | if (v[i] < size.v[i]) return true; 139 | else if (size.v[i] < v[i]) return false; 140 | 141 | return false; 142 | } 143 | 144 | [[nodiscard]] bool operator==(const Size& size) const noexcept 145 | { 146 | for (std::size_t i = 0; i < N; ++i) 147 | if (v[i] != size.v[i]) return false; 148 | return true; 149 | } 150 | 151 | [[nodiscard]] bool operator!=(const Size& size) const noexcept 152 | { 153 | for (std::size_t i = 0; i < N; ++i) 154 | if (v[i] != size.v[i]) return true; 155 | return false; 156 | } 157 | 158 | [[nodiscard]] bool isZero() const noexcept 159 | { 160 | for (const auto& c : v) 161 | if (c != T(0)) return false; 162 | return true; 163 | } 164 | 165 | [[nodiscard]] T volume() const noexcept 166 | { 167 | T result = 0; 168 | for (const auto& c : v) 169 | result *= c; 170 | return result; 171 | } 172 | }; 173 | 174 | template 175 | [[nodiscard]] inline const Size operator*(const Size& size, const Vector& v) noexcept 176 | { 177 | auto result = size; 178 | for (std::size_t i = 0; i < N; ++i) 179 | result.v[i] *= v.v[i]; 180 | return result; 181 | } 182 | 183 | template 184 | [[nodiscard]] inline const Size operator/(const Size& size, const Vector& v) noexcept 185 | { 186 | auto result = size; 187 | for (std::size_t i = 0; i < N; ++i) 188 | result.v[i] /= v.v[i]; 189 | return result; 190 | } 191 | } 192 | 193 | #endif 194 | -------------------------------------------------------------------------------- /sr/Rect.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_RECT_HPP 6 | #define SR_RECT_HPP 7 | 8 | #include "Vector.hpp" 9 | #include "Size.hpp" 10 | 11 | namespace sr 12 | { 13 | template class Rect final 14 | { 15 | public: 16 | Vector position; 17 | Size size; 18 | 19 | constexpr Rect() noexcept {} 20 | 21 | constexpr Rect(const T width, const T height) noexcept: 22 | size{width, height} 23 | { 24 | } 25 | 26 | constexpr Rect(const T x, const T y, 27 | const T width, const T height) noexcept: 28 | position{x, y}, size{width, height} 29 | { 30 | } 31 | 32 | constexpr Rect(const Vector& initPosition, 33 | const T width, const T height) noexcept: 34 | position{initPosition}, size{width, height} 35 | { 36 | } 37 | 38 | constexpr Rect(const Vector& initPosition, 39 | const Size& initSize) noexcept: 40 | position{initPosition}, size{initSize} 41 | { 42 | } 43 | 44 | [[nodiscard]] constexpr bool isEmpty() const noexcept 45 | { 46 | return size.isZero(); 47 | } 48 | 49 | void setPosition(const T x, const T y) noexcept 50 | { 51 | position.v[0] = x; 52 | position.v[1] = y; 53 | } 54 | 55 | void setPosition(const Vector& newPosition) noexcept 56 | { 57 | position = newPosition; 58 | } 59 | 60 | [[nodiscard]] constexpr T left() const noexcept 61 | { 62 | return position.v[0]; 63 | } 64 | 65 | [[nodiscard]] constexpr T bottom() const noexcept 66 | { 67 | return position.v[1]; 68 | } 69 | 70 | [[nodiscard]] constexpr T right() const noexcept 71 | { 72 | return position.v[0] + size.v[0]; 73 | } 74 | 75 | [[nodiscard]] constexpr T top() const noexcept 76 | { 77 | return position.v[1] + size.v[1]; 78 | } 79 | 80 | [[nodiscard]] constexpr Vector bottomLeft() const noexcept 81 | { 82 | return position; 83 | } 84 | 85 | [[nodiscard]] constexpr Vector topRight() const noexcept 86 | { 87 | return Vector{position.v[0] + size.v[0], position.v[1] + size.v[1]}; 88 | } 89 | 90 | [[nodiscard]] constexpr bool containsPoint(const T x, const T y) const noexcept 91 | { 92 | return x >= position.v[0] && x <= (position.v[0] + size.v[0]) && 93 | y >= position.v[1] && y <= (position.v[1] + size.v[1]); 94 | } 95 | 96 | [[nodiscard]] constexpr bool containsPoint(const Vector& point) const noexcept 97 | { 98 | return point.v[0] >= position.v[0] && point.v[0] <= (position.v[0] + size.v[0]) && 99 | point.v[1] >= position.v[1] && point.v[1] <= (position.v[1] + size.v[1]); 100 | } 101 | 102 | [[nodiscard]] constexpr bool contains(const T x, const T y, 103 | const T width, const T height) const noexcept 104 | { 105 | return containsPoint(x, y) && containsPoint(x + width, y + height); 106 | } 107 | 108 | [[nodiscard]] constexpr bool contains(const Rect& r) const noexcept 109 | { 110 | return contains(r.position.v[0], r.position.v[1], r.size.v[0], r.size.v[1]); 111 | } 112 | 113 | [[nodiscard]] bool intersects(const T x, const T y, 114 | const T width, const T height) const noexcept 115 | { 116 | T t; 117 | if ((t = x - position.v[0]) > size.v[0] || -t > width) 118 | return false; 119 | if ((t = y - position.v[1]) > size.v[1] || -t > height) 120 | return false; 121 | return true; 122 | } 123 | 124 | [[nodiscard]] constexpr bool intersects(const Rect& r) const noexcept 125 | { 126 | return intersects(r.position.v[0], r.position.v[1], r.size.v[0], r.size.v[1]); 127 | } 128 | 129 | [[nodiscard]] static bool intersect(const Rect& r1, const Rect& r2, Rect& dst) noexcept 130 | { 131 | const T xmin = std::max(r1.position.v[0], r2.position.v[0]); 132 | const T xmax = std::min(r1.right(), r2.right()); 133 | if (xmax > xmin) 134 | { 135 | const T ymin = std::max(r1.position.v[1], r2.position.v[1]); 136 | const T ymax = std::min(r1.bottom(), r2.bottom()); 137 | if (ymax > ymin) 138 | { 139 | dst.position.v[0] = xmin; 140 | dst.position.v[1] = ymin; 141 | dst.size.v[0] = xmax - xmin; 142 | dst.size.v[1] = ymax - ymin; 143 | return true; 144 | } 145 | } 146 | 147 | dst.position.v[0] = dst.position.v[1] = dst.size.v[0] = dst.size.v[1] = 0; 148 | return false; 149 | } 150 | 151 | static void combine(const Rect& r1, const Rect& r2, Rect& dst) noexcept 152 | { 153 | dst.position.v[0] = std::min(r1.position.v[0], r2.position.v[0]); 154 | dst.position.v[1] = std::min(r1.position.v[1], r2.position.v[1]); 155 | dst.size.v[0] = std::max(r1.position.v[0] + r1.size.v[0], r2.position.v[0] + r2.size.v[0]) - dst.position.v[0]; 156 | dst.size.v[1] = std::max(r1.position.v[1] + r1.size.v[1], r2.position.v[1] + r2.size.v[1]) - dst.position.v[1]; 157 | } 158 | 159 | void inflate(const T horizontalAmount, 160 | const T verticalAmount) noexcept 161 | { 162 | position.v[0] -= horizontalAmount; 163 | position.v[1] -= verticalAmount; 164 | size.v[0] += horizontalAmount * T(2); 165 | size.v[1] += verticalAmount * T(2); 166 | } 167 | 168 | [[nodiscard]] constexpr bool operator==(const Rect& other) const noexcept 169 | { 170 | return position.v[0] == other.position.v[0] && size.v[0] == other.size.v[0] && 171 | position.v[1] == other.position.v[1] && size.v[1] == other.size.v[1]; 172 | } 173 | 174 | [[nodiscard]] constexpr bool operator!=(const Rect& other) const noexcept 175 | { 176 | return position.v[0] != other.position.v[0] || size.v[0] != other.size.v[0] || 177 | position.v[1] != other.position.v[1] || size.v[1] != other.size.v[1]; 178 | } 179 | 180 | [[nodiscard]] constexpr const Rect operator*(const T scalar) const noexcept 181 | { 182 | return Rect{ 183 | position.v[0] * scalar, position.v[1] * scalar, 184 | size.v[0] * scalar, size.v[1] * scalar 185 | }; 186 | } 187 | 188 | Rect& operator*=(const T scalar) noexcept 189 | { 190 | position.v[0] *= scalar; 191 | position.v[1] *= scalar; 192 | size.v[0] *= scalar; 193 | size.v[1] *= scalar; 194 | return *this; 195 | } 196 | 197 | [[nodiscard]] constexpr const Rect operator/(const T scalar) const noexcept 198 | { 199 | return Rect{ 200 | position.v[0] / scalar, position.v[1] / scalar, 201 | size.v[0] / scalar, size.v[1] / scalar 202 | }; 203 | } 204 | 205 | Rect& operator/=(const T scalar) noexcept 206 | { 207 | position.v[0] /= scalar; 208 | position.v[1] /= scalar; 209 | size.v[0] /= scalar; 210 | size.v[1] /= scalar; 211 | return *this; 212 | } 213 | }; 214 | } 215 | 216 | #endif 217 | -------------------------------------------------------------------------------- /demo/ApplicationTVOS.mm: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ApplicationTVOS.hpp" 10 | 11 | demo::ApplicationTVOS* sharedApplication; 12 | 13 | @interface AppDelegate: UIResponder 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | -(BOOL)application:(__unused UIApplication*)application willFinishLaunchingWithOptions:(__unused NSDictionary*)launchOptions 20 | { 21 | sharedApplication->createWindow(); 22 | 23 | return YES; 24 | } 25 | 26 | -(BOOL)application:(__unused UIApplication*)application didFinishLaunchingWithOptions:(__unused NSDictionary*)launchOptions 27 | { 28 | return YES; 29 | } 30 | 31 | -(void)applicationDidBecomeActive:(__unused UIApplication*)application 32 | { 33 | } 34 | 35 | -(void)applicationWillResignActive:(__unused UIApplication*)application 36 | { 37 | } 38 | 39 | -(void)applicationDidEnterBackground:(__unused UIApplication*)application 40 | { 41 | } 42 | 43 | -(void)applicationWillEnterForeground:(__unused UIApplication*)application 44 | { 45 | } 46 | 47 | -(void)applicationWillTerminate:(__unused UIApplication*)application 48 | { 49 | } 50 | 51 | -(void)applicationDidReceiveMemoryWarning:(__unused UIApplication*)application 52 | { 53 | } 54 | 55 | @end 56 | 57 | @interface ViewController: UIViewController 58 | { 59 | demo::ApplicationTVOS* application; 60 | } 61 | 62 | @end 63 | 64 | @implementation ViewController 65 | 66 | -(id)initWithApplication:(demo::ApplicationTVOS*)initApplication 67 | { 68 | if (self = [super init]) 69 | application = initApplication; 70 | 71 | return self; 72 | } 73 | 74 | -(void)textFieldDidChange:(__unused id)sender 75 | { 76 | } 77 | 78 | -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator 79 | { 80 | [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; 81 | 82 | application->didResize(size.width, size.height); 83 | } 84 | 85 | @end 86 | 87 | @interface Canvas: UIView 88 | { 89 | demo::ApplicationTVOS* application; 90 | } 91 | 92 | @end 93 | 94 | @implementation Canvas 95 | 96 | -(id)initWithFrame:(CGRect)frameRect andApplication:(demo::ApplicationTVOS*)initApplication 97 | { 98 | if (self = [super initWithFrame:frameRect]) 99 | { 100 | application = initApplication; 101 | } 102 | 103 | return self; 104 | } 105 | 106 | -(void)drawRect:(CGRect)dirtyRect 107 | { 108 | [super drawRect:dirtyRect]; 109 | 110 | application->draw(); 111 | } 112 | 113 | -(void)draw:(__unused NSTimer*)timer 114 | { 115 | [self setNeedsDisplay]; 116 | } 117 | 118 | @end 119 | 120 | static const void* getBytePointer(void* info) 121 | { 122 | const auto frameBuffer = static_cast(info); 123 | return frameBuffer->getData().data(); 124 | } 125 | 126 | namespace demo 127 | { 128 | std::string getResourcePath() 129 | { 130 | CFBundleRef bundle = CFBundleGetMainBundle(); 131 | if (!bundle) throw std::runtime_error{"Failed to get main bundle"}; 132 | 133 | const cf::Pointer relativePath = CFBundleCopyResourcesDirectoryURL(bundle); 134 | if (!relativePath) throw std::runtime_error{"Failed to get current directory"}; 135 | 136 | const cf::Pointer absolutePath = CFURLCopyAbsoluteURL(relativePath); 137 | if (!absolutePath) throw std::runtime_error{"Failed to copy absolute URL"}; 138 | 139 | const cf::Pointer path = CFURLCopyFileSystemPath(absolutePath, kCFURLPOSIXPathStyle); 140 | if (!path) throw std::runtime_error{"Failed to copy file system path"}; 141 | 142 | const auto maximumSize = CFStringGetMaximumSizeOfFileSystemRepresentation(path); 143 | const auto resourceDirectory = std::unique_ptr(new char[static_cast(maximumSize)]); 144 | if (!CFStringGetFileSystemRepresentation(path, resourceDirectory.get(), maximumSize)) 145 | throw std::runtime_error{"Failed to get file system representation"}; 146 | 147 | return std::string{resourceDirectory.get()}; 148 | } 149 | 150 | ApplicationTVOS::ApplicationTVOS() 151 | { 152 | sharedApplication = this; 153 | pool = [[NSAutoreleasePool alloc] init]; 154 | } 155 | 156 | ApplicationTVOS::~ApplicationTVOS() 157 | { 158 | if (window) 159 | [window setRootViewController:nil]; 160 | } 161 | 162 | void ApplicationTVOS::createWindow() 163 | { 164 | screen = [UIScreen mainScreen]; 165 | 166 | window = [[UIWindow alloc] initWithFrame:[screen bounds]]; 167 | 168 | viewController = [[ViewController alloc] initWithApplication:this]; 169 | [window setRootViewController:viewController]; 170 | 171 | const CGRect windowFrame = [window bounds]; 172 | 173 | const auto w = static_cast(windowFrame.size.width * screen.scale); 174 | const auto h = static_cast(windowFrame.size.height * screen.scale); 175 | 176 | content = [[Canvas alloc] initWithFrame:windowFrame andApplication:this]; 177 | [content setContentScaleFactor:screen.scale]; 178 | [viewController setView:content]; 179 | 180 | componentsPerPixel = 4; 181 | bitsPerComponent = sizeof(std::uint8_t) * 8; 182 | 183 | CGDataProviderDirectCallbacks providerCallbacks = { 184 | 0, 185 | getBytePointer, 186 | nullptr, 187 | nullptr, 188 | nullptr 189 | }; 190 | 191 | colorSpace = CGColorSpaceCreateDeviceRGB(); 192 | provider = CGDataProviderCreateDirect(&getFrameBuffer(), w * h * componentsPerPixel, &providerCallbacks); 193 | 194 | [window makeKeyAndVisible]; 195 | 196 | timer = [[NSTimer scheduledTimerWithTimeInterval:0.016 197 | target:content 198 | selector:@selector(draw:) 199 | userInfo:[NSValue valueWithPointer:this] 200 | repeats:YES] retain]; 201 | 202 | setup(w, h); 203 | } 204 | 205 | void ApplicationTVOS::draw() 206 | { 207 | render(); 208 | 209 | const auto& frameBuffer = getFrameBuffer(); 210 | 211 | cf::Pointer image = CGImageCreate(frameBuffer.getWidth(), frameBuffer.getHeight(), 212 | bitsPerComponent, 213 | bitsPerComponent * componentsPerPixel, 214 | componentsPerPixel * frameBuffer.getWidth(), 215 | colorSpace, 216 | kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast, 217 | provider, nullptr, FALSE, kCGRenderingIntentDefault); 218 | 219 | CGContextRef context = UIGraphicsGetCurrentContext(); 220 | 221 | CGContextDrawImage(context, [content frame], image); 222 | CGContextFlush(context); 223 | } 224 | 225 | void ApplicationTVOS::didResize(CGFloat newWidth, CGFloat newHeight) 226 | { 227 | const CGDataProviderDirectCallbacks providerCallbacks = { 228 | 0, 229 | getBytePointer, 230 | nullptr, 231 | nullptr, 232 | nullptr 233 | }; 234 | 235 | const auto w = static_cast(newWidth * screen.scale); 236 | const auto h = static_cast(newHeight * screen.scale); 237 | 238 | provider = CGDataProviderCreateDirect(&getFrameBuffer(), 239 | w * h * componentsPerPixel, 240 | &providerCallbacks); 241 | 242 | onResize(w, h); 243 | } 244 | 245 | void ApplicationTVOS::run(int argc, char* argv[]) 246 | { 247 | UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 248 | } 249 | } 250 | 251 | int main(int argc, char* argv[]) 252 | { 253 | try 254 | { 255 | demo::ApplicationTVOS application; 256 | application.run(argc, argv); 257 | return EXIT_SUCCESS; 258 | } 259 | catch (const std::exception& e) 260 | { 261 | std::cerr << e.what() << std::endl; 262 | return EXIT_FAILURE; 263 | } 264 | catch (...) 265 | { 266 | return EXIT_FAILURE; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /test/test.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {614C7EC0-3262-40DF-B884-224B959A01F9} 30 | Win32Proj 31 | rtmp_relay 32 | 8.1 33 | 34 | 35 | 36 | Application 37 | true 38 | v141 39 | Unicode 40 | 41 | 42 | Application 43 | false 44 | v141 45 | true 46 | Unicode 47 | 48 | 49 | Application 50 | true 51 | v141 52 | Unicode 53 | 54 | 55 | Application 56 | false 57 | v141 58 | true 59 | Unicode 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | true 81 | ..\external\Catch2\single_include;..\sr;$(IncludePath) 82 | 83 | 84 | true 85 | ..\external\Catch2\single_include;..\sr;$(IncludePath) 86 | 87 | 88 | false 89 | ..\external\Catch2\single_include;..\sr;$(IncludePath) 90 | 91 | 92 | false 93 | ..\external\Catch2\single_include;..\sr;$(IncludePath) 94 | 95 | 96 | 97 | Level3 98 | Disabled 99 | _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 100 | true 101 | stdcpp17 102 | 103 | 104 | Console 105 | true 106 | ws2_32.lib;%(AdditionalDependencies) 107 | 108 | 109 | 110 | 111 | Level3 112 | Disabled 113 | _CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 114 | true 115 | stdcpp17 116 | 117 | 118 | Console 119 | true 120 | ws2_32.lib;%(AdditionalDependencies) 121 | 122 | 123 | 124 | 125 | Level3 126 | MaxSpeed 127 | true 128 | true 129 | _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 130 | true 131 | stdcpp17 132 | 133 | 134 | Console 135 | true 136 | true 137 | true 138 | ws2_32.lib;%(AdditionalDependencies) 139 | 140 | 141 | 142 | 143 | Level3 144 | MaxSpeed 145 | true 146 | true 147 | _CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 148 | true 149 | stdcpp17 150 | 151 | 152 | Console 153 | true 154 | true 155 | true 156 | ws2_32.lib;%(AdditionalDependencies) 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /demo/ApplicationIOS.mm: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ApplicationIOS.hpp" 10 | 11 | demo::ApplicationIOS* sharedApplication; 12 | 13 | @interface AppDelegate: UIResponder 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | -(BOOL)application:(__unused UIApplication*)application willFinishLaunchingWithOptions:(__unused NSDictionary*)launchOptions 20 | { 21 | sharedApplication->createWindow(); 22 | 23 | return YES; 24 | } 25 | 26 | -(BOOL)application:(__unused UIApplication*)application didFinishLaunchingWithOptions:(__unused NSDictionary*)launchOptions 27 | { 28 | return YES; 29 | } 30 | 31 | -(void)applicationDidBecomeActive:(__unused UIApplication*)application 32 | { 33 | } 34 | 35 | -(void)applicationWillResignActive:(__unused UIApplication*)application 36 | { 37 | } 38 | 39 | -(void)applicationDidEnterBackground:(__unused UIApplication*)application 40 | { 41 | } 42 | 43 | -(void)applicationWillEnterForeground:(__unused UIApplication*)application 44 | { 45 | } 46 | 47 | -(void)applicationWillTerminate:(__unused UIApplication*)application 48 | { 49 | } 50 | 51 | -(void)applicationDidReceiveMemoryWarning:(__unused UIApplication*)application 52 | { 53 | } 54 | 55 | @end 56 | 57 | @interface ViewController: UIViewController 58 | { 59 | demo::ApplicationIOS* application; 60 | } 61 | 62 | @end 63 | 64 | @implementation ViewController 65 | 66 | -(id)initWithApplication:(demo::ApplicationIOS*)initApplication 67 | { 68 | if (self = [super init]) 69 | application = initApplication; 70 | 71 | return self; 72 | } 73 | 74 | -(BOOL)prefersStatusBarHidden 75 | { 76 | return YES; 77 | } 78 | 79 | -(void)textFieldDidChange:(__unused id)sender 80 | { 81 | } 82 | 83 | -(NSUInteger)supportedInterfaceOrientations 84 | { 85 | return UIInterfaceOrientationMaskAll; 86 | } 87 | 88 | -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator 89 | { 90 | [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; 91 | 92 | application->didResize(size.width, size.height); 93 | } 94 | 95 | -(void)deviceOrientationDidChange:(__unused NSNotification*)notification 96 | { 97 | if (self.view) 98 | { 99 | const CGSize size = self.view.frame.size; 100 | 101 | application->didResize(size.width, size.height); 102 | } 103 | } 104 | 105 | @end 106 | 107 | @interface Canvas: UIView 108 | { 109 | demo::ApplicationIOS* application; 110 | } 111 | 112 | @end 113 | 114 | @implementation Canvas 115 | 116 | -(id)initWithFrame:(CGRect)frameRect andApplication:(demo::ApplicationIOS*)initApplication 117 | { 118 | if (self = [super initWithFrame:frameRect]) 119 | { 120 | application = initApplication; 121 | } 122 | 123 | return self; 124 | } 125 | 126 | -(void)drawRect:(CGRect)dirtyRect 127 | { 128 | [super drawRect:dirtyRect]; 129 | 130 | application->draw(); 131 | } 132 | 133 | -(void)draw:(__unused NSTimer*)timer 134 | { 135 | [self setNeedsDisplay]; 136 | } 137 | 138 | @end 139 | 140 | static const void* getBytePointer(void* info) 141 | { 142 | const auto frameBuffer = static_cast(info); 143 | return frameBuffer->getData().data(); 144 | } 145 | 146 | namespace demo 147 | { 148 | std::string getResourcePath() 149 | { 150 | CFBundleRef bundle = CFBundleGetMainBundle(); 151 | if (!bundle) throw std::runtime_error{"Failed to get main bundle"}; 152 | 153 | const cf::Pointer relativePath = CFBundleCopyResourcesDirectoryURL(bundle); 154 | if (!relativePath) throw std::runtime_error{"Failed to get current directory"}; 155 | 156 | const cf::Pointer absolutePath = CFURLCopyAbsoluteURL(relativePath); 157 | if (!absolutePath) throw std::runtime_error{"Failed to copy absolute URL"}; 158 | 159 | const cf::Pointer path = CFURLCopyFileSystemPath(absolutePath, kCFURLPOSIXPathStyle); 160 | if (!path) throw std::runtime_error{"Failed to copy file system path"}; 161 | 162 | const auto maximumSize = CFStringGetMaximumSizeOfFileSystemRepresentation(path); 163 | const auto resourceDirectory = std::unique_ptr(new char[static_cast(maximumSize)]); 164 | if (!CFStringGetFileSystemRepresentation(path, resourceDirectory.get(), maximumSize)) 165 | throw std::runtime_error{"Failed to get file system representation"}; 166 | 167 | return std::string{resourceDirectory.get()}; 168 | } 169 | 170 | ApplicationIOS::ApplicationIOS() 171 | { 172 | sharedApplication = this; 173 | pool = [[NSAutoreleasePool alloc] init]; 174 | } 175 | 176 | ApplicationIOS::~ApplicationIOS() 177 | { 178 | if (window) 179 | [window setRootViewController:nil]; 180 | } 181 | 182 | void ApplicationIOS::createWindow() 183 | { 184 | screen = [UIScreen mainScreen]; 185 | 186 | window = [[UIWindow alloc] initWithFrame:[screen bounds]]; 187 | 188 | viewController = [[ViewController alloc] initWithApplication:this]; 189 | [window setRootViewController:viewController]; 190 | 191 | [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; 192 | [[NSNotificationCenter defaultCenter] addObserver:viewController selector:@selector(deviceOrientationDidChange:) 193 | name:UIDeviceOrientationDidChangeNotification 194 | object:[UIDevice currentDevice]]; 195 | 196 | const CGRect windowFrame = [window bounds]; 197 | 198 | const auto w = static_cast(windowFrame.size.width * screen.scale); 199 | const auto h = static_cast(windowFrame.size.height * screen.scale); 200 | 201 | content = [[Canvas alloc] initWithFrame:windowFrame andApplication:this]; 202 | [content setContentScaleFactor:screen.scale]; 203 | [viewController setView:content]; 204 | 205 | componentsPerPixel = 4; 206 | bitsPerComponent = sizeof(std::uint8_t) * 8; 207 | 208 | CGDataProviderDirectCallbacks providerCallbacks = { 209 | 0, 210 | getBytePointer, 211 | nullptr, 212 | nullptr, 213 | nullptr 214 | }; 215 | 216 | colorSpace = CGColorSpaceCreateDeviceRGB(); 217 | provider = CGDataProviderCreateDirect(&getFrameBuffer(), w * h * componentsPerPixel, &providerCallbacks); 218 | 219 | [window makeKeyAndVisible]; 220 | 221 | timer = [[NSTimer scheduledTimerWithTimeInterval:0.016 222 | target:content 223 | selector:@selector(draw:) 224 | userInfo:[NSValue valueWithPointer:this] 225 | repeats:YES] retain]; 226 | 227 | setup(w, h); 228 | } 229 | 230 | void ApplicationIOS::draw() 231 | { 232 | render(); 233 | 234 | const auto& frameBuffer = getFrameBuffer(); 235 | 236 | cf::Pointer image = CGImageCreate(frameBuffer.getWidth(), frameBuffer.getHeight(), 237 | bitsPerComponent, 238 | bitsPerComponent * componentsPerPixel, 239 | componentsPerPixel * frameBuffer.getWidth(), 240 | colorSpace, 241 | kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast, 242 | provider, nullptr, FALSE, kCGRenderingIntentDefault); 243 | 244 | CGContextRef context = UIGraphicsGetCurrentContext(); 245 | 246 | CGContextDrawImage(context, [content frame], image); 247 | CGContextFlush(context); 248 | } 249 | 250 | void ApplicationIOS::didResize(CGFloat newWidth, CGFloat newHeight) 251 | { 252 | const CGDataProviderDirectCallbacks providerCallbacks = { 253 | 0, 254 | getBytePointer, 255 | nullptr, 256 | nullptr, 257 | nullptr 258 | }; 259 | 260 | const auto w = static_cast(newWidth * screen.scale); 261 | const auto h = static_cast(newHeight * screen.scale); 262 | 263 | provider = CGDataProviderCreateDirect(&getFrameBuffer(), 264 | w * h * componentsPerPixel, 265 | &providerCallbacks); 266 | 267 | onResize(w, h); 268 | } 269 | 270 | void ApplicationIOS::run(int argc, char* argv[]) 271 | { 272 | UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 273 | } 274 | } 275 | 276 | int main(int argc, char* argv[]) 277 | { 278 | try 279 | { 280 | demo::ApplicationIOS application; 281 | application.run(argc, argv); 282 | return EXIT_SUCCESS; 283 | } 284 | catch (const std::exception& e) 285 | { 286 | std::cerr << e.what() << std::endl; 287 | return EXIT_FAILURE; 288 | } 289 | catch (...) 290 | { 291 | return EXIT_FAILURE; 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /demo/SoftwareRenderer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {4E6216F3-C039-4547-9658-378F199432B0} 51 | Win32Proj 52 | SoftwareRenderer 53 | 8.1 54 | 55 | 56 | 57 | Application 58 | true 59 | v141 60 | Unicode 61 | 62 | 63 | Application 64 | false 65 | v141 66 | true 67 | Unicode 68 | 69 | 70 | Application 71 | true 72 | v141 73 | Unicode 74 | 75 | 76 | Application 77 | false 78 | v141 79 | true 80 | Unicode 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | true 102 | $(SolutionDir)$(Platform)\$(Configuration)\ 103 | $(Platform)\$(Configuration)\ 104 | $(VC_IncludePath);$(WindowsSDK_IncludePath);..\sr 105 | 106 | 107 | true 108 | $(VC_IncludePath);$(WindowsSDK_IncludePath);..\sr 109 | 110 | 111 | false 112 | $(SolutionDir)$(Platform)\$(Configuration)\ 113 | $(Platform)\$(Configuration)\ 114 | $(VC_IncludePath);$(WindowsSDK_IncludePath);..\sr 115 | 116 | 117 | false 118 | $(VC_IncludePath);$(WindowsSDK_IncludePath);..\sr 119 | 120 | 121 | 122 | 123 | 124 | Level3 125 | Disabled 126 | WIN32;_DEBUG;_CONSOLE;UNICODE;NOMINMAX;%(PreprocessorDefinitions) 127 | true 128 | stdcpp17 129 | 130 | 131 | Windows 132 | true 133 | version.lib;%(AdditionalDependencies) 134 | 135 | 136 | 137 | 138 | 139 | 140 | Level3 141 | Disabled 142 | _DEBUG;_CONSOLE;UNICODE;NOMINMAX;%(PreprocessorDefinitions) 143 | true 144 | stdcpp17 145 | 146 | 147 | Windows 148 | true 149 | version.lib;%(AdditionalDependencies) 150 | 151 | 152 | 153 | 154 | Level3 155 | 156 | 157 | MaxSpeed 158 | true 159 | true 160 | WIN32;NDEBUG;_CONSOLE;UNICODE;NOMINMAX;%(PreprocessorDefinitions) 161 | true 162 | stdcpp17 163 | 164 | 165 | Windows 166 | true 167 | true 168 | true 169 | version.lib;%(AdditionalDependencies) 170 | 171 | 172 | 173 | 174 | Level3 175 | 176 | 177 | MaxSpeed 178 | true 179 | true 180 | NDEBUG;_CONSOLE;UNICODE;NOMINMAX;%(PreprocessorDefinitions) 181 | true 182 | stdcpp17 183 | 184 | 185 | Windows 186 | true 187 | true 188 | true 189 | version.lib;%(AdditionalDependencies) 190 | 191 | 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /sr/Texture.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_TEXTURE_HPP 6 | #define SR_TEXTURE_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include "PixelFormat.hpp" 12 | #include "Sampler.hpp" 13 | 14 | namespace sr 15 | { 16 | class Texture final 17 | { 18 | public: 19 | 20 | Texture(const PixelFormat initPixelFormat = PixelFormat::rgba8, 21 | const std::size_t initWidth = 0, 22 | const std::size_t initHeight = 0, 23 | const bool initMipMaps = false): 24 | pixelFormat{initPixelFormat}, 25 | width{initWidth}, 26 | height{initHeight}, 27 | mipMaps{initMipMaps} 28 | { 29 | const auto pixelSize = getPixelSize(pixelFormat); 30 | 31 | if (pixelSize > 0 && width > 0 && height > 0) 32 | { 33 | levels.push_back(std::vector(width * height * pixelSize)); 34 | 35 | if (mipMaps || true) 36 | { 37 | auto mipmapWidth = width >> 1; 38 | auto mipmapHeight = height >> 1; 39 | 40 | while (mipmapWidth > 0 || mipmapHeight > 0) 41 | { 42 | if (mipmapWidth < 1) mipmapWidth = 1; 43 | if (mipmapHeight < 1) mipmapHeight = 1; 44 | 45 | levels.push_back(std::vector(mipmapWidth * mipmapHeight * pixelSize)); 46 | 47 | mipmapWidth >>= 1; 48 | mipmapHeight >>= 1; 49 | } 50 | } 51 | } 52 | } 53 | 54 | void resize(const std::size_t newWidth, const std::size_t newHeight) 55 | { 56 | width = newWidth; 57 | height = newHeight; 58 | 59 | const auto pixelSize = getPixelSize(pixelFormat); 60 | if (pixelSize == 0) 61 | throw std::runtime_error{"Invalid pixel format"}; 62 | 63 | levels.clear(); 64 | levels.push_back(std::vector(width * height * pixelSize)); 65 | 66 | if (mipMaps) 67 | { 68 | auto mipmapWidth = width >> 1; 69 | auto mipmapHeight = height >> 1; 70 | 71 | while (mipmapWidth > 0 || mipmapHeight > 0) 72 | { 73 | if (mipmapWidth < 1) mipmapWidth = 1; 74 | if (mipmapHeight < 1) mipmapHeight = 1; 75 | 76 | levels.push_back(std::vector(mipmapWidth * mipmapHeight * pixelSize)); 77 | 78 | mipmapWidth >>= 1; 79 | mipmapHeight >>= 1; 80 | } 81 | } 82 | } 83 | 84 | [[nodiscard]] auto getPixelFormat() const noexcept { return pixelFormat; } 85 | [[nodiscard]] auto getWidth() const noexcept { return width; } 86 | [[nodiscard]] auto getHeight() const noexcept { return height; } 87 | 88 | [[nodiscard]] std::size_t getLevelCount() const noexcept 89 | { 90 | return levels.size(); 91 | } 92 | 93 | [[nodiscard]] std::vector& getData(const std::uint32_t level = 0) 94 | { 95 | return levels[level]; 96 | } 97 | 98 | [[nodiscard]] const std::vector& getData(const std::uint32_t level = 0) const 99 | { 100 | return levels[level]; 101 | } 102 | 103 | void setData(const std::vector& buffer, 104 | const std::uint32_t level = 0) 105 | { 106 | const auto pixelSize = getPixelSize(pixelFormat); 107 | if (pixelSize == 0) 108 | throw std::runtime_error{"Invalid pixel format"}; 109 | 110 | if (buffer.size() != width * height * pixelSize) 111 | throw std::runtime_error{"Invalid buffer size"}; 112 | 113 | if (level >= levels.size()) levels.resize(level + 1); 114 | levels[level] = buffer; 115 | } 116 | 117 | [[nodiscard]] Color getPixel(const std::size_t x, 118 | const std::size_t y, 119 | const std::uint32_t level) const 120 | { 121 | const auto& buffer = levels[level]; 122 | 123 | switch (pixelFormat) 124 | { 125 | case PixelFormat::r8: 126 | { 127 | const auto* r = &buffer[(y * width + x) * 1]; 128 | return Color{*r, *r, *r, std::uint8_t(255U)}; 129 | } 130 | case PixelFormat::a8: 131 | { 132 | const auto* a = &buffer[(y * width + x) * 1]; 133 | return Color{std::uint8_t(0U), std::uint8_t(0U), std::uint8_t(0U), *a}; 134 | } 135 | case PixelFormat::rgba8: 136 | { 137 | const auto* rgba = &buffer[(y * width + x) * 4]; 138 | return Color{rgba[0], rgba[1], rgba[2], rgba[3]}; 139 | } 140 | case PixelFormat::float32: 141 | { 142 | const float f = reinterpret_cast(buffer.data())[y * width + x]; 143 | return Color{f, f, f, 1.0F}; 144 | } 145 | default: 146 | throw std::runtime_error{"Invalid pixel format"}; 147 | } 148 | } 149 | 150 | [[nodiscard]] Color sample(const Sampler* sampler, const Vector& coord) const 151 | { 152 | if (sampler && !levels.empty()) 153 | { 154 | const auto u = 155 | (sampler->addressModeX == Sampler::AddressMode::clamp) ? std::clamp(coord.v[0], 0.0F, 1.0F) * (width - 1) : 156 | (sampler->addressModeX == Sampler::AddressMode::repeat) ? std::fmod(coord.v[0], 1.0F) * (width - 1) : 157 | (sampler->addressModeX == Sampler::AddressMode::mirror) ? 1.0F - 2.0F * std::fabs(std::fmod(coord.v[0] / 2.0F, 1.0F) - 0.5F) * (width - 1) : 158 | 0.0F; 159 | 160 | const auto v = 161 | (sampler->addressModeY == Sampler::AddressMode::clamp) ? std::clamp(coord.v[1], 0.0F, 1.0F) * (height - 1) : 162 | (sampler->addressModeY == Sampler::AddressMode::repeat) ? std::fmod(coord.v[1], 1.0F) * (height - 1) : 163 | (sampler->addressModeY == Sampler::AddressMode::mirror) ? 1.0F - 2.0F * std::fabs(std::fmod(coord.v[1] / 2.0F, 1.0F) - 0.5F) * (height - 1) : 164 | 0.0F; 165 | 166 | if (sampler->filter == Sampler::Filter::point) 167 | { 168 | const auto textureX = static_cast(std::round(u)); 169 | const auto textureY = static_cast(std::round(v)); 170 | return getPixel(textureX, textureY, 0); 171 | } 172 | else if (sampler->filter == Sampler::Filter::linear) 173 | { 174 | auto textureX0 = static_cast(u - 0.5F); 175 | auto textureX1 = textureX0 + 1; 176 | auto textureY0 = static_cast(v - 0.5F); 177 | auto textureY1 = textureY0 + 1; 178 | 179 | textureX0 = std::clamp(textureX0, static_cast(0U), width - 1); 180 | textureX1 = std::clamp(textureX1, static_cast(0U), width - 1); 181 | textureY0 = std::clamp(textureY0, static_cast(0U), height - 1); 182 | textureY1 = std::clamp(textureY1, static_cast(0U), height - 1); 183 | 184 | // TODO: calculate mip level 185 | const Color color[4] = { 186 | getPixel(textureX0, textureY0, 0), 187 | getPixel(textureX1, textureY0, 0), 188 | getPixel(textureX0, textureY1, 0), 189 | getPixel(textureX1, textureY1, 0) 190 | }; 191 | 192 | const auto x0 = u - (textureX0 + 0.5F); 193 | const auto y0 = v - (textureY0 + 0.5F); 194 | const auto x1 = (textureX0 + 1.5F) - u; 195 | const auto y1 = (textureY0 + 1.5F) - v; 196 | 197 | return Color{ 198 | color[0].r * x1 * y1 + color[1].r * x0 * y1 + color[2].r * x1 * y0 + color[3].r * x0 * y0, 199 | color[0].g * x1 * y1 + color[1].g * x0 * y1 + color[2].g * x1 * y0 + color[3].g * x0 * y0, 200 | color[0].b * x1 * y1 + color[1].b * x0 * y1 + color[2].b * x1 * y0 + color[3].b * x0 * y0, 201 | color[0].a * x1 * y1 + color[1].a * x0 * y1 + color[2].a * x1 * y0 + color[3].a * x0 * y0 202 | }; 203 | } 204 | } 205 | 206 | return Color{}; 207 | } 208 | 209 | private: 210 | PixelFormat pixelFormat; 211 | std::size_t width = 0; 212 | std::size_t height = 0; 213 | bool mipMaps = false; 214 | std::vector> levels; 215 | std::uint32_t minLOD = 0; 216 | std::uint32_t maxLOD = UINT_MAX; 217 | float lodBias = 0.0F; 218 | }; 219 | 220 | inline void clear(Texture& renderTarget, const Color color) 221 | { 222 | assert(renderTarget.getPixelFormat() == PixelFormat::rgba8); 223 | 224 | const auto bufferData = reinterpret_cast(renderTarget.getData().data()); 225 | const auto rgba = color.getIntValueRaw(); 226 | 227 | const auto bufferSize = renderTarget.getWidth() * renderTarget.getHeight(); 228 | for (std::size_t p = 0; p < bufferSize; ++p) 229 | bufferData[p] = rgba; 230 | } 231 | 232 | inline void clear(Texture& renderTarget, const float depth) 233 | { 234 | assert(renderTarget.getPixelFormat() == PixelFormat::float32); 235 | 236 | const auto bufferData = reinterpret_cast(renderTarget.getData().data()); 237 | 238 | const auto bufferSize = renderTarget.getWidth() * renderTarget.getHeight(); 239 | for (std::size_t p = 0; p < bufferSize; ++p) 240 | bufferData[p] = depth; 241 | } 242 | } 243 | 244 | #endif 245 | -------------------------------------------------------------------------------- /demo/Application.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef APPLICATION_HPP 6 | #define APPLICATION_HPP 7 | 8 | #include 9 | #include 10 | #include "sr.hpp" 11 | #include "Bmp.hpp" 12 | 13 | namespace demo 14 | { 15 | inline sr::VertexShaderOutput vertexShader(const sr::Matrix& modelViewProjection, 16 | const sr::Vertex& vertex) 17 | { 18 | sr::VertexShaderOutput result; 19 | 20 | // transform to clip space 21 | result.position = modelViewProjection * vertex.position; 22 | result.color = vertex.color; 23 | result.texCoords[0] = vertex.texCoords[0]; 24 | result.texCoords[1] = vertex.texCoords[1]; 25 | result.normal = vertex.normal; 26 | 27 | return result; 28 | } 29 | 30 | inline sr::Color fragmentShader(const sr::VertexShaderOutput& input, 31 | const std::array& samplers, 32 | const std::array& textures) 33 | { 34 | const auto sampleColor = textures[0]->sample(samplers[0], input.texCoords[0]); 35 | 36 | const sr::Color result{ 37 | input.color.r * sampleColor.r, 38 | input.color.g * sampleColor.g, 39 | input.color.b * sampleColor.b, 40 | input.color.a * sampleColor.a 41 | }; 42 | 43 | return result; 44 | } 45 | 46 | std::string getResourcePath(); 47 | 48 | class Application 49 | { 50 | public: 51 | Application(): 52 | indices{ 53 | 0, 1, 2, 1, 3, 2, // front 54 | 4, 5, 6, 5, 7, 6, // back 55 | 16, 17, 18, 17, 19, 18, // bottom 56 | 20, 21, 22, 21, 23, 22, // top 57 | 8, 9, 10, 9, 11, 10, // left 58 | 12, 13, 14, 13, 15, 14 // right 59 | }, 60 | vertices{ 61 | // front 62 | sr::Vertex{sr::Vector{-20.0F, -20.0F, -20.0F, 1.0F}, sr::Color{0xFF0000FFU}, sr::Vector{0.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 63 | sr::Vertex{sr::Vector{-20.0F, 20.0F, -20.0F, 1.0F}, sr::Color{0x00FF00FFU}, sr::Vector{0.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 64 | sr::Vertex{sr::Vector{20.0F, -20.0F, -20.0F, 1.0F}, sr::Color{0x0000FFFFU}, sr::Vector{1.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 65 | sr::Vertex{sr::Vector{20.0F, 20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 66 | 67 | // back 68 | sr::Vertex{sr::Vector{-20.0F, -20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 69 | sr::Vertex{sr::Vector{-20.0F, 20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 70 | sr::Vertex{sr::Vector{20.0F, -20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 71 | sr::Vertex{sr::Vector{20.0F, 20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 72 | 73 | // left 74 | sr::Vertex{sr::Vector{-20.0F, -20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 75 | sr::Vertex{sr::Vector{-20.0F, 20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 4.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 76 | sr::Vertex{sr::Vector{-20.0F, -20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{4.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 77 | sr::Vertex{sr::Vector{-20.0F, 20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{4.0F, 4.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 78 | 79 | // right 80 | sr::Vertex{sr::Vector{20.0F, -20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFA0U}, sr::Vector{0.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 81 | sr::Vertex{sr::Vector{20.0F, 20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFA0U}, sr::Vector{0.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 82 | sr::Vertex{sr::Vector{20.0F, -20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFA0U}, sr::Vector{1.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 83 | sr::Vertex{sr::Vector{20.0F, 20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFA0U}, sr::Vector{1.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 84 | 85 | // bottom 86 | sr::Vertex{sr::Vector{-20.0F, -20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 87 | sr::Vertex{sr::Vector{-20.0F, -20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 88 | sr::Vertex{sr::Vector{20.0F, -20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 89 | sr::Vertex{sr::Vector{20.0F, -20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 90 | 91 | // top 92 | sr::Vertex{sr::Vector{-20.0F, 20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 93 | sr::Vertex{sr::Vector{-20.0F, 20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{0.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 94 | sr::Vertex{sr::Vector{20.0F, 20.0F, -20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 0.0F}, sr::Vector{0.0F, 0.0F, 1.0F}}, 95 | sr::Vertex{sr::Vector{20.0F, 20.0F, 20.0F, 1.0F}, sr::Color{0xFFFFFFFFU}, sr::Vector{1.0F, 1.0F}, sr::Vector{0.0F, 0.0F, 1.0F}} 96 | } 97 | { 98 | const sr::bmp::Bmp bmp{getResourcePath() + "/cube.bmp"}; 99 | texture = sr::Texture{sr::PixelFormat::rgba8, bmp.getWidth(), bmp.getHeight()}; 100 | texture.setData(bmp.getData(), 0); 101 | 102 | sampler.addressModeX = sr::Sampler::AddressMode::repeat; 103 | sampler.addressModeY = sr::Sampler::AddressMode::repeat; 104 | sampler.filter = sr::Sampler::Filter::linear; 105 | 106 | blendState.colorBlendSource = sr::BlendState::Factor::srcAlpha; 107 | blendState.colorBlendDest = sr::BlendState::Factor::invSrcAlpha; 108 | blendState.colorOperation = sr::BlendState::Operation::add; 109 | blendState.alphaBlendSource = sr::BlendState::Factor::one; 110 | blendState.alphaBlendDest = sr::BlendState::Factor::one; 111 | blendState.alphaOperation = sr::BlendState::Operation::add; 112 | blendState.enabled = true; 113 | 114 | depthState.read = true; 115 | depthState.write = true; 116 | } 117 | virtual ~Application() = default; 118 | 119 | Application(const Application&) = delete; 120 | Application& operator=(const Application&) = delete; 121 | Application(Application&&) = delete; 122 | Application& operator=(Application&&) = delete; 123 | 124 | void setup(std::size_t newWidth, std::size_t newHeight) 125 | { 126 | viewport.size.v[0] = static_cast(newWidth); 127 | viewport.size.v[1] = static_cast(newHeight); 128 | 129 | frameBuffer.resize(newWidth, newHeight); 130 | depthBuffer.resize(newWidth, newHeight); 131 | 132 | projection.setPerspective(sr::tau / 6.0, 133 | static_cast(newWidth) / static_cast(newHeight), 134 | 1.0F, 1000.0F); 135 | 136 | view.setTranslation(0.0F, 0.0F, 100.0F); 137 | } 138 | 139 | void render() 140 | { 141 | rotationY += 0.05F; 142 | model.setRotationY(rotationY); 143 | 144 | const auto modelViewProjection = projection * view * model; 145 | 146 | clear(frameBuffer, sr::Color{255, 255, 255, 255}); 147 | clear(depthBuffer, 1000.0F); 148 | 149 | drawTriangles(frameBuffer, 150 | depthBuffer, 151 | vertexShader, 152 | fragmentShader, 153 | {&sampler, nullptr}, 154 | {&texture, nullptr}, 155 | viewport, 156 | scissorRect, 157 | blendState, 158 | depthState, 159 | indices, 160 | vertices, 161 | modelViewProjection); 162 | } 163 | 164 | const sr::Texture& getFrameBuffer() const noexcept { return frameBuffer; } 165 | sr::Texture& getFrameBuffer() noexcept { return frameBuffer; } 166 | 167 | protected: 168 | void onResize(std::size_t newWidth, std::size_t newHeight) 169 | { 170 | viewport.size.v[0] = static_cast(newWidth); 171 | viewport.size.v[1] = static_cast(newHeight); 172 | 173 | frameBuffer.resize(newWidth, newHeight); 174 | depthBuffer.resize(newWidth, newHeight); 175 | 176 | projection.setPerspective(sr::tau / 6.0, 177 | static_cast(newWidth) / static_cast(newHeight), 178 | 1.0F, 1000.0F); 179 | } 180 | 181 | private: 182 | sr::Matrix projection = sr::Matrix::identity(); 183 | sr::Matrix view = sr::Matrix::identity(); 184 | sr::Matrix model = sr::Matrix::identity(); 185 | float rotationY = 0.0F; 186 | 187 | sr::Texture frameBuffer{sr::PixelFormat::rgba8}; 188 | sr::Texture depthBuffer{sr::PixelFormat::float32}; 189 | 190 | sr::Rect viewport; 191 | sr::Rect scissorRect{0.0F, 0.0F, 1.0F, 1.0F}; 192 | sr::BlendState blendState; 193 | sr::DepthState depthState; 194 | 195 | sr::Sampler sampler; 196 | sr::Texture texture; 197 | 198 | std::vector indices; 199 | std::vector vertices; 200 | }; 201 | } 202 | 203 | #endif 204 | -------------------------------------------------------------------------------- /sr/Renderer.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_RENDERER_HPP 6 | #define SR_RENDERER_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "BlendState.hpp" 13 | #include "Color.hpp" 14 | #include "DepthState.hpp" 15 | #include "Matrix.hpp" 16 | #include "Rect.hpp" 17 | #include "RenderError.hpp" 18 | #include "Sampler.hpp" 19 | #include "Shader.hpp" 20 | #include "Texture.hpp" 21 | #include "Vector.hpp" 22 | #include "Vertex.hpp" 23 | 24 | namespace sr 25 | { 26 | inline void drawTriangles(Texture& frameBuffer, 27 | Texture& depthBuffer, 28 | VertexShader vertexShader, 29 | FragmentShader fragmentShader, 30 | const std::array& samplers, 31 | const std::array& textures, 32 | const Rect& viewport, 33 | const Rect& scissorRect, 34 | const BlendState& blendState, 35 | const DepthState& depthState, 36 | const std::vector& indices, 37 | const std::vector& vertices, 38 | const Matrix& modelViewProjection) 39 | { 40 | const auto frameBufferData = reinterpret_cast(frameBuffer.getData().data()); 41 | const auto depthBufferData = reinterpret_cast(depthBuffer.getData().data()); 42 | 43 | for (std::size_t i = 0; i + 2 < indices.size(); i += 3) 44 | { 45 | const std::array vsOutputs{ 46 | vertexShader(modelViewProjection, vertices[indices[i + 0]]), 47 | vertexShader(modelViewProjection, vertices[indices[i + 1]]), 48 | vertexShader(modelViewProjection, vertices[indices[i + 2]]) 49 | }; 50 | 51 | std::array, 3> ndcPositions{ 52 | vsOutputs[0].position, 53 | vsOutputs[1].position, 54 | vsOutputs[2].position 55 | }; 56 | 57 | // transform to normalized device coordinates 58 | for (sr::Vector& ndcPosition : ndcPositions) 59 | ndcPosition /= ndcPosition.v[3]; 60 | 61 | std::array, 3> viewportPositions{ 62 | Vector{ndcPositions[0].v[0], ndcPositions[0].v[1]}, 63 | Vector{ndcPositions[1].v[0], ndcPositions[1].v[1]}, 64 | Vector{ndcPositions[2].v[0], ndcPositions[2].v[1]} 65 | }; 66 | 67 | Vector screenMin{ 68 | std::numeric_limits::infinity(), 69 | std::numeric_limits::infinity() 70 | }; 71 | Vector screenMax{ 72 | -std::numeric_limits::infinity(), 73 | -std::numeric_limits::infinity() 74 | }; 75 | 76 | for (auto& viewportPosition : viewportPositions) 77 | { 78 | // transform to viewport coordinates 79 | viewportPosition.v[0] = viewportPosition.v[0] * viewport.size.v[0] / 2.0F + viewport.position.v[0] + viewport.size.v[0] / 2.0F; // xndc * width / 2 + x + width / 2 80 | viewportPosition.v[1] = viewportPosition.v[1] * viewport.size.v[1] / 2.0F + viewport.position.v[1] + viewport.size.v[1] / 2.0F; // yndc * height / 2 + y + height / 2 81 | //viewportPosition.v[2] = viewportPosition.v[2] * (1.0F - 0.0F) / 2.0F + (1.0F + 0.0F) / 2.0F; // zndc * (far - near) / 2 + (far + near) / 2 82 | 83 | if (viewportPosition.v[0] < screenMin.v[0]) screenMin.v[0] = viewportPosition.v[0]; 84 | if (viewportPosition.v[0] > screenMax.v[0]) screenMax.v[0] = viewportPosition.v[0]; 85 | if (viewportPosition.v[1] < screenMin.v[1]) screenMin.v[1] = viewportPosition.v[1]; 86 | if (viewportPosition.v[1] > screenMax.v[1]) screenMax.v[1] = viewportPosition.v[1]; 87 | } 88 | 89 | screenMin.v[0] = std::clamp(screenMin.v[0], 0.0F, static_cast(frameBuffer.getWidth() - 1) * scissorRect.position.v[0]); 90 | screenMax.v[0] = std::clamp(screenMax.v[0], 0.0F, static_cast(frameBuffer.getWidth() - 1) * (scissorRect.position.v[0] + scissorRect.size.v[0])); 91 | screenMin.v[1] = std::clamp(screenMin.v[1], 0.0F, static_cast(frameBuffer.getHeight() - 1) * scissorRect.position.v[1]); 92 | screenMax.v[1] = std::clamp(screenMax.v[1], 0.0F, static_cast(frameBuffer.getHeight() - 1) * (scissorRect.position.v[1] + scissorRect.size.v[1])); 93 | 94 | const auto v0 = viewportPositions[1] - viewportPositions[0]; 95 | const auto v1 = viewportPositions[2] - viewportPositions[0]; 96 | const auto den = v0.v[0] * v1.v[1] - v1.v[0] * v0.v[1]; 97 | 98 | for (auto screenY = static_cast(screenMin.v[1]); screenY <= static_cast(screenMax.v[1]); ++screenY) 99 | for (auto screenX = static_cast(screenMin.v[0]); screenX <= static_cast(screenMax.v[0]); ++screenX) 100 | { 101 | const Vector p{ 102 | static_cast(screenX), 103 | static_cast(screenY) 104 | }; 105 | 106 | const auto v2 = p - viewportPositions[0]; 107 | 108 | // calculate barycentric coordinates 109 | const auto v = (v2.v[0] * v1.v[1] - v1.v[0] * v2.v[1]) / den; 110 | const auto w = (v0.v[0] * v2.v[1] - v2.v[0] * v0.v[1]) / den; 111 | 112 | if (v >= 0.0F && w >= 0.0F && v + w <= 1.0F) 113 | { 114 | const auto u = 1.0F - v - w; 115 | 116 | Vector clip{ 117 | u / vsOutputs[0].position.v[3], 118 | v / vsOutputs[1].position.v[3], 119 | w / vsOutputs[2].position.v[3] 120 | }; 121 | clip /= (clip.v[0] + clip.v[1] + clip.v[2]); 122 | 123 | const auto depth = ndcPositions[0].v[2] * clip.v[0] + ndcPositions[1].v[2] * clip.v[1] + ndcPositions[2].v[2] * clip.v[2]; 124 | 125 | if (depthState.read && depthBufferData[screenY * depthBuffer.getWidth() + screenX] < depth) 126 | continue; // discard the pixel 127 | 128 | if (depthState.write) 129 | depthBufferData[screenY * depthBuffer.getWidth() + screenX] = depth; 130 | 131 | VertexShaderOutput psInput; 132 | psInput.position = Vector{clip.v[0], clip.v[1], clip.v[2], 1.0F}; 133 | psInput.color = Color{ 134 | vsOutputs[0].color.r * clip.v[0] + vsOutputs[1].color.r * clip.v[1] + vsOutputs[2].color.r * clip.v[2], 135 | vsOutputs[0].color.g * clip.v[0] + vsOutputs[1].color.g * clip.v[1] + vsOutputs[2].color.g * clip.v[2], 136 | vsOutputs[0].color.b * clip.v[0] + vsOutputs[1].color.b * clip.v[1] + vsOutputs[2].color.b * clip.v[2], 137 | vsOutputs[0].color.a * clip.v[0] + vsOutputs[1].color.a * clip.v[1] + vsOutputs[2].color.a * clip.v[2] 138 | }; 139 | 140 | psInput.texCoords[0] = Vector{ 141 | vsOutputs[0].texCoords[0].v[0] * clip.v[0] + vsOutputs[1].texCoords[0].v[0] * clip.v[1] + vsOutputs[2].texCoords[0].v[0] * clip.v[2], 142 | vsOutputs[0].texCoords[0].v[1] * clip.v[0] + vsOutputs[1].texCoords[0].v[1] * clip.v[1] + vsOutputs[2].texCoords[0].v[1] * clip.v[2] 143 | }; 144 | 145 | psInput.texCoords[1] = Vector{ 146 | vsOutputs[0].texCoords[1].v[0] * clip.v[0] + vsOutputs[1].texCoords[1].v[0] * clip.v[1] + vsOutputs[2].texCoords[1].v[0] * clip.v[2], 147 | vsOutputs[0].texCoords[1].v[1] * clip.v[0] + vsOutputs[1].texCoords[1].v[1] * clip.v[1] + vsOutputs[2].texCoords[1].v[1] * clip.v[2] 148 | }; 149 | 150 | psInput.normal = vsOutputs[0].normal * clip.v[0] + vsOutputs[1].normal * clip.v[1] + vsOutputs[2].normal * clip.v[2]; 151 | 152 | const auto srcColor = fragmentShader(psInput, samplers, textures); 153 | 154 | if (blendState.enabled) 155 | { 156 | const auto pixel = reinterpret_cast(&frameBufferData[screenY * frameBuffer.getWidth() + screenX]); 157 | const Color destColor{pixel[0], pixel[1], pixel[2], pixel[3]}; 158 | 159 | // alpha blend 160 | const Color resultColor{ 161 | getValue(blendState.colorOperation, 162 | srcColor.r * getValue(blendState.colorBlendSource, srcColor.r, srcColor.a, destColor.r, destColor.a, blendState.blendFactor.r), 163 | destColor.r * getValue(blendState.colorBlendDest, srcColor.r, srcColor.a, destColor.r, destColor.a, blendState.blendFactor.r)), 164 | getValue(blendState.colorOperation, 165 | srcColor.g * getValue(blendState.colorBlendSource, srcColor.g, srcColor.a, destColor.g, destColor.a, blendState.blendFactor.g), 166 | destColor.g * getValue(blendState.colorBlendDest, srcColor.g, srcColor.a, destColor.g, destColor.a, blendState.blendFactor.g)), 167 | getValue(blendState.colorOperation, 168 | srcColor.b * getValue(blendState.colorBlendSource, srcColor.b, srcColor.a, destColor.b, destColor.a, blendState.blendFactor.b), 169 | destColor.b * getValue(blendState.colorBlendDest, srcColor.b, srcColor.a, destColor.b, destColor.a, blendState.blendFactor.b)), 170 | getValue(blendState.alphaOperation, 171 | srcColor.a * getValue(blendState.alphaBlendSource, srcColor.a, srcColor.a, destColor.a, destColor.a, blendState.blendFactor.a), 172 | destColor.a * getValue(blendState.alphaBlendDest, srcColor.a, srcColor.a, destColor.a, destColor.a, blendState.blendFactor.a)) 173 | }; 174 | 175 | frameBufferData[screenY * frameBuffer.getWidth() + screenX] = resultColor.getIntValueRaw(); 176 | } 177 | else 178 | frameBufferData[screenY * frameBuffer.getWidth() + screenX] = srcColor.getIntValueRaw(); 179 | } 180 | } 181 | } 182 | } 183 | } 184 | 185 | #endif 186 | -------------------------------------------------------------------------------- /sr/Vector.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef SR_VECTOR_HPP 6 | #define SR_VECTOR_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace sr 15 | { 16 | template class Vector final 17 | { 18 | public: 19 | std::array v{}; 20 | 21 | constexpr Vector() noexcept {} 22 | 23 | template 24 | explicit constexpr Vector(A... args) noexcept: 25 | v{args...} 26 | { 27 | } 28 | 29 | template ::type* = nullptr> 30 | explicit Vector(const Vector& vec) noexcept 31 | { 32 | for (std::size_t i = 0; i < N && i < N2; ++i) 33 | v[i] = vec.v[i]; 34 | } 35 | 36 | T& operator[](std::size_t index) noexcept { return v[index]; } 37 | [[nodiscard]] constexpr T operator[](std::size_t index) const noexcept { return v[index]; } 38 | 39 | template = 1)>::type* = nullptr> 40 | T& x() noexcept { return v[0]; } 41 | 42 | template = 1)>::type* = nullptr> 43 | [[nodiscard]] constexpr T x() const noexcept { return v[0]; } 44 | 45 | template = 2)>::type* = nullptr> 46 | T& y() noexcept { return v[1]; } 47 | 48 | template = 2)>::type* = nullptr> 49 | [[nodiscard]] constexpr T y() const noexcept { return v[1]; } 50 | 51 | template = 3)>::type* = nullptr> 52 | T& z() noexcept { return v[2]; } 53 | 54 | template = 3)>::type* = nullptr> 55 | [[nodiscard]] constexpr T z() const noexcept { return v[2]; } 56 | 57 | template = 4)>::type* = nullptr> 58 | T& w() noexcept { return v[3]; } 59 | 60 | template = 4)>::type* = nullptr> 61 | [[nodiscard]] constexpr T w() const noexcept { return v[3]; } 62 | 63 | template ::type* = nullptr> 64 | [[nodiscard]] T getAngle() const noexcept 65 | { 66 | return std::atan2(v[1], v[0]); 67 | } 68 | 69 | template ::type* = nullptr> 70 | [[nodiscard]] T getAngle(const Vector& axis) const noexcept 71 | { 72 | constexpr T dx = axis.v[0] - v[0] - v[1] * axis.v[2] + v[2] * axis.v[1]; 73 | constexpr T dy = axis.v[1] - v[1] - v[2] * axis.v[0] + v[0] * axis.v[2]; 74 | constexpr T dz = axis.v[2] - v[2] - v[0] * axis.v[1] + v[1] * axis.v[0]; 75 | 76 | return std::atan2(std::sqrt(dx * dx + dy * dy + dz * dz), dot(axis)); 77 | } 78 | 79 | template ::type* = nullptr> 80 | [[nodiscard]] T getAngle(const Vector& axis) const noexcept 81 | { 82 | constexpr T dx = v[3] * axis.v[0] - v[0] * axis.v[3] - v[1] * axis.v[2] + v[2] * axis.v[1]; 83 | constexpr T dy = v[3] * axis.v[1] - v[1] * axis.v[3] - v[2] * axis.v[0] + v[0] * axis.v[2]; 84 | constexpr T dz = v[3] * axis.v[2] - v[2] * axis.v[3] - v[0] * axis.v[1] + v[1] * axis.v[0]; 85 | 86 | return std::atan2(std::sqrt(dx * dx + dy * dy + dz * dz), dot(axis)); 87 | } 88 | 89 | void clamp(const Vector& min, const Vector& max) noexcept 90 | { 91 | for (std::size_t i = 0; i < N; ++i) 92 | if (v[i] < min.v[i]) v[i] = min.v[i]; 93 | else if (v[i] > max.v[i]) v[i] = max.v[i]; 94 | } 95 | 96 | template ::type* = nullptr> 97 | [[nodiscard]] constexpr Vector cross(const Vector& vec) const noexcept 98 | { 99 | return Vector{ 100 | (v[1] * vec.v[2]) - (v[2] * vec.v[1]), 101 | (v[2] * vec.v[0]) - (v[0] * vec.v[2]), 102 | (v[0] * vec.v[1]) - (v[1] * vec.v[0]) 103 | }; 104 | } 105 | 106 | [[nodiscard]] T distance(const Vector& vec) const noexcept 107 | { 108 | T d = 0; 109 | for (std::size_t i = 0; i < N; ++i) 110 | d += (vec.v[i] - v[i]) * (vec.v[i] - v[i]); 111 | return std::sqrt(d); 112 | } 113 | 114 | [[nodiscard]] T distanceSquared(const Vector& vec) const noexcept 115 | { 116 | T d = 0; 117 | for (std::size_t i = 0; i < N; ++i) 118 | d += (vec.v[i] - v[i]) * (vec.v[i] - v[i]); 119 | return d; 120 | } 121 | 122 | [[nodiscard]] T dot(const Vector& vec) const noexcept 123 | { 124 | T d = 0; 125 | for (std::size_t i = 0; i < N; ++i) 126 | d += v[i] * vec.v[i]; 127 | return d; 128 | } 129 | 130 | [[nodiscard]] T length() const noexcept 131 | { 132 | T l = 0; 133 | for (const auto& c : v) 134 | l += c * c; 135 | return std::sqrt(l); 136 | } 137 | 138 | [[nodiscard]] T lengthSquared() const noexcept 139 | { 140 | T l = 0; 141 | for (const auto& c : v) 142 | l += c * c; 143 | return l; 144 | } 145 | 146 | void negate() noexcept 147 | { 148 | for (auto& c : v) 149 | c = -c; 150 | } 151 | 152 | [[nodiscard]] bool isNormalized(const T tolerance = std::numeric_limits::epsilon()) const noexcept 153 | { 154 | return std::abs(T(1) - lengthSquared()) <= tolerance; 155 | } 156 | 157 | void normalize() noexcept 158 | { 159 | T squared = T(0); 160 | for (const auto& c : v) 161 | squared += c * c; 162 | 163 | if (squared == T(1)) // already normalized 164 | return; 165 | 166 | const auto length = std::sqrt(squared); 167 | if (length <= std::numeric_limits::epsilon()) // too close to zero 168 | return; 169 | 170 | const auto multiplier = T(1) / length; 171 | for (auto& c : v) 172 | c *= multiplier; 173 | } 174 | 175 | [[nodiscard]] Vector normalized() const noexcept 176 | { 177 | T squared = T(0); 178 | for (const auto& c : v) 179 | squared += c * c; 180 | 181 | if (squared == T(1)) // already normalized 182 | return *this; 183 | 184 | const auto length = std::sqrt(squared); 185 | if (length <= std::numeric_limits::epsilon()) // too close to zero 186 | return *this; 187 | 188 | const auto multiplier = T(1) / length; 189 | return *this * multiplier; 190 | } 191 | 192 | void scale(const Vector& scale) noexcept 193 | { 194 | for (std::size_t i = 0; i < N; ++i) 195 | v[i] *= scale.v[i]; 196 | } 197 | 198 | void smooth(const Vector& target, const T elapsedTime, const T responseTime) noexcept 199 | { 200 | if (elapsedTime > T(0)) 201 | *this += (target - *this) * (elapsedTime / (elapsedTime + responseTime)); 202 | } 203 | 204 | [[nodiscard]] T getMin() const noexcept 205 | { 206 | T result = v[0]; 207 | 208 | for (std::size_t i = 1; i < N; ++i) 209 | if (v[i] < result) 210 | result = v[i]; 211 | 212 | return result; 213 | } 214 | 215 | [[nodiscard]] T getMax() const noexcept 216 | { 217 | T result = v[0]; 218 | 219 | for (std::size_t i = 1; i < N; ++i) 220 | if (v[i] > result) 221 | result = v[i]; 222 | 223 | return result; 224 | } 225 | 226 | [[nodiscard]] const Vector operator+(const Vector& vec) const noexcept 227 | { 228 | Vector result = *this; 229 | for (std::size_t i = 0; i < N; ++i) 230 | result.v[i] += vec.v[i]; 231 | return result; 232 | } 233 | 234 | Vector& operator+=(const Vector& vec) noexcept 235 | { 236 | for (std::size_t i = 0; i < N; ++i) 237 | v[i] += vec.v[i]; 238 | return *this; 239 | } 240 | 241 | [[nodiscard]] const Vector operator-(const Vector& vec) const noexcept 242 | { 243 | Vector result = *this; 244 | for (std::size_t i = 0; i < N; ++i) 245 | result.v[i] -= vec.v[i]; 246 | return result; 247 | } 248 | 249 | Vector& operator-=(const Vector& vec) noexcept 250 | { 251 | for (std::size_t i = 0; i < N; ++i) 252 | v[i] -= vec.v[i]; 253 | return *this; 254 | } 255 | 256 | [[nodiscard]] const Vector operator-() const noexcept 257 | { 258 | Vector result = *this; 259 | for (auto& c : result.v) 260 | c = -c; 261 | return result; 262 | } 263 | 264 | [[nodiscard]] const Vector operator*(const T scalar) const noexcept 265 | { 266 | Vector result(*this); 267 | for (auto& c : result.v) 268 | c *= scalar; 269 | return result; 270 | } 271 | 272 | Vector& operator*=(const T scalar) noexcept 273 | { 274 | for (auto& c : v) 275 | c *= scalar; 276 | return *this; 277 | } 278 | 279 | [[nodiscard]] const Vector operator/(const T scalar) const noexcept 280 | { 281 | Vector result(*this); 282 | for (auto& c : result.v) 283 | c /= scalar; 284 | return result; 285 | } 286 | 287 | Vector& operator/=(const T scalar) noexcept 288 | { 289 | for (auto& c : v) 290 | c /= scalar; 291 | return *this; 292 | } 293 | 294 | [[nodiscard]] bool operator<(const Vector& vec) const noexcept 295 | { 296 | for (std::size_t i = 0; i < N; ++i) 297 | if (v[i] < vec.v[i]) return true; 298 | else if (vec.v[i] < v[i]) return false; 299 | 300 | return false; 301 | } 302 | 303 | [[nodiscard]] bool operator==(const Vector& vec) const noexcept 304 | { 305 | return std::equal(std::begin(v), std::end(v), std::begin(vec.v)); 306 | } 307 | 308 | [[nodiscard]] bool operator!=(const Vector& vec) const noexcept 309 | { 310 | return !std::equal(std::begin(v), std::end(v), std::begin(vec.v)); 311 | } 312 | 313 | [[nodiscard]] bool isZero() const noexcept 314 | { 315 | for (const auto& c : v) 316 | if (c != T(0)) return false; 317 | return true; 318 | } 319 | }; 320 | 321 | template 322 | [[nodiscard]] inline const Vector operator*(const T scalar, const Vector& vec) noexcept 323 | { 324 | Vector result = vec; 325 | result *= scalar; 326 | return result; 327 | } 328 | } 329 | 330 | #endif 331 | -------------------------------------------------------------------------------- /demo/Bmp.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #ifndef BMP_HPP 6 | #define BMP_HPP 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace sr 15 | { 16 | namespace bmp 17 | { 18 | class Error final: public std::runtime_error 19 | { 20 | public: 21 | using runtime_error::runtime_error; 22 | }; 23 | 24 | class Bmp final 25 | { 26 | public: 27 | static constexpr int BITMAPFILEHEADER_TYPE_BM = 0x4D42; 28 | 29 | struct BitmapFileHeader /**** BMP file header structure ****/ 30 | { 31 | unsigned short bfType; /* Magic number for file */ 32 | unsigned int bfSize; /* Size of file */ 33 | unsigned short bfReserved1; /* Reserved */ 34 | unsigned short bfReserved2; /* ... */ 35 | unsigned int bfOffBits; /* Offset to bitmap data */ 36 | }; 37 | 38 | struct BitmapInfoHeader /**** BMP file info structure ****/ 39 | { 40 | unsigned int biSize; /* Size of info header */ 41 | int biWidth; /* Width of image */ 42 | int biHeight; /* Height of image */ 43 | unsigned short biPlanes; /* Number of color planes */ 44 | unsigned short biBitCount; /* Number of bits per pixel */ 45 | unsigned int biCompression; /* Type of compression to use */ 46 | unsigned int biSizeImage; /* Size of image data */ 47 | int biXPelsPerMeter; /* X pixels per meter */ 48 | int biYPelsPerMeter; /* Y pixels per meter */ 49 | unsigned int biClrUsed; /* Number of colors used */ 50 | unsigned int biClrImportant; /* Number of important colors */ 51 | }; 52 | 53 | struct RGBQuad /**** Colormap entry structure ****/ 54 | { 55 | unsigned char rgbBlue; /* Blue value */ 56 | unsigned char rgbGreen; /* Green value */ 57 | unsigned char rgbRed; /* Red value */ 58 | unsigned char rgbReserved; /* Reserved */ 59 | }; 60 | 61 | enum Compression 62 | { 63 | RGB = 0x0000, 64 | RLE8 = 0x0001, 65 | RLE4 = 0x0002, 66 | BITFIELDS = 0x0003, 67 | JPEG = 0x0004, 68 | PNG = 0x0005, 69 | CMYK = 0x000B, 70 | CMYKRLE8 = 0x000C, 71 | CMYKRLE4 = 0x000D 72 | }; 73 | 74 | Bmp() = default; 75 | 76 | explicit Bmp(const std::string& filename) 77 | { 78 | std::ifstream f{filename, std::ios::binary}; 79 | 80 | if (!f.is_open()) 81 | throw Error{"Failed to open file"}; 82 | 83 | BitmapFileHeader header; 84 | 85 | f.read(reinterpret_cast(&header.bfType), sizeof(header.bfType)); 86 | 87 | if (header.bfType != BITMAPFILEHEADER_TYPE_BM) 88 | throw Error{"Bad bitmap file"}; 89 | 90 | f.read(reinterpret_cast(&header.bfSize), sizeof(header.bfSize)); 91 | f.read(reinterpret_cast(&header.bfReserved1), sizeof(header.bfReserved1)); 92 | f.read(reinterpret_cast(&header.bfReserved2), sizeof(header.bfReserved2)); 93 | f.read(reinterpret_cast(&header.bfOffBits), sizeof(header.bfOffBits)); 94 | 95 | BitmapInfoHeader infoHeader; 96 | f.read(reinterpret_cast(&infoHeader.biSize), sizeof(infoHeader.biSize)); 97 | 98 | std::size_t offset = 0; 99 | 100 | if (offset + sizeof(infoHeader.biWidth) < infoHeader.biSize) 101 | { 102 | f.read(reinterpret_cast(&infoHeader.biWidth), sizeof(infoHeader.biWidth)); 103 | offset += sizeof(infoHeader.biWidth); 104 | } 105 | 106 | if (offset + sizeof(infoHeader.biHeight) < infoHeader.biSize) 107 | { 108 | f.read(reinterpret_cast(&infoHeader.biHeight), sizeof(infoHeader.biHeight)); 109 | offset += sizeof(infoHeader.biHeight); 110 | } 111 | 112 | if (offset + sizeof(infoHeader.biPlanes) < infoHeader.biSize) 113 | { 114 | f.read(reinterpret_cast(&infoHeader.biPlanes), sizeof(infoHeader.biPlanes)); 115 | offset += sizeof(infoHeader.biPlanes); 116 | } 117 | 118 | if (offset + sizeof(infoHeader.biBitCount) < infoHeader.biSize) 119 | { 120 | f.read(reinterpret_cast(&infoHeader.biBitCount), sizeof(infoHeader.biBitCount)); 121 | offset += sizeof(infoHeader.biBitCount); 122 | } 123 | 124 | if (offset + sizeof(infoHeader.biCompression) < infoHeader.biSize) 125 | { 126 | f.read(reinterpret_cast(&infoHeader.biCompression), sizeof(infoHeader.biCompression)); 127 | offset += sizeof(infoHeader.biCompression); 128 | } 129 | 130 | if (infoHeader.biCompression != RGB) 131 | throw Error{"Compression not supported"}; 132 | 133 | if (offset + sizeof(infoHeader.biSizeImage) < infoHeader.biSize) 134 | { 135 | f.read(reinterpret_cast(&infoHeader.biSizeImage), sizeof(infoHeader.biSizeImage)); 136 | offset += sizeof(infoHeader.biSizeImage); 137 | } 138 | 139 | if (offset + sizeof(infoHeader.biXPelsPerMeter) < infoHeader.biSize) 140 | { 141 | f.read(reinterpret_cast(&infoHeader.biXPelsPerMeter), sizeof(infoHeader.biXPelsPerMeter)); 142 | offset += sizeof(infoHeader.biXPelsPerMeter); 143 | } 144 | 145 | if (offset + sizeof(infoHeader.biYPelsPerMeter) < infoHeader.biSize) 146 | { 147 | f.read(reinterpret_cast(&infoHeader.biYPelsPerMeter), sizeof(infoHeader.biYPelsPerMeter)); 148 | offset += sizeof(infoHeader.biYPelsPerMeter); 149 | } 150 | 151 | if (offset + sizeof(infoHeader.biClrUsed) < infoHeader.biSize) 152 | { 153 | f.read(reinterpret_cast(&infoHeader.biClrUsed), sizeof(infoHeader.biClrUsed)); 154 | offset += sizeof(infoHeader.biClrUsed); 155 | } 156 | 157 | if (offset + sizeof(infoHeader.biClrImportant) < infoHeader.biSize) 158 | { 159 | f.read(reinterpret_cast(&infoHeader.biClrImportant), sizeof(infoHeader.biClrImportant)); 160 | offset += sizeof(infoHeader.biClrImportant); 161 | } 162 | 163 | f.seekg(header.bfOffBits, std::ios::beg); 164 | 165 | data.resize(infoHeader.biWidth * std::abs(infoHeader.biHeight) * sizeof(RGBQuad)); 166 | std::fill(data.begin(), data.end(), 255); 167 | 168 | if (infoHeader.biHeight > 0) // bottom to top 169 | { 170 | for (int y = infoHeader.biHeight - 1; y >= 0; --y) 171 | for (int x = 0; x < infoHeader.biWidth; ++x) 172 | f.read(reinterpret_cast(&data[(y * infoHeader.biWidth + x) * sizeof(RGBQuad)]), 173 | infoHeader.biBitCount / 8); 174 | } 175 | else // top to bottom 176 | { 177 | for (int y = 0; y < std::abs(infoHeader.biHeight); ++y) 178 | for (int x = 0; x < infoHeader.biWidth; ++x) 179 | f.read(reinterpret_cast(&data[(y * infoHeader.biWidth + x) * sizeof(RGBQuad)]), 180 | infoHeader.biBitCount / 8); 181 | } 182 | 183 | width = static_cast(infoHeader.biWidth); 184 | height = static_cast(std::abs(infoHeader.biHeight)); 185 | } 186 | 187 | auto getWidth() const noexcept { return width; } 188 | auto getHeight() const noexcept { return height; } 189 | auto& getData() const noexcept { return data; } 190 | 191 | void setData(std::size_t newWidth, 192 | std::size_t newHeight, 193 | const std::vector& newData) 194 | { 195 | width = newWidth; 196 | height = newHeight; 197 | data = newData; 198 | } 199 | 200 | void save(const std::string& filename) 201 | { 202 | std::ofstream f{filename, std::ios::binary | std::ios::trunc}; 203 | 204 | if (!f.is_open()) 205 | throw Error{"Failed to open file"}; 206 | 207 | BitmapFileHeader header; 208 | header.bfType = BITMAPFILEHEADER_TYPE_BM; 209 | header.bfSize = static_cast(width * height * 4 + 14 + 40); 210 | header.bfReserved1 = 0; 211 | header.bfReserved2 = 0; 212 | header.bfOffBits = 14 + 40; // size of BITMAPFILEHEADER + size of BITMAPINFOHEADER 213 | 214 | f.write(reinterpret_cast(&header.bfType), sizeof(header.bfType)); 215 | f.write(reinterpret_cast(&header.bfSize), sizeof(header.bfSize)); 216 | f.write(reinterpret_cast(&header.bfReserved1), sizeof(header.bfReserved1)); 217 | f.write(reinterpret_cast(&header.bfReserved2), sizeof(header.bfReserved2)); 218 | f.write(reinterpret_cast(&header.bfOffBits), sizeof(header.bfOffBits)); 219 | 220 | BitmapInfoHeader infoHeader; 221 | infoHeader.biSize = 40; 222 | infoHeader.biWidth = static_cast(width); 223 | infoHeader.biHeight = -static_cast(height); 224 | infoHeader.biPlanes = 1; 225 | infoHeader.biBitCount = 32; 226 | infoHeader.biCompression = 0; 227 | infoHeader.biSizeImage = 0; 228 | infoHeader.biXPelsPerMeter = 0; 229 | infoHeader.biYPelsPerMeter = 0; 230 | infoHeader.biClrUsed = 0; 231 | infoHeader.biClrImportant = 0; 232 | 233 | f.write(reinterpret_cast(&infoHeader.biSize), sizeof(infoHeader.biSize)); 234 | f.write(reinterpret_cast(&infoHeader.biWidth), sizeof(infoHeader.biWidth)); 235 | f.write(reinterpret_cast(&infoHeader.biHeight), sizeof(infoHeader.biHeight)); 236 | f.write(reinterpret_cast(&infoHeader.biPlanes), sizeof(infoHeader.biPlanes)); 237 | f.write(reinterpret_cast(&infoHeader.biBitCount), sizeof(infoHeader.biBitCount)); 238 | f.write(reinterpret_cast(&infoHeader.biCompression), sizeof(infoHeader.biCompression)); 239 | f.write(reinterpret_cast(&infoHeader.biSizeImage), sizeof(infoHeader.biSizeImage)); 240 | f.write(reinterpret_cast(&infoHeader.biXPelsPerMeter), sizeof(infoHeader.biXPelsPerMeter)); 241 | f.write(reinterpret_cast(&infoHeader.biYPelsPerMeter), sizeof(infoHeader.biYPelsPerMeter)); 242 | f.write(reinterpret_cast(&infoHeader.biClrUsed), sizeof(infoHeader.biClrUsed)); 243 | f.write(reinterpret_cast(&infoHeader.biClrImportant), sizeof(infoHeader.biClrImportant)); 244 | 245 | f.write(reinterpret_cast(data.data()), data.size()); 246 | } 247 | 248 | private: 249 | std::size_t width = 0; 250 | std::size_t height = 0; 251 | std::vector data; 252 | }; 253 | } 254 | } 255 | 256 | #endif 257 | -------------------------------------------------------------------------------- /demo/ApplicationMacOS.mm: -------------------------------------------------------------------------------- 1 | // 2 | // SoftwareRenderer 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ApplicationMacOS.hpp" 10 | 11 | @interface AppDelegate: NSObject 12 | { 13 | demo::Application* application; 14 | } 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | -(id)initWithApplication:(demo::Application*)initApplication 20 | { 21 | if (self = [super init]) 22 | application = initApplication; 23 | 24 | return self; 25 | } 26 | 27 | -(void)applicationWillFinishLaunching:(__unused NSNotification*)notification 28 | { 29 | } 30 | 31 | -(void)applicationDidFinishLaunching:(__unused NSNotification*)notification 32 | { 33 | } 34 | 35 | -(void)applicationWillTerminate:(__unused NSNotification*)notification 36 | { 37 | } 38 | 39 | -(BOOL)applicationShouldTerminateAfterLastWindowClosed:(__unused NSApplication*)sender 40 | { 41 | return YES; 42 | } 43 | 44 | -(void)applicationDidBecomeActive:(__unused NSNotification*)notification 45 | { 46 | } 47 | 48 | -(void)applicationDidResignActive:(__unused NSNotification*)notification 49 | { 50 | } 51 | 52 | @end 53 | 54 | @interface WindowDelegate: NSObject 55 | { 56 | demo::ApplicationMacOS* application; 57 | } 58 | 59 | @end 60 | 61 | @implementation WindowDelegate 62 | 63 | -(id)initWithApplication:(demo::ApplicationMacOS*)initApplication 64 | { 65 | if (self = [super init]) 66 | { 67 | application = initApplication; 68 | } 69 | 70 | return self; 71 | } 72 | 73 | -(void)windowDidResize:(__unused NSNotification*)notification 74 | { 75 | application->didResize(); 76 | } 77 | 78 | @end 79 | 80 | @interface Canvas: NSView 81 | { 82 | demo::ApplicationMacOS* application; 83 | } 84 | 85 | @end 86 | 87 | @implementation Canvas 88 | 89 | -(id)initWithFrame:(NSRect)frameRect andApplication:(demo::ApplicationMacOS*)initApplication 90 | { 91 | if (self = [super initWithFrame:frameRect]) 92 | { 93 | application = initApplication; 94 | } 95 | 96 | return self; 97 | } 98 | 99 | -(void)drawRect:(NSRect)dirtyRect 100 | { 101 | [super drawRect:dirtyRect]; 102 | 103 | application->draw(); 104 | } 105 | 106 | -(void)draw:(__unused NSTimer*)timer 107 | { 108 | [self setNeedsDisplay:YES]; 109 | } 110 | 111 | @end 112 | 113 | namespace 114 | { 115 | const void* getBytePointer(void* info) 116 | { 117 | const auto frameBuffer = static_cast(info); 118 | return frameBuffer->getData().data(); 119 | } 120 | 121 | void createMainMenu(NSApplication* application) 122 | { 123 | NSMenu* mainMenu = [[[NSMenu alloc] init] autorelease]; 124 | 125 | // Apple menu 126 | NSMenuItem* mainMenuItem = [mainMenu addItemWithTitle:@"" 127 | action:nil 128 | keyEquivalent:@""]; 129 | 130 | NSMenu* applicationMenu = [[[NSMenu alloc] init] autorelease]; 131 | mainMenuItem.submenu = applicationMenu; 132 | 133 | NSString* bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleDisplayName"]; 134 | if (!bundleName) 135 | bundleName = NSBundle.mainBundle.infoDictionary[@"CFBundleName"]; 136 | 137 | [applicationMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"About", nil), bundleName] 138 | action:@selector(orderFrontStandardAboutPanel:) 139 | keyEquivalent:@""]; 140 | 141 | [applicationMenu addItem:[NSMenuItem separatorItem]]; 142 | 143 | NSMenuItem* servicesItem = [applicationMenu addItemWithTitle:NSLocalizedString(@"Services", nil) 144 | action:nil 145 | keyEquivalent:@""]; 146 | 147 | NSMenu* servicesMenu = [[[NSMenu alloc] init] autorelease]; 148 | servicesItem.submenu = servicesMenu; 149 | application.servicesMenu = servicesMenu; 150 | 151 | [applicationMenu addItem:[NSMenuItem separatorItem]]; 152 | 153 | [applicationMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Hide", nil), bundleName] 154 | action:@selector(hide:) 155 | keyEquivalent:@"h"]; 156 | 157 | NSMenuItem* hideOthersItem = [applicationMenu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) 158 | action:@selector(hideOtherApplications:) 159 | keyEquivalent:@"h"]; 160 | hideOthersItem.keyEquivalentModifierMask = NSEventModifierFlagOption | NSEventModifierFlagCommand; 161 | 162 | [applicationMenu addItemWithTitle:NSLocalizedString(@"Show All", nil) 163 | action:@selector(unhideAllApplications:) 164 | keyEquivalent:@""]; 165 | 166 | [applicationMenu addItem:[NSMenuItem separatorItem]]; 167 | 168 | [applicationMenu addItemWithTitle:[NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"Quit", nil), bundleName] 169 | action:@selector(terminate:) 170 | keyEquivalent:@"q"]; 171 | 172 | // View menu 173 | NSMenuItem* viewItem = [mainMenu addItemWithTitle:NSLocalizedString(@"View", nil) 174 | action:nil 175 | keyEquivalent:@""]; 176 | 177 | NSMenu* viewMenu = [[[NSMenu alloc] initWithTitle:NSLocalizedString(@"View", nil)] autorelease]; 178 | viewItem.submenu = viewMenu; 179 | 180 | // Window menu 181 | NSMenuItem* windowsItem = [mainMenu addItemWithTitle:NSLocalizedString(@"Window", nil) 182 | action:nil 183 | keyEquivalent:@""]; 184 | 185 | NSMenu* windowsMenu = [[[NSMenu alloc] initWithTitle:NSLocalizedString(@"Window", nil)] autorelease]; 186 | [windowsMenu addItemWithTitle:NSLocalizedString(@"Minimize", nil) action:@selector(performMiniaturize:) keyEquivalent:@"m"]; 187 | [windowsMenu addItemWithTitle:NSLocalizedString(@"Zoom", nil) action:@selector(performZoom:) keyEquivalent:@""]; 188 | 189 | windowsItem.submenu = windowsMenu; 190 | application.windowsMenu = windowsMenu; 191 | 192 | // Help menu 193 | NSMenuItem* helpItem = [mainMenu addItemWithTitle:NSLocalizedString(@"Help", nil) 194 | action:nil 195 | keyEquivalent:@""]; 196 | 197 | NSMenu* helpMenu = [[[NSMenu alloc] initWithTitle:NSLocalizedString(@"Help", nil)] autorelease]; 198 | helpItem.submenu = helpMenu; 199 | application.helpMenu = helpMenu; 200 | 201 | application.mainMenu = mainMenu; 202 | } 203 | } 204 | 205 | namespace demo 206 | { 207 | std::string getResourcePath() 208 | { 209 | CFBundleRef bundle = CFBundleGetMainBundle(); 210 | if (!bundle) throw std::runtime_error{"Failed to get main bundle"}; 211 | 212 | const cf::Pointer relativePath = CFBundleCopyResourcesDirectoryURL(bundle); 213 | if (!relativePath) throw std::runtime_error{"Failed to get current directory"}; 214 | 215 | const cf::Pointer absolutePath = CFURLCopyAbsoluteURL(relativePath); 216 | if (!absolutePath) throw std::runtime_error{"Failed to copy absolute URL"}; 217 | 218 | const cf::Pointer path = CFURLCopyFileSystemPath(absolutePath, kCFURLPOSIXPathStyle); 219 | if (!path) throw std::runtime_error{"Failed to copy file system path"}; 220 | 221 | const auto maximumSize = CFStringGetMaximumSizeOfFileSystemRepresentation(path); 222 | const auto resourceDirectory = std::unique_ptr(new char[static_cast(maximumSize)]); 223 | if (!CFStringGetFileSystemRepresentation(path, resourceDirectory.get(), maximumSize)) 224 | throw std::runtime_error{"Failed to get file system representation"}; 225 | 226 | return std::string{resourceDirectory.get()}; 227 | } 228 | 229 | ApplicationMacOS::ApplicationMacOS() 230 | { 231 | pool = [[NSAutoreleasePool alloc] init]; 232 | 233 | appDelegate = [[AppDelegate alloc] initWithApplication:this]; 234 | 235 | NSApplication* sharedApplication = [NSApplication sharedApplication]; 236 | [sharedApplication activateIgnoringOtherApps:YES]; 237 | [sharedApplication setDelegate:appDelegate]; 238 | createMainMenu(sharedApplication); 239 | 240 | NSScreen* screen = [NSScreen mainScreen]; 241 | 242 | // create window 243 | const CGSize windowSize = CGSizeMake(std::round(screen.frame.size.width * 0.6), 244 | std::round(screen.frame.size.height * 0.6)); 245 | 246 | const NSRect frame = NSMakeRect(std::round(screen.frame.size.width / 2.0F - windowSize.width / 2.0F), 247 | std::round(screen.frame.size.height / 2.0F - windowSize.height / 2.0F), 248 | windowSize.width, windowSize.height); 249 | 250 | const NSWindowStyleMask windowStyleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; 251 | 252 | window = [[NSWindow alloc] initWithContentRect:frame 253 | styleMask:windowStyleMask 254 | backing:NSBackingStoreBuffered 255 | defer:NO 256 | screen:screen]; 257 | [window setReleasedWhenClosed:NO]; 258 | [window setTabbingMode:NSWindowTabbingModeDisallowed]; 259 | 260 | [window setAcceptsMouseMovedEvents:YES]; 261 | windowDelegate = [[WindowDelegate alloc] initWithApplication:this]; 262 | [window setDelegate:windowDelegate]; 263 | 264 | [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; 265 | [window setTitle:@"SoftwareRenderer"]; 266 | [window setReleasedWhenClosed:NO]; 267 | 268 | const NSRect windowFrame = [window contentRectForFrameRect:[window frame]]; 269 | 270 | const auto w = static_cast(windowFrame.size.width); 271 | const auto h = static_cast(windowFrame.size.height); 272 | 273 | content = [[Canvas alloc] initWithFrame:windowFrame andApplication:this]; 274 | 275 | componentsPerPixel = 4; 276 | bitsPerComponent = sizeof(std::uint8_t) * 8; 277 | 278 | CGDataProviderDirectCallbacks providerCallbacks = { 279 | 0, 280 | getBytePointer, 281 | nullptr, 282 | nullptr, 283 | nullptr 284 | }; 285 | 286 | colorSpace = CGColorSpaceCreateDeviceRGB(); 287 | 288 | provider = CGDataProviderCreateDirect(&getFrameBuffer(), w * h * componentsPerPixel, &providerCallbacks); 289 | 290 | [window setContentView:content]; 291 | [window makeKeyAndOrderFront:nil]; 292 | 293 | [content setNeedsDisplay:TRUE]; 294 | 295 | timer = [[NSTimer scheduledTimerWithTimeInterval:1.0 / 60.0 296 | target:content 297 | selector:@selector(draw:) 298 | userInfo:[NSValue valueWithPointer:this] 299 | repeats:YES] retain]; 300 | 301 | setup(w, h); 302 | } 303 | 304 | ApplicationMacOS::~ApplicationMacOS() 305 | { 306 | if (window) 307 | [window setDelegate:nil]; 308 | } 309 | 310 | void ApplicationMacOS::draw() 311 | { 312 | render(); 313 | 314 | const auto& frameBuffer = getFrameBuffer(); 315 | 316 | cf::Pointer image = CGImageCreate(frameBuffer.getWidth(), frameBuffer.getHeight(), 317 | bitsPerComponent, 318 | bitsPerComponent * componentsPerPixel, 319 | componentsPerPixel * frameBuffer.getWidth(), 320 | colorSpace, 321 | kCGBitmapByteOrder32Big | kCGImageAlphaNoneSkipLast, 322 | provider, nullptr, FALSE, kCGRenderingIntentDefault); 323 | 324 | CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; 325 | 326 | CGContextDrawImage(context, [content frame], image); 327 | CGContextFlush(context); 328 | } 329 | 330 | void ApplicationMacOS::didResize() 331 | { 332 | const NSRect windowFrame = [window contentRectForFrameRect:[window frame]]; 333 | 334 | const CGDataProviderDirectCallbacks providerCallbacks = { 335 | 0, 336 | getBytePointer, 337 | nullptr, 338 | nullptr, 339 | nullptr 340 | }; 341 | 342 | const auto w = static_cast(windowFrame.size.width); 343 | const auto h = static_cast(windowFrame.size.height); 344 | 345 | provider = CGDataProviderCreateDirect(&getFrameBuffer(), 346 | w * h * componentsPerPixel, 347 | &providerCallbacks); 348 | 349 | onResize(w, h); 350 | } 351 | 352 | void ApplicationMacOS::run() 353 | { 354 | NSApplication* sharedApplication = [NSApplication sharedApplication]; 355 | [sharedApplication run]; 356 | } 357 | } 358 | 359 | int main() 360 | { 361 | try 362 | { 363 | demo::ApplicationMacOS application; 364 | application.run(); 365 | return EXIT_SUCCESS; 366 | } 367 | catch (const std::exception& e) 368 | { 369 | std::cerr << e.what() << std::endl; 370 | return EXIT_FAILURE; 371 | } 372 | catch (...) 373 | { 374 | return EXIT_FAILURE; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /test/test.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 303E879D251EE589008B7E24 /* tests.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 303E879C251EE589008B7E24 /* tests.cpp */; }; 11 | C6C90FD721A5A24D00B5FCB7 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C6C90FD621A5A24D00B5FCB7 /* main.cpp */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | C6C90FD121A5A24D00B5FCB7 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 303E879C251EE589008B7E24 /* tests.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tests.cpp; sourceTree = ""; }; 28 | 30E132CE27F83E0A0079F035 /* RenderError.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = RenderError.hpp; sourceTree = ""; }; 29 | 30E132CF27F83E0A0079F035 /* Vector.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Vector.hpp; sourceTree = ""; }; 30 | 30E132D027F83E0A0079F035 /* Vertex.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Vertex.hpp; sourceTree = ""; }; 31 | 30E132D127F83E0A0079F035 /* Matrix.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Matrix.hpp; sourceTree = ""; }; 32 | 30E132D227F83E0A0079F035 /* DepthState.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DepthState.hpp; sourceTree = ""; }; 33 | 30E132D327F83E0A0079F035 /* Renderer.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Renderer.hpp; sourceTree = ""; }; 34 | 30E132D427F83E0A0079F035 /* Color.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Color.hpp; sourceTree = ""; }; 35 | 30E132D527F83E0A0079F035 /* BlendState.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BlendState.hpp; sourceTree = ""; }; 36 | 30E132D627F83E0A0079F035 /* Rect.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Rect.hpp; sourceTree = ""; }; 37 | 30E132D727F83E0A0079F035 /* Constants.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Constants.hpp; sourceTree = ""; }; 38 | 30E132D827F83E0A0079F035 /* Shader.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Shader.hpp; sourceTree = ""; }; 39 | 30E132D927F83E0A0079F035 /* Sampler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sampler.hpp; sourceTree = ""; }; 40 | 30E132DA27F83E0A0079F035 /* sr.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = sr.hpp; sourceTree = ""; }; 41 | 30E132DB27F83E0A0079F035 /* Texture.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Texture.hpp; sourceTree = ""; }; 42 | 30E132DC27F83E0A0079F035 /* Size.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Size.hpp; sourceTree = ""; }; 43 | 30E132DD27F83E0B0079F035 /* PixelFormat.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = PixelFormat.hpp; sourceTree = ""; }; 44 | C6C90FD321A5A24D00B5FCB7 /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | C6C90FD621A5A24D00B5FCB7 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 46 | /* End PBXFileReference section */ 47 | 48 | /* Begin PBXFrameworksBuildPhase section */ 49 | C6C90FD021A5A24D00B5FCB7 /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 30E132CD27F83DEF0079F035 /* sr */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 30E132D527F83E0A0079F035 /* BlendState.hpp */, 63 | 30E132D427F83E0A0079F035 /* Color.hpp */, 64 | 30E132D727F83E0A0079F035 /* Constants.hpp */, 65 | 30E132D227F83E0A0079F035 /* DepthState.hpp */, 66 | 30E132D127F83E0A0079F035 /* Matrix.hpp */, 67 | 30E132DD27F83E0B0079F035 /* PixelFormat.hpp */, 68 | 30E132D627F83E0A0079F035 /* Rect.hpp */, 69 | 30E132D327F83E0A0079F035 /* Renderer.hpp */, 70 | 30E132CE27F83E0A0079F035 /* RenderError.hpp */, 71 | 30E132D927F83E0A0079F035 /* Sampler.hpp */, 72 | 30E132D827F83E0A0079F035 /* Shader.hpp */, 73 | 30E132DC27F83E0A0079F035 /* Size.hpp */, 74 | 30E132DA27F83E0A0079F035 /* sr.hpp */, 75 | 30E132DB27F83E0A0079F035 /* Texture.hpp */, 76 | 30E132CF27F83E0A0079F035 /* Vector.hpp */, 77 | 30E132D027F83E0A0079F035 /* Vertex.hpp */, 78 | ); 79 | name = sr; 80 | path = ../sr; 81 | sourceTree = ""; 82 | }; 83 | C6C90FCA21A5A24D00B5FCB7 = { 84 | isa = PBXGroup; 85 | children = ( 86 | 30E132CD27F83DEF0079F035 /* sr */, 87 | C6C90FD621A5A24D00B5FCB7 /* main.cpp */, 88 | C6C90FD421A5A24D00B5FCB7 /* Products */, 89 | 303E879C251EE589008B7E24 /* tests.cpp */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | C6C90FD421A5A24D00B5FCB7 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | C6C90FD321A5A24D00B5FCB7 /* test */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | C6C90FD221A5A24D00B5FCB7 /* test */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = C6C90FDA21A5A24D00B5FCB7 /* Build configuration list for PBXNativeTarget "test" */; 107 | buildPhases = ( 108 | C6C90FCF21A5A24D00B5FCB7 /* Sources */, 109 | C6C90FD021A5A24D00B5FCB7 /* Frameworks */, 110 | C6C90FD121A5A24D00B5FCB7 /* CopyFiles */, 111 | ); 112 | buildRules = ( 113 | ); 114 | dependencies = ( 115 | ); 116 | name = test; 117 | productName = WebsocketTest; 118 | productReference = C6C90FD321A5A24D00B5FCB7 /* test */; 119 | productType = "com.apple.product-type.tool"; 120 | }; 121 | /* End PBXNativeTarget section */ 122 | 123 | /* Begin PBXProject section */ 124 | C6C90FCB21A5A24D00B5FCB7 /* Project object */ = { 125 | isa = PBXProject; 126 | attributes = { 127 | LastUpgradeCheck = 1010; 128 | ORGANIZATIONNAME = "Elviss Strazdins"; 129 | TargetAttributes = { 130 | C6C90FD221A5A24D00B5FCB7 = { 131 | CreatedOnToolsVersion = 10.1; 132 | }; 133 | }; 134 | }; 135 | buildConfigurationList = C6C90FCE21A5A24D00B5FCB7 /* Build configuration list for PBXProject "test" */; 136 | compatibilityVersion = "Xcode 9.3"; 137 | developmentRegion = en; 138 | hasScannedForEncodings = 0; 139 | knownRegions = ( 140 | en, 141 | ); 142 | mainGroup = C6C90FCA21A5A24D00B5FCB7; 143 | productRefGroup = C6C90FD421A5A24D00B5FCB7 /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | C6C90FD221A5A24D00B5FCB7 /* test */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXSourcesBuildPhase section */ 153 | C6C90FCF21A5A24D00B5FCB7 /* Sources */ = { 154 | isa = PBXSourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | C6C90FD721A5A24D00B5FCB7 /* main.cpp in Sources */, 158 | 303E879D251EE589008B7E24 /* tests.cpp in Sources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXSourcesBuildPhase section */ 163 | 164 | /* Begin XCBuildConfiguration section */ 165 | C6C90FD821A5A24D00B5FCB7 /* Debug */ = { 166 | isa = XCBuildConfiguration; 167 | buildSettings = { 168 | ALWAYS_SEARCH_USER_PATHS = NO; 169 | CLANG_ANALYZER_NONNULL = YES; 170 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 171 | CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; 172 | CLANG_CXX_LANGUAGE_STANDARD = "c++17"; 173 | CLANG_CXX_LIBRARY = "libc++"; 174 | CLANG_ENABLE_MODULES = YES; 175 | CLANG_ENABLE_OBJC_ARC = YES; 176 | CLANG_ENABLE_OBJC_WEAK = YES; 177 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; 178 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; 179 | CLANG_WARN_ASSIGN_ENUM = YES; 180 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_COMMA = YES; 183 | CLANG_WARN_CONSTANT_CONVERSION = YES; 184 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 185 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 186 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 187 | CLANG_WARN_EMPTY_BODY = YES; 188 | CLANG_WARN_ENUM_CONVERSION = YES; 189 | CLANG_WARN_FLOAT_CONVERSION = YES; 190 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; 191 | CLANG_WARN_INFINITE_RECURSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 194 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 195 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 196 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 197 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 198 | CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES; 199 | CLANG_WARN_STRICT_PROTOTYPES = YES; 200 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 201 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 202 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 203 | CLANG_WARN_UNREACHABLE_CODE = YES; 204 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 205 | DEBUG_INFORMATION_FORMAT = dwarf; 206 | ENABLE_STRICT_OBJC_MSGSEND = YES; 207 | ENABLE_TESTABILITY = YES; 208 | GCC_NO_COMMON_BLOCKS = YES; 209 | GCC_OPTIMIZATION_LEVEL = 0; 210 | GCC_PREPROCESSOR_DEFINITIONS = ( 211 | "DEBUG=1", 212 | "$(inherited)", 213 | ); 214 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 215 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 216 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 217 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 219 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 220 | GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; 221 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 222 | GCC_WARN_PEDANTIC = YES; 223 | GCC_WARN_SHADOW = YES; 224 | GCC_WARN_SIGN_COMPARE = YES; 225 | GCC_WARN_UNDECLARED_SELECTOR = YES; 226 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 227 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 228 | GCC_WARN_UNUSED_FUNCTION = YES; 229 | GCC_WARN_UNUSED_LABEL = YES; 230 | GCC_WARN_UNUSED_PARAMETER = YES; 231 | GCC_WARN_UNUSED_VARIABLE = YES; 232 | MACOSX_DEPLOYMENT_TARGET = 10.12; 233 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 234 | MTL_FAST_MATH = YES; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = macosx; 237 | }; 238 | name = Debug; 239 | }; 240 | C6C90FD921A5A24D00B5FCB7 /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_NONNULL = YES; 245 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 246 | CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "c++17"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_ENABLE_OBJC_WEAK = YES; 252 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_INTEGER = YES; 253 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES; 254 | CLANG_WARN_ASSIGN_ENUM = YES; 255 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 256 | CLANG_WARN_BOOL_CONVERSION = YES; 257 | CLANG_WARN_COMMA = YES; 258 | CLANG_WARN_CONSTANT_CONVERSION = YES; 259 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 261 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_FLOAT_CONVERSION = YES; 265 | CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES; 266 | CLANG_WARN_INFINITE_RECURSION = YES; 267 | CLANG_WARN_INT_CONVERSION = YES; 268 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 269 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 270 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 271 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 272 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 273 | CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES; 274 | CLANG_WARN_STRICT_PROTOTYPES = YES; 275 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 276 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 277 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 278 | CLANG_WARN_UNREACHABLE_CODE = YES; 279 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 280 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 281 | ENABLE_NS_ASSERTIONS = NO; 282 | ENABLE_STRICT_OBJC_MSGSEND = YES; 283 | GCC_NO_COMMON_BLOCKS = YES; 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 286 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 287 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 288 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 289 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 290 | GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; 291 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 292 | GCC_WARN_PEDANTIC = YES; 293 | GCC_WARN_SHADOW = YES; 294 | GCC_WARN_SIGN_COMPARE = YES; 295 | GCC_WARN_UNDECLARED_SELECTOR = YES; 296 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 297 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_LABEL = YES; 300 | GCC_WARN_UNUSED_PARAMETER = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | MACOSX_DEPLOYMENT_TARGET = 10.12; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | MTL_FAST_MATH = YES; 305 | SDKROOT = macosx; 306 | }; 307 | name = Release; 308 | }; 309 | C6C90FDB21A5A24D00B5FCB7 /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | HEADER_SEARCH_PATHS = ( 313 | ../external/Catch2/single_include, 314 | ../sr, 315 | ); 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | }; 318 | name = Debug; 319 | }; 320 | C6C90FDC21A5A24D00B5FCB7 /* Release */ = { 321 | isa = XCBuildConfiguration; 322 | buildSettings = { 323 | HEADER_SEARCH_PATHS = ( 324 | ../external/Catch2/single_include, 325 | ../sr, 326 | ); 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | }; 329 | name = Release; 330 | }; 331 | /* End XCBuildConfiguration section */ 332 | 333 | /* Begin XCConfigurationList section */ 334 | C6C90FCE21A5A24D00B5FCB7 /* Build configuration list for PBXProject "test" */ = { 335 | isa = XCConfigurationList; 336 | buildConfigurations = ( 337 | C6C90FD821A5A24D00B5FCB7 /* Debug */, 338 | C6C90FD921A5A24D00B5FCB7 /* Release */, 339 | ); 340 | defaultConfigurationIsVisible = 0; 341 | defaultConfigurationName = Release; 342 | }; 343 | C6C90FDA21A5A24D00B5FCB7 /* Build configuration list for PBXNativeTarget "test" */ = { 344 | isa = XCConfigurationList; 345 | buildConfigurations = ( 346 | C6C90FDB21A5A24D00B5FCB7 /* Debug */, 347 | C6C90FDC21A5A24D00B5FCB7 /* Release */, 348 | ); 349 | defaultConfigurationIsVisible = 0; 350 | defaultConfigurationName = Release; 351 | }; 352 | /* End XCConfigurationList section */ 353 | }; 354 | rootObject = C6C90FCB21A5A24D00B5FCB7 /* Project object */; 355 | } 356 | --------------------------------------------------------------------------------