├── .clang-format ├── .editorconfig ├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── .prettierrc ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── _config.yml ├── bin └── node-raylib ├── docs ├── API.md ├── README.md ├── _config.yml ├── bindings │ ├── README.md │ ├── flattening_arguments.md │ ├── function_bindings.md │ ├── optimization │ │ ├── colors_as_integers.md │ │ └── pointer_accessible_structs.md │ └── raylib_api_json.md └── generator │ ├── README.md │ ├── example_function_binding.md │ ├── example_type_converters.md │ └── pass_by_reference.md ├── drm └── index.js ├── examples ├── audio │ ├── audio_module_playing.js │ ├── audio_multichannel_sound.js │ ├── audio_music_stream.js │ ├── audio_raw_stream.js │ ├── audio_sound_loading.js │ └── resources │ │ ├── chiptun1.mod │ │ ├── coin.wav │ │ ├── guitar_noodling.ogg │ │ ├── mini1111.xm │ │ ├── sound.wav │ │ ├── spring.wav │ │ ├── tanatana.flac │ │ ├── tanatana.ogg │ │ └── weird.wav ├── core │ ├── core_2d_camera.js │ ├── core_3d_camera_first_person.js │ ├── core_3d_camera_mode.js │ ├── core_basic_window.js │ ├── core_basic_window.png │ ├── core_input_mouse.js │ ├── core_random_values.js │ ├── core_split_screen.js │ ├── core_vr_simulator.js │ ├── core_world_screen.js │ └── resources │ │ ├── distortion100.fs │ │ └── distortion330.fs ├── models │ ├── models_rlgl_solar_system.js │ └── resources │ │ ├── angle_gauge.png │ │ ├── background.png │ │ ├── billboard.png │ │ ├── cubicmap.png │ │ ├── cubicmap_atlas.png │ │ ├── dresden_square.hdr │ │ ├── guy │ │ ├── guy.blend │ │ ├── guy.iqm │ │ ├── guyanim.iqm │ │ └── guytex.png │ │ ├── heightmap.png │ │ ├── models │ │ ├── bridge.obj │ │ ├── bridge_diffuse.png │ │ ├── castle.obj │ │ ├── castle_diffuse.png │ │ ├── cube.obj │ │ ├── cube_diffuse.png │ │ ├── house.obj │ │ ├── house_diffuse.png │ │ ├── market.obj │ │ ├── market_diffuse.png │ │ ├── turret.obj │ │ ├── turret_diffuse.png │ │ ├── well.obj │ │ └── well_diffuse.png │ │ ├── pbr │ │ ├── trooper.obj │ │ ├── trooper_albedo.png │ │ ├── trooper_ao.png │ │ ├── trooper_metalness.png │ │ ├── trooper_normals.png │ │ └── trooper_roughness.png │ │ ├── pitch.png │ │ ├── plane.obj │ │ ├── plane.png │ │ ├── plane_diffuse.png │ │ └── shaders │ │ ├── brdf.fs │ │ ├── brdf.vs │ │ ├── cubemap.fs │ │ ├── cubemap.vs │ │ ├── irradiance.fs │ │ ├── pbr.fs │ │ ├── pbr.vs │ │ ├── prefilter.fs │ │ ├── skybox.fs │ │ └── skybox.vs ├── others │ └── easings_testbed.js ├── raw │ ├── README.md │ ├── bunnymark.js │ └── wabbit_alpha.png ├── raygui │ └── raygui_basic_window.js ├── shaders │ ├── resources │ │ ├── generic.vert.glsl │ │ ├── grayscaler.frag.glsl │ │ └── raylib_logo.png │ └── shaders_and_uniforms.js ├── shapes │ ├── shapes_basic_shapes.js │ ├── shapes_easings_ball_anim.js │ └── shapes_logo_raylib.js ├── text │ ├── resources │ │ ├── pixantiqua.fnt │ │ ├── pixantiqua.png │ │ └── pixantiqua.ttf │ ├── text_font_drawtextrec.js │ └── text_font_loading.js └── textures │ ├── resources │ ├── raylib_logo.png │ └── wabbit_alpha.png │ ├── textures_bunnymark.js │ ├── textures_from_memory.js │ ├── textures_image_loading.js │ └── textures_logo_raylib.js ├── index.js ├── logo └── raylib-node_256x256.png ├── man └── node-raylib.1 ├── package-lock.json ├── package.json ├── src ├── extras │ ├── raygui.h │ ├── reasings.h │ └── rlgl.h └── generated │ ├── node-raylib-drm.js │ ├── node-raylib.cc │ ├── node-raylib.d.ts │ └── node-raylib.js ├── templates ├── simple_game │ ├── README.md │ ├── package.json │ └── simple_game.js └── typescript_game │ ├── .gitignore │ ├── README.md │ ├── package.json │ └── typescript_game.ts ├── test ├── node-raylib.test.js └── resources │ ├── node-raylib-test.js │ └── rabbit.png └── tools ├── crossbuild-drm.sh ├── crossbuild.sh ├── generate.js ├── generate_templates ├── ArgumentTypeConversion.js ├── node-raylib-bindings.js ├── node-raylib-definitions.js └── node-raylib-wrapper.js ├── pkg.js └── postinstall.js /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 2 3 | UseTab: Never 4 | ColumnLimit: 0 5 | BinPackArguments: false 6 | BinPackParameters: false 7 | AllowAllArgumentsOnNextLine: false 8 | AllowAllParametersOfDeclarationOnNextLine: false 9 | AlwaysBreakAfterReturnType: None 10 | PenaltyBreakAssignment: 100000000 11 | PenaltyBreakBeforeFirstCallParameter: 100000000 12 | PenaltyBreakComment: 100000000 13 | PenaltyBreakFirstLessLess: 100000000 14 | PenaltyBreakString: 100000000 15 | PenaltyExcessCharacter: 100000000 16 | PenaltyReturnTypeOnItsOwnLine: 100000000 17 | PenaltyIndentedWhitespace: 100000000 18 | ContinuationIndentWidth: 2 19 | AlignAfterOpenBracket: DontAlign 20 | AlignOperands: false 21 | BreakBeforeBinaryOperators: None 22 | BreakBeforeTernaryOperators: false 23 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.txt] 12 | indent_style = tabs 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | jobs: 8 | create_release: 9 | runs-on: ubuntu-latest 10 | outputs: 11 | upload_url: ${{ steps.create_release.outputs.upload_url }} 12 | steps: 13 | - name: create_release 14 | id: create_release 15 | uses: actions/create-release@v1 16 | with: 17 | draft: false 18 | prerelease: false 19 | release_name: Automated Release ${{ github.ref }} 20 | tag_name: ${{ github.ref }} 21 | body: This is an automated release for ${{ github.ref }}. It will be used to speed up `npm install`. 22 | env: 23 | GITHUB_TOKEN: ${{ github.token }} 24 | linux-arm-drm: 25 | runs-on: ubuntu-latest 26 | needs: create_release 27 | steps: 28 | - name: checkout 29 | uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 0 32 | - name: Set up QEMU 33 | uses: docker/setup-qemu-action@v2 34 | - name: Cache Node Dependencies 35 | id: cache 36 | uses: actions/cache@v2 37 | with: 38 | path: ./node_modules 39 | key: modules-${{ hashFiles('package-lock.json') }} 40 | - name: Build for arm 41 | run: docker run --platform linux/arm --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild-drm.sh 42 | - name: upload linux artifact 43 | uses: actions/upload-release-asset@v1 44 | env: 45 | GITHUB_TOKEN: ${{ github.token }} 46 | with: 47 | upload_url: ${{ needs.create_release.outputs.upload_url }} 48 | asset_path: ./build/Release/node-raylib.node 49 | asset_name: node-raylib-linux-arm-drm.node 50 | asset_content_type: application/octet-stream 51 | linux-arm64-drm: 52 | runs-on: ubuntu-latest 53 | needs: create_release 54 | steps: 55 | - name: checkout 56 | uses: actions/checkout@v2 57 | with: 58 | fetch-depth: 0 59 | - name: Set up QEMU 60 | uses: docker/setup-qemu-action@v2 61 | - name: Cache Node Dependencies 62 | id: cache 63 | uses: actions/cache@v2 64 | with: 65 | path: ./node_modules 66 | key: modules-${{ hashFiles('package-lock.json') }} 67 | - name: Build for arm 68 | run: docker run --platform linux/arm64 --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild-drm.sh 69 | - name: upload linux artifact 70 | uses: actions/upload-release-asset@v1 71 | env: 72 | GITHUB_TOKEN: ${{ github.token }} 73 | with: 74 | upload_url: ${{ needs.create_release.outputs.upload_url }} 75 | asset_path: ./build/Release/node-raylib.node 76 | asset_name: node-raylib-linux-arm64-drm.node 77 | asset_content_type: application/octet-stream 78 | linux-arm: 79 | runs-on: ubuntu-latest 80 | needs: create_release 81 | steps: 82 | - name: checkout 83 | uses: actions/checkout@v2 84 | with: 85 | fetch-depth: 0 86 | - name: Set up QEMU 87 | uses: docker/setup-qemu-action@v2 88 | - name: Cache Node Dependencies 89 | id: cache 90 | uses: actions/cache@v2 91 | with: 92 | path: ./node_modules 93 | key: modules-${{ hashFiles('package-lock.json') }} 94 | - name: Build for arm 95 | run: docker run --platform linux/arm --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild.sh 96 | - name: upload linux artifact 97 | uses: actions/upload-release-asset@v1 98 | env: 99 | GITHUB_TOKEN: ${{ github.token }} 100 | with: 101 | upload_url: ${{ needs.create_release.outputs.upload_url }} 102 | asset_path: ./build/Release/node-raylib.node 103 | asset_name: node-raylib-linux-arm.node 104 | asset_content_type: application/octet-stream 105 | linux-arm64: 106 | runs-on: ubuntu-latest 107 | needs: create_release 108 | steps: 109 | - name: checkout 110 | uses: actions/checkout@v2 111 | with: 112 | fetch-depth: 0 113 | - name: Set up QEMU 114 | uses: docker/setup-qemu-action@v2 115 | - name: Cache Node Dependencies 116 | id: cache 117 | uses: actions/cache@v2 118 | with: 119 | path: ./node_modules 120 | key: modules-${{ hashFiles('package-lock.json') }} 121 | - name: Build for arm 122 | run: docker run --platform linux/arm64 --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild.sh 123 | - name: upload linux artifact 124 | uses: actions/upload-release-asset@v1 125 | env: 126 | GITHUB_TOKEN: ${{ github.token }} 127 | with: 128 | upload_url: ${{ needs.create_release.outputs.upload_url }} 129 | asset_path: ./build/Release/node-raylib.node 130 | asset_name: node-raylib-linux-arm64.node 131 | asset_content_type: application/octet-stream 132 | linux: 133 | runs-on: ubuntu-latest 134 | needs: create_release 135 | steps: 136 | - name: checkout 137 | uses: actions/checkout@v2 138 | with: 139 | fetch-depth: 0 140 | - name: Cache Node Dependencies 141 | id: cache 142 | uses: actions/cache@v2 143 | with: 144 | path: ./node_modules 145 | key: modules-${{ hashFiles('package-lock.json') }} 146 | - name: Install Node Dependencies 147 | if: steps.cache.outputs.cache-hit != 'true' 148 | run: npm ci --ignore-scripts 149 | - name: Install System Dependencies 150 | run: sudo apt-get update && sudo apt-get install -y xorg-dev libglu1-mesa-dev 151 | - name: Build Node Addon 152 | run: npm run compile 153 | - name: upload linux artifact 154 | uses: actions/upload-release-asset@v1 155 | env: 156 | GITHUB_TOKEN: ${{ github.token }} 157 | with: 158 | upload_url: ${{ needs.create_release.outputs.upload_url }} 159 | asset_path: ./build/Release/node-raylib.node 160 | asset_name: node-raylib-linux-x64.node 161 | asset_content_type: application/octet-stream 162 | windows: 163 | runs-on: windows-latest 164 | needs: create_release 165 | steps: 166 | - name: checkout 167 | uses: actions/checkout@v2 168 | with: 169 | fetch-depth: 0 170 | - name: Cache Node Dependencies 171 | id: cache 172 | uses: actions/cache@v2 173 | with: 174 | path: ./node_modules 175 | key: modules-${{ hashFiles('package-lock.json') }} 176 | - name: Install Node Dependencies 177 | if: steps.cache.outputs.cache-hit != 'true' 178 | run: npm ci --ignore-scripts 179 | - name: Build Node Addon 180 | run: npm run compile 181 | - name: upload windows artifact 182 | uses: actions/upload-release-asset@v1 183 | env: 184 | GITHUB_TOKEN: ${{ github.token }} 185 | with: 186 | upload_url: ${{ needs.create_release.outputs.upload_url }} 187 | asset_path: ./build/Release/node-raylib.node 188 | asset_name: node-raylib-win32-x64.node 189 | asset_content_type: application/octet-stream 190 | macos: 191 | runs-on: macos-latest 192 | needs: create_release 193 | steps: 194 | - name: checkout 195 | uses: actions/checkout@v2 196 | with: 197 | fetch-depth: 0 198 | - name: Cache Node Dependencies 199 | id: cache 200 | uses: actions/cache@v2 201 | with: 202 | path: ./node_modules 203 | key: modules-${{ hashFiles('package-lock.json') }} 204 | - name: Install Node Dependencies 205 | if: steps.cache.outputs.cache-hit != 'true' 206 | run: npm ci --ignore-scripts 207 | - name: Build Node Addon 208 | run: npm run compile 209 | - name: upload macos artifact 210 | uses: actions/upload-release-asset@v1 211 | env: 212 | GITHUB_TOKEN: ${{ github.token }} 213 | with: 214 | upload_url: ${{ needs.create_release.outputs.upload_url }} 215 | asset_path: ./build/Release/node-raylib.node 216 | asset_name: node-raylib-darwin-x64.node 217 | asset_content_type: application/octet-stream -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | style: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: checkout 10 | uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | - uses: actions/setup-node@v2 14 | - name: Code Style Check 15 | run: npx -y prettier -c . 16 | # run: npx @biomejs/biome format . # do this after we move to biome config 17 | 18 | build: 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | node-version: [18.x] 23 | os: [ubuntu, macos, windows] 24 | runs-on: ${{ matrix.os }}-latest 25 | steps: 26 | - name: checkout 27 | uses: actions/checkout@v2 28 | with: 29 | fetch-depth: 0 30 | - name: Use Node.js ${{ matrix.node-version }} 31 | uses: actions/setup-node@v2 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | - name: Cache Node Dependencies 35 | id: cache 36 | uses: actions/cache@v2 37 | with: 38 | path: ./node_modules 39 | key: modules-${{ hashFiles('package-lock.json') }} 40 | - name: Install Node Dependencies 41 | if: steps.cache.outputs.cache-hit != 'true' 42 | run: npm ci --ignore-scripts 43 | - name: Install System Dependencies 44 | if: matrix.os == 'ubuntu' 45 | run: sudo apt-get update && sudo apt-get install -y xorg-dev libglu1-mesa-dev 46 | - name: Build Node Addon 47 | run: npm run compile 48 | - name: Test 49 | run: npm test 50 | build-linux-arm: 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: checkout 54 | uses: actions/checkout@v2 55 | with: 56 | fetch-depth: 0 57 | - name: Set up QEMU 58 | uses: docker/setup-qemu-action@v2 59 | - name: Cache Node Dependencies 60 | id: cache 61 | uses: actions/cache@v2 62 | with: 63 | path: ./node_modules 64 | key: modules-${{ hashFiles('package-lock.json') }} 65 | - name: Build for arm 66 | run: docker run --platform linux/arm --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild.sh 67 | build-linux-arm-drm: 68 | runs-on: ubuntu-latest 69 | steps: 70 | - name: checkout 71 | uses: actions/checkout@v2 72 | with: 73 | fetch-depth: 0 74 | - name: Set up QEMU 75 | uses: docker/setup-qemu-action@v2 76 | - name: Cache Node Dependencies 77 | id: cache 78 | uses: actions/cache@v2 79 | with: 80 | path: ./node_modules 81 | key: modules-${{ hashFiles('package-lock.json') }} 82 | - name: Build for arm 83 | run: docker run --platform linux/arm --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild-drm.sh 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | *.zip 4 | *.tar.gz 5 | .vscode 6 | .DS_Store -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "endOfLine": "lf", 7 | "htmlWhitespaceSensitivity": "css", 8 | "insertPragma": false, 9 | "jsxSingleQuote": true, 10 | "printWidth": 9999, 11 | "proseWrap": "preserve", 12 | "quoteProps": "as-needed", 13 | "requirePragma": false, 14 | "semi": false, 15 | "singleAttributePerLine": false, 16 | "singleQuote": true, 17 | "tabWidth": 2, 18 | "trailingComma": "none", 19 | "useTabs": false, 20 | "vueIndentScriptAndStyle": false 21 | } 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | 3 | # 2025-02-15: based on https://github.com/raysan5/raylib/blob/master/src/external/glfw/CMakeLists.txt, make sure the CMake version are between 3.4 and 3.28 or the whole raylib lib will face fatal errors 4 | cmake_minimum_required(VERSION 3.4...3.28 FATAL_ERROR) 5 | project (node-raylib 6 | VERSION 0.10.0 7 | DESCRIPTION "Node.js bindings for raylib" 8 | HOMEPAGE_URL "https://github.com/RobLoach/node-raylib" 9 | LANGUAGES C CXX) 10 | 11 | if ( CMAKE_COMPILER_IS_GNUCC ) 12 | set(CMAKE_C_FLAGS "-fPIC ${CMAKE_C_FLAGS} -Wno-unused-result") 13 | set(CMAKE_CXX_FLAGS "-Wall -Wextra") 14 | endif() 15 | # 2025-02-15: based on @link: https://learn.microsoft.com/en-us/cpp/build/reference/o-options-optimize-code?view=msvc-170. MSVC only accept O2 and don't have O3 optimization flag 16 | if ( MSVC ) 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /w") 18 | set(CMAKE_CXX_FLAGS_RELEASE "-O2") 19 | else() 20 | set(CMAKE_CXX_FLAGS_RELEASE "-O3") 21 | endif() 22 | 23 | if(NOT CMAKE_BUILD_TYPE) 24 | set(CMAKE_BUILD_TYPE Release) 25 | endif() 26 | 27 | set(CMAKE_CXX_FLAGS_DEBUG "-g") 28 | 29 | # version doesn't seem to pick correct version 30 | #find_package(raylib 5.5 QUIET EXACT) 31 | if (NOT raylib_FOUND) 32 | include(FetchContent) 33 | FetchContent_Declare( 34 | raylib 35 | GIT_REPOSITORY https://github.com/raysan5/raylib.git 36 | GIT_TAG 5.5 37 | GIT_SHALLOW TRUE 38 | ) 39 | FetchContent_GetProperties(raylib) 40 | if (NOT raylib_POPULATED) 41 | set(FETCHCONTENT_QUIET NO) 42 | FetchContent_MakeAvailable(raylib) 43 | set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 44 | endif() 45 | endif() 46 | 47 | set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) 48 | set(BUILD_GAMES OFF CACHE BOOL "" FORCE) 49 | 50 | # Add all the include directories. 51 | include_directories(${PROJECT_NAME} 52 | "${CMAKE_JS_INC}" 53 | "${CMAKE_SOURCE_DIR}/node_modules/node-addon-api" 54 | "${CMAKE_CURRENT_SOURCE_DIR}/node_modules/node-addon-api" 55 | "${CMAKE_SOURCE_DIR}/node_modules/node-addon-api/src" 56 | "${CMAKE_CURRENT_SOURCE_DIR}/node_modules/node-addon-api/src" 57 | "${CMAKE_CURRENT_SOURCE_DIR}/../node-addon-api" 58 | "${CMAKE_CURRENT_SOURCE_DIR}/../node-addon-api/src" 59 | ) 60 | 61 | # This is hardcoded so that updates force a re-compile. 62 | add_library(${PROJECT_NAME} SHARED 63 | # src/lib/AddDefine.h 64 | # src/lib/AddFunction.h 65 | # src/lib/CleanUp.h 66 | # src/lib/GetArgFromParam.h 67 | # src/lib/ValidArgs.h 68 | # src/addon.cc 69 | # src/node-raymath.h 70 | # src/node-rlgl.h 71 | # src/lib/WrappedFunctions.h 72 | src/generated/node-raylib.cc 73 | ) 74 | 75 | ## Suffix the node module with .node. 76 | set_target_properties(${PROJECT_NAME} PROPERTIES 77 | PREFIX "" 78 | SUFFIX ".node" 79 | ) 80 | 81 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20) 82 | 83 | target_include_directories(${PROJECT_NAME} PUBLIC 84 | "${CMAKE_JS_INC}" 85 | "${CMAKE_SOURCE_DIR}/node_modules/node-addon-api" 86 | "${CMAKE_CURRENT_SOURCE_DIR}/node_modules/node-addon-api" 87 | "${CMAKE_SOURCE_DIR}/node_modules/node-addon-api/src" 88 | "${CMAKE_CURRENT_SOURCE_DIR}/node_modules/node-addon-api/src" 89 | "${CMAKE_CURRENT_SOURCE_DIR}/../node-addon-api" 90 | "${CMAKE_CURRENT_SOURCE_DIR}/../node-addon-api/src" 91 | ) 92 | 93 | # Link raylib. 94 | target_link_libraries(${PROJECT_NAME} 95 | ${CMAKE_JS_LIB} 96 | raylib 97 | ) 98 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | license 2 | ======= 3 | 4 | node-raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, 5 | BSD-like license that allows static linking with closed source software: 6 | 7 | Copyright (c) 2019 Rob Loach (@RobLoach) 8 | 9 | This software is provided "as-is", without any express or implied warranty. In no event 10 | will the authors be held liable for any damages arising from the use of this software. 11 | 12 | Permission is granted to anyone to use this software for any purpose, including commercial 13 | applications, and to alter it and redistribute it freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not claim that you 16 | wrote the original software. If you use this software in a product, an acknowledgment 17 | in the product documentation would be appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be misrepresented 20 | as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![node-raylib Logo](logo/raylib-node_256x256.png) 2 | 3 | # node-raylib [![npm version](http://img.shields.io/npm/v/raylib.svg)](https://npmjs.org/package/raylib "View this project on npm") [![Tests](https://github.com/RobLoach/node-raylib/actions/workflows/test.yml/badge.svg)](https://github.com/RobLoach/node-raylib/actions/workflows/test.yml "See automated test status on GitHub Actions") 4 | 5 | [Node.js](https://nodejs.org) bindings for [raylib](https://www.raylib.com/), a simple and easy-to-use library to enjoy videogames programming (www.raylib.com). 6 | 7 | ## Examples 8 | 9 | | Name | Description | Author | 10 | |:-----|:------------|:-------| 11 | | [Offical Examples](examples) | Ports of raylib's examples to node-raylib | [@RobLoach](https://github.com/robloach), [@twunky](https://github.com/twuky), [@konsumer](https://github.com/konsumer) | 12 | | [Flappy](https://github.com/arthurmassanes/flappy) | A [Flappy Bird](https://en.wikipedia.org/wiki/Flappy_Bird) clone | [@arthurmassanes](https://github.com/arthurmassanes) | 13 | | [Retro RPG Template](https://github.com/notnullgames/raylib-example-retro_rpg) | Small prototype to build a retro-inspired RPG game | [@konsumer](https://github.com/konsumer) | 14 | | [Chip8](https://github.com/taniarascia/chip8) | A [CHIP-8](https://en.wikipedia.org/wiki/CHIP-8) emulator whose native client using node-raylib | [@taniarascia](https://github.com/taniarascia) | 15 | 16 | ## Dependencies 17 | 18 | - [Node.js](https://nodejs.org) >= 10 19 | 20 | ## Usage 21 | 22 | 1. Create a new Node.js project: 23 | ``` bash 24 | mkdir myexample 25 | cd myexample 26 | ``` 27 | 28 | 2. Create a `package.json` file with: 29 | ``` json 30 | { 31 | "dependencies": { 32 | "raylib": "*" 33 | }, 34 | "scripts": { 35 | "start": "node index.js" 36 | } 37 | } 38 | ``` 39 | 40 | 3. Create a `index.js` JavaScript file, like [`core_basic_window.js`](examples/core/core_basic_window.js): 41 | ``` javascript 42 | const r = require('raylib') 43 | 44 | const screenWidth = 800 45 | const screenHeight = 450 46 | r.InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window") 47 | r.SetTargetFPS(60) 48 | 49 | while (!r.WindowShouldClose()) { 50 | r.BeginDrawing(); 51 | r.ClearBackground(r.RAYWHITE) 52 | r.DrawText("Congrats! You created your first node-raylib window!", 120, 200, 20, r.LIGHTGRAY) 53 | r.EndDrawing() 54 | } 55 | r.CloseWindow() 56 | ``` 57 | 58 | 4. Have npm install the dependencies for you: 59 | ``` bash 60 | npm install 61 | ``` 62 | 63 | 5. Run `index.js` through Node.js: 64 | ``` bash 65 | npm start 66 | ``` 67 | ![Screenshot](examples/core/core_basic_window.png) 68 | 69 | Check for more [examples](examples) organized by raylib modules. 70 | 71 | ## Installation 72 | 73 | Raylib is implemented as bindings with [node-addon-api](https://github.com/nodejs/node-addon-api). Bindings are prebuilt for many platforms in [Releases](https://github.com/RobLoach/node-raylib/releases). If your platform is not supported by a prebuilt binary, you will need CMake to build the native addon. Windows users building manually will also require MSVC Build Tools 2019, or Visual Studio 2019 with build tools for C/C++. 74 | 75 | In general, to install node-raylib locally, use [npm](https://www.npmjs.com/): 76 | ``` 77 | npm install raylib 78 | ``` 79 | 80 | ## DRM 81 | On some ARM devices like Raspberry PI, raylib can be used in a DRM mode instead of rendering to an x11 window. This version requires a seperate 82 | native addon, and on installation on ARM devices node-raylib will include both. To use the DRM mode, import `raylib/drm` instead. 83 | ```js 84 | import * as rl from 'raylib/drm/index.js' 85 | const rl = require('raylib/drm') 86 | ``` 87 | 88 | ### CLI 89 | 90 | The project comes with a [`node-raylib`](https://github.com/RobLoach/node-raylib/blob/master/bin/node-raylib) command-line tool to run `node-raylib` files directly: 91 | 92 | ``` bash 93 | # Unix 94 | ./bin/node-raylib core_basic_window.js 95 | 96 | # Windows 97 | node bin/node-raylib core_basic_window.js 98 | ``` 99 | 100 | The CLI can be installed globally through `npm` or `npx` for no-install: 101 | 102 | ``` bash 103 | npm install raylib --global 104 | node-raylib --version 105 | npx -y raylib --version 106 | ``` 107 | 108 | ## Development 109 | 110 | [node-addon-api](https://github.com/nodejs/node-addon-api) is used to construct the bindings. Raylib provides a header parser that generates a JSON file containing information on the API. The code binding raylib to NodeJS is automatically generated based on this file. For information on how bindings are written for raylib read the [documentation](docs). Code generators for the C++ bindings, TypeScript definitions, and JavaScript wrapper functions are found in the [tools/generate_templates folder](tools/generate_templates). 111 | 112 | ### Testing 113 | Run the following to run tests... 114 | 115 | ``` 116 | git clone https://github.com/RobLoach/node-raylib.git 117 | cd node-raylib 118 | npm i 119 | npm t 120 | ``` 121 | 122 | ### TypeScript Definitions 123 | 124 | Typescript definitions are provided by a generator based on raylib's header parser. See [node-raylib-definitions.js](tools/generate_templates/node-raylib-definitions.js) for information on how to generate them. 125 | 126 | ### Package 127 | 128 | To build the packaged releases, use the following command: 129 | 130 | ``` 131 | npm run pkg 132 | ``` 133 | 134 | ## License 135 | 136 | *node-raylib* is licensed under an unmodified zlib/libpng license, which is an OSI-certified, 137 | BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details. 138 | 139 | *Copyright (c) 2022 Rob Loach ([@RobLoach](https://twitter.com/RobLoach))* 140 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /bin/node-raylib: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // sometimes eval is not evil... 4 | /* eslint no-eval: 0 */ 5 | 6 | const fs = require('fs') 7 | const path = require('path') 8 | const raylib = require('..') 9 | const pkg = require('../package.json') 10 | 11 | const usage = ` 12 | Node.js bindings for raylib. 13 | Usage 14 | $ node-raylib [file] 15 | 16 | Examples 17 | $ node-raylib - runs data/index.js or index.js 18 | $ node-raylib core_basic_window.js 19 | ` 20 | 21 | // find best file for enry-point 22 | let filename = process.argv[2] 23 | 24 | if (filename === '--help') { 25 | console.error(usage) 26 | process.exit(1) 27 | } 28 | 29 | if (filename === '--version') { 30 | console.log(pkg.version) 31 | process.exit(0) 32 | } 33 | 34 | if (!filename) { 35 | const f1 = path.join(process.cwd(), 'data', 'index.js') 36 | const f2 = path.join(process.cwd(), 'index.js') 37 | if (raylib.FileExists(f1)) { 38 | filename = f1 39 | } else if (raylib.FileExists(f2)) { 40 | filename = f2 41 | } 42 | else { 43 | console.error(usage) 44 | process.exit(0) 45 | } 46 | } 47 | 48 | if (!raylib.FileExists(filename)) { 49 | console.error('Provided file does not exist.') 50 | console.error(usage) 51 | process.exit(1) 52 | } 53 | 54 | // Add a "raylib" module alias for when it's not found. 55 | const moduleAlias = require('module-alias') 56 | moduleAlias.addAlias('raylib', path.join(__dirname, '..')) 57 | 58 | // Retrieve the context of the file. 59 | const realpath = fs.realpathSync(filename) 60 | 61 | // Switch to the file's directory so that file loading works. 62 | process.chdir(path.dirname(realpath)) 63 | 64 | // Inject raylib as a global variable. 65 | global.raylib = raylib 66 | 67 | // Load up the module's path. 68 | require(realpath) 69 | 70 | // load the file, but don't require it (so you can use this file in an exe bundler) 71 | eval(fs.readFileSync(realpath)) 72 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # node-raylib Documentation 2 | 3 | These are some general documentation notes for node-raylib. 4 | 5 | - [API Documentation](API.md) 6 | - [Bindings](bindings) 7 | - [Generator](generator) 8 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /docs/bindings/README.md: -------------------------------------------------------------------------------- 1 | # Node Addon API bindings 2 | 3 | [node-addon-api](https://github.com/nodejs/node-addon-api) is used to build bindings between NodeJS and raylib. 4 | 5 | In order to automate creating bindings, raylib provides a JSON file with information about its API. [https://github.com/raysan5/raylib/tree/master/parser](https://github.com/raysan5/raylib/tree/master/parser) 6 | 7 | Node-raylib parses this JSON file and automatically generates c++ node-addon-api code that binds each function in the raylib API. 8 | 9 | - [Flattening Arguments](flattening_arguments.md) 10 | - [Function Bindings](function_bindings.md) 11 | - [Raylib API JSON](raylib_api_json.md) 12 | -------------------------------------------------------------------------------- /docs/bindings/flattening_arguments.md: -------------------------------------------------------------------------------- 1 | # Flattening Arguments 2 | When you call a native addon function, converting data from JS into C++ data is _very expensive_ work. That work gets increasingly more expensive if you want to convert a Javascript object _into_ a C++ struct. 3 | 4 | Of course, it IS possible to do this with node-addon-api: you can call `Napi::Object obj = info[index].As();` to get a Javascript object in C++. Then you can call `obj.Get("property_name");` to access a property of that object. You then need to again use `.As<>()` to convert that property to the C++ type you want to use. `.As()` is _VERY_ expensive - an order of _magnitude_ slower than converting a primitive type like `Napi::Number` or `Napi::Boolean`. If you are interested in writing a fast C++ binding - avoid `.As()` or `.Get()` _wherever possible_. 5 | 6 | Node-raylib gets around this by generating function bindings that _flatten_ any struct / object arguments into a longer list of individual arguments. 7 | 8 | That means binding `DrawTexture()` would look like this: 9 | ```cpp 10 | void BindDrawTexture(const Napi::CallbackInfo& info) { 11 | DrawTexture( 12 | (Texture2D) { 13 | info[0].As(), 14 | info[1].As(), 15 | info[2].As(), 16 | info[3].As(), 17 | info[4].As(), 18 | }, 19 | info[5].As(), 20 | info[6].As(), 21 | (Color){ 22 | (int)info[7].As(), 23 | (int)info[8].As(), 24 | (int)info[9].As(), 25 | (int)info[10].As() 26 | } 27 | ); 28 | } 29 | ``` 30 | Notice how with this binding, it has to _build_ a `Texture2D` and a `Color` out of arguments passed in via the `info` array. 31 | This means that the way the function is used from JS changes as well. You would normally call `DrawTexture()` with four arguments like so: 32 | ```js 33 | let myTexture = { 34 | id: 0, 35 | width: 128, 36 | height: 128, 37 | mipmaps: 1, 38 | format: 7 // default format for PNGs 39 | } 40 | let myTextureColor = { 41 | r: 255, 42 | g: 255, 43 | b: 255, 44 | a: 255 45 | } 46 | raylib.DrawTexture(myTexture, 10, 10, myTextureColor) 47 | ``` 48 | 49 | But by flattening the arguments - It needs to be called with 11 elements, like this: 50 | ```js 51 | raylib.DrawTexture( 52 | // texture 53 | myTexture.id, myTexture.width, myTexture.height, myTexture.mipmaps, myTexture.format, 54 | // position 55 | 10, 10, 56 | myTextureColor.r, myTextureColor.g, myTextureColor.b, myTextureColor.a // color 57 | ) 58 | ``` 59 | This sounds a little tedious to have to write each time. So the code generator will also generate wrapper functions that handle flattening the arguments for the user, so they can use the function with four arguments like they expect (and the API usage does not deviate from the original raylib API). 60 | ```js 61 | export function DrawTexture(texture: Texture, posX: Number, posY: Number, tint: Color) { 62 | raylib.DrawTexture(...processTextureInput(texture), posX, posY, ...processColorInput(color)) 63 | } 64 | 65 | // initial process just flattens 66 | function processColorInput(color: Color) { 67 | return [color.r, color.g, color.b, color.a] 68 | } 69 | 70 | // initial process just flattens 71 | function processTextureInput(texture: Texture) { 72 | return [texture.id, texture.width, texture.height, texture.mipmaps, texture.format] 73 | } 74 | ``` 75 | This may seem like a lot of effort to implement. But when benchmarking this change, compared to using `Napi::Object`s as input for Texture and Color, I was able to _multiply_ the framerate of the benchmark by _6_, so flattening the structs in JS before calling the native function _far outweighs_ the cost of using C++ to parse the JS objects. -------------------------------------------------------------------------------- /docs/bindings/optimization/colors_as_integers.md: -------------------------------------------------------------------------------- 1 | # Colors as integers 2 | It's a well known concept in graphics programming that a color can be 'encoded' into a 32 bit integer. Most graphics libraries expect colors to be represented by four 8-bit integers ranging from 0-255 that represent the red, green, blue, and alpha channels of the color. In node-addon-api bindings, using a smaller amount of arguments for function can drastically improve the speed the function can be completed, as less data needs to be copied between JS and C++. So by converting colors to integers in the API, a significant performance gain can be had. 3 | 4 | In JS you can use bitwise operators to combine the color channels into a single number: 5 | ```js 6 | let c = {r: 255, g: 128, b: 0, a: 255} 7 | var cr = c.r & 0xFF; 8 | var cg = c.g & 0xFF; 9 | var cb = c.b & 0xFF; 10 | var ca = c.a & 0xFF; 11 | var rgba = (cr << 24) + (cg << 16) + (cb << 8) + (ca) 12 | ``` 13 | Raylib comes with a function to decode an unsigned integer into an instance of a Color. So using the number with the node-addon-api becomes trivial: 14 | ```cpp 15 | Napi::Number num = info[0].As(); 16 | Color color = GetColor(num); 17 | ``` 18 | ```cpp 19 | void BindDrawTexture(const Napi::CallbackInfo& info) { 20 | DrawTexture( 21 | *(Texture2D*)info[0].As().Int64Value(), 22 | info[1].As(), 23 | info[2].As(), 24 | // convert a color inline 25 | GetColor(info[3].As()) 26 | ); 27 | } 28 | ``` 29 | In order to preserve compatibility for end-users of the API, JS wrappers need to convert instances of color objects to integers whenever an API function that requires a color is called: 30 | ```ts 31 | function process_color(color: Color) { 32 | var cr = color.r & 0xFF; 33 | var cg = color.g & 0xFF; 34 | var cb = color.b & 0xFF; 35 | var ca = color.a & 0xFF; 36 | return (cr << 24) + (cg << 16) + (cb << 8) + (ca) 37 | } 38 | 39 | export function DrawTexture(texture: Texture, x: number, y: number, color: Color) { 40 | raylib.DrawTexture(texture.pointer, x, y, process_color(color)) 41 | } 42 | ``` -------------------------------------------------------------------------------- /docs/bindings/optimization/pointer_accessible_structs.md: -------------------------------------------------------------------------------- 1 | # Pointer-Accessed Structs 2 | To improve performance of the bindings, some certain structs will be cached by the C++ binding, and when using them from the NodeJS API, only a number representing their pointer in C++ memory will be used. 3 | ## Requirements 4 | Not all structs would make sense to be cached in C++ memory. Some structs, like colors or Rectangles, are only instantiated and used once, then immediately cleared by the JS garbage collector. Data in JS and C++ is only ever _copied_ when passed in and out of functions, never referenced. So even if a variable in JavaScript is cleared by garbage collection, it would not be cleared in C++ memory, leading to memory leaks. So only structs that have clear Load() and Unload() functions in the raylib API can make use of a cache in C++ memory. 5 | A good example of this is a raylib `Texture`. 6 | 7 | ## Loading 8 | 9 | When a texture is loaded - the C++ Binding will additionally allocate space in memory for that texture, and return a pointer to that space with the rest of the texture data to the user. 10 | ```cpp 11 | // LoadTexture function binding 12 | Napi::Object BindLoadTexture(const Napi::CallbackInfo& info) { 13 | // call raylib function and store result 14 | Texture texture = LoadTexture(info[0].As().Utf8Value().c_str()); 15 | // assign memory in the raylib memory pool for the texture 16 | void* ptr = MemAlloc(sizeof(Texture2D)); 17 | // copy the texture data to that memory location 18 | *(Texture2D*)ptr = tex; 19 | // return a JS object to NodeJS containing the texture, with the pointer included 20 | Napi::Object out = Napi::Object::New(env); 21 | out.Set("id", texture.id); 22 | out.Set("width", texture.width); 23 | out.Set("height", texture.height); 24 | out.Set("mipmaps", texture.mipmaps); 25 | out.Set("format", texture.format); 26 | // a void* needs to be cast as an integer to convert to a Javascript number 27 | out.Set("pointer", (int64_t) ptr); 28 | return out; 29 | } 30 | ``` 31 | Since raylib's API does not define `pointer` as a property of a `Texture`, the property will be hidden from the object when recieved in NodeJS: 32 | ```js 33 | export function LoadTexture(path) { 34 | let texture = raylib.LoadTexture(path) 35 | return processTextureOutput(texture) 36 | } 37 | 38 | function processTextureOutput(texture) { 39 | const ob = { 40 | id: texture.id, 41 | width: texture.width, 42 | height: texture.height, 43 | mipmaps: texture.mipmaps, 44 | format: texture.format 45 | } 46 | // pointer property will not appear in console.log() 47 | // or when iterating values of the texture 48 | Object.defineProperty(obj, 'pointer', { 49 | enumerable: false, 50 | writable: false, 51 | value: texture.pointer 52 | }) 53 | // make all properties of the texture readonly (immutable) 54 | Object.freeze(ob) 55 | return ob 56 | } 57 | ``` 58 | 59 | ## Usage in NodeJS 60 | 61 | Consequently, when calling a C++ binding function that uses a pointer-accessible struct, only a single integer needs to be passed from NodeJS to the binding, which dramatically speeds up execution time of the bound function. 62 | ```cpp 63 | void BindDrawTexturePointer(const Napi::CallbackInfo& info) { 64 | DrawTexture( 65 | // convert the JS Number to a pointer. 66 | // Then cast that pointer as a Texture pointer and access the texture 67 | *(Texture2D*)info[0].As().Int64Value(), 68 | info[1].As(), 69 | info[2].As(), 70 | GetColor(info[3].As()) 71 | ); 72 | } 73 | ``` 74 | Users of node-raylib should not have to concern themselves with working with pointers in order to use the library, however. The Raylib API expects DrawTexture to use a Texture as an argument, not a Javascript number that represents a pointer. So to avoid confusion, the NodeJS API will wrap functions that require a Texture as input, and make use of the hidden pointer for the user: 75 | ```ts 76 | export function DrawTexture(texture: Texture, x: number, y: number, color: Color) { 77 | raylib.DrawTexture(texture.pointer, x, y, color) 78 | } 79 | ``` 80 | 81 | ## Unloading 82 | 83 | Additionally, when a pointer accessible struct is deleted or unloaded, that struct needs to be freed from the raylib memory pool to prevent memory leaking. 84 | 85 | We free the struct in C-space: 86 | ```cpp 87 | void BindUnloadTexture(const Napi::CallbackInfo& info) { 88 | UnloadTexture(*(Texture2D*)info[0].As().Int64Value()); 89 | MemFree((void*)info[0].As().Int64Value()); 90 | } 91 | ``` 92 | 93 | And pull out the pointer from the object, in JS-space: 94 | ```ts 95 | export function UnloadTexture(texture: Texture) { 96 | raylib.UnloadTexture(texture.pointer) 97 | } 98 | ``` 99 | Note: Currently this does not delete the Javascript object representing the tetxure, or change the value of the pointer. Maybe the wrapper function for unloading should `unfreeze` the object, and set its pointer/properties to `undefined` then `freeze` again? 100 | 101 | Note: There should be some sort of guide or warning to users about instances where they want to 'reload' the same texture: 102 | ```js 103 | let texture = r.LoadTexture('sprite.png') 104 | // expect user to make some change to sprite.png on disk 105 | // ... then later 106 | texture = r.LoadTexture('sprite.png') 107 | // C++ memory leak!! user should call r.UnloadTexture(texture) first 108 | ``` -------------------------------------------------------------------------------- /docs/bindings/raylib_api_json.md: -------------------------------------------------------------------------------- 1 | # Raylib API JSON 2 | 3 | Raylib's API JSON contains three types of definitions: 4 | 5 | ## Structs 6 | Structs map out the native types used by raylib to represent complex data. 7 | ```json 8 | { 9 | "name": "Vector2", 10 | "description": "Vector2, 2 components", 11 | "fields": [ 12 | { 13 | "type": "float", 14 | "name": "x", 15 | "description": "Vector x component" 16 | }, 17 | { 18 | "type": "float", 19 | "name": "y", 20 | "description": "Vector y component" 21 | } 22 | ] 23 | }, 24 | 25 | { 26 | "name": "Camera2D", 27 | "description": "Camera2D, defines position/orientation in 2d space", 28 | "fields": [ 29 | { 30 | "type": "Vector2", 31 | "name": "offset", 32 | "description": "Camera offset (displacement from target)" 33 | }, 34 | { 35 | "type": "Vector2", 36 | "name": "target", 37 | "description": "Camera target (rotation and zoom origin)" 38 | }, 39 | { 40 | "type": "float", 41 | "name": "rotation", 42 | "description": "Camera rotation in degrees" 43 | }, 44 | { 45 | "type": "float", 46 | "name": "zoom", 47 | "description": "Camera zoom (scaling), should be 1.0f by default" 48 | } 49 | ] 50 | } 51 | ``` 52 | In NodeJS, raylib structs are accessed and can be created as plain javascript objects. 53 | ```js 54 | let myCamera = { 55 | offset: {x: 0, y: 0}, 56 | target: {x: 50, y: 50}, 57 | rotation: 0, 58 | zoom: 1 59 | } 60 | ``` 61 | Some structs may have optional constructor functions. This acts the same as instantiating an object with the same parameters. 62 | ```js 63 | let color_a = r.Color(128, 128, 128, 255) 64 | let color_b = {r: 128, g: 128, b: 128, a: 255} 65 | console.log(color_a) // => {r: 128, g: 128, b: 128, a: 255} 66 | ``` 67 | ### Pointer Accessible structs 68 | To improve performance of the bindings, some structs in JS will also contain a "hidden" property defining the pointer location of the struct in C++ memory. When making API calls, only the pointer is needed to reference the struct. Read more in [pointer_accessible_structs.md](./pointer_accessible_structs.md) 69 | ### Colors 70 | Another improvement to performance is converting a JS color object to an integer number to pass to the C++ addon. This reduces the cost of converting a color from JS to C++ by ~75%. Read more in [colors_as_integers.md](./colors_as_integers.md) 71 | ## Enums 72 | 73 | ## Functions 74 | Functions make up the bulk of the api, and the primary job of the binding generator is to properly handle converting types between C++ and JS to pass as arguments and return values from raylib. -------------------------------------------------------------------------------- /docs/generator/README.md: -------------------------------------------------------------------------------- 1 | # node-raylib Generator 2 | 3 | - [Example Function Bindings](example_function_binding.md) 4 | - [Example Type Converters](example_type_converters.md) 5 | - [Pass By Reference](pass_by_reference.md) 6 | -------------------------------------------------------------------------------- /docs/generator/example_function_binding.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ```cpp 4 | void BindDrawPixelV(const Napi::CallbackInfo& info, int& index) { 5 | // create an index variable to index into info[] with 6 | int index = -1; 7 | DrawPixelV( 8 | Vector2FromValue(info, index), // for each function argument, call ${arg.type}FromValue(info, index) 9 | ColorFromValue(info, index) 10 | ); 11 | // no return type on this function 12 | } 13 | ``` 14 | 15 | ```cpp 16 | Napi::Value BindWindowShouldClose(const Napi::CallbackInfo& info) { 17 | int index = -1; // unused since no arguments are needed - maybe only conditionally add this line 18 | return ToValue(info.Env(), 19 | WindowShouldClose() 20 | ); // if the function is not void, pass it's result into a ToValue() call 21 | } 22 | ``` 23 | 24 | ```cpp 25 | Napi::Object BindLoadTexture(const Napi::CallbackInfo& info) { 26 | int index = -1; 27 | return ToValue(info.Env(), 28 | LoadTexture( 29 | StringFromValue(info, index) 30 | ) 31 | ); 32 | } 33 | ``` -------------------------------------------------------------------------------- /docs/generator/example_type_converters.md: -------------------------------------------------------------------------------- 1 | 2 | ```cpp 3 | Napi::Value ToValue(napi_env env, float obj) { 4 | return Napi::Number::New(env, obj); 5 | } 6 | 7 | Napi::Value ToValue(napi_env env, int obj) { 8 | return Napi::Number::New(env, obj); 9 | } 10 | 11 | Napi::Value ToValue(napi_env env, Vector2 obj) { 12 | Napi::Object out = Napi::Object::New(env); 13 | out.Set("x", ToValue(env, obj.x)); // float 14 | out.Set("y", ToValue(env, obj.y)); // float 15 | return out; 16 | } 17 | 18 | Napi::Value ToValue(napi_env env, Texture2D obj) { 19 | Napi::Object out = Napi::Object::New(env); 20 | out.Set("id", ToValue(env, obj.x)); // int 21 | out.Set("width", ToValue(env, obj.y)); // int 22 | out.Set("height", ToValue(env, obj.x)); // int 23 | out.Set("mipmaps", ToValue(env, obj.y)); // int 24 | out.Set("format", ToValue(env, obj.x)); // int 25 | // future: texture.pointer 26 | return out; 27 | } 28 | 29 | float FloatFromValue(const Napi::CallbackInfo& info, int& index) { 30 | return info[index += 1].As(); 31 | } 32 | 33 | Vector2 Vector2FromValue(const Napi::CallbackInfo& info, int& index) { 34 | return (Vector2) { 35 | FloatFromValue(info, index += 1), 36 | FloatFromValue(info, index += 1) 37 | }; 38 | } 39 | ``` -------------------------------------------------------------------------------- /docs/generator/pass_by_reference.md: -------------------------------------------------------------------------------- 1 | # Pass By Reference functions 2 | Some functions in raylib expect one (or more) of their arguments, instead of being an instance of a variable - to be a _pointer_ to that variable. That way, it can _modify_ that variable and not have to return it. This is effectively passing the variable as a reference. 3 | 4 | A good example of this in the raylib API are functions that manipulate `Image` structs. When you call `ImageDrawPixel()` - the actual data property of the Image gets updated! 5 | 6 | This causes problems when trying to use these functions from JS. When you make a call to a native addon function - all of your arguments to that function are _copied_ over to C++. So when C++ modifies those variables, it's modifying the copy, and not the version you have in JS. 7 | 8 | We can get around this by writing our function binding to _return_ the modified copy back to JS. This means we need to _change_ the return type of `BindImageDrawPixel` from `void` (which is what the raylib API specifies for it) to an `Napi::Object` that represents the now updated Image. 9 | 10 | ```cpp 11 | Napi::Value BindImageDrawPixel(const Napi::CallbackInfo& info) { 12 | 13 | Image obj = //convert from info[0] to Image 14 | 15 | // this function is void because it is pass-by-reference 16 | ImageDrawPixel(obj/* <- Image reference */, ...); 17 | 18 | // now we must reference the updated values, 19 | // and build a new Napi::Object to return to the user 20 | return ToValue(info.Env(), obj); // assume there is some function Napi::Value ToValue(napi_env env, Image obj) {} 21 | } 22 | ``` 23 | Now when the user calls the bound function from JS - the function _returns_ a new `Image` object that they need to replace their existing one with. To make that easier, the NodeJS `ImageDrawPixel()` should wrap the native addon function: 24 | ```ts 25 | export function ImageDrawPixel(image: Image, posX: number, posY: number, color: Color) { 26 | // store updated copy 27 | let new_image = raylib.ImageDrawPixel(image, posX, posY, color) 28 | for (let key in image) { 29 | // copy each value over to the original 30 | image[key] = new_image[key] 31 | } 32 | } 33 | ``` 34 | 35 | ## Code Generation 36 | Fortunately, the functions where this applies all follow a similar format. It's always the first argument that is the object being passed by reference. This makes it easy to determine which struct type needs to be returned by the modified function binding. 37 | 38 | For quick reference, here are the functions in rayliib 4.0 this concept applies to: 39 | ```js 40 | let by_reference_functions = [ 41 | 'UpdateCamera', 42 | 'ImageFormat', 43 | 'ImageToPOT', 44 | 'ImageCrop', 45 | 'ImageAlphaCrop', 46 | 'ImageAlphaClear', 47 | 'ImageAlphaMask', 48 | 'ImageAlphaPremultiply', 49 | 'ImageResize', 50 | 'ImageResizeNN', 51 | 'ImageResizeCanvas', 52 | 'ImageMipmaps', 53 | 'ImageDither', 54 | 'ImageFlipVertical', 55 | 'ImageFlipHorizontal', 56 | 'ImageRotateCW', 57 | 'ImageRotateCCW', 58 | 'ImageColorTint', 59 | 'ImageColorInvert', 60 | 'ImageColorGrayscale', 61 | 'ImageColorContrast', 62 | 'ImageColorBrightness', 63 | 'ImageColorReplace', 64 | 'ImageClearBackground', 65 | 'ImageDrawPixel', 66 | 'ImageDrawPixelV', 67 | 'ImageDrawLine', 68 | 'ImageDrawLineV', 69 | 'ImageDrawCircle', 70 | 'ImageDrawCircleV', 71 | 'ImageDrawRectangle', 72 | 'ImageDrawRectangleV', 73 | 'ImageDrawRectangleRec', 74 | 'ImageDrawRectangleLines', 75 | 'ImageDraw', 76 | 'ImageDrawText', 77 | 'ImageDrawTextEx', 78 | 'GenTextureMipmaps', 79 | 'GenTextureMipmaps', 80 | 'UploadMesh', 81 | 'GenMeshTangents', 82 | 'GenMeshBinormals', 83 | 'SetMaterialTexture', 84 | 'SetModelMeshMaterial', 85 | 'WaveFormat', 86 | 'WaveCrop' 87 | ] 88 | ``` -------------------------------------------------------------------------------- /drm/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * node-raylib 3 | * 4 | * https://github.com/robloach/node-raylib 5 | */ 6 | 7 | const raylib = require('../src/generated/node-raylib-drm') 8 | const { format } = require('node:util') 9 | 10 | // Constants 11 | raylib.MAX_GAMEPADS = 4 12 | raylib.MAX_GAMEPAD_AXIS = 8 13 | raylib.MAX_GAMEPAD_BUTTONS = 32 14 | raylib.MAX_TOUCH_POINTS = 10 15 | raylib.MAX_KEY_PRESSED_QUEUE = 16 16 | raylib.DEG2RAD = Math.PI / 180 17 | 18 | // Wrapped Functions 19 | 20 | /** 21 | * Text formatting with variables (sprintf style) 22 | */ 23 | raylib.TextFormat = format 24 | 25 | /** 26 | * Define one vertex (color) - 4 byte 27 | * @param {number} r 28 | * @param {number} g 29 | * @param {number} b 30 | * @param {number} a 31 | */ 32 | raylib.rlColor4ub = (r, g, b, a) => { 33 | // workaround as the C addon version isn't compiling? 34 | raylib.rlColor4f(r / 255, g / 255, b / 255, a / 255) 35 | } 36 | 37 | // Export the bindings for the module. 38 | module.exports = raylib 39 | -------------------------------------------------------------------------------- /examples/audio/audio_module_playing.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [audio] example - Module playing (streaming) 4 | * 5 | * This example has been created using raylib 1.5 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2016 Ramon Santamaria (@raysan5) 9 | * Ported to javascript 2022 by David Konsumer (@konsumer) 10 | * 11 | ********************************************************************************************/ 12 | 13 | const r = require('../../index.js') 14 | 15 | const MAX_CIRCLES = 64 16 | 17 | const screenWidth = 800 18 | const screenHeight = 450 19 | 20 | r.SetConfigFlags(r.FLAG_MSAA_4X_HINT) // NOTE: Try to enable MSAA 4X 21 | r.InitWindow(screenWidth, screenHeight, 'raylib [audio] example - module playing (streaming)') 22 | r.InitAudioDevice() // Initialize audio device 23 | 24 | const colors = [r.ORANGE, r.RED, r.GOLD, r.LIME, r.BLUE, r.VIOLET, r.BROWN, r.LIGHTGRAY, r.PINK, r.YELLOW, r.GREEN, r.SKYBLUE, r.PURPLE, r.BEIGE] 25 | 26 | const music = r.LoadMusicStream('resources/mini1111.xm') 27 | r.PlayMusicStream(music) 28 | music.looping = false 29 | 30 | let pitch = 1 31 | let pause = false 32 | let timePlayed = 0 33 | 34 | const circles = [...Array(MAX_CIRCLES)].map(() => { 35 | const radius = r.GetRandomValue(10, 40) 36 | return { 37 | alpha: 0, 38 | radius, 39 | position: { 40 | x: r.GetRandomValue(radius, screenWidth - radius), 41 | y: r.GetRandomValue(radius, screenHeight - radius) 42 | }, 43 | speed: r.GetRandomValue(1, 100) / 2000, 44 | color: colors[r.GetRandomValue(0, colors.length - 1)] 45 | } 46 | }) 47 | 48 | r.SetTargetFPS(60) 49 | while (!r.WindowShouldClose()) { 50 | r.UpdateMusicStream(music) // Update music buffer with new stream data 51 | 52 | // Restart music playing (stop and play) 53 | if (r.IsKeyPressed(r.KEY_SPACE)) { 54 | r.StopMusicStream(music) 55 | r.PlayMusicStream(music) 56 | } 57 | 58 | // Pause/Resume music playing 59 | if (r.IsKeyPressed(r.KEY_P)) { 60 | pause = !pause 61 | 62 | if (pause) r.PauseMusicStream(music) 63 | else r.ResumeMusicStream(music) 64 | } 65 | 66 | if (r.IsKeyDown(r.KEY_DOWN)) pitch -= 0.01 67 | else if (r.IsKeyDown(r.KEY_UP)) pitch += 0.01 68 | 69 | r.SetMusicPitch(music, pitch) 70 | 71 | // Get timePlayed scaled to bar dimensions 72 | timePlayed = r.GetMusicTimePlayed(music) / r.GetMusicTimeLength(music) * (screenWidth - 40) 73 | 74 | // Color circles animation 75 | if (!pause) { 76 | for (const circle of circles) { 77 | circle.alpha += circle.speed 78 | circle.radius += circle.speed * 10.0 79 | if (circle.alpha > 1) circle.speed *= -1 80 | if (circle.alpha <= 0) { 81 | circle.alpha = 0 82 | circle.radius = r.GetRandomValue(10, 40) 83 | circle.position.x = r.GetRandomValue(circle.radius, (screenWidth - circle.radius)) 84 | circle.position.y = r.GetRandomValue(circle.radius, (screenHeight - circle.radius)) 85 | circle.color = colors[r.GetRandomValue(0, colors.length - 1)] 86 | circle.speed = r.GetRandomValue(1, 100) / 2000.0 87 | } 88 | } 89 | } 90 | 91 | // Draw 92 | // ---------------------------------------------------------------------------------- 93 | r.BeginDrawing() 94 | r.ClearBackground(r.RAYWHITE) 95 | for (const circle of circles) { 96 | r.DrawCircleV(circle.position, circle.radius, r.Fade(circle.color, circle.alpha)) 97 | } 98 | 99 | // Draw time bar 100 | r.DrawRectangle(20, screenHeight - 20 - 12, screenWidth - 40, 12, r.LIGHTGRAY) 101 | r.DrawRectangle(20, screenHeight - 20 - 12, timePlayed, 12, r.MAROON) 102 | r.DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, r.GRAY) 103 | 104 | r.EndDrawing() 105 | } 106 | 107 | r.UnloadMusicStream(music) 108 | r.CloseAudioDevice() 109 | r.CloseWindow() 110 | -------------------------------------------------------------------------------- /examples/audio/audio_multichannel_sound.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [audio] example - Multichannel sound playing 4 | * 5 | * This example has been created using raylib 2.6 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Example contributed by Chris Camacho (@codifies) and reviewed by Ramon Santamaria (@raysan5) 9 | * 10 | * Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) 11 | * 12 | ********************************************************************************************/ 13 | 14 | const r = require('../../index.js') 15 | const { join } = require('node:path') 16 | 17 | // Initialization 18 | // -------------------------------------------------------------------------------------- 19 | const screenWidth = 800 20 | const screenHeight = 450 21 | 22 | r.InitWindow(screenWidth, screenHeight, 'raylib [audio] example - Multichannel sound playing') 23 | 24 | r.InitAudioDevice() // Initialize audio device 25 | 26 | const fxWav = r.LoadSound(join(__dirname, 'resources', 'sound.wav')) // Load WAV audio file 27 | const fxOgg = r.LoadSound(join(__dirname, 'resources', 'tanatana.ogg')) // Load OGG audio file 28 | 29 | let frame = 0 30 | 31 | r.SetSoundVolume(fxWav, 0.2) 32 | r.PlaySound(fxOgg) 33 | 34 | let inhibitWav = false 35 | let inhibitOgg = false 36 | let maxFrame = 60 37 | 38 | let soundsCounter = 0 39 | 40 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 41 | // -------------------------------------------------------------------------------------- 42 | 43 | // Main game loop 44 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 45 | // Update 46 | // ---------------------------------------------------------------------------------- 47 | frame++ 48 | 49 | if (r.IsKeyDown(r.KEY_ENTER)) inhibitWav = !inhibitWav 50 | if (r.IsKeyDown(r.KEY_SPACE)) inhibitOgg = !inhibitOgg 51 | 52 | // Deliberatly hammer the play pool to see what dropping old pool entries sounds like.... 53 | if ((frame % 5) === 0) { 54 | if (!inhibitWav) r.PlaySoundMulti(fxWav) 55 | } 56 | 57 | if (frame === maxFrame) { 58 | if (!inhibitOgg) r.PlaySoundMulti(fxOgg) 59 | 60 | frame = 0 61 | maxFrame = r.GetRandomValue(6, 12) 62 | } 63 | 64 | soundsCounter = r.GetSoundsPlaying() 65 | // ---------------------------------------------------------------------------------- 66 | 67 | // Draw 68 | // ---------------------------------------------------------------------------------- 69 | r.BeginDrawing() 70 | 71 | r.ClearBackground(r.RAYWHITE) 72 | 73 | r.DrawText('Multichannel sound abuse!', 200, 180, 20, r.LIGHTGRAY) 74 | r.DrawText('Space to inhibit new ogg triggering', 200, 200, 20, r.LIGHTGRAY) 75 | r.DrawText('Enter to inhibit new wav triggering', 200, 220, 20, r.LIGHTGRAY) 76 | 77 | r.DrawText(r.FormatText('Number of concurrentsounds: %i', soundsCounter), 200, 280, 20, r.LIGHTGRAY) 78 | 79 | r.EndDrawing() 80 | // ---------------------------------------------------------------------------------- 81 | } 82 | 83 | // De-Initialization 84 | // -------------------------------------------------------------------------------------- 85 | r.StopSoundMulti() // We must stop the buffer pool before unloading 86 | 87 | r.UnloadSound(fxWav) // Unload sound data 88 | r.UnloadSound(fxOgg) // Unload sound data 89 | 90 | r.CloseAudioDevice() // Close audio device 91 | 92 | r.CloseWindow() // Close window and OpenGL context 93 | // -------------------------------------------------------------------------------------- 94 | -------------------------------------------------------------------------------- /examples/audio/audio_music_stream.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [audio] example - Music playing (streaming) 4 | * 5 | * This example has been created using raylib 1.3 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2015 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | const { join } = require('node:path') 14 | 15 | // Initialization 16 | // -------------------------------------------------------------------------------------- 17 | const screenWidth = 800 18 | const screenHeight = 450 19 | 20 | r.InitWindow(screenWidth, screenHeight, 'raylib [audio] example - music playing (streaming)') 21 | 22 | r.InitAudioDevice() // Initialize audio device 23 | 24 | const music = r.LoadMusicStream(join(__dirname, 'resources', 'guitar_noodling.ogg')) 25 | if (!music) { 26 | console.error('Error loading guitar_noodling.ogg') 27 | } 28 | 29 | r.PlayMusicStream(music) 30 | 31 | let timePlayed = 0 32 | let pause = false 33 | 34 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 35 | // -------------------------------------------------------------------------------------- 36 | 37 | // Main game loop 38 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 39 | // Update 40 | // ---------------------------------------------------------------------------------- 41 | r.UpdateMusicStream(music) // Update music buffer with new stream data 42 | 43 | // Restart music playing (stop and play) 44 | if (r.IsKeyPressed(r.KEY_SPACE)) { 45 | r.StopMusicStream(music) 46 | r.PlayMusicStream(music) 47 | } 48 | 49 | // Pause/Resume music playing 50 | if (r.IsKeyPressed(r.KEY_P)) { 51 | pause = !pause 52 | 53 | if (pause) r.PauseMusicStream(music) 54 | else r.ResumeMusicStream(music) 55 | } 56 | 57 | // Get timePlayed scaled to bar dimensions (400 pixels) 58 | timePlayed = r.GetMusicTimePlayed(music) / r.GetMusicTimeLength(music) * 400 59 | 60 | if (timePlayed > 400) r.StopMusicStream(music) 61 | // ---------------------------------------------------------------------------------- 62 | 63 | // Draw 64 | // ---------------------------------------------------------------------------------- 65 | r.BeginDrawing() 66 | 67 | r.ClearBackground(r.RAYWHITE) 68 | 69 | r.DrawText('MUSIC SHOULD BE PLAYING!', 255, 150, 20, r.LIGHTGRAY) 70 | 71 | r.DrawRectangle(200, 200, 400, 12, r.LIGHTGRAY) 72 | r.DrawRectangle(200, 200, timePlayed, 12, r.MAROON) 73 | r.DrawRectangleLines(200, 200, 400, 12, r.GRAY) 74 | 75 | r.DrawText('PRESS SPACE TO RESTART MUSIC', 215, 250, 20, r.LIGHTGRAY) 76 | r.DrawText('PRESS P TO PAUSE/RESUME MUSIC', 208, 280, 20, r.LIGHTGRAY) 77 | 78 | r.EndDrawing() 79 | // ---------------------------------------------------------------------------------- 80 | } 81 | 82 | // De-Initialization 83 | // -------------------------------------------------------------------------------------- 84 | r.UnloadMusicStream(music) // Unload music stream buffers from RAM 85 | 86 | r.CloseAudioDevice() // Close audio device (music streaming is automatically stopped) 87 | 88 | r.CloseWindow() // Close window and OpenGL context 89 | // -------------------------------------------------------------------------------------- 90 | -------------------------------------------------------------------------------- /examples/audio/audio_raw_stream.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [audio] example - Raw audio streaming 4 | * 5 | * This example has been created using raylib 1.6 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Example created by Ramon Santamaria (@raysan5) and reviewed by James Hofmann (@triplefox) 9 | * 10 | * Copyright (c) 2015-2019 Ramon Santamaria (@raysan5) and James Hofmann (@triplefox) 11 | * Ported to javascript 2022 by David Konsumer (@konsumer) 12 | * 13 | ********************************************************************************************/ 14 | 15 | const r = require('../../index.js') 16 | 17 | function memcpy (src, srcOffset, dst, dstOffset, length) { 18 | let i 19 | 20 | src = src.subarray || src.slice ? src : src.buffer 21 | dst = dst.subarray || dst.slice ? dst : dst.buffer 22 | 23 | src = srcOffset 24 | ? src.subarray 25 | ? src.subarray(srcOffset, length && srcOffset + length) 26 | : src.slice(srcOffset, length && srcOffset + length) 27 | : src 28 | 29 | if (dst.set) { 30 | dst.set(src, dstOffset) 31 | } else { 32 | for (i = 0; i < src.length; i++) { 33 | dst[i + dstOffset] = src[i] 34 | } 35 | } 36 | 37 | return dst 38 | } 39 | 40 | const MAX_SAMPLES = 512 41 | const MAX_SAMPLES_PER_UPDATE = 4096 42 | 43 | const screenWidth = 800 44 | const screenHeight = 450 45 | 46 | r.InitWindow(screenWidth, screenHeight, 'raylib [audio] example - raw audio streaming') 47 | 48 | r.InitAudioDevice() // Initialize audio device 49 | 50 | r.SetAudioStreamBufferSizeDefault(MAX_SAMPLES_PER_UPDATE) 51 | 52 | // Init raw audio stream (sample rate: 22050, sample size: 16bit-short, channels: 1-mono) 53 | const stream = r.LoadAudioStream(44100, 16, 1) 54 | 55 | // Buffer for the single cycle waveform we are synthesizing 56 | const data = new Uint8Array(MAX_SAMPLES) 57 | 58 | // Frame buffer, describing the waveform when repeated over the course of a frame 59 | const writeBuf = new Uint8Array(MAX_SAMPLES_PER_UPDATE) 60 | let writeLength 61 | let readLength 62 | 63 | r.PlayAudioStream(stream) // Start processing stream buffer (no data loaded currently) 64 | 65 | // Position read in to determine next frequency 66 | let mousePosition = { x: -100, y: -100 } 67 | 68 | // Cycles per second (hz) 69 | let frequency = 440 70 | 71 | // Previous value, used to test if sine needs to be rewritten, and to smoothly modulate frequency 72 | let oldFrequency = 1 73 | 74 | // Cursor to read and copy the samples of the sine wave buffer 75 | let readCursor = 0 76 | 77 | // Computed size in samples of the sine wave 78 | let waveLength = 1 79 | let oldWavelength = 1 80 | 81 | const position = { x: 0, y: 0 } 82 | 83 | r.SetTargetFPS(30) // Set our game to run at 30 frames-per-second 84 | 85 | while (!r.WindowShouldClose()) { 86 | // Update 87 | // ---------------------------------------------------------------------------------- 88 | 89 | // Sample mouse input. 90 | mousePosition = r.GetMousePosition() 91 | 92 | if (r.IsMouseButtonDown(r.MOUSE_BUTTON_LEFT)) { 93 | frequency = 40 + mousePosition.y 94 | const pan = mousePosition.x / screenWidth 95 | r.SetAudioStreamPan(stream, pan) 96 | } 97 | 98 | // Rewrite the sine wave. 99 | // Compute two cycles to allow the buffer padding, simplifying any modulation, resampling, etc. 100 | if (frequency !== oldFrequency) { 101 | // Compute wavelength. Limit size in both directions. 102 | oldWavelength = waveLength 103 | waveLength = 22050 / frequency 104 | if (waveLength > MAX_SAMPLES / 2) waveLength = MAX_SAMPLES / 2 105 | if (waveLength < 1) waveLength = 1 106 | 107 | // Write sine wave. 108 | for (let i = 0; i < waveLength * 2; i++) { 109 | data[i] = Math.sin((2 * Math.PI * i / waveLength) * 32000) 110 | } 111 | 112 | // Scale read cursor's position to minimize transition artifacts 113 | readCursor = (readCursor * (waveLength / oldWavelength)) 114 | oldFrequency = frequency 115 | } 116 | 117 | // Refill audio stream if required 118 | if (r.IsAudioStreamProcessed(stream)) { 119 | // Synthesize a buffer that is exactly the requested size 120 | let writeCursor = 0 121 | 122 | while (writeCursor < MAX_SAMPLES_PER_UPDATE) { 123 | // Start by trying to write the whole chunk at once 124 | writeLength = MAX_SAMPLES_PER_UPDATE - writeCursor 125 | 126 | // Limit to the maximum readable size 127 | readLength = waveLength - readCursor 128 | 129 | if (writeLength > readLength) writeLength = readLength 130 | 131 | // Write the slice 132 | memcpy(writeBuf, writeCursor, data, readCursor, writeLength) 133 | 134 | // Update cursors and loop audio 135 | readCursor = (readCursor + writeLength) % waveLength 136 | 137 | writeCursor += writeLength 138 | } 139 | 140 | // Copy finished frame to audio stream 141 | r.UpdateAudioStream(stream, writeBuf, MAX_SAMPLES_PER_UPDATE) 142 | } 143 | // ---------------------------------------------------------------------------------- 144 | 145 | // Draw 146 | // ---------------------------------------------------------------------------------- 147 | r.BeginDrawing() 148 | 149 | r.ClearBackground(r.RAYWHITE) 150 | 151 | r.DrawText(`sine frequency: ${frequency}`, r.GetScreenWidth() - 220, 10, 20, r.RED) 152 | r.DrawText('click mouse button to change frequency or pan', 10, 10, 20, r.DARKGRAY) 153 | 154 | // Draw the current buffer state proportionate to the screen 155 | for (let i = 0; i < screenWidth; i++) { 156 | position.x = i 157 | position.y = 250 + 50 * data[i * MAX_SAMPLES / screenWidth] / 32000.0 158 | 159 | r.DrawPixelV(position, r.RED) 160 | } 161 | 162 | r.EndDrawing() 163 | // ---------------------------------------------------------------------------------- 164 | } 165 | 166 | r.UnloadAudioStream(stream) 167 | r.CloseAudioDevice() 168 | r.CloseWindow() 169 | -------------------------------------------------------------------------------- /examples/audio/audio_sound_loading.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [audio] example - Sound loading and playing 4 | * 5 | * This example has been created using raylib 1.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | const { join } = require('node:path') 14 | 15 | // Initialization 16 | // -------------------------------------------------------------------------------------- 17 | const screenWidth = 800 18 | const screenHeight = 450 19 | 20 | r.InitWindow(screenWidth, screenHeight, 'raylib [audio] example - sound loading and playing') 21 | 22 | r.InitAudioDevice() // Initialize audio device 23 | 24 | const fxWav = r.LoadSound(join(__dirname, 'resources', 'sound.wav')) // Load WAV audio file 25 | const fxOgg = r.LoadSound(join(__dirname, 'resources', 'tanatana.ogg')) // Load OGG audio file 26 | 27 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 28 | // -------------------------------------------------------------------------------------- 29 | 30 | // Main game loop 31 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 32 | // Update 33 | // ---------------------------------------------------------------------------------- 34 | if (r.IsKeyPressed(r.KEY_SPACE)) r.PlaySound(fxWav) // Play WAV sound 35 | if (r.IsKeyPressed(r.KEY_ENTER)) r.PlaySound(fxOgg) // Play OGG sound 36 | // ---------------------------------------------------------------------------------- 37 | 38 | // Draw 39 | // ---------------------------------------------------------------------------------- 40 | r.BeginDrawing() 41 | 42 | r.ClearBackground(r.RAYWHITE) 43 | 44 | r.DrawText('Press SPACE to PLAY the WAV sound!', 200, 180, 20, r.LIGHTGRAY) 45 | r.DrawText('Press ENTER to PLAY the OGG sound!', 200, 220, 20, r.LIGHTGRAY) 46 | 47 | r.EndDrawing() 48 | // ---------------------------------------------------------------------------------- 49 | } 50 | 51 | // De-Initialization 52 | // -------------------------------------------------------------------------------------- 53 | r.UnloadSound(fxWav) // Unload sound data 54 | r.UnloadSound(fxOgg) // Unload sound data 55 | 56 | r.CloseAudioDevice() // Close audio device 57 | 58 | r.CloseWindow() // Close window and OpenGL context 59 | // -------------------------------------------------------------------------------------- 60 | -------------------------------------------------------------------------------- /examples/audio/resources/chiptun1.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/chiptun1.mod -------------------------------------------------------------------------------- /examples/audio/resources/coin.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/coin.wav -------------------------------------------------------------------------------- /examples/audio/resources/guitar_noodling.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/guitar_noodling.ogg -------------------------------------------------------------------------------- /examples/audio/resources/mini1111.xm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/mini1111.xm -------------------------------------------------------------------------------- /examples/audio/resources/sound.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/sound.wav -------------------------------------------------------------------------------- /examples/audio/resources/spring.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/spring.wav -------------------------------------------------------------------------------- /examples/audio/resources/tanatana.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/tanatana.flac -------------------------------------------------------------------------------- /examples/audio/resources/tanatana.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/tanatana.ogg -------------------------------------------------------------------------------- /examples/audio/resources/weird.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/audio/resources/weird.wav -------------------------------------------------------------------------------- /examples/core/core_2d_camera.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - 2d camera 4 | * 5 | * This example has been created using raylib 1.5 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2016 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require("../../index.js"); 13 | 14 | const MAX_BUILDINGS = 100; 15 | 16 | // Initialization 17 | // -------------------------------------------------------------------------------------- 18 | const screenWidth = 800; 19 | const screenHeight = 450; 20 | 21 | r.InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera"); 22 | 23 | const player = r.Rectangle(400, 280, 40, 40); 24 | const buildings = []; 25 | const buildColors = []; 26 | 27 | let spacing = 0; 28 | 29 | for (let i = 0; i < MAX_BUILDINGS; i++) { 30 | const height = r.GetRandomValue(100, 800); 31 | const newBuilding = r.Rectangle( 32 | -6000 + spacing, 33 | screenHeight - 130 - height, 34 | r.GetRandomValue(50, 200), 35 | height 36 | ); 37 | spacing += newBuilding.width; 38 | buildings.push(newBuilding); 39 | buildColors.push( 40 | r.Color( 41 | r.GetRandomValue(200, 240), 42 | r.GetRandomValue(200, 240), 43 | r.GetRandomValue(200, 250), 44 | 255 45 | ) 46 | ); 47 | } 48 | 49 | const camera = r.Camera2D( 50 | r.Vector2(screenWidth / 2, screenHeight / 2), 51 | r.Vector2(player.x + 20, player.y + 20), 52 | 0, 53 | 1 54 | ); 55 | 56 | r.SetTargetFPS(60); // Set our game to run at 60 frames-per-second 57 | // -------------------------------------------------------------------------------------- 58 | 59 | // Main game loop 60 | while (!r.WindowShouldClose()) { 61 | // Detect window close button or ESC key 62 | // Update 63 | // ---------------------------------------------------------------------------------- 64 | if (r.IsKeyDown(r.KEY_RIGHT)) { 65 | player.x += 2; // Player movement 66 | camera.offset.x -= 2; // Camera displacement with player movement 67 | } else if (r.IsKeyDown(r.KEY_LEFT)) { 68 | player.x -= 2; // Player movement 69 | camera.offset.x += 2; // Camera displacement with player movement 70 | } 71 | 72 | // Camera target follows player 73 | camera.target = r.Vector2(player.x + 20, player.y + 20); 74 | 75 | // Camera rotation controls 76 | if (r.IsKeyDown(r.KEY_A)) { 77 | camera.rotation--; 78 | } else if (r.IsKeyDown(r.KEY_S)) { 79 | camera.rotation++; 80 | } 81 | 82 | // Limit camera rotation to 80 degrees (-40 to 40) 83 | if (camera.rotation > 40) { 84 | camera.rotation = 40; 85 | } else if (camera.rotation < -40) { 86 | camera.rotation = -40; 87 | } 88 | 89 | // Camera zoom controls 90 | camera.zoom += r.GetMouseWheelMove() * 0.05; 91 | 92 | if (camera.zoom > 3) camera.zoom = 3; 93 | else if (camera.zoom < 0.1) camera.zoom = 0.1; 94 | 95 | // Camera reset (zoom and rotation) 96 | if (r.IsKeyPressed(r.KEY_R)) { 97 | camera.zoom = 1.0; 98 | camera.rotation = 0; 99 | } 100 | // ---------------------------------------------------------------------------------- 101 | 102 | // Draw 103 | // ---------------------------------------------------------------------------------- 104 | r.BeginDrawing(); 105 | 106 | r.ClearBackground(r.RAYWHITE); 107 | 108 | r.BeginMode2D(camera); 109 | 110 | r.DrawRectangle(-6000, 320, 13000, 8000, r.DARKGRAY); 111 | 112 | for (let i = 0; i < MAX_BUILDINGS; i++) { 113 | r.DrawRectangleRec(buildings[i], buildColors[i]); 114 | } 115 | 116 | r.DrawRectangleRec(player, r.RED); 117 | 118 | r.DrawLine( 119 | camera.target.x, 120 | -screenHeight * 10, 121 | camera.target.x, 122 | screenHeight * 10, 123 | r.GREEN 124 | ); 125 | r.DrawLine( 126 | -screenWidth * 10, 127 | camera.target.y, 128 | screenWidth * 10, 129 | camera.target.y, 130 | r.GREEN 131 | ); 132 | 133 | r.EndMode2D(); 134 | 135 | r.DrawText("SCREEN AREA", 640, 10, 20, r.RED); 136 | 137 | r.DrawRectangle(0, 0, screenWidth, 5, r.RED); 138 | r.DrawRectangle(0, 5, 5, screenHeight - 10, r.RED); 139 | r.DrawRectangle(screenWidth - 5, 5, 5, screenHeight - 10, r.RED); 140 | r.DrawRectangle(0, screenHeight - 5, screenWidth, 5, r.RED); 141 | 142 | r.DrawRectangle(10, 10, 250, 113, r.Fade(r.SKYBLUE, 0.5)); 143 | r.DrawRectangleLines(10, 10, 250, 113, r.BLUE); 144 | 145 | r.DrawText("Free 2d camera controls:", 20, 20, 10, r.BLACK); 146 | r.DrawText("- Right/Left to move Offset", 40, 40, 10, r.DARKGRAY); 147 | r.DrawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, r.DARKGRAY); 148 | r.DrawText("- A / S to Rotate", 40, 80, 10, r.DARKGRAY); 149 | r.DrawText("- R to reset Zoom and Rotation", 40, 100, 10, r.DARKGRAY); 150 | 151 | r.EndDrawing(); 152 | // ---------------------------------------------------------------------------------- 153 | } 154 | 155 | // De-Initialization 156 | // -------------------------------------------------------------------------------------- 157 | r.CloseWindow(); // Close window and OpenGL context 158 | // -------------------------------------------------------------------------------------- 159 | -------------------------------------------------------------------------------- /examples/core/core_3d_camera_first_person.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - 3d camera first person 4 | * 5 | * This example has been created using raylib 1.3 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2015 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | const MAX_COLUMNS = 20 15 | 16 | // Initialization 17 | // -------------------------------------------------------------------------------------- 18 | const screenWidth = 800 19 | const screenHeight = 450 20 | 21 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - 3d camera first person') 22 | 23 | // Define the camera to look into our 3d world (position, target, up vector) 24 | const camera = r.Camera( 25 | r.Vector3(4, 2, 4), 26 | r.Vector3(0, 1.8, 0), 27 | r.Vector3(0, 1, 0), 28 | 60, 29 | r.CAMERA_PERSPECTIVE 30 | ) 31 | 32 | // Generates some random columns 33 | const heights = [] 34 | const positions = [] 35 | const colors = [] 36 | 37 | for (let i = 0; i < MAX_COLUMNS; i++) { 38 | const newHeight = r.GetRandomValue(1, 12) 39 | heights.push(newHeight) 40 | positions.push(r.Vector3(r.GetRandomValue(-15, 15), newHeight / 2, r.GetRandomValue(-15, 15))) 41 | colors.push(r.Color(r.GetRandomValue(20, 255), r.GetRandomValue(10, 55), 30, 255)) 42 | } 43 | 44 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 45 | // -------------------------------------------------------------------------------------- 46 | 47 | // Main game loop 48 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 49 | // Update 50 | // ---------------------------------------------------------------------------------- 51 | r.UpdateCamera(camera, r.CAMERA_FIRST_PERSON) // Update camera 52 | // ---------------------------------------------------------------------------------- 53 | 54 | // Draw 55 | // ---------------------------------------------------------------------------------- 56 | r.BeginDrawing() 57 | 58 | r.ClearBackground(r.RAYWHITE) 59 | 60 | r.BeginMode3D(camera) 61 | 62 | r.DrawPlane(r.Vector3(), r.Vector2(32, 32), r.LIGHTGRAY) // Draw ground 63 | r.DrawCube(r.Vector3(-16, 2.5, 0), 1, 5, 32, r.BLUE) // Draw a blue wall 64 | r.DrawCube(r.Vector3(16, 2.5, 0), 1, 5, 32, r.LIME) // Draw a green wall 65 | r.DrawCube(r.Vector3(0, 2.5, 16), 32, 5, 1, r.GOLD) // Draw a yellow wall 66 | 67 | // Draw some cubes around 68 | for (let i = 0; i < MAX_COLUMNS; i++) { 69 | r.DrawCube(positions[i], 2, heights[i], 2, colors[i]) 70 | r.DrawCubeWires(positions[i], 2, heights[i], 2, r.MAROON) 71 | } 72 | 73 | r.EndMode3D() 74 | 75 | r.DrawRectangle(10, 10, 220, 70, r.Fade(r.SKYBLUE, 0.5)) 76 | r.DrawRectangleLines(10, 10, 220, 70, r.BLUE) 77 | 78 | r.DrawText('First person camera default controls:', 20, 20, 10, r.BLACK) 79 | r.DrawText('- Move with keys: W, A, S, D', 40, 40, 10, r.DARKGRAY) 80 | r.DrawText('- Mouse move to look around', 40, 60, 10, r.DARKGRAY) 81 | 82 | r.EndDrawing() 83 | // ---------------------------------------------------------------------------------- 84 | } 85 | 86 | // De-Initialization 87 | // -------------------------------------------------------------------------------------- 88 | r.CloseWindow() // Close window and OpenGL context 89 | // -------------------------------------------------------------------------------------- 90 | -------------------------------------------------------------------------------- /examples/core/core_3d_camera_mode.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - Initialize 3d camera mode 4 | * 5 | * This example has been created using raylib 1.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - 3d camera mode') 20 | 21 | // Define the camera to look into our 3d world 22 | const camera = r.Camera3D( 23 | r.Vector3(0, 10, 10), // Camera position 24 | r.Vector3(), // Camera looking at point 25 | r.Vector3(0, 1, 0), // Camera up vector (rotation towards target) 26 | 45, // Camera field-of-view Y 27 | r.CAMERA_PERSPECTIVE // Camera mode type 28 | ) 29 | 30 | const cubePosition = r.Vector3() 31 | 32 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 33 | // -------------------------------------------------------------------------------------- 34 | 35 | // Main game loop 36 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 37 | // Update 38 | // ---------------------------------------------------------------------------------- 39 | // TODO: Update your variables here 40 | // ---------------------------------------------------------------------------------- 41 | 42 | // Draw 43 | // ---------------------------------------------------------------------------------- 44 | r.BeginDrawing() 45 | 46 | r.ClearBackground(r.RAYWHITE) 47 | 48 | r.BeginMode3D(camera) 49 | 50 | r.DrawCube(cubePosition, 2, 2, 2, r.RED) 51 | r.DrawCubeWires(cubePosition, 2, 2, 2, r.MAROON) 52 | 53 | r.DrawGrid(10, 1) 54 | 55 | r.EndMode3D() 56 | 57 | r.DrawText('Welcome to the third dimension!', 10, 40, 20, r.DARKGRAY) 58 | 59 | r.DrawFPS(10, 10) 60 | 61 | r.EndDrawing() 62 | // ---------------------------------------------------------------------------------- 63 | } 64 | 65 | // De-Initialization 66 | // -------------------------------------------------------------------------------------- 67 | r.CloseWindow() // Close window and OpenGL context 68 | // -------------------------------------------------------------------------------------- 69 | -------------------------------------------------------------------------------- /examples/core/core_basic_window.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - Basic window 4 | * 5 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 6 | * 7 | * Copyright (c) 2018 Rob Loach (@RobLoach) 8 | * 9 | ********************************************************************************************/ 10 | 11 | const r = require('../../index.js') 12 | 13 | // Initialization 14 | // -------------------------------------------------------------------------------------- 15 | const screenWidth = 800 16 | const screenHeight = 450 17 | 18 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - basic window') 19 | 20 | r.SetTargetFPS(60) 21 | // -------------------------------------------------------------------------------------- 22 | 23 | // Main game loop 24 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 25 | // Update 26 | // ---------------------------------------------------------------------------------- 27 | // TODO: Update your variables here 28 | // ---------------------------------------------------------------------------------- 29 | 30 | // Draw 31 | // ---------------------------------------------------------------------------------- 32 | r.BeginDrawing() 33 | 34 | r.ClearBackground(r.RAYWHITE) 35 | 36 | r.DrawText('Congrats! You created your first node-raylib window!', 120, 200, 20, r.LIGHTGRAY) 37 | 38 | r.EndDrawing() 39 | // ---------------------------------------------------------------------------------- 40 | } 41 | 42 | // De-Initialization 43 | // -------------------------------------------------------------------------------------- 44 | r.CloseWindow() // Close window and OpenGL context 45 | // -------------------------------------------------------------------------------------- 46 | -------------------------------------------------------------------------------- /examples/core/core_basic_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/core/core_basic_window.png -------------------------------------------------------------------------------- /examples/core/core_input_mouse.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - Mouse input 4 | * 5 | * This example has been created using raylib 1.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - mouse input') 20 | 21 | let ballPosition = r.Vector2(-100, -100) 22 | let ballColor = r.DARKBLUE 23 | 24 | r.SetTargetFPS(60) 25 | // --------------------------------------------------------------------------------------- 26 | 27 | // Main game loop 28 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 29 | // Update 30 | // ---------------------------------------------------------------------------------- 31 | ballPosition = r.GetMousePosition() 32 | 33 | if (r.IsMouseButtonPressed(r.MOUSE_BUTTON_LEFT)) { 34 | ballColor = r.MAROON 35 | } else if (r.IsMouseButtonPressed(r.MOUSE_BUTTON_MIDDLE)) { 36 | ballColor = r.LIME 37 | } else if (r.IsMouseButtonPressed(r.MOUSE_BUTTON_RIGHT)) { 38 | ballColor = r.DARKBLUE 39 | } 40 | // ---------------------------------------------------------------------------------- 41 | 42 | // Draw 43 | // ---------------------------------------------------------------------------------- 44 | r.BeginDrawing() 45 | 46 | r.ClearBackground(r.RAYWHITE) 47 | 48 | r.DrawCircleV(ballPosition, 40, ballColor) 49 | 50 | r.DrawText('move ball with mouse and click mouse button to change color', 10, 10, 20, r.DARKGRAY) 51 | 52 | r.EndDrawing() 53 | // ---------------------------------------------------------------------------------- 54 | } 55 | 56 | // De-Initialization 57 | // -------------------------------------------------------------------------------------- 58 | r.CloseWindow() // Close window and OpenGL context 59 | // -------------------------------------------------------------------------------------- 60 | -------------------------------------------------------------------------------- /examples/core/core_random_values.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - Generate random values 4 | * 5 | * This example has been created using raylib 1.1 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - generate random values') 20 | 21 | let framesCounter = 0 // Variable used to count frames 22 | 23 | let randValue = r.GetRandomValue(-8, 5) // Get a random integer number between -8 and 5 (both included) 24 | 25 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 26 | // -------------------------------------------------------------------------------------- 27 | 28 | // Main game loop 29 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 30 | // Update 31 | // ---------------------------------------------------------------------------------- 32 | framesCounter++ 33 | 34 | // Every two seconds (120 frames) a new random value is generated 35 | if (((framesCounter / 120) % 2) === 1) { 36 | randValue = r.GetRandomValue(-8, 5) 37 | framesCounter = 0 38 | } 39 | // ---------------------------------------------------------------------------------- 40 | 41 | // Draw 42 | // ---------------------------------------------------------------------------------- 43 | r.BeginDrawing() 44 | 45 | r.ClearBackground(r.RAYWHITE) 46 | 47 | r.DrawText('Every 2 seconds a new random value is generated:', 130, 100, 20, r.MAROON) 48 | 49 | r.DrawText(r.FormatText('%i', randValue), 360, 180, 80, r.LIGHTGRAY) 50 | 51 | r.EndDrawing() 52 | // ---------------------------------------------------------------------------------- 53 | } 54 | 55 | // De-Initialization 56 | // -------------------------------------------------------------------------------------- 57 | r.CloseWindow() // Close window and OpenGL context 58 | // -------------------------------------------------------------------------------------- 59 | -------------------------------------------------------------------------------- /examples/core/core_split_screen.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - split screen 4 | * 5 | * This example has been created using raylib 3.7 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5) 9 | * 10 | * Copyright (c) 2021 Jeffery Myers (@JeffM2501) 11 | * 12 | ********************************************************************************************/ 13 | 14 | const r = require('../../index.js') 15 | 16 | function InitCamera () { 17 | return { 18 | position: r.Vector3(0, 0, 0), 19 | target: r.Vector3(0, 0, 0), 20 | up: r.Vector3(0, 0, 0), 21 | fovy: 0, 22 | projection: 0 23 | } 24 | } 25 | 26 | // Initialization 27 | // -------------------------------------------------------------------------------------- 28 | const screenWidth = 800 29 | const screenHeight = 450 30 | 31 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - split screen') 32 | 33 | // Generate a simple texture to use for trees 34 | const img = r.GenImageChecked(256, 256, 32, 32, r.DARKGRAY, r.WHITE) 35 | const textureGrid = r.LoadTextureFromImage(img) 36 | r.UnloadImage(img) 37 | r.SetTextureFilter(textureGrid, r.TEXTURE_FILTER_ANISOTROPIC_16X) 38 | r.SetTextureWrap(textureGrid, r.TEXTURE_WRAP_CLAMP) 39 | 40 | // Setup player 1 camera and screen 41 | const cameraPlayer1 = InitCamera() 42 | cameraPlayer1.fovy = 45 43 | cameraPlayer1.up.y = 1 44 | cameraPlayer1.target.y = 1 45 | cameraPlayer1.position.z = -3 46 | cameraPlayer1.position.y = 1 47 | 48 | const screenPlayer1 = r.LoadRenderTexture(screenWidth / 2, screenHeight) 49 | 50 | // Setup player two camera and screen 51 | const cameraPlayer2 = InitCamera() 52 | cameraPlayer2.fovy = 45 53 | cameraPlayer2.up.y = 1 54 | cameraPlayer2.target.y = 3 55 | cameraPlayer2.position.x = -3 56 | cameraPlayer2.position.y = 3 57 | 58 | const screenPlayer2 = r.LoadRenderTexture(screenWidth / 2, screenHeight) 59 | 60 | // Build a flipped rectangle the size of the split view to use for drawing later 61 | const splitScreenRect = r.Rectangle(0, 0, screenPlayer1.texture.width, -screenPlayer1.texture.height) 62 | 63 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 64 | // -------------------------------------------------------------------------------------- 65 | 66 | // Scene drawing 67 | function DrawScene () { 68 | const count = 5 69 | const spacing = 4 70 | 71 | // Grid of cube trees on a plane to make a "world" 72 | r.DrawPlane(r.Vector3(0, 0, 0), r.Vector2(50, 50), r.BEIGE) // Simple world plane 73 | 74 | for (let x = -count * spacing; x <= count * spacing; x += spacing) { 75 | for (let z = -count * spacing; z <= count * spacing; z += spacing) { 76 | r.DrawCubeTexture(textureGrid, r.Vector3(x, 1.5, z), 1, 1, 1, r.GREEN) 77 | r.DrawCubeTexture(textureGrid, r.Vector3(x, 0.5, z), 0.25, 1, 0.25, r.BROWN) 78 | } 79 | } 80 | 81 | // Draw a cube at each player's position 82 | r.DrawCube(cameraPlayer1.position, 1, 1, 1, r.RED) 83 | r.DrawCube(cameraPlayer2.position, 1, 1, 1, r.BLUE) 84 | } 85 | 86 | // Main game loop 87 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 88 | // Update 89 | // ---------------------------------------------------------------------------------- 90 | // If anyone moves this frame, how far will they move based on the time since the last frame 91 | // this moves thigns at 10 world units per second, regardless of the actual FPS 92 | const offsetThisFrame = 10 * r.GetFrameTime() 93 | 94 | // Move Player1 forward and backwards (no turning) 95 | if (r.IsKeyDown(r.KEY_W)) { 96 | cameraPlayer1.position.z += offsetThisFrame 97 | cameraPlayer1.target.z += offsetThisFrame 98 | } else if (r.IsKeyDown(r.KEY_S)) { 99 | cameraPlayer1.position.z -= offsetThisFrame 100 | cameraPlayer1.target.z -= offsetThisFrame 101 | } 102 | 103 | // Move Player2 forward and backwards (no turning) 104 | if (r.IsKeyDown(r.KEY_UP)) { 105 | cameraPlayer2.position.x += offsetThisFrame 106 | cameraPlayer2.target.x += offsetThisFrame 107 | } else if (r.IsKeyDown(r.KEY_DOWN)) { 108 | cameraPlayer2.position.x -= offsetThisFrame 109 | cameraPlayer2.target.x -= offsetThisFrame 110 | } 111 | // ---------------------------------------------------------------------------------- 112 | 113 | // Draw 114 | // ---------------------------------------------------------------------------------- 115 | // Draw Player1 view to the render texture 116 | r.BeginTextureMode(screenPlayer1) 117 | r.ClearBackground(r.SKYBLUE) 118 | r.BeginMode3D(cameraPlayer1) 119 | DrawScene() 120 | r.EndMode3D() 121 | r.DrawText('PLAYER1 W/S to move', 10, 10, 20, r.RED) 122 | r.EndTextureMode() 123 | 124 | // Draw Player2 view to the render texture 125 | r.BeginTextureMode(screenPlayer2) 126 | r.ClearBackground(r.SKYBLUE) 127 | r.BeginMode3D(cameraPlayer2) 128 | DrawScene() 129 | r.EndMode3D() 130 | r.DrawText('PLAYER2 UP/DOWN to move', 10, 10, 20, r.BLUE) 131 | r.EndTextureMode() 132 | 133 | // Draw both views render textures to the screen side by side 134 | r.BeginDrawing() 135 | r.ClearBackground(r.BLACK) 136 | r.DrawTextureRec(screenPlayer1.texture, splitScreenRect, r.Vector2(0, 0), r.WHITE) 137 | r.DrawTextureRec(screenPlayer2.texture, splitScreenRect, r.Vector2(screenWidth / 2.0, 0), r.WHITE) 138 | r.EndDrawing() 139 | } 140 | 141 | // De-Initialization 142 | // -------------------------------------------------------------------------------------- 143 | r.UnloadRenderTexture(screenPlayer1) // Unload render texture 144 | r.UnloadRenderTexture(screenPlayer2) // Unload render texture 145 | r.UnloadTexture(textureGrid) // Unload texture 146 | 147 | r.CloseWindow() // Close window and OpenGL context 148 | // -------------------------------------------------------------------------------------- 149 | -------------------------------------------------------------------------------- /examples/core/core_vr_simulator.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - VR Simulator (Oculus Rift CV1 parameters) 4 | * 5 | * This example has been created using raylib 1.7 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2017 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | const { join } = require('node:path') 14 | 15 | // #if defined(PLATFORM_DESKTOP) 16 | const GLSL_VERSION = 330 17 | // #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB 18 | // #define GLSL_VERSION 100 19 | // #endif 20 | 21 | // Initialization 22 | // -------------------------------------------------------------------------------------- 23 | const screenWidth = 800 24 | const screenHeight = 450 25 | 26 | // NOTE: screenWidth/screenHeight should match VR device aspect ratio 27 | 28 | r.SetConfigFlags(r.FLAG_MSAA_4X_HINT) 29 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - vr simulator') 30 | 31 | // Init VR simulator (Oculus Rift CV1 parameters) 32 | r.InitVrSimulator() 33 | 34 | const hmd = r.VrDeviceInfo() // VR device parameters (head-mounted-device) 35 | 36 | // Oculus Rift CV1 parameters for simulator 37 | hmd.hResolution = 2160 // HMD horizontal resolution in pixels 38 | hmd.vResolution = 1200 // HMD vertical resolution in pixels 39 | hmd.hScreenSize = 0.133793 // HMD horizontal size in meters 40 | hmd.vScreenSize = 0.0669 // HMD vertical size in meters 41 | hmd.vScreenCenter = 0.04678 // HMD screen center in meters 42 | hmd.eyeToScreenDistance = 0.041 // HMD distance between eye and display in meters 43 | hmd.lensSeparationDistance = 0.07 // HMD lens separation distance in meters 44 | hmd.interpupillaryDistance = 0.07 // HMD IPD (distance between pupils) in meters 45 | 46 | // NOTE: CV1 uses a Fresnel-hybrid-asymmetric lenses with specific distortion compute shaders. 47 | // Following parameters are an approximation to distortion stereo rendering but results differ from actual device. 48 | hmd.lensDistortionValues[0] = 1.0 // HMD lens distortion constant parameter 0 49 | hmd.lensDistortionValues[1] = 0.22 // HMD lens distortion constant parameter 1 50 | hmd.lensDistortionValues[2] = 0.24 // HMD lens distortion constant parameter 2 51 | hmd.lensDistortionValues[3] = 0.0 // HMD lens distortion constant parameter 3 52 | hmd.chromaAbCorrection[0] = 0.996 // HMD chromatic aberration correction parameter 0 53 | hmd.chromaAbCorrection[1] = -0.004 // HMD chromatic aberration correction parameter 1 54 | hmd.chromaAbCorrection[2] = 1.014 // HMD chromatic aberration correction parameter 2 55 | hmd.chromaAbCorrection[3] = 0.0 // HMD chromatic aberration correction parameter 3 56 | 57 | // Distortion shader (uses device lens distortion and chroma) 58 | const distortion = r.LoadShader(0, r.FormatText(join(__dirname, 'resources', 'distortion%i.fs'), GLSL_VERSION)) 59 | 60 | r.SetVrConfiguration(hmd, distortion) // Set Vr device parameters for stereo rendering 61 | 62 | // Define the camera to look into our 3d world 63 | const camera = r.Camera() 64 | camera.position = r.Vector3(5.0, 2.0, 5.0) // Camera position 65 | camera.target = r.Vector3(0.0, 2.0, 0.0) // Camera looking at point 66 | camera.up = r.Vector3(0.0, 1.0, 0.0) // Camera up vector (rotation towards target) 67 | camera.fovy = 60.0 // Camera field-of-view Y 68 | camera.type = r.CAMERA_PERSPECTIVE // Camera type 69 | 70 | const cubePosition = r.Vector3() 71 | 72 | r.SetTargetFPS(90) // Set our game to run at 90 frames-per-second 73 | // -------------------------------------------------------------------------------------- 74 | 75 | // Main game loop 76 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 77 | // Update 78 | // ---------------------------------------------------------------------------------- 79 | r.UpdateCamera(camera, r.CAMERA_FIRST_PERSON) // Update camera (simulator mode) 80 | 81 | if (r.IsKeyPressed(r.KEY_SPACE)) r.ToggleVrMode() // Toggle VR mode 82 | // ---------------------------------------------------------------------------------- 83 | 84 | // Draw 85 | // ---------------------------------------------------------------------------------- 86 | r.BeginDrawing() 87 | 88 | r.ClearBackground(r.RAYWHITE) 89 | 90 | r.BeginVrDrawing() 91 | 92 | r.BeginMode3D(camera) 93 | 94 | r.DrawCube(cubePosition, 2.0, 2.0, 2.0, r.RED) 95 | r.DrawCubeWires(cubePosition, 2.0, 2.0, 2.0, r.MAROON) 96 | 97 | r.DrawGrid(40, 1.0) 98 | 99 | r.EndMode3D() 100 | 101 | r.EndVrDrawing() 102 | 103 | r.DrawFPS(10, 10) 104 | 105 | r.EndDrawing() 106 | // ---------------------------------------------------------------------------------- 107 | } 108 | 109 | // De-Initialization 110 | // -------------------------------------------------------------------------------------- 111 | r.UnloadShader(distortion) // Unload distortion shader 112 | 113 | r.CloseVrSimulator() // Close VR simulator 114 | 115 | r.CloseWindow() // Close window and OpenGL context 116 | // -------------------------------------------------------------------------------------- 117 | -------------------------------------------------------------------------------- /examples/core/core_world_screen.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - World to screen 4 | * 5 | * This example has been created using raylib 1.3 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2015 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [core] example - 3d camera free') 20 | 21 | // Define the camera to look into our 3d world 22 | const camera = r.Camera( 23 | r.Vector3(10, 10, 10), 24 | r.Vector3(0, 0, 0), 25 | r.Vector3(0, 1, 0), 26 | 45, 27 | r.CAMERA_PERSPECTIVE 28 | ) 29 | 30 | const cubePosition = r.Vector3() 31 | let cubeScreenPosition = r.Vector2() 32 | 33 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 34 | // -------------------------------------------------------------------------------------- 35 | 36 | // Main game loop 37 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 38 | // Update 39 | // ---------------------------------------------------------------------------------- 40 | r.UpdateCamera(camera, r.CAMERA_FREE) // Update camera 41 | 42 | // Calculate cube screen space position (with a little offset to be in top) 43 | const cubePositionVector = r.Vector3(cubePosition.x, cubePosition.y + 2.5, cubePosition.z) 44 | cubeScreenPosition = r.GetWorldToScreen(cubePositionVector, camera) 45 | // ---------------------------------------------------------------------------------- 46 | 47 | // Draw 48 | // ---------------------------------------------------------------------------------- 49 | r.BeginDrawing() 50 | 51 | r.ClearBackground(r.RAYWHITE) 52 | 53 | r.BeginMode3D(camera) 54 | 55 | r.DrawCube(cubePosition, 2, 2, 2, r.RED) 56 | r.DrawCubeWires(cubePosition, 2, 2, 2, r.MAROON) 57 | 58 | r.DrawGrid(10, 1) 59 | 60 | r.EndMode3D() 61 | 62 | r.DrawText('Enemy: 100 / 100', cubeScreenPosition.x - r.MeasureText('Enemy: 100 / 100', 20) / 2, cubeScreenPosition.y, 20, r.BLACK) 63 | r.DrawText('Text is always on top of the cube', (screenWidth - r.MeasureText('Text is always on top of the cube', 20)) / 2, 25, 20, r.GRAY) 64 | 65 | r.EndDrawing() 66 | // ---------------------------------------------------------------------------------- 67 | } 68 | 69 | // De-Initialization 70 | // -------------------------------------------------------------------------------------- 71 | r.CloseWindow() // Close window and OpenGL context 72 | // -------------------------------------------------------------------------------------- 73 | -------------------------------------------------------------------------------- /examples/core/resources/distortion100.fs: -------------------------------------------------------------------------------- 1 | #version 100 2 | 3 | precision mediump float; 4 | 5 | // Input vertex attributes (from vertex shader) 6 | varying vec2 fragTexCoord; 7 | varying vec4 fragColor; 8 | 9 | // Input uniform values 10 | uniform sampler2D texture0; 11 | uniform vec4 colDiffuse; 12 | 13 | // NOTE: Add here your custom variables 14 | uniform vec2 leftLensCenter; 15 | uniform vec2 rightLensCenter; 16 | uniform vec2 leftScreenCenter; 17 | uniform vec2 rightScreenCenter; 18 | uniform vec2 scale; 19 | uniform vec2 scaleIn; 20 | uniform vec4 hmdWarpParam; 21 | uniform vec4 chromaAbParam; 22 | 23 | void main() 24 | { 25 | // Compute lens distortion 26 | vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; 27 | vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; 28 | vec2 theta = (fragTexCoord - lensCenter)*scaleIn; 29 | float rSq = theta.x*theta.x + theta.y*theta.y; 30 | vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); 31 | vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); 32 | vec2 tcBlue = lensCenter + scale*thetaBlue; 33 | 34 | if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) 35 | { 36 | // Set black fragment for everything outside the lens border 37 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 38 | } 39 | else 40 | { 41 | // Compute color chroma aberration 42 | float blue = texture2D(texture0, tcBlue).b; 43 | vec2 tcGreen = lensCenter + scale*theta1; 44 | float green = texture2D(texture0, tcGreen).g; 45 | 46 | vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); 47 | vec2 tcRed = lensCenter + scale*thetaRed; 48 | 49 | float red = texture2D(texture0, tcRed).r; 50 | gl_FragColor = vec4(red, green, blue, 1.0); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/core/resources/distortion330.fs: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // NOTE: Add here your custom variables 15 | uniform vec2 leftLensCenter = vec2(0.288, 0.5); 16 | uniform vec2 rightLensCenter = vec2(0.712, 0.5); 17 | uniform vec2 leftScreenCenter = vec2(0.25, 0.5); 18 | uniform vec2 rightScreenCenter = vec2(0.75, 0.5); 19 | uniform vec2 scale = vec2(0.25, 0.45); 20 | uniform vec2 scaleIn = vec2(4, 2.2222); 21 | uniform vec4 hmdWarpParam = vec4(1, 0.22, 0.24, 0); 22 | uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); 23 | 24 | void main() 25 | { 26 | // Compute lens distortion 27 | vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; 28 | vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; 29 | vec2 theta = (fragTexCoord - lensCenter)*scaleIn; 30 | float rSq = theta.x*theta.x + theta.y*theta.y; 31 | vec2 theta1 = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq); 32 | vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); 33 | vec2 tcBlue = lensCenter + scale*thetaBlue; 34 | 35 | if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) 36 | { 37 | // Set black fragment for everything outside the lens border 38 | finalColor = vec4(0.0, 0.0, 0.0, 1.0); 39 | } 40 | else 41 | { 42 | // Compute color chroma aberration 43 | float blue = texture(texture0, tcBlue).b; 44 | vec2 tcGreen = lensCenter + scale*theta1; 45 | float green = texture(texture0, tcGreen).g; 46 | 47 | vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); 48 | vec2 tcRed = lensCenter + scale*thetaRed; 49 | 50 | float red = texture(texture0, tcRed).r; 51 | finalColor = vec4(red, green, blue, 1.0); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /examples/models/models_rlgl_solar_system.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [models] example - rlgl module usage with push/pop matrix transformations 4 | * 5 | * NOTE: This example uses [rlgl] module functionality (pseudo-OpenGL 1.1 style coding) 6 | * 7 | * Example originally created with raylib 2.5, last time updated with raylib 4.0 8 | * 9 | * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, 10 | * BSD-like license that allows static linking with closed source software 11 | * 12 | * Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) 13 | * 14 | ********************************************************************************************/ 15 | 16 | const r = require('../../index.js') 17 | // ------------------------------------------------------------------------------------ 18 | // Module Functions Declaration 19 | // ------------------------------------------------------------------------------------ 20 | 21 | // ------------------------------------------------------------------------------------ 22 | // Program main entry point 23 | // ------------------------------------------------------------------------------------ 24 | 25 | // Initialization 26 | // -------------------------------------------------------------------------------------- 27 | const screenWidth = 800 28 | const screenHeight = 450 29 | 30 | const sunRadius = 4.0 31 | const earthRadius = 0.6 32 | const earthOrbitRadius = 8.0 33 | const moonRadius = 0.16 34 | const moonOrbitRadius = 1.5 35 | 36 | r.InitWindow(screenWidth, screenHeight, 'node raylib [models] example - rlgl module usage with push/pop matrix transformations') 37 | 38 | const camera = { 39 | position: { x: 16, y: 16, z: 16 }, 40 | target: { x: 0, y: 0, z: 0 }, 41 | up: { x: 0, y: 1, z: 0 }, 42 | fovy: 45, 43 | projection: r.CAMERA_PERSPECTIVE 44 | } 45 | 46 | const rotationSpeed = 0.2 // General system rotation speed 47 | 48 | let earthRotation = 0.0 // Rotation of earth around itself (days) in degrees 49 | let earthOrbitRotation = 0.0 // Rotation of earth around the Sun (years) in degrees 50 | let moonRotation = 0.0 // Rotation of moon around itself 51 | let moonOrbitRotation = 0.0 // Rotation of moon around earth in degrees 52 | 53 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 54 | // -------------------------------------------------------------------------------------- 55 | 56 | // Main game loop 57 | while (!r.WindowShouldClose()) { 58 | // Update 59 | // ---------------------------------------------------------------------------------- 60 | r.UpdateCamera(camera, r.CAMERA_FREE) 61 | 62 | earthRotation += (5.0 * rotationSpeed) 63 | earthOrbitRotation += (365 / 360.0 * (5.0 * rotationSpeed) * rotationSpeed) 64 | moonRotation += (2.0 * rotationSpeed) 65 | moonOrbitRotation += (8.0 * rotationSpeed) 66 | // ---------------------------------------------------------------------------------- 67 | 68 | // Draw 69 | // ---------------------------------------------------------------------------------- 70 | r.BeginDrawing() 71 | 72 | r.ClearBackground(r.RAYWHITE) 73 | 74 | r.BeginMode3D(camera) 75 | 76 | r.rlPushMatrix() 77 | r.rlScalef(sunRadius, sunRadius, sunRadius) // Scale Sun 78 | DrawSphereBasic(r.GOLD) // Draw the Sun 79 | r.rlPopMatrix() 80 | 81 | r.rlPushMatrix() 82 | r.rlRotatef(earthOrbitRotation, 0.0, 1.0, 0.0) // Rotation for Earth orbit around Sun 83 | r.rlTranslatef(earthOrbitRadius, 0.0, 0.0) // Translation for Earth orbit 84 | 85 | r.rlPushMatrix() 86 | r.rlRotatef(earthRotation, 0.25, 1.0, 0.0) // Rotation for Earth itself 87 | r.rlScalef(earthRadius, earthRadius, earthRadius)// Scale Earth 88 | 89 | DrawSphereBasic(r.BLUE) // Draw the Earth 90 | r.rlPopMatrix() 91 | 92 | r.rlRotatef(moonOrbitRotation, 0.0, 1.0, 0.0) // Rotation for Moon orbit around Earth 93 | r.rlTranslatef(moonOrbitRadius, 0.0, 0.0) // Translation for Moon orbit 94 | r.rlRotatef(moonRotation, 0.0, 1.0, 0.0) // Rotation for Moon itself 95 | r.rlScalef(moonRadius, moonRadius, moonRadius) // Scale Moon 96 | 97 | DrawSphereBasic(r.LIGHTGRAY) // Draw the Moon 98 | r.rlPopMatrix() 99 | 100 | // Some reference elements (not affected by previous matrix transformations) 101 | r.DrawCircle3D({ x: 0, y: 0, z: 0 }, earthOrbitRadius, { x: 1, y: 0, z: 0 }, 90.0, r.Fade(r.RED, 0.5)) 102 | r.DrawGrid(20, 1.0) 103 | 104 | r.EndMode3D() 105 | 106 | r.DrawText('EARTH ORBITING AROUND THE SUN!', 400, 10, 20, r.MAROON) 107 | r.DrawFPS(10, 10) 108 | 109 | r.EndDrawing() 110 | // ---------------------------------------------------------------------------------- 111 | } 112 | 113 | // De-Initialization 114 | // -------------------------------------------------------------------------------------- 115 | r.CloseWindow() // Close window and OpenGL context 116 | // -------------------------------------------------------------------------------------- 117 | 118 | // -------------------------------------------------------------------------------------------- 119 | // Module Functions Definitions (local) 120 | // -------------------------------------------------------------------------------------------- 121 | 122 | // Draw sphere without any matrix transformation 123 | // NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f 124 | function DrawSphereBasic (color) { 125 | const rings = 16 126 | const slices = 16 127 | 128 | // Make sure there is enough space in the internal render batch 129 | // buffer to store all required vertex, batch is reseted if required 130 | r.rlCheckRenderBatchLimit((rings + 2) * slices * 6) 131 | 132 | r.rlBegin(r.RL_TRIANGLES) 133 | r.rlColor4ub(color.r, color.g, color.b, color.a) 134 | const cosf = Math.cos 135 | const sinf = Math.sin 136 | r.DEG2RAD = Math.PI / 180 137 | for (let i = 0; i < (rings + 2); i++) { 138 | for (let j = 0; j < slices; j++) { 139 | r.rlVertex3f(cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * i)) * sinf(r.DEG2RAD * (j * 360 / slices)), 140 | sinf(r.DEG2RAD * (270 + (180 / (rings + 1)) * i)), 141 | cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * i)) * cosf(r.DEG2RAD * (j * 360 / slices))) 142 | r.rlVertex3f(cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))) * sinf(r.DEG2RAD * ((j + 1) * 360 / slices)), 143 | sinf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))), 144 | cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))) * cosf(r.DEG2RAD * ((j + 1) * 360 / slices))) 145 | r.rlVertex3f(cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))) * sinf(r.DEG2RAD * (j * 360 / slices)), 146 | sinf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))), 147 | cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))) * cosf(r.DEG2RAD * (j * 360 / slices))) 148 | 149 | r.rlVertex3f(cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * i)) * sinf(r.DEG2RAD * (j * 360 / slices)), 150 | sinf(r.DEG2RAD * (270 + (180 / (rings + 1)) * i)), 151 | cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * i)) * cosf(r.DEG2RAD * (j * 360 / slices))) 152 | r.rlVertex3f(cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i))) * sinf(r.DEG2RAD * ((j + 1) * 360 / slices)), 153 | sinf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i))), 154 | cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i))) * cosf(r.DEG2RAD * ((j + 1) * 360 / slices))) 155 | r.rlVertex3f(cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))) * sinf(r.DEG2RAD * ((j + 1) * 360 / slices)), 156 | sinf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))), 157 | cosf(r.DEG2RAD * (270 + (180 / (rings + 1)) * (i + 1))) * cosf(r.DEG2RAD * ((j + 1) * 360 / slices))) 158 | } 159 | } 160 | r.rlEnd() 161 | } 162 | -------------------------------------------------------------------------------- /examples/models/resources/angle_gauge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/angle_gauge.png -------------------------------------------------------------------------------- /examples/models/resources/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/background.png -------------------------------------------------------------------------------- /examples/models/resources/billboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/billboard.png -------------------------------------------------------------------------------- /examples/models/resources/cubicmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/cubicmap.png -------------------------------------------------------------------------------- /examples/models/resources/cubicmap_atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/cubicmap_atlas.png -------------------------------------------------------------------------------- /examples/models/resources/dresden_square.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/dresden_square.hdr -------------------------------------------------------------------------------- /examples/models/resources/guy/guy.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/guy/guy.blend -------------------------------------------------------------------------------- /examples/models/resources/guy/guy.iqm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/guy/guy.iqm -------------------------------------------------------------------------------- /examples/models/resources/guy/guyanim.iqm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/guy/guyanim.iqm -------------------------------------------------------------------------------- /examples/models/resources/guy/guytex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/guy/guytex.png -------------------------------------------------------------------------------- /examples/models/resources/heightmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/heightmap.png -------------------------------------------------------------------------------- /examples/models/resources/models/bridge_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/bridge_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/models/castle_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/castle_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/models/cube.obj: -------------------------------------------------------------------------------- 1 | # reference material 2 | #mtllib cube.mtl 3 | 4 | # object box 5 | 6 | # vertex (XZY) 7 | v 5.5 0 1.5 8 | v 8.5 0 1.5 9 | v 5.5 0 -1.5 10 | v 8.5 0 -1.5 11 | v 5.5 3 1.5 12 | v 8.5 3 1.5 13 | v 5.5 3 -1.5 14 | v 8.5 3 -1.5 15 | 16 | # normals (XYZ) 17 | vn 0 -1 0 18 | vn 0 1 0 19 | vn 0 0 1 20 | vn 1 0 0 21 | vn 0 0 -1 22 | vn -1 0 0 23 | 24 | # UVs (XY) 25 | vt 0.5 0 0 26 | vt 1 0 0 27 | vt 1 0.5 0 28 | vt 0.5 0.5 0 29 | vt 0.5 0.5 0 30 | vt 1 0.5 0 31 | vt 0.5 1 0 32 | vt 1 1 0 33 | vt 0 0.5 0 34 | vt 1 0.5 0 35 | vt 1 0 0 36 | vt 0 0 0 37 | vt 0 0.5 0 38 | vt 1 0.5 0 39 | vt 1 1 0 40 | vt 0 1 0 41 | vt 0.5 0 0 42 | vt 0 0 0 43 | vt 0 0.5 0 44 | vt 0.5 0.5 0 45 | vt 0 0.5 0 46 | vt 0.5 0.5 0 47 | vt 0.5 1 0 48 | vt 0 1 0 49 | 50 | # merger 51 | g box 52 | 53 | # reference material 54 | #usemtl mat01 55 | 56 | # faces 57 | f 1/9/1 3/10/1 4/11/1 58 | f 4/11/1 2/12/1 1/9/1 59 | f 5/13/2 6/14/2 8/15/2 60 | f 8/15/2 7/16/2 5/13/2 61 | f 1/17/6 2/18/6 6/19/6 62 | f 6/19/6 5/20/6 1/17/6 63 | f 2/6/1 4/5/1 8/7/1 64 | f 8/7/1 6/8/1 2/6/1 65 | f 4/2/3 3/1/3 7/4/3 66 | f 7/4/3 8/3/3 4/2/3 67 | f 3/22/5 1/21/5 5/24/5 68 | f 5/24/5 7/23/5 3/22/5 69 | -------------------------------------------------------------------------------- /examples/models/resources/models/cube_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/cube_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/models/house_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/house_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/models/market_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/market_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/models/turret_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/turret_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/models/well_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/models/well_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/pbr/trooper_albedo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/pbr/trooper_albedo.png -------------------------------------------------------------------------------- /examples/models/resources/pbr/trooper_ao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/pbr/trooper_ao.png -------------------------------------------------------------------------------- /examples/models/resources/pbr/trooper_metalness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/pbr/trooper_metalness.png -------------------------------------------------------------------------------- /examples/models/resources/pbr/trooper_normals.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/pbr/trooper_normals.png -------------------------------------------------------------------------------- /examples/models/resources/pbr/trooper_roughness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/pbr/trooper_roughness.png -------------------------------------------------------------------------------- /examples/models/resources/pitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/pitch.png -------------------------------------------------------------------------------- /examples/models/resources/plane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/plane.png -------------------------------------------------------------------------------- /examples/models/resources/plane_diffuse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/models/resources/plane_diffuse.png -------------------------------------------------------------------------------- /examples/models/resources/shaders/brdf.fs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * BRDF LUT Generation - Bidirectional reflectance distribution function fragment shader 4 | * 5 | * REF: https://github.com/HectorMF/BRDFGenerator 6 | * 7 | * Copyright (c) 2017 Victor Fisac 8 | * 9 | **********************************************************************************************/ 10 | 11 | #version 330 12 | 13 | 14 | // Input vertex attributes (from vertex shader) 15 | in vec2 fragTexCoord; 16 | 17 | // Constant values 18 | const float PI = 3.14159265359; 19 | const uint MAX_SAMPLES = 1024u; 20 | 21 | // Output fragment color 22 | out vec4 finalColor; 23 | 24 | vec2 Hammersley(uint i, uint N); 25 | float RadicalInverseVdC(uint bits); 26 | float GeometrySchlickGGX(float NdotV, float roughness); 27 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness); 28 | vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); 29 | vec2 IntegrateBRDF(float NdotV, float roughness); 30 | 31 | float RadicalInverseVdC(uint bits) 32 | { 33 | bits = (bits << 16u) | (bits >> 16u); 34 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 35 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 36 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 37 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 38 | return float(bits) * 2.3283064365386963e-10; // / 0x100000000 39 | } 40 | 41 | // Compute Hammersley coordinates 42 | vec2 Hammersley(uint i, uint N) 43 | { 44 | return vec2(float(i)/float(N), RadicalInverseVdC(i)); 45 | } 46 | 47 | // Integrate number of importance samples for (roughness and NoV) 48 | vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) 49 | { 50 | float a = roughness*roughness; 51 | float phi = 2.0 * PI * Xi.x; 52 | float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y)); 53 | float sinTheta = sqrt(1.0 - cosTheta*cosTheta); 54 | 55 | // Transform from spherical coordinates to cartesian coordinates (halfway vector) 56 | vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); 57 | 58 | // Transform from tangent space H vector to world space sample vector 59 | vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0)); 60 | vec3 tangent = normalize(cross(up, N)); 61 | vec3 bitangent = cross(N, tangent); 62 | vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z; 63 | 64 | return normalize(sampleVec); 65 | } 66 | 67 | float GeometrySchlickGGX(float NdotV, float roughness) 68 | { 69 | // For IBL k is calculated different 70 | float k = (roughness*roughness)/2.0; 71 | 72 | float nom = NdotV; 73 | float denom = NdotV*(1.0 - k) + k; 74 | 75 | return nom/denom; 76 | } 77 | 78 | // Compute the geometry term for the BRDF given roughness squared, NoV, NoL 79 | float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) 80 | { 81 | float NdotV = max(dot(N, V), 0.0); 82 | float NdotL = max(dot(N, L), 0.0); 83 | float ggx2 = GeometrySchlickGGX(NdotV, roughness); 84 | float ggx1 = GeometrySchlickGGX(NdotL, roughness); 85 | 86 | return ggx1*ggx2; 87 | } 88 | 89 | vec2 IntegrateBRDF(float NdotV, float roughness) 90 | { 91 | float A = 0.0; 92 | float B = 0.0; 93 | vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV); 94 | vec3 N = vec3(0.0, 0.0, 1.0); 95 | 96 | for (uint i = 0u; i < MAX_SAMPLES; i++) 97 | { 98 | // Generate a sample vector that's biased towards the preferred alignment direction (importance sampling) 99 | 100 | vec2 Xi = Hammersley(i, MAX_SAMPLES); // Compute a Hammersely coordinate 101 | vec3 H = ImportanceSampleGGX(Xi, N, roughness); // Integrate number of importance samples for (roughness and NoV) 102 | vec3 L = normalize(2.0*dot(V, H)*H - V); // Compute reflection vector L 103 | 104 | float NdotL = max(L.z, 0.0); // Compute normal dot light 105 | float NdotH = max(H.z, 0.0); // Compute normal dot half 106 | float VdotH = max(dot(V, H), 0.0); // Compute view dot half 107 | 108 | if (NdotL > 0.0) 109 | { 110 | float G = GeometrySmith(N, V, L, roughness); // Compute the geometry term for the BRDF given roughness squared, NoV, NoL 111 | float GVis = (G*VdotH)/(NdotH*NdotV); // Compute the visibility term given G, VoH, NoH, NoV, NoL 112 | float Fc = pow(1.0 - VdotH, 5.0); // Compute the fresnel term given VoH 113 | 114 | A += (1.0 - Fc)*GVis; // Sum the result given fresnel, geometry, visibility 115 | B += Fc*GVis; 116 | } 117 | } 118 | 119 | // Calculate brdf average sample 120 | A /= float(MAX_SAMPLES); 121 | B /= float(MAX_SAMPLES); 122 | 123 | return vec2(A, B); 124 | } 125 | 126 | void main() 127 | { 128 | // Calculate brdf based on texture coordinates 129 | vec2 brdf = IntegrateBRDF(fragTexCoord.x, fragTexCoord.y); 130 | 131 | // Calculate final fragment color 132 | finalColor = vec4(brdf.r, brdf.g, 0.0, 1.0); 133 | } 134 | -------------------------------------------------------------------------------- /examples/models/resources/shaders/brdf.vs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Bidirectional reflectance distribution function vertex shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes 12 | in vec3 vertexPosition; 13 | in vec2 vertexTexCoord; 14 | 15 | // Output vertex attributes (to fragment shader) 16 | out vec2 fragTexCoord; 17 | 18 | void main() 19 | { 20 | // Calculate fragment position based on model transformations 21 | fragTexCoord = vertexTexCoord; 22 | 23 | // Calculate final vertex position 24 | gl_Position = vec4(vertexPosition, 1.0); 25 | } -------------------------------------------------------------------------------- /examples/models/resources/shaders/cubemap.fs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Equirectangular to cubemap fragment shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes (from vertex shader) 12 | in vec3 fragPosition; 13 | 14 | // Input uniform values 15 | uniform sampler2D equirectangularMap; 16 | 17 | // Output fragment color 18 | out vec4 finalColor; 19 | 20 | vec2 SampleSphericalMap(vec3 v) 21 | { 22 | vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); 23 | uv *= vec2(0.1591, 0.3183); 24 | uv += 0.5; 25 | return uv; 26 | } 27 | 28 | void main() 29 | { 30 | // Normalize local position 31 | vec2 uv = SampleSphericalMap(normalize(fragPosition)); 32 | 33 | // Fetch color from texture map 34 | vec3 color = texture(equirectangularMap, uv).rgb; 35 | 36 | // Calculate final fragment color 37 | finalColor = vec4(color, 1.0); 38 | } 39 | -------------------------------------------------------------------------------- /examples/models/resources/shaders/cubemap.vs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Equirectangular to cubemap vertex shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes 12 | in vec3 vertexPosition; 13 | 14 | // Input uniform values 15 | uniform mat4 projection; 16 | uniform mat4 view; 17 | 18 | // Output vertex attributes (to fragment shader) 19 | out vec3 fragPosition; 20 | 21 | void main() 22 | { 23 | // Calculate fragment position based on model transformations 24 | fragPosition = vertexPosition; 25 | 26 | // Calculate final vertex position 27 | gl_Position = projection*view*vec4(vertexPosition, 1.0); 28 | } 29 | -------------------------------------------------------------------------------- /examples/models/resources/shaders/irradiance.fs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Irradiance cubemap fragment shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes (from vertex shader) 12 | in vec3 fragPosition; 13 | 14 | // Input uniform values 15 | uniform samplerCube environmentMap; 16 | 17 | // Constant values 18 | const float PI = 3.14159265359f; 19 | 20 | // Output fragment color 21 | out vec4 finalColor; 22 | 23 | void main() 24 | { 25 | // The sample direction equals the hemisphere's orientation 26 | vec3 normal = normalize(fragPosition); 27 | 28 | vec3 irradiance = vec3(0.0); 29 | 30 | vec3 up = vec3(0.0, 1.0, 0.0); 31 | vec3 right = cross(up, normal); 32 | up = cross(normal, right); 33 | 34 | float sampleDelta = 0.025f; 35 | float nrSamples = 0.0f; 36 | 37 | for (float phi = 0.0; phi < 2.0*PI; phi += sampleDelta) 38 | { 39 | for (float theta = 0.0; theta < 0.5*PI; theta += sampleDelta) 40 | { 41 | // Spherical to cartesian (in tangent space) 42 | vec3 tangentSample = vec3(sin(theta)*cos(phi), sin(theta)*sin(phi), cos(theta)); 43 | 44 | // tangent space to world 45 | vec3 sampleVec = tangentSample.x*right + tangentSample.y*up + tangentSample.z*normal; 46 | 47 | // Fetch color from environment cubemap 48 | irradiance += texture(environmentMap, sampleVec).rgb*cos(theta)*sin(theta); 49 | nrSamples++; 50 | } 51 | } 52 | 53 | // Calculate irradiance average value from samples 54 | irradiance = PI*irradiance*(1.0/float(nrSamples)); 55 | 56 | // Calculate final fragment color 57 | finalColor = vec4(irradiance, 1.0); 58 | } 59 | -------------------------------------------------------------------------------- /examples/models/resources/shaders/pbr.vs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Physically based rendering vertex shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes 12 | in vec3 vertexPosition; 13 | in vec2 vertexTexCoord; 14 | in vec3 vertexNormal; 15 | in vec4 vertexTangent; 16 | 17 | // Input uniform values 18 | uniform mat4 mvp; 19 | uniform mat4 matModel; 20 | 21 | // Output vertex attributes (to fragment shader) 22 | out vec3 fragPosition; 23 | out vec2 fragTexCoord; 24 | out vec3 fragNormal; 25 | out vec3 fragTangent; 26 | out vec3 fragBinormal; 27 | 28 | void main() 29 | { 30 | // Calculate binormal from vertex normal and tangent 31 | vec3 vertexBinormal = cross(vertexNormal, vec3(vertexTangent)); 32 | 33 | // Calculate fragment normal based on normal transformations 34 | mat3 normalMatrix = transpose(inverse(mat3(matModel))); 35 | 36 | // Calculate fragment position based on model transformations 37 | fragPosition = vec3(matModel*vec4(vertexPosition, 1.0f)); 38 | 39 | // Send vertex attributes to fragment shader 40 | fragTexCoord = vertexTexCoord; 41 | fragNormal = normalize(normalMatrix*vertexNormal); 42 | fragTangent = normalize(normalMatrix*vec3(vertexTangent)); 43 | fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal); 44 | fragBinormal = normalize(normalMatrix*vertexBinormal); 45 | fragBinormal = cross(fragNormal, fragTangent); 46 | 47 | // Calculate final vertex position 48 | gl_Position = mvp*vec4(vertexPosition, 1.0); 49 | } -------------------------------------------------------------------------------- /examples/models/resources/shaders/prefilter.fs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Prefiltered environment for reflections fragment shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | #define MAX_SAMPLES 1024u 11 | #define CUBEMAP_RESOLUTION 1024.0 12 | 13 | // Input vertex attributes (from vertex shader) 14 | in vec3 fragPosition; 15 | 16 | // Input uniform values 17 | uniform samplerCube environmentMap; 18 | uniform float roughness; 19 | 20 | // Constant values 21 | const float PI = 3.14159265359f; 22 | 23 | // Output fragment color 24 | out vec4 finalColor; 25 | 26 | float DistributionGGX(vec3 N, vec3 H, float roughness); 27 | float RadicalInverse_VdC(uint bits); 28 | vec2 Hammersley(uint i, uint N); 29 | vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); 30 | 31 | float DistributionGGX(vec3 N, vec3 H, float roughness) 32 | { 33 | float a = roughness*roughness; 34 | float a2 = a*a; 35 | float NdotH = max(dot(N, H), 0.0); 36 | float NdotH2 = NdotH*NdotH; 37 | 38 | float nom = a2; 39 | float denom = (NdotH2*(a2 - 1.0) + 1.0); 40 | denom = PI*denom*denom; 41 | 42 | return nom/denom; 43 | } 44 | 45 | float RadicalInverse_VdC(uint bits) 46 | { 47 | bits = (bits << 16u) | (bits >> 16u); 48 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 49 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 50 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 51 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 52 | return float(bits) * 2.3283064365386963e-10; // / 0x100000000 53 | } 54 | 55 | vec2 Hammersley(uint i, uint N) 56 | { 57 | return vec2(float(i)/float(N), RadicalInverse_VdC(i)); 58 | } 59 | 60 | vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) 61 | { 62 | float a = roughness*roughness; 63 | float phi = 2.0 * PI * Xi.x; 64 | float cosTheta = sqrt((1.0 - Xi.y)/(1.0 + (a*a - 1.0)*Xi.y)); 65 | float sinTheta = sqrt(1.0 - cosTheta*cosTheta); 66 | 67 | // Transform from spherical coordinates to cartesian coordinates (halfway vector) 68 | vec3 H = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); 69 | 70 | // Transform from tangent space H vector to world space sample vector 71 | vec3 up = ((abs(N.z) < 0.999) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0)); 72 | vec3 tangent = normalize(cross(up, N)); 73 | vec3 bitangent = cross(N, tangent); 74 | vec3 sampleVec = tangent*H.x + bitangent*H.y + N*H.z; 75 | 76 | return normalize(sampleVec); 77 | } 78 | 79 | void main() 80 | { 81 | // Make the simplyfying assumption that V equals R equals the normal 82 | vec3 N = normalize(fragPosition); 83 | vec3 R = N; 84 | vec3 V = R; 85 | 86 | vec3 prefilteredColor = vec3(0.0); 87 | float totalWeight = 0.0; 88 | 89 | for (uint i = 0u; i < MAX_SAMPLES; i++) 90 | { 91 | // Generate a sample vector that's biased towards the preferred alignment direction (importance sampling) 92 | vec2 Xi = Hammersley(i, MAX_SAMPLES); 93 | vec3 H = ImportanceSampleGGX(Xi, N, roughness); 94 | vec3 L = normalize(2.0*dot(V, H)*H - V); 95 | 96 | float NdotL = max(dot(N, L), 0.0); 97 | if(NdotL > 0.0) 98 | { 99 | // Sample from the environment's mip level based on roughness/pdf 100 | float D = DistributionGGX(N, H, roughness); 101 | float NdotH = max(dot(N, H), 0.0); 102 | float HdotV = max(dot(H, V), 0.0); 103 | float pdf = D*NdotH/(4.0*HdotV) + 0.0001; 104 | 105 | float resolution = CUBEMAP_RESOLUTION; 106 | float saTexel = 4.0*PI/(6.0*resolution*resolution); 107 | float saSample = 1.0/(float(MAX_SAMPLES)*pdf + 0.0001); 108 | float mipLevel = ((roughness == 0.0) ? 0.0 : 0.5*log2(saSample/saTexel)); 109 | 110 | prefilteredColor += textureLod(environmentMap, L, mipLevel).rgb*NdotL; 111 | totalWeight += NdotL; 112 | } 113 | } 114 | 115 | // Calculate prefilter average color 116 | prefilteredColor = prefilteredColor/totalWeight; 117 | 118 | // Calculate final fragment color 119 | finalColor = vec4(prefilteredColor, 1.0); 120 | } 121 | -------------------------------------------------------------------------------- /examples/models/resources/shaders/skybox.fs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Background skybox fragment shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes (from vertex shader) 12 | in vec3 fragPosition; 13 | 14 | // Input uniform values 15 | uniform samplerCube environmentMap; 16 | 17 | // Output fragment color 18 | out vec4 finalColor; 19 | 20 | void main() 21 | { 22 | // Fetch color from texture map 23 | vec3 color = texture(environmentMap, fragPosition).rgb; 24 | 25 | // Apply gamma correction 26 | color = color/(color + vec3(1.0)); 27 | color = pow(color, vec3(1.0/2.2)); 28 | 29 | // Calculate final fragment color 30 | finalColor = vec4(color, 1.0); 31 | } 32 | -------------------------------------------------------------------------------- /examples/models/resources/shaders/skybox.vs: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * rPBR [shader] - Background skybox vertex shader 4 | * 5 | * Copyright (c) 2017 Victor Fisac 6 | * 7 | **********************************************************************************************/ 8 | 9 | #version 330 10 | 11 | // Input vertex attributes 12 | in vec3 vertexPosition; 13 | 14 | // Input uniform values 15 | uniform mat4 projection; 16 | uniform mat4 view; 17 | 18 | // Output vertex attributes (to fragment shader) 19 | out vec3 fragPosition; 20 | 21 | void main() 22 | { 23 | // Calculate fragment position based on model transformations 24 | fragPosition = vertexPosition; 25 | 26 | // Remove translation from the view matrix 27 | mat4 rotView = mat4(mat3(view)); 28 | vec4 clipPos = projection*rotView*vec4(vertexPosition, 1.0); 29 | 30 | // Calculate final vertex position 31 | gl_Position = clipPos.xyww; 32 | } 33 | -------------------------------------------------------------------------------- /examples/others/easings_testbed.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [easings] example - Easings Testbed 4 | * 5 | * This example has been created using raylib 2.5 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Example contributed by Juan Miguel López (@flashback-fx) and reviewed by Ramon Santamaria (@raysan5) 9 | * 10 | * Copyright (c) 2019 Juan Miguel López (@flashback-fx ) and Ramon Santamaria (@raysan5) 11 | * Ported to javascript 2022 by David Konsumer (@konsumer) 12 | * 13 | ********************************************************************************************/ 14 | const r = require('../../index.js') 15 | 16 | const FONT_SIZE = 20 17 | 18 | const D_STEP = 20.0 19 | const D_STEP_FINE = 2.0 20 | const D_MIN = 1.0 21 | const D_MAX = 10000.0 22 | 23 | const screenWidth = 800 24 | const screenHeight = 450 25 | 26 | r.InitWindow(screenWidth, screenHeight, 'raylib [easings] example - easings testbed') 27 | 28 | const ballPosition = { x: 100.0, y: 200.0 } 29 | 30 | let t = 0.0 // Current time (in any unit measure, but same unit as duration) 31 | let d = 300.0 // Total time it should take to complete (duration) 32 | let paused = true 33 | let boundedT = true // If true, t will stop when d >= td, otherwise t will keep adding td to its value every loop 34 | 35 | let easingX = 0 // Easing selected for x axis 36 | let easingY = 0 // Easing selected for y axis 37 | 38 | r.SetTargetFPS(60) 39 | 40 | // Easing functions reference data 41 | const easingTypes = Object.keys(r) 42 | .filter(name => name.startsWith('Ease')) 43 | .map(funcName => { 44 | return { name: funcName, func: r[funcName] } 45 | }) 46 | 47 | // NoEase function, used when "no easing" is selected for any axis. 48 | easingTypes.push({ name: 'None', func: (t, b, c, d) => b }) 49 | 50 | while (!r.WindowShouldClose()) { 51 | // Update 52 | // ---------------------------------------------------------------------------------- 53 | if (r.IsKeyPressed(r.KEY_T)) boundedT = !boundedT 54 | 55 | // Choose easing for the X axis 56 | if (r.IsKeyPressed(r.KEY_RIGHT)) { 57 | easingX++ 58 | easingX %= easingTypes.length 59 | } else if (r.IsKeyPressed(r.KEY_LEFT)) { 60 | easingX-- 61 | easingX = easingX > 0 ? easingX : easingTypes.length - 1 62 | } 63 | 64 | // Choose easing for the Y axis 65 | if (r.IsKeyPressed(r.KEY_DOWN)) { 66 | easingY++ 67 | easingY %= easingTypes.length 68 | } else if (r.IsKeyPressed(r.KEY_UP)) { 69 | easingY-- 70 | easingY = easingY > 0 ? easingY : easingTypes.length - 1 71 | } 72 | 73 | // Change d (duration) value 74 | if (r.IsKeyPressed(r.KEY_W) && d < D_MAX - D_STEP) d += D_STEP 75 | else if (r.IsKeyPressed(r.KEY_Q) && d > D_MIN + D_STEP) d -= D_STEP 76 | 77 | if (r.IsKeyDown(r.KEY_S) && d < D_MAX - D_STEP_FINE) d += D_STEP_FINE 78 | else if (r.IsKeyDown(r.KEY_A) && d > D_MIN + D_STEP_FINE) d -= D_STEP_FINE 79 | 80 | // Play, pause and restart controls 81 | if (r.IsKeyPressed(r.KEY_SPACE) || r.IsKeyPressed(r.KEY_T) || 82 | r.IsKeyPressed(r.KEY_RIGHT) || r.IsKeyPressed(r.KEY_LEFT) || 83 | r.IsKeyPressed(r.KEY_DOWN) || r.IsKeyPressed(r.KEY_UP) || 84 | r.IsKeyPressed(r.KEY_W) || r.IsKeyPressed(r.KEY_Q) || 85 | r.IsKeyDown(r.KEY_S) || r.IsKeyDown(r.KEY_A) || 86 | (r.IsKeyPressed(r.KEY_ENTER) && (boundedT === true) && (t >= d))) { 87 | t = 0.0 88 | ballPosition.x = 100.0 89 | ballPosition.y = 100.0 90 | paused = true 91 | } 92 | 93 | if (r.IsKeyPressed(r.KEY_ENTER)) paused = !paused 94 | 95 | // Movement computation 96 | if (!paused && ((boundedT && t < d) || !boundedT)) { 97 | ballPosition.x = easingTypes[easingX].func(t, 100.0, 700.0 - 100.0, d) 98 | ballPosition.y = easingTypes[easingY].func(t, 100.0, 400.0 - 100.0, d) 99 | t += 1.0 100 | } 101 | r.BeginDrawing() 102 | r.ClearBackground(r.RAYWHITE) 103 | 104 | // Draw information text 105 | r.DrawText(`Easing x: ${easingTypes[easingX].name}`, 0, FONT_SIZE * 2, FONT_SIZE, r.LIGHTGRAY) 106 | r.DrawText(`Easing y: ${easingTypes[easingY].name}`, 0, FONT_SIZE * 3, FONT_SIZE, r.LIGHTGRAY) 107 | r.DrawText(`t (${boundedT ? 'b' : 'u'}) = ${t} d = ${d}`, 0, FONT_SIZE * 4, FONT_SIZE, r.LIGHTGRAY) 108 | 109 | // Draw instructions text 110 | r.DrawText('Use ENTER to play or pause movement, use SPACE to restart', 0, r.GetScreenHeight() - FONT_SIZE * 2, FONT_SIZE, r.LIGHTGRAY) 111 | r.DrawText('Use D and W or A and S keys to change duration', 0, r.GetScreenHeight() - FONT_SIZE * 3, FONT_SIZE, r.LIGHTGRAY) 112 | r.DrawText('Use LEFT or RIGHT keys to choose easing for the x axis', 0, r.GetScreenHeight() - FONT_SIZE * 4, FONT_SIZE, r.LIGHTGRAY) 113 | r.DrawText('Use UP or DOWN keys to choose easing for the y axis', 0, r.GetScreenHeight() - FONT_SIZE * 5, FONT_SIZE, r.LIGHTGRAY) 114 | 115 | // Draw ball 116 | r.DrawCircleV(ballPosition, 16.0, r.MAROON) 117 | 118 | r.EndDrawing() 119 | } 120 | 121 | r.CloseWindow() 122 | -------------------------------------------------------------------------------- /examples/raw/README.md: -------------------------------------------------------------------------------- 1 | # "Raw" testers 2 | 3 | We use the examples in here with partial versions of library to test new techniques for binding with raylib. 4 | 5 | It's not really useful outside of dev, but ideas in here should eventually make their way into the library. -------------------------------------------------------------------------------- /examples/raw/bunnymark.js: -------------------------------------------------------------------------------- 1 | // performance test of drawing a bunch of bunnies with current code 2 | 3 | const r = require('../../index.js') 4 | const { join } = require('node:path') 5 | 6 | const MAX_BUNNIES = 1000000 7 | 8 | const MAX_BATCH_ELEMENTS = 8192 9 | 10 | class Bunny { 11 | constructor () { 12 | this.pos = { 13 | x: Math.random() * screenWidth, 14 | y: Math.random() * screenHeight 15 | } 16 | 17 | this.speed = { 18 | x: (-250 + Math.random() * 500) / 60, 19 | y: (-250 + Math.random() * 500) / 60 20 | } 21 | 22 | this.color = r.Color( 23 | 80 + Math.random() * 170, 24 | 80 + Math.random() * 170, 25 | 80 + Math.random() * 170, 26 | 255 27 | ) 28 | } 29 | 30 | update () { 31 | this.pos.x += this.speed.x 32 | this.pos.y += this.speed.y 33 | 34 | if (this.pos.x > screenWidth || this.pos.x < 0) { this.speed.x = -this.speed.x } 35 | 36 | if (this.pos.y > screenHeight || this.pos.y < 0) { this.speed.y = -this.speed.y } 37 | } 38 | } 39 | 40 | const bunnies = [] 41 | 42 | const screenWidth = 800 43 | const screenHeight = 450 44 | 45 | r.InitWindow(screenWidth, screenHeight, 'raylib [textures] example - bunnymark') 46 | 47 | console.log('LOAD ', join(__dirname, 'wabbit_alpha.png')) 48 | const texBunny = r.LoadTexture(join(__dirname, 'wabbit_alpha.png').toString()) 49 | 50 | while (!r.WindowShouldClose()) { 51 | if (r.GetFPS() > 60) { 52 | for (let i = 0; i < 25; i++) { 53 | if (bunnies.length < MAX_BUNNIES) { 54 | bunnies.push(new Bunny()) 55 | } 56 | } 57 | } 58 | 59 | r.BeginDrawing() 60 | r.ClearBackground(r.RAYWHITE) 61 | for (const bunny of bunnies) { 62 | bunny.update() 63 | r.DrawTexture(texBunny, bunny.pos.x, bunny.pos.y, bunny.color) 64 | } 65 | r.DrawRectangle(0, 0, screenWidth, 40, r.BLACK) 66 | r.DrawText('bunnies: ' + bunnies.length, 120, 10, 20, r.GREEN) 67 | r.DrawText('batched draw calls: ' + (1 + bunnies.length / MAX_BATCH_ELEMENTS), 320, 10, 20, r.MAROON) 68 | r.DrawFPS(10, 10) 69 | r.EndDrawing() 70 | } 71 | 72 | r.UnloadTexture(texBunny) 73 | 74 | r.CloseWindow() 75 | -------------------------------------------------------------------------------- /examples/raw/wabbit_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/raw/wabbit_alpha.png -------------------------------------------------------------------------------- /examples/raygui/raygui_basic_window.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [raygui] example - Basic window 4 | * 5 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 6 | * 7 | * Copyright (c) 2022 Rob Loach (@RobLoach) 8 | * 9 | ********************************************************************************************/ 10 | const r = require('../../index.js') 11 | 12 | // Initialization 13 | // -------------------------------------------------------------------------------------- 14 | const screenWidth = 800 15 | const screenHeight = 450 16 | let showMessageBox = false 17 | let backgroundColor = r.RAYWHITE 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [raygui] example - basic window') 20 | 21 | r.SetTargetFPS(60) 22 | 23 | // r.GuiLoadStyleDefault() 24 | // -------------------------------------------------------------------------------------- 25 | 26 | // Main game loop 27 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 28 | // Update 29 | // ---------------------------------------------------------------------------------- 30 | // TODO: Update your variables here 31 | // ---------------------------------------------------------------------------------- 32 | 33 | // Draw 34 | // ---------------------------------------------------------------------------------- 35 | r.BeginDrawing() 36 | 37 | r.ClearBackground(backgroundColor) 38 | 39 | if (r.GuiButton(r.Rectangle(30, 100, 200, 30), 'Change Background Color')) { 40 | showMessageBox = true 41 | } 42 | 43 | if (showMessageBox) { 44 | switch (r.GuiMessageBox(r.Rectangle(r.GetScreenWidth() / 2 - 200, r.GetScreenHeight() / 2 - 50, 400, 100), 'Change Background Color', 'Do you really want to change the background?', 'Yes;No')) { 45 | case 0: 46 | case 2: 47 | showMessageBox = false 48 | break 49 | case 1: // Yes 50 | backgroundColor = r.Color( 51 | r.GetRandomValue(0, 255), 52 | r.GetRandomValue(0, 255), 53 | r.GetRandomValue(0, 255), 54 | 255 55 | ) 56 | showMessageBox = false 57 | } 58 | } 59 | 60 | r.EndDrawing() 61 | // ---------------------------------------------------------------------------------- 62 | } 63 | 64 | // De-Initialization 65 | // -------------------------------------------------------------------------------------- 66 | r.CloseWindow() // Close window and OpenGL context 67 | // -------------------------------------------------------------------------------------- 68 | -------------------------------------------------------------------------------- /examples/shaders/resources/generic.vert.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes 4 | in vec3 vertexPosition; 5 | in vec2 vertexTexCoord; 6 | in vec3 vertexNormal; 7 | in vec4 vertexColor; 8 | 9 | // Input uniform values 10 | uniform mat4 mvp; 11 | 12 | // Output vertex attributes (to fragment shader) 13 | out vec2 fragTexCoord; 14 | out vec4 fragColor; 15 | 16 | // NOTE: Add here your custom variables 17 | 18 | void main() 19 | { 20 | // Send vertex attributes to fragment shader 21 | fragTexCoord = vertexTexCoord; 22 | fragColor = vertexColor; 23 | 24 | // Calculate final vertex position 25 | gl_Position = mvp*vec4(vertexPosition, 1.0); 26 | } -------------------------------------------------------------------------------- /examples/shaders/resources/grayscaler.frag.glsl: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | 10 | // lets test the uniform I/O to manipulate the ramp 11 | uniform float dt; 12 | uniform vec3 weights; 13 | 14 | // Output fragment color 15 | out vec4 finalColor; 16 | 17 | void main() 18 | { 19 | // Texel color fetching from texture sampler 20 | vec4 texelColor = texture(texture0, fragTexCoord)*fragColor*dt; 21 | 22 | // Convert texel color to grayscale using NTSC conversion weights 23 | // float gray = dot(texelColor.rgb, vec3(0.299, 0.587, 0.114)); 24 | float gray = dot(texelColor.rgb, weights); 25 | 26 | // Calculate final fragment color 27 | finalColor = vec4(gray, gray, gray, texelColor.a); 28 | } -------------------------------------------------------------------------------- /examples/shaders/resources/raylib_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/shaders/resources/raylib_logo.png -------------------------------------------------------------------------------- /examples/shaders/shaders_and_uniforms.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [shaders] example - Drawing textures with an effective shader 4 | * 5 | ********************************************************************************************/ 6 | const r = require('../../index.js') 7 | const { readFileSync } = require('node:fs') 8 | const { resolve, join } = require('node:path') 9 | 10 | // Initialization 11 | // -------------------------------------------------------------------------------------- 12 | const screenWidth = 800 13 | const screenHeight = 450 14 | 15 | r.InitWindow(screenWidth, screenHeight, 'raylib [textures] example - texture loading and drawing') 16 | 17 | // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) 18 | const texture = r.LoadTexture(join(__dirname, 'resources', 'raylib_logo.png')) // Texture loading 19 | // --------------------------------------------------------------------------------------- 20 | 21 | const glGrayscaler = { 22 | vert: readFileSync(resolve(__dirname, 'resources', 'generic.vert.glsl'), 'utf-8'), 23 | frag: readFileSync(resolve(__dirname, 'resources', 'grayscaler.frag.glsl'), 'utf-8') 24 | } 25 | 26 | const shGrayscaler = r.LoadShaderCode(glGrayscaler.vert, glGrayscaler.frag) 27 | const ntscWeights = r.Vector3(0.299, 0.587, 0.114) 28 | 29 | if (!shGrayscaler) { 30 | console.error('[!] Failed to load shader!') 31 | process.exit(1) 32 | } else { 33 | shGrayscaler.dt = r.GetShaderLocation(shGrayscaler, 'dt') 34 | const uWeights = r.GetShaderLocation(shGrayscaler, 'weights') 35 | 36 | if (!shGrayscaler.dt || !uWeights) { 37 | console.error('[!] Failed to find ID for uniforms!') 38 | process.exit(1) 39 | } 40 | 41 | // this won't change during rendering 42 | r.SetShaderValueVector3(shGrayscaler, uWeights, ntscWeights) 43 | } 44 | 45 | // Main game loop 46 | let dt = 0.0 47 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 48 | // Update 49 | // ---------------------------------------------------------------------------------- 50 | // TODO: Update your variables here 51 | // ---------------------------------------------------------------------------------- 52 | 53 | // Draw 54 | // ---------------------------------------------------------------------------------- 55 | r.BeginDrawing() 56 | 57 | // calculate a new uniform value using a basic sine wave over time 58 | dt += r.GetFrameTime() 59 | const newUv = (Math.sin(dt) + 1) / 2 60 | r.SetShaderValueFloat(shGrayscaler, shGrayscaler.dt, newUv) 61 | 62 | r.ClearBackground(r.RAYWHITE) 63 | 64 | r.BeginShaderMode(shGrayscaler) 65 | r.DrawTexture(texture, screenWidth / 2 - texture.width / 2, screenHeight / 2 - texture.height / 2, r.WHITE) 66 | r.EndShaderMode() 67 | 68 | r.DrawText('this IS a texture!', 360, 370, 10, r.GRAY) 69 | 70 | r.EndDrawing() 71 | // ---------------------------------------------------------------------------------- 72 | } 73 | 74 | // De-Initialization 75 | // -------------------------------------------------------------------------------------- 76 | r.UnloadTexture(texture) // Texture unloading 77 | r.UnloadShader(shGrayscaler) 78 | 79 | r.CloseWindow() // Close window and OpenGL context 80 | // -------------------------------------------------------------------------------------- 81 | -------------------------------------------------------------------------------- /examples/shapes/shapes_basic_shapes.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [shapes] example - Draw basic shapes 2d (rectangle, circle, line...) 4 | * 5 | * This example has been created using raylib 1.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [shapes] example - basic shapes drawing') 20 | 21 | r.SetTargetFPS(60) 22 | // -------------------------------------------------------------------------------------- 23 | 24 | // Main game loop 25 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 26 | // Update 27 | // ---------------------------------------------------------------------------------- 28 | // TODO: Update your variables here 29 | // ---------------------------------------------------------------------------------- 30 | 31 | // Draw 32 | // ---------------------------------------------------------------------------------- 33 | r.BeginDrawing() 34 | 35 | r.ClearBackground(r.RAYWHITE) 36 | 37 | r.DrawText('some basic shapes available on raylib', 20, 20, 20, r.DARKGRAY) 38 | 39 | const position = { 40 | x: 100, 41 | y: 100 42 | } 43 | const size = { 44 | x: 200, 45 | y: 150 46 | } 47 | r.DrawRectangleV(position, size, r.DARKBLUE) 48 | 49 | r.DrawRectangleRec({ 50 | x: 50, 51 | y: 50, 52 | width: 50, 53 | height: 50 54 | }, r.PINK) 55 | 56 | /* 57 | DrawCircle(screenWidth/4, 120, 35, DARKBLUE); 58 | 59 | DrawRectangle(screenWidth/4*2 - 60, 100, 120, 60, RED); 60 | DrawRectangleLines(screenWidth/4*2 - 40, 320, 80, 60, ORANGE); // NOTE: Uses QUADS internally, not lines 61 | DrawRectangleGradientH(screenWidth/4*2 - 90, 170, 180, 130, MAROON, GOLD); 62 | 63 | DrawTriangle((Vector2){screenWidth/4*3, 80}, 64 | (Vector2){screenWidth/4*3 - 60, 150}, 65 | (Vector2){screenWidth/4*3 + 60, 150}, VIOLET); 66 | 67 | DrawPoly((Vector2){screenWidth/4*3, 320}, 6, 80, 0, BROWN); 68 | 69 | DrawCircleGradient(screenWidth/4, 220, 60, GREEN, SKYBLUE); 70 | 71 | // NOTE: We draw all LINES based shapes together to optimize internal drawing, 72 | // this way, all LINES are rendered in a single draw pass 73 | DrawLine(18, 42, screenWidth - 18, 42, BLACK); 74 | DrawCircleLines(screenWidth/4, 340, 80, DARKBLUE); 75 | DrawTriangleLines((Vector2){screenWidth/4*3, 160}, 76 | (Vector2){screenWidth/4*3 - 20, 230}, 77 | (Vector2){screenWidth/4*3 + 20, 230}, DARKBLUE); 78 | */ 79 | r.EndDrawing() 80 | // ---------------------------------------------------------------------------------- 81 | } 82 | 83 | // De-Initialization 84 | // -------------------------------------------------------------------------------------- 85 | r.CloseWindow() // Close window and OpenGL context 86 | // -------------------------------------------------------------------------------------- 87 | -------------------------------------------------------------------------------- /examples/shapes/shapes_easings_ball_anim.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [shapes] example - easings ball anim 4 | * 5 | * This example has been created using raylib 2.5 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014-2019 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [shapes] example - easings ball anim') 20 | 21 | // Ball variable value to be animated with easings 22 | let ballPositionX = -100 23 | let ballRadius = 20 24 | let ballAlpha = 0 25 | 26 | let state = 0 27 | let framesCounter = 0 28 | 29 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 30 | // -------------------------------------------------------------------------------------- 31 | 32 | // Main game loop 33 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 34 | // Update 35 | // ---------------------------------------------------------------------------------- 36 | if (state === 0) { // Move ball position X with easing 37 | framesCounter++ 38 | ballPositionX = r.EaseElasticOut(framesCounter, -100, screenWidth / 2.0 + 100, 120) 39 | 40 | if (framesCounter >= 120) { 41 | framesCounter = 0 42 | state = 1 43 | } 44 | } else if (state === 1) { // Increase ball radius with easing 45 | framesCounter++ 46 | ballRadius = r.EaseElasticIn(framesCounter, 20, 500, 200) 47 | 48 | if (framesCounter >= 200) { 49 | framesCounter = 0 50 | state = 2 51 | } 52 | } else if (state === 2) { // Change ball alpha with easing (background color blending) 53 | framesCounter++ 54 | ballAlpha = r.EaseCubicOut(framesCounter, 0.0, 1.0, 200) 55 | 56 | if (framesCounter >= 200) { 57 | framesCounter = 0 58 | state = 3 59 | } 60 | } else if (state === 3) { // Reset state to play again 61 | if (r.IsKeyPressed(r.KEY_ENTER)) { 62 | // Reset required variables to play again 63 | ballPositionX = -100 64 | ballRadius = 20 65 | ballAlpha = 0.0 66 | state = 0 67 | } 68 | } 69 | 70 | if (r.IsKeyPressed(r.KEY_R)) { 71 | framesCounter = 0 72 | } 73 | // ---------------------------------------------------------------------------------- 74 | 75 | // Draw 76 | // ---------------------------------------------------------------------------------- 77 | r.BeginDrawing() 78 | r.ClearBackground(r.RAYWHITE) 79 | 80 | if (state >= 2) { 81 | r.DrawRectangle(0, 0, screenWidth, screenHeight, r.GREEN) 82 | } 83 | 84 | r.DrawCircle(ballPositionX, 200, ballRadius, r.Fade(r.RED, 1.0 - ballAlpha)) 85 | 86 | if (state === 3) { 87 | r.DrawText('PRESS [ENTER] TO PLAY AGAIN!', 240, 200, 20, r.BLACK) 88 | } 89 | r.EndDrawing() 90 | // ---------------------------------------------------------------------------------- 91 | } 92 | 93 | // De-Initialization 94 | // -------------------------------------------------------------------------------------- 95 | r.CloseWindow() // Close window and OpenGL context 96 | // -------------------------------------------------------------------------------------- 97 | -------------------------------------------------------------------------------- /examples/shapes/shapes_logo_raylib.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [shapes] example - Draw raylib logo using basic shapes 4 | * 5 | * This example has been created using raylib 1.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | 14 | // Initialization 15 | // -------------------------------------------------------------------------------------- 16 | const screenWidth = 800 17 | const screenHeight = 450 18 | 19 | r.InitWindow(screenWidth, screenHeight, 'raylib [shapes] example - raylib logo using shapes') 20 | 21 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 22 | // -------------------------------------------------------------------------------------- 23 | 24 | // Main game loop 25 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 26 | // Update 27 | // ---------------------------------------------------------------------------------- 28 | // TODO: Update your variables here 29 | // ---------------------------------------------------------------------------------- 30 | const background = r.RAYWHITE 31 | const foreground = r.Color(64, 65, 55) 32 | const nodeGreen = r.Color(131, 205, 41) 33 | 34 | // Draw 35 | // ---------------------------------------------------------------------------------- 36 | r.BeginDrawing() 37 | 38 | r.ClearBackground(background) 39 | 40 | r.DrawRectangle(screenWidth / 2 - 128, screenHeight / 2 - 128, 256, 256, foreground) 41 | r.DrawRectangle(screenWidth / 2 - 112, screenHeight / 2 - 112, 224, 224, background) 42 | r.DrawText('raylib', screenWidth / 2 - 44, screenHeight / 2 + 48, 50, foreground) 43 | r.DrawText('node', screenWidth / 2 - 74, screenHeight / 2 + 18, 50, nodeGreen) 44 | 45 | r.DrawText('this is NOT a texture!', 350, 370, 10, foreground) 46 | 47 | r.EndDrawing() 48 | // ---------------------------------------------------------------------------------- 49 | } 50 | 51 | // De-Initialization 52 | // -------------------------------------------------------------------------------------- 53 | r.CloseWindow() // Close window and OpenGL context 54 | // -------------------------------------------------------------------------------------- 55 | -------------------------------------------------------------------------------- /examples/text/resources/pixantiqua.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/text/resources/pixantiqua.png -------------------------------------------------------------------------------- /examples/text/resources/pixantiqua.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/text/resources/pixantiqua.ttf -------------------------------------------------------------------------------- /examples/text/text_font_drawtextrec.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [text] font - DrawTextRec example 4 | * 5 | * This example has been created using raylib 3.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2021 - Rob Loach (@RobLoach), originally written by @gtrxAC 9 | * https://github.com/RobLoach/node-raylib/issues/85 10 | * 11 | ********************************************************************************************/ 12 | 13 | // Initialization 14 | // -------------------------------------------------------------------------------------- 15 | const r = require('../../index.js') 16 | 17 | const rec = r.Rectangle(100, 100, 500, 500) 18 | 19 | r.InitWindow(1024, 768, 'test') 20 | // -------------------------------------------------------------------------------------- 21 | 22 | // Main game loop 23 | while (!r.WindowShouldClose()) { 24 | // Draw 25 | // ---------------------------------------------------------------------------------- 26 | r.BeginDrawing() 27 | r.ClearBackground(r.BLACK) 28 | r.DrawRectangleLinesEx(rec, 1, r.RED) 29 | r.DrawTextRec(r.GetFontDefault(), 'hello world', rec, 10, 1, true, r.WHITE) 30 | r.EndDrawing() 31 | // ---------------------------------------------------------------------------------- 32 | } 33 | 34 | r.CloseWindow() // Close window and OpenGL context 35 | -------------------------------------------------------------------------------- /examples/text/text_font_loading.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [text] example - Font loading 4 | * 5 | * raylib can load fonts from multiple file formats: 6 | * 7 | * - TTF/OTF > Sprite font atlas is generated on loading, user can configure 8 | * some of the generation parameters (size, characters to include) 9 | * - BMFonts > Angel code font fileformat, sprite font image must be provided 10 | * together with the .fnt file, font generation cna not be configured 11 | * - XNA Spritefont > Sprite font image, following XNA Spritefont conventions, 12 | * Characters in image must follow some spacing and order rules 13 | * 14 | * This example has been created using raylib 2.6 (www.raylib.com) 15 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 16 | * 17 | * Copyright (c) 2016-2019 Ramon Santamaria (@raysan5) 18 | * 19 | ********************************************************************************************/ 20 | 21 | const r = require('../../index.js') 22 | const { join } = require('node:path') 23 | 24 | // Initialization 25 | // -------------------------------------------------------------------------------------- 26 | const screenWidth = 800 27 | const screenHeight = 450 28 | 29 | r.InitWindow(screenWidth, screenHeight, 'raylib [text] example - font loading') 30 | 31 | // Define characters to draw 32 | // NOTE: raylib supports UTF-8 encoding, following list is actually codified as UTF8 internally 33 | const msg = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHI\nJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmn\nopqrstuvwxyz{|}~¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓ\nÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷\nøùúûüýþÿ" 34 | 35 | // NOTE: Textures/Fonts MUST be loaded after Window initialization (OpenGL context is required) 36 | 37 | // BMFont (AngelCode) : Font data and image atlas have been generated using external program 38 | const fontBm = r.LoadFont(join(__dirname, 'resources', 'pixantiqua.fnt')) 39 | 40 | // TTF font : Font data and atlas are generated directly from TTF 41 | // NOTE: We define a font base size of 32 pixels tall and up-to 250 characters 42 | // TODO: Fix LoadFontEx 43 | // const fontTtf = r.LoadFontEx(__dirname + "/resources/pixantiqua.ttf", 32, 0, 250); 44 | 45 | let useTtf = false 46 | 47 | r.SetTargetFPS(60) // Set our game to run at 60 frames-per-second 48 | // -------------------------------------------------------------------------------------- 49 | 50 | // Main game loop 51 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 52 | // Update 53 | // ---------------------------------------------------------------------------------- 54 | useTtf = r.IsKeyDown(r.KEY_SPACE) 55 | // ---------------------------------------------------------------------------------- 56 | 57 | // Draw 58 | // ---------------------------------------------------------------------------------- 59 | r.BeginDrawing() 60 | 61 | r.ClearBackground(r.RAYWHITE) 62 | 63 | r.DrawText('Hold SPACE to use TTF generated font', 20, 20, 20, r.LIGHTGRAY) 64 | 65 | if (!useTtf) { 66 | r.DrawTextEx(fontBm, msg, r.Vector2(20, 100), fontBm.baseSize, 2, r.MAROON) 67 | r.DrawText('Using BMFont (Angelcode) imported', 20, r.GetScreenHeight() - 30, 20, r.GRAY) 68 | } else { 69 | // r.DrawTextEx(fontTtf, msg, r.Vector2(20, 100), fontTtf.baseSize, 2, r.LIME); 70 | r.DrawText('Using TTF font generated', 20, r.GetScreenHeight() - 30, 20, r.GRAY) 71 | } 72 | 73 | r.EndDrawing() 74 | // ---------------------------------------------------------------------------------- 75 | } 76 | 77 | // De-Initialization 78 | // -------------------------------------------------------------------------------------- 79 | r.UnloadFont(fontBm) // AngelCode Font unloading 80 | // r.UnloadFont(fontTtf); // TTF Font unloading 81 | 82 | r.CloseWindow() // Close window and OpenGL context 83 | // -------------------------------------------------------------------------------------- 84 | -------------------------------------------------------------------------------- /examples/textures/resources/raylib_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/textures/resources/raylib_logo.png -------------------------------------------------------------------------------- /examples/textures/resources/wabbit_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/examples/textures/resources/wabbit_alpha.png -------------------------------------------------------------------------------- /examples/textures/textures_bunnymark.js: -------------------------------------------------------------------------------- 1 | // performance test of drawing a bunch of bunnies with current code 2 | 3 | const r = require('../../index.js') 4 | const { join } = require('node:path') 5 | 6 | const MAX_BUNNIES = 1000000 7 | 8 | const MAX_BATCH_ELEMENTS = 8192 9 | 10 | function Bunny () { 11 | const col = r.Color(r.GetRandomValue(50, 240), r.GetRandomValue(80, 240), r.GetRandomValue(100, 240), 255) 12 | return { 13 | position: r.GetMousePosition(), 14 | speed: r.Vector2(r.GetRandomValue(-250, 250) / 60, r.GetRandomValue(-250, 250) / 60), 15 | color: col 16 | } 17 | } 18 | 19 | const bunnies = [] 20 | 21 | const screenWidth = 800 22 | const screenHeight = 450 23 | 24 | r.InitWindow(screenWidth, screenHeight, 'raylib [textures] example - bunnymark') 25 | 26 | const texBunny = r.LoadTexture(join(__dirname, '..', 'textures', 'resources', 'wabbit_alpha.png')) 27 | const hWidth = texBunny.width / 2 28 | const hHeight = texBunny.height / 2 29 | 30 | while (!r.WindowShouldClose()) { 31 | if (r.GetFPS() > 60) { 32 | for (let i = 0; i < 10; i++) { 33 | if (bunnies.length < MAX_BUNNIES) { 34 | bunnies.push(Bunny()) 35 | } 36 | } 37 | } 38 | 39 | r.BeginDrawing() 40 | r.ClearBackground(r.RAYWHITE) 41 | 42 | for (const bun of bunnies) { 43 | bun.position.x += bun.speed.x 44 | bun.position.y += bun.speed.y 45 | 46 | if (((bun.position.x + hWidth) > r.GetScreenWidth()) || 47 | ((bun.position.x + hWidth) < 0)) bun.speed.x *= -1 48 | if (((bun.position.y + hHeight) > r.GetScreenHeight()) || 49 | ((bun.position.y + hHeight - 40) < 0)) bun.speed.y *= -1 50 | 51 | r.DrawTexture(texBunny, bun.position.x, bun.position.y, bun.color) 52 | } 53 | 54 | r.DrawRectangle(0, 0, screenWidth, 40, r.BLACK) 55 | r.DrawText('bunnies: ' + bunnies.length, 120, 10, 20, r.GREEN) 56 | r.DrawText('batched draw calls: ' + (1 + bunnies.length / MAX_BATCH_ELEMENTS), 320, 10, 20, r.MAROON) 57 | r.DrawFPS(10, 10) 58 | r.EndDrawing() 59 | } 60 | 61 | r.UnloadTexture(texBunny) 62 | 63 | r.CloseWindow() 64 | -------------------------------------------------------------------------------- /examples/textures/textures_from_memory.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [textures] example - Image loading and texture creation from memory 4 | * 5 | * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) 6 | * 7 | * This example has been created using raylib 3.5 (www.raylib.com) 8 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | const { resolve } = require('node:path') 14 | const { readFileSync } = require('node:fs') 15 | 16 | // Initialization 17 | // -------------------------------------------------------------------------------------- 18 | const screenWidth = 800 19 | const screenHeight = 450 20 | 21 | // Load the image using fs module into a `Buffer` instance. 22 | // NOTE: this `Buffer` could come from *anywhere* (statically encoded in your source code, as a 23 | // partial read from a custom resource packer file, etc.) 24 | const raw = readFileSync(resolve(__dirname, 'resources', 'wabbit_alpha.png')) 25 | 26 | r.InitWindow(screenWidth, screenHeight, 'raylib [textures] example - image loading from memory') 27 | 28 | // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) 29 | const image = r.LoadImageFromMemory('png', raw, raw.length) // Loaded in CPU memory (RAM) 30 | if (!image) { 31 | console.error('image failed to load!') 32 | process.exit(1) 33 | } 34 | 35 | // TODO: Fix thrown exception 36 | // INFO: [/home/rob/Documents/node-raylib/examples/textures/resources/raylib_logo.png] Image loaded successfully (256x256) 37 | // terminate called after throwing an instance of 'char const*' 38 | // [1] 26364 abort (core dumped) bin/node-raylib examples/textures/textures_image_loading.js 39 | 40 | const texture = r.LoadTextureFromImage(image) // Image converted to texture, GPU memory (VRAM) 41 | 42 | r.UnloadImage(image) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM 43 | // --------------------------------------------------------------------------------------- 44 | 45 | // Main game loop 46 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 47 | // Update 48 | 49 | // Draw 50 | // ---------------------------------------------------------------------------------- 51 | r.BeginDrawing() 52 | 53 | r.ClearBackground(r.RAYWHITE) 54 | 55 | r.DrawTexture(texture, screenWidth / 2 - texture.width / 2, screenHeight / 2 - texture.height / 2, r.WHITE) 56 | 57 | r.DrawText('this IS a texture loaded from memory!', 300, 370, 10, r.GRAY) 58 | 59 | r.EndDrawing() 60 | // ---------------------------------------------------------------------------------- 61 | } 62 | 63 | // De-Initialization 64 | // -------------------------------------------------------------------------------------- 65 | r.UnloadTexture(texture) // Texture unloading 66 | 67 | r.CloseWindow() // Close window and OpenGL context 68 | // -------------------------------------------------------------------------------------- 69 | -------------------------------------------------------------------------------- /examples/textures/textures_image_loading.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [textures] example - Image loading and texture creation 4 | * 5 | * NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) 6 | * 7 | * This example has been created using raylib 1.3 (www.raylib.com) 8 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 9 | * 10 | * Copyright (c) 2015 Ramon Santamaria (@raysan5) 11 | * 12 | ********************************************************************************************/ 13 | 14 | const r = require('../../index.js') 15 | const { join } = require('node:path') 16 | 17 | // Initialization 18 | // -------------------------------------------------------------------------------------- 19 | const screenWidth = 800 20 | const screenHeight = 450 21 | 22 | r.InitWindow(screenWidth, screenHeight, 'raylib [textures] example - image loading') 23 | 24 | // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) 25 | const image = r.LoadImage(join(__dirname, 'resources', 'raylib_logo.png')) // Loaded in CPU memory (RAM) 26 | 27 | // TODO: Fix thrown exception 28 | // INFO: [/home/rob/Documents/node-raylib/examples/textures/resources/raylib_logo.png] Image loaded successfully (256x256) 29 | // terminate called after throwing an instance of 'char const*' 30 | // [1] 26364 abort (core dumped) bin/node-raylib examples/textures/textures_image_loading.js 31 | 32 | const texture = r.LoadTextureFromImage(image) // Image converted to texture, GPU memory (VRAM) 33 | 34 | r.UnloadImage(image) // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM 35 | // --------------------------------------------------------------------------------------- 36 | 37 | // Main game loop 38 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 39 | // Update 40 | // ---------------------------------------------------------------------------------- 41 | // TODO: Update your variables here 42 | // ---------------------------------------------------------------------------------- 43 | 44 | // Draw 45 | // ---------------------------------------------------------------------------------- 46 | r.BeginDrawing() 47 | 48 | r.ClearBackground(r.RAYWHITE) 49 | 50 | r.DrawTexture(texture, screenWidth / 2 - texture.width / 2, screenHeight / 2 - texture.height / 2, r.WHITE) 51 | 52 | r.DrawText('this IS a texture loaded from an image!', 300, 370, 10, r.GRAY) 53 | 54 | r.EndDrawing() 55 | // ---------------------------------------------------------------------------------- 56 | } 57 | 58 | // De-Initialization 59 | // -------------------------------------------------------------------------------------- 60 | r.UnloadTexture(texture) // Texture unloading 61 | 62 | r.CloseWindow() // Close window and OpenGL context 63 | // -------------------------------------------------------------------------------------- 64 | -------------------------------------------------------------------------------- /examples/textures/textures_logo_raylib.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [textures] example - Texture loading and drawing 4 | * 5 | * This example has been created using raylib 1.0 (www.raylib.com) 6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 7 | * 8 | * Copyright (c) 2014 Ramon Santamaria (@raysan5) 9 | * 10 | ********************************************************************************************/ 11 | 12 | const r = require('../../index.js') 13 | const { join } = require('node:path') 14 | 15 | // Initialization 16 | // -------------------------------------------------------------------------------------- 17 | const screenWidth = 800 18 | const screenHeight = 450 19 | 20 | r.InitWindow(screenWidth, screenHeight, 'raylib [textures] example - texture loading and drawing') 21 | 22 | // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) 23 | const texture = r.LoadTexture(join(__dirname, 'resources', 'raylib_logo.png')) // Texture loading 24 | // --------------------------------------------------------------------------------------- 25 | 26 | // Main game loop 27 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 28 | // Update 29 | // ---------------------------------------------------------------------------------- 30 | // TODO: Update your variables here 31 | // ---------------------------------------------------------------------------------- 32 | 33 | // Draw 34 | // ---------------------------------------------------------------------------------- 35 | r.BeginDrawing() 36 | 37 | r.ClearBackground(r.RAYWHITE) 38 | 39 | r.DrawTexture(texture, screenWidth / 2 - texture.width / 2, screenHeight / 2 - texture.height / 2, r.WHITE) 40 | 41 | r.DrawText('this IS a texture!', 360, 370, 10, r.GRAY) 42 | 43 | r.EndDrawing() 44 | // ---------------------------------------------------------------------------------- 45 | } 46 | 47 | // De-Initialization 48 | // -------------------------------------------------------------------------------------- 49 | r.UnloadTexture(texture) // Texture unloading 50 | 51 | r.CloseWindow() // Close window and OpenGL context 52 | // -------------------------------------------------------------------------------------- 53 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * node-raylib 3 | * 4 | * https://github.com/robloach/node-raylib 5 | */ 6 | 7 | const raylib = require('./src/generated/node-raylib') 8 | const { format } = require('node:util') 9 | 10 | // Constants 11 | raylib.MAX_GAMEPADS = 4 12 | raylib.MAX_GAMEPAD_AXIS = 8 13 | raylib.MAX_GAMEPAD_BUTTONS = 32 14 | raylib.MAX_TOUCH_POINTS = 10 15 | raylib.MAX_KEY_PRESSED_QUEUE = 16 16 | raylib.DEG2RAD = Math.PI / 180 17 | 18 | // Wrapped Functions 19 | 20 | /** 21 | * Text formatting with variables (sprintf style) 22 | */ 23 | raylib.TextFormat = format 24 | 25 | /** 26 | * Define one vertex (color) - 4 byte 27 | * @param {number} r 28 | * @param {number} g 29 | * @param {number} b 30 | * @param {number} a 31 | */ 32 | raylib.rlColor4ub = (r, g, b, a) => { 33 | // workaround as the C addon version isn't compiling? 34 | raylib.rlColor4f(r / 255, g / 255, b / 255, a / 255) 35 | } 36 | 37 | // Export the bindings for the module. 38 | module.exports = raylib 39 | -------------------------------------------------------------------------------- /logo/raylib-node_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/logo/raylib-node_256x256.png -------------------------------------------------------------------------------- /man/node-raylib.1: -------------------------------------------------------------------------------- 1 | .TH NODE-RAYLIB 1 2 | .SH NAME 3 | node-raylib \- run a raylib application written in Node.js 4 | .SH SYNOPSIS 5 | .B node-raylib 6 | .IR file 7 | .SH DESCRIPTION 8 | .B node-raylib 9 | runs the given Node.js application with raylib. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raylib", 3 | "version": "0.20.0", 4 | "description": "Node.js bindings for raylib 5.5", 5 | "main": "index.js", 6 | "types": "src/generated/node-raylib.d.ts", 7 | "scripts": { 8 | "start": "bin/node-raylib examples/core/core_basic_window.js", 9 | "benchmark": "bin/node-raylib examples/textures/textures_bunnymark.js", 10 | "test": "vitest run --globals --reporter verbose", 11 | "test:watch": "vitest --globals --ui", 12 | "posttest": "standard tools test examples index.js", 13 | "test:fix": "standard --fix", 14 | "postinstall": "node tools/postinstall.js || npm run compile", 15 | "clean": "rm -rf build", 16 | "precompile": "npm i --no-save node-addon-api cmake-js", 17 | "compile": "cmake-js compile", 18 | "precompile-drm": "npm i --no-save node-addon-api cmake-js", 19 | "compile-drm": "cmake-js compile --CDPLATFORM=DRM", 20 | "prepkg": "npm run build", 21 | "pkg": "node tools/pkg.js", 22 | "gen:code": "node tools/generate.js", 23 | "gen:docs": "jsdoc2md > docs/API.md" 24 | }, 25 | "repository": "RobLoach/node-raylib", 26 | "engines": { 27 | "node": ">=10" 28 | }, 29 | "bin": "./bin/node-raylib", 30 | "man": "./man/node-raylib.1", 31 | "keywords": [ 32 | "raylib" 33 | ], 34 | "author": { 35 | "name": "Rob Loach", 36 | "url": "https://robloach.net" 37 | }, 38 | "contributors": [ 39 | { 40 | "name": "konsumer", 41 | "url": "https://keybase.io/konsumer" 42 | }, 43 | { 44 | "name": "twuky", 45 | "url": "http://tuckie.zone" 46 | }, 47 | { 48 | "name": "The 99's Puppycat", 49 | "url": "https://github.com/huyhoang160593" 50 | } 51 | ], 52 | "license": "Zlib", 53 | "files": [ 54 | "bin/*", 55 | "man/node-raylib.1", 56 | "src/*", 57 | "src/generated/*", 58 | "CMakeLists.txt", 59 | "index.js", 60 | "CHANGELOG.md", 61 | "tools/postinstall.js", 62 | "drm/*" 63 | ], 64 | "bugs": { 65 | "url": "https://github.com/RobLoach/node-raylib/issues" 66 | }, 67 | "homepage": "https://github.com/RobLoach/node-raylib", 68 | "dependencies": { 69 | "cross-fetch": "^3.1.5", 70 | "module-alias": "^2.2.2" 71 | }, 72 | "devDependencies": { 73 | "@raylib/api": "5.5.0", 74 | "@vitest/ui": "^0.29.7", 75 | "archiver": "^5.3.1", 76 | "jsdoc-to-markdown": "^8.0.0", 77 | "pkg": "^5.8.1", 78 | "standard": "^17.0.0", 79 | "vitest": "^0.29.7" 80 | }, 81 | "jsdoc2md": { 82 | "files": [ 83 | "src/generated/node-raylib.js" 84 | ] 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /templates/simple_game/README.md: -------------------------------------------------------------------------------- 1 | # node-raylib Simple Game 2 | 3 | This is an example of a simple game. 4 | 5 | ## Usage 6 | 7 | ``` 8 | npm i 9 | npm start 10 | ``` 11 | -------------------------------------------------------------------------------- /templates/simple_game/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raylib-simple-game", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "simple_game.js", 7 | "scripts": { 8 | "start": "node simple_game.js" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "dependencies": { 13 | "raylib": "*" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /templates/simple_game/simple_game.js: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib - Simple Game template 4 | * 5 | * 6 | * 7 | * 8 | * This game has been created using raylib (www.raylib.com) 9 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 10 | * 11 | * Copyright (c) 2014-2018 Ramon Santamaria (@raysan5) 12 | * 13 | ********************************************************************************************/ 14 | 15 | const r = require('raylib') 16 | 17 | // Initialization (Note windowTitle is unused on Android) 18 | // -------------------------------------------------------------------------------------- 19 | const screenWidth = 800 20 | const screenHeight = 450 21 | 22 | r.InitWindow(screenWidth, screenHeight, 'raylib template - simple game') 23 | let currentScreen = 'LOGO' 24 | 25 | // TODO: Initialize all required variables and load all required data here! 26 | 27 | let framesCounter = 0 // Useful to count frames 28 | 29 | r.SetTargetFPS(60) // Set desired framerate (frames-per-second) 30 | // -------------------------------------------------------------------------------------- 31 | 32 | // Main game loop 33 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 34 | // Update 35 | // ---------------------------------------------------------------------------------- 36 | switch (currentScreen) { 37 | case 'LOGO': 38 | // TODO: Update LOGO screen variables here! 39 | 40 | framesCounter++ // Count frames 41 | 42 | // Wait for 2 seconds (120 frames) before jumping to TITLE screen 43 | if (framesCounter > 120) { 44 | currentScreen = 'TITLE' 45 | } 46 | break 47 | case 'TITLE': 48 | // TODO: Update TITLE screen variables here! 49 | 50 | // Press enter to change to GAMEPLAY screen 51 | if (r.IsKeyPressed(r.KEY_ENTER) || r.IsGestureDetected(r.GESTURE_TAP)) { 52 | currentScreen = 'GAMEPLAY' 53 | } 54 | break 55 | case 'GAMEPLAY': 56 | // TODO: Update GAMEPLAY screen variables here! 57 | 58 | // Press enter to change to ENDING screen 59 | if (r.IsKeyPressed(r.KEY_ENTER) || r.IsGestureDetected(r.GESTURE_TAP)) { 60 | currentScreen = 'ENDING' 61 | } 62 | break 63 | case 'ENDING': 64 | // TODO: Update ENDING screen variables here! 65 | 66 | // Press enter to return to TITLE screen 67 | if (r.IsKeyPressed(r.KEY_ENTER) || r.IsGestureDetected(r.GESTURE_TAP)) { 68 | currentScreen = 'TITLE' 69 | } 70 | break 71 | default: break 72 | } 73 | // ---------------------------------------------------------------------------------- 74 | 75 | // Draw 76 | // ---------------------------------------------------------------------------------- 77 | r.BeginDrawing() 78 | 79 | r.ClearBackground(r.RAYWHITE) 80 | 81 | switch (currentScreen) { 82 | case 'LOGO': 83 | // TODO: Draw LOGO screen here! 84 | r.DrawText('LOGO SCREEN', 20, 20, 40, r.LIGHTGRAY) 85 | r.DrawText('WAIT for 2 SECONDS...', 290, 220, 20, r.GRAY) 86 | break 87 | case 'TITLE': 88 | // TODO: Draw TITLE screen here! 89 | r.DrawRectangle(0, 0, screenWidth, screenHeight, r.GREEN) 90 | r.DrawText('TITLE SCREEN', 20, 20, 40, r.DARKGREEN) 91 | r.DrawText('PRESS ENTER or TAP to JUMP to GAMEPLAY SCREEN', 120, 220, 20, r.DARKGREEN) 92 | break 93 | case 'GAMEPLAY': 94 | // TODO: Draw GAMEPLAY screen here! 95 | r.DrawRectangle(0, 0, screenWidth, screenHeight, r.PURPLE) 96 | r.DrawText('GAMEPLAY SCREEN', 20, 20, 40, r.MAROON) 97 | r.DrawText('PRESS ENTER or TAP to JUMP to ENDING SCREEN', 130, 220, 20, r.MAROON) 98 | break 99 | case 'ENDING': 100 | // TODO: Draw ENDING screen here! 101 | r.DrawRectangle(0, 0, screenWidth, screenHeight, r.BLUE) 102 | r.DrawText('ENDING SCREEN', 20, 20, 40, r.DARKBLUE) 103 | r.DrawText('PRESS ENTER or TAP to RETURN to TITLE SCREEN', 120, 220, 20, r.DARKBLUE) 104 | break 105 | } 106 | 107 | r.EndDrawing() 108 | // ---------------------------------------------------------------------------------- 109 | } 110 | 111 | // De-Initialization 112 | // -------------------------------------------------------------------------------------- 113 | 114 | // TODO: Unload all loaded data (textures, fonts, audio) here! 115 | 116 | r.CloseWindow() // Close window and OpenGL context 117 | // -------------------------------------------------------------------------------------- 118 | -------------------------------------------------------------------------------- /templates/typescript_game/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | -------------------------------------------------------------------------------- /templates/typescript_game/README.md: -------------------------------------------------------------------------------- 1 | # node-raylib TypeScript Game 2 | 3 | This is an example of using TypeScript with 4 | node-raylib. 5 | 6 | ## Usage 7 | 8 | ``` 9 | npm i 10 | npm start 11 | ``` 12 | -------------------------------------------------------------------------------- /templates/typescript_game/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raylib-typescript-game", 3 | "description": "Example of using raylib with TypeScript.", 4 | "private": true, 5 | "version": "1.0.0", 6 | "main": "typescript_game.js", 7 | "scripts": { 8 | "build": "tsc typescript_game.ts", 9 | "prestart": "npm run build", 10 | "start": "node typescript_game.js" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "raylib": "*", 16 | "typescript": "^3.8.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /templates/typescript_game/typescript_game.ts: -------------------------------------------------------------------------------- 1 | /******************************************************************************************* 2 | * 3 | * raylib [core] example - Basic window 4 | * 5 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) 6 | * 7 | * Copyright (c) 2018 Rob Loach (@RobLoach) 8 | * 9 | ********************************************************************************************/ 10 | 11 | import * as r from "raylib" 12 | 13 | // Initialization 14 | //-------------------------------------------------------------------------------------- 15 | const screenWidth: number = 800 16 | const screenHeight: number = 450 17 | 18 | r.InitWindow(screenWidth, screenHeight, "raylib [core] example - typescript game") 19 | 20 | r.SetTargetFPS(60) 21 | //-------------------------------------------------------------------------------------- 22 | 23 | // Main game loop 24 | while (!r.WindowShouldClose()) { // Detect window close button or ESC key 25 | // Update 26 | //---------------------------------------------------------------------------------- 27 | // TODO: Update your variables here 28 | //---------------------------------------------------------------------------------- 29 | 30 | // Draw 31 | //---------------------------------------------------------------------------------- 32 | r.BeginDrawing() 33 | 34 | r.ClearBackground(r.RAYWHITE) 35 | 36 | r.DrawText("Congrats! You created your first window!", 190, 200, 20, r.LIGHTGRAY) 37 | 38 | r.EndDrawing() 39 | //---------------------------------------------------------------------------------- 40 | } 41 | 42 | // De-Initialization 43 | //-------------------------------------------------------------------------------------- 44 | r.CloseWindow() // Close window and OpenGL context 45 | //-------------------------------------------------------------------------------------- 46 | -------------------------------------------------------------------------------- /test/node-raylib.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect */ 2 | 3 | const { execFileSync } = require('child_process') 4 | const path = require('path') 5 | const r = require('..') 6 | const pkg = require('../package.json') 7 | 8 | r.SetTraceLogLevel(r.LOG_WARNING) 9 | 10 | describe('raylib', () => { 11 | describe('window', () => { 12 | it('IsWindowReady() false before initializing window', () => { 13 | expect(r.IsWindowReady()).toBeFalsy() 14 | }) 15 | }) 16 | 17 | describe('audio', () => { 18 | it('IsAudioDeviceReady()', () => { 19 | expect(r.IsAudioDeviceReady()).toBeFalsy() 20 | }) 21 | }) 22 | 23 | describe('TextFormat', () => { 24 | it('TextFormat()', () => { 25 | const str = 'Hello, %s! Number %i' 26 | const num = 5 27 | expect(r.TextFormat(str, 'Ray', num)).toEqual('Hello, Ray! Number 5') 28 | }) 29 | }) 30 | 31 | describe('file', () => { 32 | it('FileExists()', () => { 33 | expect(r.FileExists(path.join(__dirname, '..', 'package.json'))).toBeTruthy() 34 | }) 35 | 36 | it('IsFileExtension()', () => { 37 | expect(r.IsFileExtension('something.txt', '.txt')).toBeTruthy() 38 | expect(r.IsFileExtension('something.txt', '.md')).toBeFalsy() 39 | }) 40 | }) 41 | 42 | describe('enums', () => { 43 | it('KEY_A', () => { 44 | expect(r.KEY_A).toBe(65) 45 | }) 46 | it('PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA', () => { 47 | expect(r.PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA).toBe(20) 48 | }) 49 | }) 50 | 51 | describe('color', () => { 52 | it('GetColor()', () => { 53 | const colorInt = r.ColorToInt(r.DARKPURPLE) 54 | expect(typeof colorInt).toBe('number') 55 | const colorPurple = r.GetColor(colorInt) 56 | expect(colorPurple).toHaveProperty('r') 57 | expect(colorPurple).toHaveProperty('g') 58 | expect(colorPurple).toHaveProperty('b') 59 | expect(colorPurple).toHaveProperty('a') 60 | expect(colorPurple.g).toBe(r.DARKPURPLE.g) 61 | }) 62 | }) 63 | 64 | describe('camera', () => { 65 | it('Camera()', () => { 66 | const camera = r.Camera( 67 | r.Vector3(5, 4, 5), 68 | r.Vector3(0, 2, 0), 69 | r.Vector3(0, 1, 0), 70 | 45, 71 | r.CAMERA_PERSPECTIVE) 72 | expect(camera.position.x).toEqual(5) 73 | 74 | const matrix = r.GetCameraMatrix(camera) 75 | expect(matrix.m2).toBeGreaterThan(0.6) 76 | expect(matrix.m2).toBeLessThan(0.8) 77 | }) 78 | }) 79 | 80 | describe('image', () => { 81 | it('should load images correctly', () => { 82 | const rabbit = path.join(__dirname, 'resources', 'rabbit.png') 83 | const image = r.LoadImage(rabbit) 84 | expect(image).not.toBeNull() 85 | const imageWidth = image.width 86 | expect(imageWidth).toBe(32) 87 | r.UnloadImage(image) 88 | }) 89 | it('should correctly use wrapped image manipulation functions', () => { 90 | const rabbit = path.join(__dirname, 'resources', 'rabbit.png') 91 | const image = r.LoadImage(rabbit) 92 | const initialWidth = image.width 93 | expect(initialWidth).toBe(32) 94 | 95 | // Rotate the image 96 | r.ImageRotateCW(image) 97 | const modifiedWidth = image.width 98 | expect(modifiedWidth).toBe(42) 99 | r.UnloadImage(image) 100 | }) 101 | it('should return empty object when failing to load', () => { 102 | const missingImage = 'missingImage.png' 103 | const image = r.LoadImage(missingImage) 104 | expect(image.data).toBe(0) 105 | }) 106 | }) 107 | 108 | describe('easings', () => { 109 | it('EaseLinearOut()', () => { 110 | const t = 10 111 | const b = 20 112 | const c = 30 113 | const d = 40 114 | const out = r.EaseLinearOut(t, b, c, d) 115 | expect(out).toBe(c * t / d + b) 116 | }) 117 | }) 118 | }) 119 | 120 | // these are not correct tests on windows 121 | if (process.platform !== 'win32') { 122 | describe('cli', () => { 123 | const cliPath = path.join(__dirname, '..', 'bin', 'node-raylib') 124 | 125 | it('should execute on a script', () => { 126 | const out = execFileSync(cliPath, [ 127 | path.join(__dirname, 'resources', 'node-raylib-test.js') 128 | ]) 129 | expect(out.toString()).toContain('Test runner') 130 | }) 131 | 132 | it('should display the help', () => { 133 | let output = '' 134 | try { // we expect an error here - jest fails tests when any errors are thrown 135 | execFileSync(cliPath, ['--help']).toString() 136 | } catch (e) { 137 | output = e.toString() 138 | } 139 | 140 | expect(output).toContain(pkg.description) 141 | }) 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /test/resources/node-raylib-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Tests for the node-raylib CLI tool. 3 | */ 4 | 5 | const r = require('raylib') 6 | const assert = require('node:assert') 7 | 8 | console.log('node-raylib-test: Test runner for node-raylib CLI') 9 | 10 | // Tests 11 | assert(!r.IsWindowReady(), 'r.IsWindowReady') 12 | -------------------------------------------------------------------------------- /test/resources/rabbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobLoach/node-raylib/f1c2f075bcb5b7fba397fccbe7c09386b49b98bf/test/resources/rabbit.png -------------------------------------------------------------------------------- /tools/crossbuild-drm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this is used inside a docker (running with qemu binfmt) to easily crossbuild project 4 | # run with docker run --platform linux/arm64 -it --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild.sh 5 | 6 | apt-get update 7 | apt-get install -y xorg-dev libglu1-mesa-dev cmake libdrm-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev 8 | # npm ci 9 | # rm -r build 10 | npm run compile-drm -------------------------------------------------------------------------------- /tools/crossbuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this is used inside a docker (running with qemu binfmt) to easily crossbuild project 4 | # run with docker run --platform linux/arm64 -it --rm -v "${PWD}:/work" -w /work node ./tools/crossbuild.sh 5 | 6 | apt-get update 7 | apt-get install -y xorg-dev libglu1-mesa-dev cmake 8 | npm run compile -------------------------------------------------------------------------------- /tools/generate.js: -------------------------------------------------------------------------------- 1 | // this will generate the wrappers & type-adapters in src/generated 2 | 3 | const { writeFileSync } = require("node:fs"); 4 | const raylibApi = require("@raylib/api"); 5 | const path = require("node:path"); 6 | 7 | // use this to keep from wrapping things 8 | const blocklist = [ 9 | // Error: invalid conversion from ‘void (*)(int, const char*, ...)’ to ‘void (*)()’ [-fpermissive] 10 | "TraceLog", 11 | "TextFormat", 12 | 13 | // Edge case: need additional wrapping to function 14 | "SetShaderValue", 15 | "SetShaderValueV", 16 | 17 | // Callbacks: Not supported yet 18 | "SetTraceLogCallback", 19 | "SetLoadFileDataCallback", 20 | "SetSaveFileDataCallback", 21 | "SetLoadFileTextCallback", 22 | "SetSaveFileTextCallback", 23 | 24 | "AudioCallback", 25 | "AttachAudioMixedProcessor", 26 | "DetachAudioMixedProcessor", 27 | 28 | // Callbacks: AudioStream 29 | "SetAudioStreamCallback", 30 | "AttachAudioStreamProcessor", 31 | "DetachAudioStreamProcessor", 32 | 33 | // Structs 34 | // @todo Verify Structs that have array properties 35 | "VrStereoConfig", // Matrix[2], float[2] 36 | "VrDeviceInfo", // float[4] 37 | "Material", // float[4] 38 | 39 | // raymath 40 | // @todo Fix helper structs for float arrays 41 | "Vector3ToFloatV", // float3 42 | "MatrixToFloatV", // float16 43 | 44 | // raygui 45 | "UnloadGuiStyle", // Commented out 46 | "LoadGuiStyle", // Commented out 47 | 48 | // rlgl 49 | "rlEnableStatePointer", // undefined 50 | "rlDisableStatePointer", // undefined 51 | ]; 52 | 53 | // these functions expect the first argument to be passed as a reference in C++ 54 | // so some extra wrapping needs to be done to return updated values from C++ to JS 55 | const byreflist = [ 56 | "UpdateCamera", 57 | "ImageFormat", 58 | "ImageToPOT", 59 | "ImageCrop", 60 | "ImageAlphaCrop", 61 | "ImageAlphaClear", 62 | "ImageAlphaMask", 63 | "ImageAlphaPremultiply", 64 | "ImageResize", 65 | "ImageResizeNN", 66 | "ImageResizeCanvas", 67 | "ImageMipmaps", 68 | "ImageDither", 69 | "ImageFlipVertical", 70 | "ImageFlipHorizontal", 71 | "ImageRotateCW", 72 | "ImageRotateCCW", 73 | "ImageColorTint", 74 | "ImageColorInvert", 75 | "ImageColorGrayscale", 76 | "ImageColorContrast", 77 | "ImageColorBrightness", 78 | "ImageColorReplace", 79 | "ImageClearBackground", 80 | "ImageDrawPixel", 81 | "ImageDrawPixelV", 82 | "ImageDrawLine", 83 | "ImageDrawLineV", 84 | "ImageDrawCircle", 85 | "ImageDrawCircleV", 86 | "ImageDrawRectangle", 87 | "ImageDrawRectangleV", 88 | "ImageDrawRectangleRec", 89 | "ImageDrawRectangleLines", 90 | "ImageDraw", 91 | "ImageDrawText", 92 | "ImageDrawTextEx", 93 | "GenTextureMipmaps", 94 | "GenTextureMipmaps", 95 | "UploadMesh", 96 | "GenMeshTangents", 97 | "GenMeshBinormals", 98 | "SetMaterialTexture", 99 | "SetModelMeshMaterial", 100 | "WaveFormat", 101 | "WaveCrop", 102 | ]; 103 | 104 | // these are aliased types, so you use the resolved-type 105 | const typeAliases = { 106 | Quaternion: "Vector4", 107 | RenderTexture2D: "Texture", 108 | Texture2D: "Texture", 109 | Camera: "Camera3D", 110 | }; 111 | 112 | // used to find array-size in defs 113 | const rSize = /\[([0-9]+)\]/g; 114 | 115 | // pre-process the data for later analysis 116 | function getDefs() { 117 | const { structs, enums, functions } = raylibApi.raylib; 118 | for (const struct of structs) { 119 | // take multi-fields (like in Matrix) and make them all distinct fields 120 | 121 | // temporary fix for building on Mac/Win? Wonder why this is necessary 122 | if (struct.name === "BoneInfo") { 123 | struct.fields[0].type = "char"; 124 | struct.fields[1].type = "char"; 125 | } 126 | 127 | let newfields = []; 128 | for (const i in struct.fields) { 129 | const field = struct.fields[i]; 130 | 131 | if (field.name.includes(",")) { 132 | newfields = [ 133 | ...newfields, 134 | ...field.name.split(",").map((n) => { 135 | return { 136 | ...field, 137 | name: n.trim(), 138 | }; 139 | }), 140 | ].sort((a, b) => a.name.match(/\d+/)[0] - b.name.match(/\d+/)[0]); 141 | } else { 142 | newfields.push(field); 143 | } 144 | } 145 | struct.fields = newfields; 146 | 147 | // find all arrays in structs, and give all fields a size and stripped name for later 148 | for (const field of struct.fields) { 149 | const n = [...field.name.matchAll(rSize)]; 150 | if (n.length) { 151 | field.size = Number.parseInt(n[0][1]); 152 | field.name = field.name.replace(rSize, ""); 153 | } else { 154 | field.size = 1; 155 | } 156 | const type = field.type.replace(/[* ]+/g, ""); 157 | if (typeAliases[type]) { 158 | field.type = typeAliases[type]; 159 | } 160 | } 161 | 162 | // TODO: should I also process *-refs to seperate name & the fact it's a ref? 163 | } 164 | 165 | // aliases 166 | // structs.push({ ...structs.find(s => s.name === 'Vector4'), name: 'Quaternion' }) 167 | 168 | // XXX: Since array support isn't complete, just filter out all structs & functions that use them, 169 | // so we get an (incomplete) wrapper that will build. 170 | 171 | for (const struct of structs) { 172 | const usesArray = struct.fields.find((f) => f.size !== 1); 173 | if (usesArray) { 174 | blocklist.push(struct.name); 175 | } 176 | } 177 | 178 | // filter out all functions that use blocked types 179 | for (const f of functions) { 180 | if (blocklist.includes(f.returnType.replace(/[* ]/g, ""))) { 181 | blocklist.push(f.name); 182 | } else { 183 | for (const param of f.params || []) { 184 | if (blocklist.includes(param.type.replace(/[* ]/g, ""))) { 185 | blocklist.push(f.name); 186 | break; 187 | } 188 | } 189 | } 190 | } 191 | 192 | // Add the Easings API 193 | const reasings = raylibApi.reasings; 194 | functions.push(...reasings.functions); 195 | 196 | // Add Raymath 197 | const raymath = raylibApi.raymath; 198 | functions.push(...raymath.functions); 199 | 200 | // Add Raygui 201 | const raygui = raylibApi.raygui; 202 | functions.push(...raygui.functions); 203 | enums.push(...raygui.enums); 204 | 205 | const rlgl = raylibApi.rlgl; 206 | functions.push(...rlgl.functions); 207 | enums.push(...rlgl.enums); 208 | const tmp = { 209 | name: "RLGL Defines", 210 | description: "", 211 | values: [], 212 | }; 213 | for (const define of rlgl.defines) { 214 | if (define.type === "INT") { 215 | tmp.values.push({ 216 | name: define.name, 217 | description: define.description, 218 | value: define.value, 219 | }); 220 | } 221 | } 222 | enums.push(tmp); 223 | for (const struct of rlgl.structs) { 224 | if (struct.name === "rlVertexBuffer") { 225 | struct.fields.splice(4, 6); 226 | struct.fields.splice(4, 0, { 227 | description: 228 | "Vertex indices (in case vertex data comes indexed) (6 indices per quad)", 229 | name: "indices", 230 | type: "unsigned int *", 231 | }); 232 | } 233 | } 234 | structs.push( 235 | ...rlgl.structs.filter((s) => { 236 | return s.name !== "Matrix"; 237 | }) 238 | ); 239 | 240 | return { structs, enums, functions }; 241 | } 242 | 243 | const { structs, enums, functions } = getDefs(); 244 | const GenBindings = require("./generate_templates/node-raylib-bindings.js"); 245 | const GenWrapper = require("./generate_templates/node-raylib-wrapper.js"); 246 | const GenTSDefs = require("./generate_templates/node-raylib-definitions.js"); 247 | const GenDRMWrapper = GenWrapper({ 248 | enums, 249 | blocklist, 250 | functions, 251 | structs, 252 | byreflist, 253 | }).replace("node-raylib.node", "node-raylib-drm.node"); 254 | writeFileSync( 255 | path.join(__dirname, "..", "src", "generated", "node-raylib.cc"), 256 | GenBindings({ enums, blocklist, functions, structs, byreflist }) 257 | ); 258 | writeFileSync( 259 | path.join(__dirname, "..", "src", "generated", "node-raylib.js"), 260 | GenWrapper({ enums, blocklist, functions, structs, byreflist }) 261 | ); 262 | writeFileSync( 263 | path.join(__dirname, "..", "src", "generated", "node-raylib.d.ts"), 264 | GenTSDefs({ enums, blocklist, functions, structs, byreflist }) 265 | ); 266 | writeFileSync( 267 | path.join(__dirname, "..", "src", "generated", "node-raylib-drm.js"), 268 | GenDRMWrapper 269 | ); 270 | -------------------------------------------------------------------------------- /tools/generate_templates/ArgumentTypeConversion.js: -------------------------------------------------------------------------------- 1 | function ArgumentTypeConversion (arg) { 2 | if (arg === 'const unsigned char *') { return 'Buffer' } 3 | if (arg === 'unsigned char *') { return 'Buffer' } 4 | 5 | if (arg === 'char') { return 'string' } 6 | if (arg === 'char *') { return 'string' } 7 | if (arg === 'const char *') { return 'string' } 8 | if (arg === 'char[32]') { return 'string' } 9 | 10 | if (arg === 'int') { return 'number' } 11 | if (arg === 'float') { return 'number' } 12 | if (arg === 'unsigned int') { return 'number' } 13 | if (arg === 'unsigned char') { return 'number' } 14 | if (arg === 'long') { return 'number' } 15 | if (arg === 'double') { return 'number' } 16 | if (arg === 'unsigned long long') { return 'BigInt' } 17 | 18 | if (arg === 'bool') { return 'boolean' } 19 | 20 | // pointers 21 | arg = arg.replace('const ', '') 22 | if (arg.includes('*')) { 23 | return 'number' 24 | } 25 | // float[2], float[4] etc. should be converted to pointers as well 26 | if (arg.includes(']')) { 27 | return 'number' 28 | } 29 | 30 | return arg 31 | } 32 | 33 | module.exports = ArgumentTypeConversion 34 | -------------------------------------------------------------------------------- /tools/generate_templates/node-raylib-definitions.js: -------------------------------------------------------------------------------- 1 | const ArgumentTypeConversion = require("./ArgumentTypeConversion"); 2 | 3 | const FunctionDefinition = (func) => { 4 | return `/** ${func.description} */ 5 | export function ${func.name}(${ 6 | !func.params 7 | ? "" 8 | : func.params 9 | .map( 10 | (param) => `${param.name}: ${ArgumentTypeConversion(param.type)}` 11 | ) 12 | .join(", ") 13 | }): ${ArgumentTypeConversion(func.returnType)} 14 | `; 15 | }; 16 | 17 | const StructInterface = (struct) => { 18 | return `/** ${struct.description} */ 19 | export interface ${struct.name} { 20 | ${struct.fields 21 | .filter((field) => !field.type.trim().startsWith("#")) 22 | .map( 23 | (field) => 24 | `/** ${field.description}. (${field.type}) */\n ${ 25 | field.name 26 | }: ${ArgumentTypeConversion(field.type)}` 27 | ) 28 | .join("\n ")} 29 | }`; 30 | }; 31 | 32 | module.exports = ({ functions, structs, enums, blocklist }) => { 33 | return `// GENERATED CODE: DO NOT MODIFY 34 | declare module "raylib" { 35 | ${structs.map(StructInterface).join("\n ")} 36 | 37 | /** RenderTexture, fbo for texture rendering */ 38 | export type RenderTexture2D = RenderTexture 39 | 40 | /** Texture, tex data stored in GPU memory (VRAM) */ 41 | export type Texture2D = Texture 42 | 43 | /** Texture, tex data stored in GPU memory (VRAM) */ 44 | export type TextureCubemap = Texture 45 | 46 | /** Camera, defines position/orientation in 3d space */ 47 | export type Camera = Camera3D | Camera2D 48 | 49 | /** Quaternion, same as Vector4 */ 50 | export type Quaternion = Vector4 51 | ${functions 52 | .filter(({ name }) => !blocklist.includes(name)) 53 | .map(FunctionDefinition) 54 | .join("\n ")} 55 | 56 | /** Set shader uniform float */ 57 | export function SetShaderFloat(shader: Shader, locIndex: number, value: number): void 58 | /** Set shader uniform int */ 59 | export function SetShaderInt(shader: Shader, locIndex: number, value: number): void 60 | /** Set shader uniform Vec2 */ 61 | export function SetShaderVec2(shader: Shader, locIndex: number, value: Vector2): void 62 | /** Set shader uniform Vec3 */ 63 | export function SetShaderVec3(shader: Shader, locIndex: number, value: Vector3): void 64 | /** Set shader uniform Vec4 */ 65 | export function SetShaderVec4(shader: Shader, locIndex: number, value: Vector4): void 66 | 67 | export function Camera3D( 68 | /** Camera position. (Vector3) */ 69 | position: Vector3, 70 | /** Camera target it looks-at. (Vector3) */ 71 | target: Vector3, 72 | /** Camera up vector (rotation over its axis). (Vector3) */ 73 | up: Vector3, 74 | /** Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic. (float) */ 75 | fovy: number, 76 | /** Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC. (int) */ 77 | projection: number, 78 | ): Camera3D 79 | 80 | export function Vector3( 81 | /** Vector x component. (float) */ 82 | x: number, 83 | /** Vector y component. (float) */ 84 | y: number, 85 | /** Vector z component. (float) */ 86 | z: number, 87 | ): Vector3 88 | 89 | export function Vector2( 90 | /** Vector x component. (float) */ 91 | x: number, 92 | /** Vector y component. (float) */ 93 | y: number, 94 | ): Vector2 95 | 96 | ${enums 97 | .map((e) => { 98 | return e.values 99 | .map( 100 | (v) => 101 | ` /** ${v.description} */\n export const ${v.name} = ${v.value}` 102 | ) 103 | .join("\n"); 104 | }) 105 | .join("\n")} 106 | 107 | export const LIGHTGRAY: { r: 200, g: 200, b: 200, a: 255 } 108 | export const GRAY: { r: 130, g: 130, b: 130, a: 255 } 109 | export const DARKGRAY: { r: 80, g: 80, b: 80, a: 255 } 110 | export const YELLOW: { r: 253, g: 249, b: 0, a: 255 } 111 | export const GOLD: { r: 255, g: 203, b: 0, a: 255 } 112 | export const ORANGE: { r: 255, g: 161, b: 0, a: 255 } 113 | export const PINK: { r: 255, g: 109, b: 194, a: 255 } 114 | export const RED: { r: 230, g: 41, b: 55, a: 255 } 115 | export const MAROON: { r: 190, g: 33, b: 55, a: 255 } 116 | export const GREEN: { r: 0, g: 228, b: 48, a: 255 } 117 | export const LIME: { r: 0, g: 158, b: 47, a: 255 } 118 | export const DARKGREEN: { r: 0, g: 117, b: 44, a: 255 } 119 | export const SKYBLUE: { r: 102, g: 191, b: 255, a: 255 } 120 | export const BLUE: { r: 0, g: 121, b: 241, a: 255 } 121 | export const DARKBLUE: { r: 0, g: 82, b: 172, a: 255 } 122 | export const PURPLE: { r: 200, g: 122, b: 255, a: 255 } 123 | export const VIOLET: { r: 135, g: 60, b: 190, a: 255 } 124 | export const DARKPURPLE: { r: 112, g: 31, b: 126, a: 255 } 125 | export const BEIGE: { r: 211, g: 176, b: 131, a: 255 } 126 | export const BROWN: { r: 127, g: 106, b: 79, a: 255 } 127 | export const DARKBROWN: { r: 76, g: 63, b: 47, a: 255 } 128 | export const WHITE: { r: 255, g: 255, b: 255, a: 255 } 129 | export const BLACK: { r: 0, g: 0, b: 0, a: 255 } 130 | export const BLANK: { r: 0, g: 0, b: 0, a: 0 } 131 | export const MAGENTA: { r: 255, g: 0, b: 255, a: 255 } 132 | export const RAYWHITE: { r: 245, g: 245, b: 245, a: 255 } 133 | 134 | 135 | } 136 | `; 137 | }; 138 | -------------------------------------------------------------------------------- /tools/pkg.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('pkg') 2 | const archiver = require('archiver') 3 | const fs = require('node:fs') 4 | const path = require('node:path') 5 | 6 | // Options 7 | let binaryFilename = 'node-raylib' 8 | let packageName = `${binaryFilename}-${process.platform}-${process.arch}` 9 | let compression = 'tar' 10 | let compressOptions = {} 11 | 12 | // Platform overrides 13 | if (process.platform === 'win32') { 14 | binaryFilename += '.exe' 15 | packageName += '.zip' 16 | compression = 'zip' 17 | } else { 18 | packageName += '.tar.gz' 19 | compressOptions = { 20 | store: true, 21 | gzip: true 22 | } 23 | } 24 | 25 | // Create the package. 26 | pkg() 27 | 28 | /** 29 | * Build the binary with pkg. 30 | */ 31 | async function pkg () { 32 | await exec(['.', '--target', 'host', '--output', path.join('build', 'Release', binaryFilename)]) 33 | await compress() 34 | } 35 | 36 | /** 37 | * Compress the files into the archive. 38 | */ 39 | async function compress () { 40 | const output = fs.createWriteStream(packageName) 41 | const archive = archiver(compression, compressOptions) 42 | archive.on('error', err => { 43 | throw err 44 | }) 45 | archive.pipe(output) 46 | archive.directory('build/Release', false) 47 | archive.directory('examples') 48 | archive.file('README.md') 49 | archive.file('LICENSE.md') 50 | archive.finalize() 51 | } 52 | -------------------------------------------------------------------------------- /tools/postinstall.js: -------------------------------------------------------------------------------- 1 | // this is run after npm install 2 | // download pre-made module, if possible & exit 1, or exit 0 to tell system if it needs to build 3 | 4 | const fs = require("node:fs/promises"); 5 | const path = require("node:path"); 6 | const fetch = require("cross-fetch"); 7 | 8 | let targetPath = path.join( 9 | __dirname, 10 | "..", 11 | "build", 12 | "Release", 13 | "node-raylib.node" 14 | ); 15 | 16 | const { version } = require("../package.json"); 17 | 18 | function toBuffer(ab) { 19 | const buf = Buffer.alloc(ab.byteLength); 20 | const view = new Uint8Array(ab); 21 | for (let i = 0; i < buf.length; ++i) { 22 | buf[i] = view[i]; 23 | } 24 | return buf; 25 | } 26 | 27 | async function exists(path) { 28 | try { 29 | await fs.access(path); 30 | return true; 31 | } catch { 32 | return false; 33 | } 34 | } 35 | 36 | async function main() { 37 | if (await exists(targetPath)) { 38 | console.log("Found node-raylib.node."); 39 | process.exit(0); 40 | } 41 | 42 | let url = `https://github.com/RobLoach/node-raylib/releases/download/v${version}/node-raylib-${process.platform}-${process.arch}.node`; 43 | 44 | console.log(`Checking for ${url}`); 45 | 46 | try { 47 | await fs.mkdir(path.join(__dirname, "..", "build")); 48 | } catch (e) {} 49 | 50 | try { 51 | await fs.mkdir(path.join(__dirname, "..", "build", "Release")); 52 | } catch (e) {} 53 | 54 | if ( 55 | process.platform === "linux" && 56 | (process.arch === "arm" || process.arch === "arm64") 57 | ) { 58 | targetPath = path.join( 59 | __dirname, 60 | "..", 61 | "build", 62 | "Release", 63 | "node-raylib-drm.node" 64 | ); 65 | url = `https://github.com/RobLoach/node-raylib/releases/download/v${version}/node-raylib-${process.platform}-${process.arch}-drm.node`; 66 | try { 67 | const data = await fetch(url) 68 | .then((r) => { 69 | if (r.status !== 200) { 70 | throw new Error(`Status: ${r.status}`); 71 | } 72 | return r; 73 | }) 74 | .then((r) => r.arrayBuffer()); 75 | await fs.writeFile(targetPath, toBuffer(data)); 76 | console.log("Found DRM on releases."); 77 | } catch (e) {} 78 | } 79 | 80 | try { 81 | const data = await fetch(url) 82 | .then((r) => { 83 | if (r.status !== 200) { 84 | throw new Error(`Status: ${r.status}`); 85 | } 86 | return r; 87 | }) 88 | .then((r) => r.arrayBuffer()); 89 | await fs.writeFile(targetPath, toBuffer(data)); 90 | console.log("Found on releases."); 91 | process.exit(0); 92 | } catch (e) { 93 | console.error(e.message); 94 | } 95 | 96 | // couldn't find it, so tell postinstall to compile it 97 | console.log("Not found. Building."); 98 | process.exit(1); 99 | } 100 | main(); 101 | --------------------------------------------------------------------------------