refactor: 💡 remove orphaned files
This commit is contained in:
parent
8e413f89ec
commit
c148443b7b
13 changed files with 54 additions and 1038 deletions
|
|
@ -1,36 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Assert all 3 repos are on the given branch.
|
||||
# Exit 0 if all match. Exit 1 with details if any mismatch (including detached HEAD).
|
||||
# Usage: ./assert-on-branch.sh <branch-name>
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=./repos.sh
|
||||
source "$SCRIPT_DIR/repos.sh"
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <branch-name>" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
WANT="$1"
|
||||
mismatched=()
|
||||
for repo in "${REPOS[@]}"; do
|
||||
p=$(repo_path "$repo")
|
||||
cur=$(git -C "$p" branch --show-current)
|
||||
if [ "$cur" != "$WANT" ]; then
|
||||
mismatched+=("$repo: on '${cur:-DETACHED-HEAD}' (want '$WANT')")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#mismatched[@]} -eq 0 ]; then
|
||||
echo "All 3 repos on branch: $WANT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Branch mismatch:" >&2
|
||||
for line in "${mismatched[@]}"; do
|
||||
echo " $line" >&2
|
||||
done
|
||||
exit 1
|
||||
|
|
@ -237,8 +237,8 @@ emar rcs "${STUBS_BUILD}/libgit2_stub.a" "${STUBS_BUILD}/libgit2_stub.o"
|
|||
emcc -c "${STUBS_DIR}/curl_stub.c" -o "${STUBS_BUILD}/curl_stub.o"
|
||||
emar rcs "${STUBS_BUILD}/libcurl_stub.a" "${STUBS_BUILD}/curl_stub.o"
|
||||
|
||||
# Note: GLU tesselator is now implemented in wasm/stubs/glu_wasm_impl.cpp
|
||||
# It's compiled as part of the GAL library (requires KiCad headers)
|
||||
# Note: the GLU tesselator is provided by kicad/libs/kimath/glu_tess/glu_tess_impl.cpp,
|
||||
# compiled as part of the GAL library (requires KiCad headers).
|
||||
|
||||
# Compile NNG stub (IPC API requires NNG but sockets don't work in WASM)
|
||||
emcc -c -I"${STUBS_DIR}" "${STUBS_DIR}/nng_stub.c" -o "${STUBS_BUILD}/nng_stub.o"
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
# WASM Compatibility Layer for KiCad
|
||||
# This directory contains WASM-specific implementations that replace
|
||||
# platform-specific code in KiCad without modifying KiCad's source.
|
||||
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
project(kicad_wasm_compat)
|
||||
|
||||
# Only build for Emscripten
|
||||
if(NOT EMSCRIPTEN)
|
||||
message(FATAL_ERROR "This project is only for Emscripten builds")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Include directories
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/config
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shims
|
||||
)
|
||||
|
||||
# kiplatform WASM implementation
|
||||
add_subdirectory(kiplatform)
|
||||
|
||||
# libcontext Asyncify fiber implementation
|
||||
add_subdirectory(libcontext)
|
||||
|
|
@ -1,74 +1,83 @@
|
|||
# WASM Compatibility Layer
|
||||
|
||||
This directory contains WASM-specific implementations that allow KiCad to run in a web browser **without modifying KiCad's source code**.
|
||||
This directory contains WASM-specific implementations that allow KiCad to run in a
|
||||
web browser while keeping our KiCad fork **as close to upstream as possible**.
|
||||
|
||||
## Principle
|
||||
|
||||
Instead of patching KiCad source files, we:
|
||||
1. Override include paths to use our headers first
|
||||
2. Provide alternative implementations for platform-specific code
|
||||
3. Link our libraries instead of system libraries
|
||||
1. Provide alternative implementations for platform-specific code (kiplatform)
|
||||
2. Stub out libraries/features that can't work in the browser (libgit2, curl, nng, scripting, 3D viewer, ...)
|
||||
3. Expose KiCad to JavaScript via Embind bindings
|
||||
4. Override host package detection during cross-compilation (cmake find-modules)
|
||||
|
||||
The KiCad-side hooks for this are small `if(EMSCRIPTEN)` branches in KiCad's own
|
||||
CMakeLists that pull sources from this directory — see "How it's wired" below.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
wasm/
|
||||
├── CMakeLists.txt # Master CMake for compatibility layer
|
||||
├── README.md # This file
|
||||
├── kiplatform/ # Platform abstraction implementations
|
||||
│ ├── CMakeLists.txt
|
||||
├── kiplatform/ # Platform abstraction implementations (compiled into KiCad)
|
||||
│ ├── app.cpp # App lifecycle (paths, startup)
|
||||
│ ├── drivers.cpp # GPU detection (returns "WebGL")
|
||||
│ ├── environment.cpp # Environment variables (localStorage)
|
||||
│ ├── environment.cpp # Environment variables
|
||||
│ ├── io.cpp # File I/O (WASM virtual filesystem)
|
||||
│ ├── policy.cpp # Security policy (always permissive)
|
||||
│ ├── secrets.cpp # Credential storage (localStorage)
|
||||
│ ├── secrets.cpp # Credential storage
|
||||
│ ├── sysinfo.cpp # System information
|
||||
│ └── printing.cpp # Print support (browser print())
|
||||
├── libcontext/ # Coroutine/fiber implementation
|
||||
│ ├── CMakeLists.txt
|
||||
│ └── fcontext_wasm.cpp # Emscripten Asyncify fibers
|
||||
├── shims/ # Header overrides
|
||||
│ └── *.h # Headers that redirect to our impls
|
||||
└── config/ # Build configuration
|
||||
├── kicad_wasm_config.h # Version and feature config
|
||||
└── setup.h # Platform setup
|
||||
│ ├── printing.cpp # Print support (browser print())
|
||||
│ └── ui.cpp # UI helpers
|
||||
├── bindings/ # Embind bindings exposing each app to JavaScript
|
||||
│ ├── pcbnew_embind.cpp
|
||||
│ ├── eeschema_embind.cpp
|
||||
│ ├── pl_editor_embind.cpp
|
||||
│ └── calculator_embind.cpp
|
||||
├── stubs/ # Stub implementations + header shims for unavailable deps
|
||||
│ ├── *.c / *.cpp # libgit2, curl, nng, scripting, 3D viewer, frame stubs, ...
|
||||
│ ├── char_traits_uint16_workaround.h
|
||||
│ └── GL/ nng/ ngspice/ # Header stubs found via include paths
|
||||
└── cmake/ # CMake find-module overrides for cross-compilation
|
||||
└── Find*.cmake / Use*.cmake
|
||||
```
|
||||
|
||||
## How It Works
|
||||
## How it's wired
|
||||
|
||||
### Include Path Override
|
||||
### kiplatform — compiled into KiCad
|
||||
|
||||
When building KiCad for WASM, we add our directories first in the include path:
|
||||
The `kiplatform/*.cpp` files are added directly to KiCad's kiplatform library by an
|
||||
`if(EMSCRIPTEN)` branch in `kicad/libs/kiplatform/CMakeLists.txt`, which references
|
||||
them as `${PROJECT_SOURCE_DIR}/../wasm/kiplatform/*.cpp`. There is no separate
|
||||
`libkiplatform_wasm.a`.
|
||||
|
||||
```bash
|
||||
-I$PROJECT_ROOT/wasm/shims
|
||||
-I$PROJECT_ROOT/wasm/kiplatform
|
||||
-I$PROJECT_ROOT/stubs/include
|
||||
```
|
||||
### stubs — compiled by the build script and KiCad CMakeLists
|
||||
|
||||
This means when KiCad includes `<kiplatform/app.h>`, it finds our version first.
|
||||
`scripts/kicad/build-kicad-target.sh` compiles the C stubs (`libgit2_stub.c`,
|
||||
`curl_stub.c`, `nng_stub.c`) and force-includes `char_traits_uint16_workaround.h`.
|
||||
App-specific `*_frame_stub.cpp` / `*_scripting_stub.cpp` are picked up per app, and
|
||||
the remaining `*_stub.cpp` files are pulled in by `if(EMSCRIPTEN)` branches in the
|
||||
KiCad fork's own CMakeLists. Header stubs under `GL/`, `nng/`, `ngspice/` are resolved
|
||||
via include paths.
|
||||
|
||||
### Library Override
|
||||
### bindings — per app
|
||||
|
||||
We build `libkiplatform_wasm.a` and link it instead of the native kiplatform:
|
||||
`build-kicad-target.sh` compiles `wasm/bindings/<app>_embind.cpp` for the app being
|
||||
built (apps without an embind file get an empty placeholder object).
|
||||
|
||||
```bash
|
||||
-L$BUILD_ROOT/wasm -lkiplatform_wasm
|
||||
```
|
||||
### cmake — module path
|
||||
|
||||
### CMake Integration
|
||||
`build-kicad-target.sh` passes `-DCMAKE_MODULE_PATH="${PROJECT_ROOT}/wasm/cmake"` so
|
||||
the WASM find-module stubs override host package detection.
|
||||
|
||||
The main KiCad build is configured to find our implementations:
|
||||
|
||||
```cmake
|
||||
-DCMAKE_MODULE_PATH="$PROJECT_ROOT/cmake"
|
||||
-DKIPLATFORM_LIBRARY="$BUILD_ROOT/wasm/libkiplatform_wasm.a"
|
||||
```
|
||||
> Coroutine/fiber support is **not** in this directory — it comes from the KiCad fork's
|
||||
> `kicad/thirdparty/libcontext/libcontext.cpp` (`LIBCONTEXT_PLATFORM_wasm32`). The GLU
|
||||
> tesselator comes from `kicad/libs/kimath/glu_tess/glu_tess_impl.cpp`.
|
||||
|
||||
## Adding New Implementations
|
||||
|
||||
1. Create the implementation file in the appropriate directory
|
||||
2. Add it to the CMakeLists.txt
|
||||
3. Ensure the header interface matches KiCad's expected interface
|
||||
4. Test with a minimal build before full integration
|
||||
1. Create the implementation file in the appropriate directory (`kiplatform/`, `stubs/`, `bindings/`).
|
||||
2. Wire it in: a stub C file goes in `build-kicad-target.sh`; a kiplatform/app source
|
||||
goes in the relevant `if(EMSCRIPTEN)` branch of the KiCad-side CMakeLists.
|
||||
3. Ensure the header interface matches KiCad's expected interface.
|
||||
4. Test with a minimal build before full integration.
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
# FindKiplatformWASM.cmake
|
||||
# Find module for WASM implementation of kiplatform
|
||||
#
|
||||
# This module is used instead of the platform-specific kiplatform
|
||||
# when building KiCad for WebAssembly.
|
||||
#
|
||||
# This module defines:
|
||||
# KIPLATFORM_FOUND - System has kiplatform for WASM
|
||||
# KIPLATFORM_INCLUDE_DIRS - Include directories
|
||||
# KIPLATFORM_LIBRARIES - Libraries to link
|
||||
# KIPLATFORM_SOURCES - Source files to compile
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
message(FATAL_ERROR "FindKiplatformWASM is only for Emscripten builds")
|
||||
endif()
|
||||
|
||||
# Get the directory containing this file
|
||||
get_filename_component(_FIND_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
get_filename_component(KIPLATFORM_WASM_DIR "${_FIND_DIR}/../kiplatform" ABSOLUTE)
|
||||
|
||||
# Define source files
|
||||
set(KIPLATFORM_SOURCES
|
||||
${KIPLATFORM_WASM_DIR}/app.cpp
|
||||
${KIPLATFORM_WASM_DIR}/drivers.cpp
|
||||
${KIPLATFORM_WASM_DIR}/environment.cpp
|
||||
${KIPLATFORM_WASM_DIR}/io.cpp
|
||||
${KIPLATFORM_WASM_DIR}/policy.cpp
|
||||
${KIPLATFORM_WASM_DIR}/secrets.cpp
|
||||
${KIPLATFORM_WASM_DIR}/sysinfo.cpp
|
||||
${KIPLATFORM_WASM_DIR}/ui.cpp
|
||||
)
|
||||
|
||||
# Include directory for platform headers
|
||||
# We use KiCad's own headers, just provide our implementation
|
||||
set(KIPLATFORM_INCLUDE_DIRS ${KIPLATFORM_WASM_DIR})
|
||||
|
||||
# No prebuilt library - sources are compiled directly into KiCad
|
||||
set(KIPLATFORM_LIBRARIES "")
|
||||
|
||||
# Mark as found
|
||||
set(KIPLATFORM_FOUND TRUE)
|
||||
|
||||
find_package_handle_standard_args(KiplatformWASM
|
||||
REQUIRED_VARS KIPLATFORM_SOURCES KIPLATFORM_INCLUDE_DIRS
|
||||
)
|
||||
|
||||
mark_as_advanced(KIPLATFORM_SOURCES KIPLATFORM_INCLUDE_DIRS KIPLATFORM_LIBRARIES)
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# FindLibcontextWASM.cmake
|
||||
# Find module for WASM implementation of libcontext using Asyncify
|
||||
#
|
||||
# This module replaces the platform-specific libcontext assembly
|
||||
# when building KiCad for WebAssembly.
|
||||
#
|
||||
# This module defines:
|
||||
# LIBCONTEXT_FOUND - System has libcontext for WASM
|
||||
# LIBCONTEXT_INCLUDE_DIRS - Include directories
|
||||
# LIBCONTEXT_LIBRARIES - Libraries to link (none for WASM)
|
||||
# LIBCONTEXT_SOURCES - Source files to compile
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
message(FATAL_ERROR "FindLibcontextWASM is only for Emscripten builds")
|
||||
endif()
|
||||
|
||||
# Get the directory containing this file
|
||||
get_filename_component(_FIND_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
get_filename_component(LIBCONTEXT_WASM_DIR "${_FIND_DIR}/../libcontext" ABSOLUTE)
|
||||
|
||||
# Define source files
|
||||
set(LIBCONTEXT_SOURCES
|
||||
${LIBCONTEXT_WASM_DIR}/libcontext_wasm.cpp
|
||||
)
|
||||
|
||||
# Include directories
|
||||
set(LIBCONTEXT_INCLUDE_DIRS ${LIBCONTEXT_WASM_DIR})
|
||||
|
||||
# No prebuilt library
|
||||
set(LIBCONTEXT_LIBRARIES "")
|
||||
|
||||
# Mark as found
|
||||
set(LIBCONTEXT_FOUND TRUE)
|
||||
|
||||
find_package_handle_standard_args(LibcontextWASM
|
||||
REQUIRED_VARS LIBCONTEXT_SOURCES LIBCONTEXT_INCLUDE_DIRS
|
||||
)
|
||||
|
||||
mark_as_advanced(LIBCONTEXT_SOURCES LIBCONTEXT_INCLUDE_DIRS LIBCONTEXT_LIBRARIES)
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
# KiCad WASM Configuration
|
||||
# This file configures KiCad to use WASM-specific implementations
|
||||
|
||||
if(NOT EMSCRIPTEN)
|
||||
message(FATAL_ERROR "KiCadWASMConfig.cmake is only for Emscripten builds")
|
||||
endif()
|
||||
|
||||
# Set the path to WASM compatibility layer
|
||||
get_filename_component(KICAD_WASM_DIR "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE)
|
||||
|
||||
message(STATUS "KiCad WASM compatibility layer: ${KICAD_WASM_DIR}")
|
||||
|
||||
# Add include directories for WASM implementations
|
||||
# These will be searched BEFORE KiCad's own includes, allowing us to
|
||||
# provide our own implementations without modifying KiCad source
|
||||
set(KICAD_WASM_INCLUDE_DIRS
|
||||
${KICAD_WASM_DIR}/kiplatform
|
||||
${KICAD_WASM_DIR}/libcontext
|
||||
${KICAD_WASM_DIR}/config
|
||||
${KICAD_WASM_DIR}/shims
|
||||
)
|
||||
|
||||
# Define macro to indicate we're using WASM platform
|
||||
add_definitions(-DKICAD_PLATFORM_WASM=1)
|
||||
add_definitions(-D__WXUNIVERSAL__=1)
|
||||
|
||||
# Emscripten-specific compile options
|
||||
add_compile_options(
|
||||
-pthread
|
||||
-sUSE_PTHREADS=1
|
||||
)
|
||||
|
||||
# Emscripten-specific link options
|
||||
add_link_options(
|
||||
-pthread
|
||||
-sUSE_PTHREADS=1
|
||||
-sPTHREAD_POOL_SIZE=4
|
||||
-sASYNCIFY=1
|
||||
-sASYNCIFY_STACK_SIZE=65536
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sINITIAL_MEMORY=256MB
|
||||
-sMAXIMUM_MEMORY=4GB
|
||||
-sMODULARIZE=1
|
||||
-sEXPORT_ES6=1
|
||||
-sENVIRONMENT=web,worker
|
||||
)
|
||||
|
||||
# Export variables for use by parent CMakeLists
|
||||
set(KICAD_WASM_INCLUDE_DIRS ${KICAD_WASM_INCLUDE_DIRS} PARENT_SCOPE)
|
||||
set(KICAD_WASM_DIR ${KICAD_WASM_DIR} PARENT_SCOPE)
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# WASM implementation of kiplatform
|
||||
# Provides WASM-specific implementations of KiCad's platform abstraction layer
|
||||
|
||||
set(KIPLATFORM_WASM_SRCS
|
||||
app.cpp
|
||||
drivers.cpp
|
||||
environment.cpp
|
||||
io.cpp
|
||||
policy.cpp
|
||||
secrets.cpp
|
||||
sysinfo.cpp
|
||||
ui.cpp
|
||||
)
|
||||
|
||||
add_library(kiplatform_wasm STATIC ${KIPLATFORM_WASM_SRCS})
|
||||
|
||||
target_include_directories(kiplatform_wasm PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../config
|
||||
${PROJECT_ROOT}/kicad/libs/kiplatform/include
|
||||
)
|
||||
|
||||
# Link with wxWidgets
|
||||
target_link_libraries(kiplatform_wasm
|
||||
# wxWidgets will be added during main build
|
||||
)
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# WASM implementation of libcontext using Emscripten Asyncify
|
||||
# Provides fiber/coroutine support for KiCad's router
|
||||
|
||||
set(LIBCONTEXT_WASM_SRCS
|
||||
libcontext_wasm.cpp
|
||||
)
|
||||
|
||||
add_library(libcontext_wasm STATIC ${LIBCONTEXT_WASM_SRCS})
|
||||
|
||||
target_include_directories(libcontext_wasm PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Asyncify is required for fiber support
|
||||
# These flags must also be set on the final executable
|
||||
target_compile_options(libcontext_wasm PRIVATE
|
||||
-pthread
|
||||
)
|
||||
|
||||
target_link_options(libcontext_wasm INTERFACE
|
||||
-sASYNCIFY=1
|
||||
-sASYNCIFY_STACK_SIZE=65536
|
||||
)
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* WASM implementation of libcontext using Emscripten Asyncify
|
||||
*
|
||||
* Emscripten's Asyncify allows us to implement fiber/coroutine semantics
|
||||
* by saving and restoring the WebAssembly call stack.
|
||||
*
|
||||
* Implementation strategy:
|
||||
* - Each fiber context stores an Asyncify data buffer
|
||||
* - jump_fcontext suspends current execution and resumes target
|
||||
* - make_fcontext creates a new context with a function entry point
|
||||
*
|
||||
* Note: This requires the WASM module to be compiled with:
|
||||
* -sASYNCIFY=1
|
||||
* -sASYNCIFY_STACK_SIZE=65536 (or larger if needed)
|
||||
*
|
||||
* For more information on Asyncify:
|
||||
* https://emscripten.org/docs/porting/asyncify.html
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/fiber.h>
|
||||
#endif
|
||||
|
||||
// Match the API from libcontext.h
|
||||
#define LIBCONTEXT_CALL_CONVENTION
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
namespace libcontext
|
||||
{
|
||||
|
||||
// Context structure that stores fiber state
|
||||
struct fiber_context {
|
||||
emscripten_fiber_t fiber;
|
||||
void (*entry_func)(intptr_t);
|
||||
intptr_t entry_arg;
|
||||
bool initialized;
|
||||
bool running;
|
||||
// Stack for asyncify data
|
||||
char asyncify_stack[65536];
|
||||
// C stack
|
||||
char* c_stack;
|
||||
size_t c_stack_size;
|
||||
};
|
||||
|
||||
// Current running context
|
||||
static fiber_context* g_current_context = nullptr;
|
||||
static fiber_context g_main_context;
|
||||
static bool g_main_initialized = false;
|
||||
|
||||
// Fiber entry wrapper
|
||||
static void fiber_entry_wrapper(void* arg)
|
||||
{
|
||||
fiber_context* ctx = (fiber_context*)arg;
|
||||
if (ctx && ctx->entry_func) {
|
||||
ctx->entry_func(ctx->entry_arg);
|
||||
}
|
||||
// If entry function returns, we need to handle it
|
||||
// In original libcontext, this would call _exit
|
||||
// For WASM, we'll just return to main context
|
||||
}
|
||||
|
||||
typedef void* fcontext_t;
|
||||
|
||||
void LIBCONTEXT_CALL_CONVENTION release_fcontext( fcontext_t ctx )
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
if (ctx) {
|
||||
fiber_context* fctx = (fiber_context*)ctx;
|
||||
if (fctx->c_stack) {
|
||||
free(fctx->c_stack);
|
||||
}
|
||||
free(fctx);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
intptr_t LIBCONTEXT_CALL_CONVENTION jump_fcontext( fcontext_t* ofc, fcontext_t nfc,
|
||||
intptr_t vp, bool preserve_fpu )
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// Initialize main context if needed
|
||||
if (!g_main_initialized) {
|
||||
memset(&g_main_context, 0, sizeof(g_main_context));
|
||||
emscripten_fiber_init_from_current_context(
|
||||
&g_main_context.fiber,
|
||||
g_main_context.asyncify_stack,
|
||||
sizeof(g_main_context.asyncify_stack)
|
||||
);
|
||||
g_main_context.initialized = true;
|
||||
g_main_context.running = true;
|
||||
g_current_context = &g_main_context;
|
||||
g_main_initialized = true;
|
||||
}
|
||||
|
||||
fiber_context* old_ctx = g_current_context;
|
||||
fiber_context* new_ctx = (fiber_context*)nfc;
|
||||
|
||||
if (!new_ctx || !new_ctx->initialized) {
|
||||
fprintf(stderr, "jump_fcontext: invalid target context\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Store the argument in the new context
|
||||
new_ctx->entry_arg = vp;
|
||||
|
||||
// Save the old context pointer
|
||||
if (ofc) {
|
||||
*ofc = (fcontext_t)old_ctx;
|
||||
}
|
||||
|
||||
// Switch contexts
|
||||
g_current_context = new_ctx;
|
||||
old_ctx->running = false;
|
||||
new_ctx->running = true;
|
||||
|
||||
// Perform the fiber switch
|
||||
emscripten_fiber_swap(&old_ctx->fiber, &new_ctx->fiber);
|
||||
|
||||
// When we return here, we've been switched back to
|
||||
// Return the value passed to us
|
||||
return g_current_context->entry_arg;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
fcontext_t LIBCONTEXT_CALL_CONVENTION make_fcontext( void* sp, size_t size,
|
||||
void (* fn)( intptr_t ) )
|
||||
{
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// Allocate context structure
|
||||
fiber_context* ctx = (fiber_context*)malloc(sizeof(fiber_context));
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
memset(ctx, 0, sizeof(fiber_context));
|
||||
|
||||
ctx->entry_func = fn;
|
||||
ctx->entry_arg = 0;
|
||||
ctx->c_stack = (char*)sp - size; // sp points to top of stack
|
||||
ctx->c_stack_size = size;
|
||||
|
||||
// Initialize the fiber
|
||||
// Note: sp is the TOP of the stack (highest address)
|
||||
// The stack grows downward, so we need to pass the bottom
|
||||
void* stack_bottom = (char*)sp - size;
|
||||
|
||||
emscripten_fiber_init(
|
||||
&ctx->fiber,
|
||||
fiber_entry_wrapper,
|
||||
ctx, // User data for entry function
|
||||
stack_bottom, // C stack (bottom)
|
||||
size, // C stack size
|
||||
ctx->asyncify_stack, // Asyncify stack
|
||||
sizeof(ctx->asyncify_stack) // Asyncify stack size
|
||||
);
|
||||
|
||||
ctx->initialized = true;
|
||||
ctx->running = false;
|
||||
|
||||
return (fcontext_t)ctx;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
}; // namespace libcontext
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* WASM implementation of libcontext using Emscripten Asyncify
|
||||
*
|
||||
* This header provides WASM-specific definitions for the libcontext API.
|
||||
* The implementation uses Emscripten's Asyncify feature to implement
|
||||
* fiber-like context switching in WebAssembly.
|
||||
*
|
||||
* Asyncify works by:
|
||||
* 1. Instrumenting the WASM code to save/restore the call stack
|
||||
* 2. Allowing execution to suspend at any point
|
||||
* 3. Resuming execution from where it was suspended
|
||||
*
|
||||
* This is used by KiCad's router for coroutine-based routing.
|
||||
*/
|
||||
|
||||
#ifndef LIBCONTEXT_WASM_H
|
||||
#define LIBCONTEXT_WASM_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
// Define WASM platform
|
||||
#define LIBCONTEXT_PLATFORM_wasm
|
||||
#define LIBCONTEXT_CALL_CONVENTION
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libcontext {
|
||||
#endif
|
||||
|
||||
typedef void* fcontext_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void LIBCONTEXT_CALL_CONVENTION release_fcontext( fcontext_t ctx );
|
||||
|
||||
intptr_t LIBCONTEXT_CALL_CONVENTION jump_fcontext( fcontext_t* ofc, fcontext_t nfc,
|
||||
intptr_t vp, bool preserve_fpu = true );
|
||||
|
||||
fcontext_t LIBCONTEXT_CALL_CONVENTION make_fcontext( void* sp, size_t size,
|
||||
void (* fn)( intptr_t ) );
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // namespace libcontext
|
||||
#endif
|
||||
|
||||
#endif // LIBCONTEXT_WASM_H
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
/**
|
||||
* gl_immediate_shim.js
|
||||
*
|
||||
* Custom OpenGL immediate mode shims for KiCad WASM port.
|
||||
* Fixes Emscripten LEGACY_GL_EMULATION issues:
|
||||
* 1. Color-per-vertex requirement - injects color before each vertex automatically
|
||||
* 2. Missing double-precision functions (glVertex2d, glVertex3d, glColor3d, glColor4d)
|
||||
*
|
||||
* Usage: emcc ... --js-library=lib/gl_immediate_shim.js
|
||||
*/
|
||||
|
||||
addToLibrary({
|
||||
// ==================================================================
|
||||
// GLImmediateShim - State tracking and function wrapping
|
||||
// ==================================================================
|
||||
|
||||
$GLImmediateShim__deps: ['$GLImmediate', 'glBegin', 'glEnd', 'glVertex2f', 'glVertex3f', 'glColor3f', 'glColor4f'],
|
||||
$GLImmediateShim__postset: 'GLImmediateShim.init();',
|
||||
$GLImmediateShim: {
|
||||
// Current color state (persistent across vertices)
|
||||
currentColor: null,
|
||||
|
||||
// Track if we're inside glBegin/glEnd block
|
||||
inBeginEnd: false,
|
||||
|
||||
// Track if color was called since the last vertex
|
||||
// This prevents double-injection when code already calls color per vertex
|
||||
colorCalledSinceLastVertex: false,
|
||||
|
||||
// Original functions we're wrapping
|
||||
origFns: {},
|
||||
|
||||
// Initialization flag
|
||||
initialized: false,
|
||||
|
||||
init: function() {
|
||||
if (GLImmediateShim.initialized) return;
|
||||
if (typeof GLImmediate === 'undefined') {
|
||||
// GLImmediate not ready yet, will be called again
|
||||
console.log('[GLImmediateShim] Waiting for GLImmediate...');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[GLImmediateShim] Initializing OpenGL immediate mode shims');
|
||||
|
||||
// Initialize current color to white (OpenGL default)
|
||||
GLImmediateShim.currentColor = new Float32Array([1.0, 1.0, 1.0, 1.0]);
|
||||
|
||||
// Store original functions
|
||||
GLImmediateShim.origFns = {
|
||||
glBegin: _glBegin,
|
||||
glEnd: _glEnd,
|
||||
glVertex2f: _glVertex2f,
|
||||
glVertex3f: _glVertex3f,
|
||||
glColor3f: _glColor3f,
|
||||
glColor4f: _glColor4f,
|
||||
};
|
||||
|
||||
// Install shims
|
||||
_glBegin = GLImmediateShim.shimBegin;
|
||||
_glEnd = GLImmediateShim.shimEnd;
|
||||
_glVertex2f = GLImmediateShim.shimVertex2f;
|
||||
_glVertex3f = GLImmediateShim.shimVertex3f;
|
||||
_glColor3f = GLImmediateShim.shimColor3f;
|
||||
_glColor4f = GLImmediateShim.shimColor4f;
|
||||
|
||||
GLImmediateShim.initialized = true;
|
||||
console.log('[GLImmediateShim] Initialized successfully');
|
||||
},
|
||||
|
||||
// ==================================================================
|
||||
// Shim implementations
|
||||
// ==================================================================
|
||||
|
||||
shimBegin: function(mode) {
|
||||
GLImmediateShim.inBeginEnd = true;
|
||||
GLImmediateShim.colorCalledSinceLastVertex = false;
|
||||
GLImmediateShim.origFns.glBegin(mode);
|
||||
},
|
||||
|
||||
shimEnd: function() {
|
||||
GLImmediateShim.inBeginEnd = false;
|
||||
GLImmediateShim.origFns.glEnd();
|
||||
},
|
||||
|
||||
shimColor3f: function(r, g, b) {
|
||||
var c = GLImmediateShim.currentColor;
|
||||
c[0] = r;
|
||||
c[1] = g;
|
||||
c[2] = b;
|
||||
c[3] = 1.0;
|
||||
|
||||
if (GLImmediateShim.inBeginEnd) {
|
||||
// Mark that color was explicitly called for this vertex
|
||||
GLImmediateShim.colorCalledSinceLastVertex = true;
|
||||
}
|
||||
|
||||
GLImmediateShim.origFns.glColor3f(r, g, b);
|
||||
},
|
||||
|
||||
shimColor4f: function(r, g, b, a) {
|
||||
var c = GLImmediateShim.currentColor;
|
||||
c[0] = r;
|
||||
c[1] = g;
|
||||
c[2] = b;
|
||||
c[3] = a;
|
||||
|
||||
if (GLImmediateShim.inBeginEnd) {
|
||||
// Mark that color was explicitly called for this vertex
|
||||
GLImmediateShim.colorCalledSinceLastVertex = true;
|
||||
}
|
||||
|
||||
GLImmediateShim.origFns.glColor4f(r, g, b, a);
|
||||
},
|
||||
|
||||
// Inject current color before each vertex (only if not already called)
|
||||
injectColor: function() {
|
||||
if (!GLImmediateShim.inBeginEnd) return;
|
||||
|
||||
// Only inject color if it wasn't already called for this vertex
|
||||
if (!GLImmediateShim.colorCalledSinceLastVertex) {
|
||||
var c = GLImmediateShim.currentColor;
|
||||
GLImmediateShim.origFns.glColor4f(c[0], c[1], c[2], c[3]);
|
||||
}
|
||||
|
||||
// Reset the flag for the next vertex
|
||||
GLImmediateShim.colorCalledSinceLastVertex = false;
|
||||
},
|
||||
|
||||
shimVertex2f: function(x, y) {
|
||||
GLImmediateShim.injectColor();
|
||||
GLImmediateShim.origFns.glVertex2f(x, y);
|
||||
},
|
||||
|
||||
shimVertex3f: function(x, y, z) {
|
||||
GLImmediateShim.injectColor();
|
||||
GLImmediateShim.origFns.glVertex3f(x, y, z);
|
||||
},
|
||||
},
|
||||
|
||||
// ==================================================================
|
||||
// Double-precision vertex functions (missing in Emscripten)
|
||||
// ==================================================================
|
||||
|
||||
glVertex2d__deps: ['glVertex2f'],
|
||||
glVertex2d: function(x, y) {
|
||||
_glVertex2f(x, y);
|
||||
},
|
||||
|
||||
glVertex3d__deps: ['glVertex3f'],
|
||||
glVertex3d: function(x, y, z) {
|
||||
_glVertex3f(x, y, z);
|
||||
},
|
||||
|
||||
glVertex4d__deps: ['glVertex4f'],
|
||||
glVertex4d: function(x, y, z, w) {
|
||||
_glVertex4f(x, y, z, w);
|
||||
},
|
||||
|
||||
// ==================================================================
|
||||
// Double-precision color functions
|
||||
// ==================================================================
|
||||
|
||||
glColor3d__deps: ['glColor3f'],
|
||||
glColor3d: function(r, g, b) {
|
||||
_glColor3f(r, g, b);
|
||||
},
|
||||
|
||||
glColor4d__deps: ['glColor4f'],
|
||||
glColor4d: function(r, g, b, a) {
|
||||
_glColor4f(r, g, b, a);
|
||||
},
|
||||
});
|
||||
|
||||
// Ensure GLImmediateShim is included in the build
|
||||
if (typeof extraLibraryFuncs !== 'undefined') {
|
||||
extraLibraryFuncs.push('$GLImmediateShim');
|
||||
}
|
||||
|
|
@ -1,333 +0,0 @@
|
|||
/**
|
||||
* GLU Tesselator implementation for WebAssembly builds
|
||||
*
|
||||
* Uses KiCad's earcut-based POLYGON_TRIANGULATION instead of GLU library.
|
||||
* This enables OpenGL GAL to work in WASM with proper polygon triangulation.
|
||||
*/
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <geometry/polygon_triangulation.h>
|
||||
|
||||
// GL types
|
||||
typedef double GLdouble;
|
||||
typedef float GLfloat;
|
||||
typedef unsigned int GLenum;
|
||||
typedef unsigned char GLboolean;
|
||||
typedef void GLvoid;
|
||||
typedef void (*_GLUfuncptr)(void);
|
||||
|
||||
// GL constants
|
||||
#ifndef GL_TRUE
|
||||
#define GL_TRUE 1
|
||||
#endif
|
||||
#ifndef GL_FALSE
|
||||
#define GL_FALSE 0
|
||||
#endif
|
||||
|
||||
// GLU constants
|
||||
#define GLU_TESS_BEGIN 100100
|
||||
#define GLU_TESS_VERTEX 100101
|
||||
#define GLU_TESS_END 100102
|
||||
#define GLU_TESS_ERROR 100103
|
||||
#define GLU_TESS_EDGE_FLAG 100104
|
||||
#define GLU_TESS_COMBINE 100105
|
||||
#define GLU_TESS_BEGIN_DATA 100106
|
||||
#define GLU_TESS_VERTEX_DATA 100107
|
||||
#define GLU_TESS_END_DATA 100108
|
||||
#define GLU_TESS_ERROR_DATA 100109
|
||||
#define GLU_TESS_EDGE_FLAG_DATA 100110
|
||||
#define GLU_TESS_COMBINE_DATA 100111
|
||||
|
||||
#define GLU_TESS_WINDING_RULE 100140
|
||||
#define GLU_TESS_WINDING_ODD 100130
|
||||
#define GLU_TESS_WINDING_NONZERO 100131
|
||||
#define GLU_TESS_WINDING_POSITIVE 100132
|
||||
#define GLU_TESS_WINDING_NEGATIVE 100133
|
||||
#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134
|
||||
|
||||
// Vertex data stored during tesselation
|
||||
struct TessVertex
|
||||
{
|
||||
std::array<GLdouble, 3> coords;
|
||||
void* userData; // The data pointer passed to gluTessVertex
|
||||
};
|
||||
|
||||
struct GLUtesselator
|
||||
{
|
||||
// Callbacks
|
||||
void (*vertexCallback)(void* vertex) = nullptr;
|
||||
void (*vertexDataCallback)(void* vertex, void* userData) = nullptr;
|
||||
void (*combineCallback)(GLdouble coords[3], void* vertex_data[4],
|
||||
GLfloat weight[4], void** dataOut) = nullptr;
|
||||
void (*combineDataCallback)(GLdouble coords[3], void* vertex_data[4],
|
||||
GLfloat weight[4], void** dataOut, void* userData) = nullptr;
|
||||
void (*edgeFlagCallback)(GLboolean flag) = nullptr;
|
||||
void (*edgeFlagDataCallback)(GLboolean flag, void* userData) = nullptr;
|
||||
void (*errorCallback)(GLenum error) = nullptr;
|
||||
void (*errorDataCallback)(GLenum error, void* userData) = nullptr;
|
||||
void (*beginCallback)(GLenum type) = nullptr;
|
||||
void (*beginDataCallback)(GLenum type, void* userData) = nullptr;
|
||||
void (*endCallback)() = nullptr;
|
||||
void (*endDataCallback)(void* userData) = nullptr;
|
||||
|
||||
// Contour data - each contour is a list of vertices
|
||||
std::vector<std::vector<TessVertex>> contours;
|
||||
std::vector<TessVertex>* currentContour = nullptr;
|
||||
|
||||
// User data passed to gluTessBeginPolygon
|
||||
void* polygonUserData = nullptr;
|
||||
|
||||
// Properties
|
||||
GLenum windingRule = GLU_TESS_WINDING_POSITIVE;
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
GLUtesselator* gluNewTess()
|
||||
{
|
||||
return new GLUtesselator();
|
||||
}
|
||||
|
||||
void gluDeleteTess(GLUtesselator* tess)
|
||||
{
|
||||
delete tess;
|
||||
}
|
||||
|
||||
void gluTessCallback(GLUtesselator* tess, GLenum which, _GLUfuncptr fn)
|
||||
{
|
||||
if (!tess) return;
|
||||
|
||||
switch (which) {
|
||||
case GLU_TESS_VERTEX:
|
||||
tess->vertexCallback = (void(*)(void*))fn;
|
||||
break;
|
||||
case GLU_TESS_VERTEX_DATA:
|
||||
tess->vertexDataCallback = (void(*)(void*, void*))fn;
|
||||
break;
|
||||
case GLU_TESS_COMBINE:
|
||||
tess->combineCallback = (void(*)(GLdouble[3], void*[4], GLfloat[4], void**))fn;
|
||||
break;
|
||||
case GLU_TESS_COMBINE_DATA:
|
||||
tess->combineDataCallback = (void(*)(GLdouble[3], void*[4], GLfloat[4], void**, void*))fn;
|
||||
break;
|
||||
case GLU_TESS_EDGE_FLAG:
|
||||
tess->edgeFlagCallback = (void(*)(GLboolean))fn;
|
||||
break;
|
||||
case GLU_TESS_EDGE_FLAG_DATA:
|
||||
tess->edgeFlagDataCallback = (void(*)(GLboolean, void*))fn;
|
||||
break;
|
||||
case GLU_TESS_ERROR:
|
||||
tess->errorCallback = (void(*)(GLenum))fn;
|
||||
break;
|
||||
case GLU_TESS_ERROR_DATA:
|
||||
tess->errorDataCallback = (void(*)(GLenum, void*))fn;
|
||||
break;
|
||||
case GLU_TESS_BEGIN:
|
||||
tess->beginCallback = (void(*)(GLenum))fn;
|
||||
break;
|
||||
case GLU_TESS_BEGIN_DATA:
|
||||
tess->beginDataCallback = (void(*)(GLenum, void*))fn;
|
||||
break;
|
||||
case GLU_TESS_END:
|
||||
tess->endCallback = (void(*)())fn;
|
||||
break;
|
||||
case GLU_TESS_END_DATA:
|
||||
tess->endDataCallback = (void(*)(void*))fn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gluTessProperty(GLUtesselator* tess, GLenum which, GLdouble value)
|
||||
{
|
||||
if (!tess) return;
|
||||
|
||||
if (which == GLU_TESS_WINDING_RULE)
|
||||
tess->windingRule = static_cast<GLenum>(value);
|
||||
}
|
||||
|
||||
void gluGetTessProperty(GLUtesselator* tess, GLenum which, GLdouble* value)
|
||||
{
|
||||
if (!tess || !value) return;
|
||||
|
||||
if (which == GLU_TESS_WINDING_RULE)
|
||||
*value = static_cast<GLdouble>(tess->windingRule);
|
||||
}
|
||||
|
||||
void gluTessNormal(GLUtesselator* tess, GLdouble x, GLdouble y, GLdouble z)
|
||||
{
|
||||
// Ignored - earcut works in 2D (XY plane)
|
||||
(void)tess; (void)x; (void)y; (void)z;
|
||||
}
|
||||
|
||||
void gluTessBeginPolygon(GLUtesselator* tess, void* userData)
|
||||
{
|
||||
if (!tess) return;
|
||||
|
||||
tess->contours.clear();
|
||||
tess->currentContour = nullptr;
|
||||
tess->polygonUserData = userData;
|
||||
}
|
||||
|
||||
void gluTessBeginContour(GLUtesselator* tess)
|
||||
{
|
||||
if (!tess) return;
|
||||
|
||||
tess->contours.emplace_back();
|
||||
tess->currentContour = &tess->contours.back();
|
||||
}
|
||||
|
||||
void gluTessVertex(GLUtesselator* tess, GLdouble coords[3], void* data)
|
||||
{
|
||||
if (!tess || !tess->currentContour) return;
|
||||
|
||||
TessVertex v;
|
||||
v.coords = {coords[0], coords[1], coords[2]};
|
||||
v.userData = data;
|
||||
tess->currentContour->push_back(v);
|
||||
}
|
||||
|
||||
void gluTessEndContour(GLUtesselator* tess)
|
||||
{
|
||||
if (!tess) return;
|
||||
tess->currentContour = nullptr;
|
||||
}
|
||||
|
||||
void gluTessEndPolygon(GLUtesselator* tess)
|
||||
{
|
||||
if (!tess) return;
|
||||
|
||||
// Need at least one contour with 3+ vertices
|
||||
if (tess->contours.empty())
|
||||
return;
|
||||
|
||||
const auto& mainContour = tess->contours[0];
|
||||
if (mainContour.size() < 3)
|
||||
return;
|
||||
|
||||
// Convert to SHAPE_LINE_CHAIN for earcut
|
||||
// Note: earcut works with integer coordinates, so we scale appropriately
|
||||
// KiCad uses nanometers internally, so coordinates are already integers
|
||||
SHAPE_LINE_CHAIN chain;
|
||||
for (const auto& v : mainContour) {
|
||||
chain.Append(VECTOR2I(static_cast<int>(v.coords[0]),
|
||||
static_cast<int>(v.coords[1])));
|
||||
}
|
||||
chain.SetClosed(true);
|
||||
|
||||
// Triangulate using KiCad's earcut implementation
|
||||
SHAPE_POLY_SET::TRIANGULATED_POLYGON result(-1);
|
||||
POLYGON_TRIANGULATION triangulator(result);
|
||||
|
||||
if (!triangulator.TesselatePolygon(chain, nullptr)) {
|
||||
// Tesselation failed - call error callback if registered
|
||||
if (tess->errorDataCallback)
|
||||
tess->errorDataCallback(100151, tess->polygonUserData); // GLU_TESS_ERROR1
|
||||
else if (tess->errorCallback)
|
||||
tess->errorCallback(100151);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call edge flag callback to indicate we're producing triangles
|
||||
// (edge flag callback forces GLU to output only triangles, which earcut always does)
|
||||
if (tess->edgeFlagDataCallback)
|
||||
tess->edgeFlagDataCallback(GL_TRUE, tess->polygonUserData);
|
||||
else if (tess->edgeFlagCallback)
|
||||
tess->edgeFlagCallback(GL_TRUE);
|
||||
|
||||
// Output triangles via vertex callback
|
||||
// The result contains triangle indices that map back to input vertices
|
||||
size_t triCount = result.GetTriangleCount();
|
||||
|
||||
for (size_t i = 0; i < triCount; i++) {
|
||||
VECTOR2I a, b, c;
|
||||
result.GetTriangle(i, a, b, c);
|
||||
|
||||
// Find the original vertex indices by matching coordinates
|
||||
// (POLYGON_TRIANGULATION preserves vertex order for the first N vertices)
|
||||
auto findVertex = [&](const VECTOR2I& pt) -> size_t {
|
||||
for (size_t j = 0; j < mainContour.size(); j++) {
|
||||
if (static_cast<int>(mainContour[j].coords[0]) == pt.x &&
|
||||
static_cast<int>(mainContour[j].coords[1]) == pt.y) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return 0; // fallback
|
||||
};
|
||||
|
||||
size_t idxA = findVertex(a);
|
||||
size_t idxB = findVertex(b);
|
||||
size_t idxC = findVertex(c);
|
||||
|
||||
// Call vertex callback for each triangle vertex
|
||||
// The callback receives the user data pointer that was passed to gluTessVertex
|
||||
if (tess->vertexDataCallback) {
|
||||
tess->vertexDataCallback(mainContour[idxA].userData, tess->polygonUserData);
|
||||
tess->vertexDataCallback(mainContour[idxB].userData, tess->polygonUserData);
|
||||
tess->vertexDataCallback(mainContour[idxC].userData, tess->polygonUserData);
|
||||
} else if (tess->vertexCallback) {
|
||||
tess->vertexCallback(mainContour[idxA].userData);
|
||||
tess->vertexCallback(mainContour[idxB].userData);
|
||||
tess->vertexCallback(mainContour[idxC].userData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned char* gluErrorString(GLenum error)
|
||||
{
|
||||
static const unsigned char errStr[] = "GLU tesselator error";
|
||||
(void)error;
|
||||
return errStr;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Quadric stubs - not implemented (used for 3D primitives, not critical)
|
||||
//=============================================================================
|
||||
|
||||
typedef struct GLUquadric GLUquadric;
|
||||
|
||||
GLUquadric* gluNewQuadric()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void gluDeleteQuadric(GLUquadric* q)
|
||||
{
|
||||
(void)q;
|
||||
}
|
||||
|
||||
void gluQuadricDrawStyle(GLUquadric* q, int s)
|
||||
{
|
||||
(void)q; (void)s;
|
||||
}
|
||||
|
||||
void gluQuadricNormals(GLUquadric* q, int n)
|
||||
{
|
||||
(void)q; (void)n;
|
||||
}
|
||||
|
||||
void gluCylinder(GLUquadric* q, double b, double t, double h, int sl, int st)
|
||||
{
|
||||
(void)q; (void)b; (void)t; (void)h; (void)sl; (void)st;
|
||||
}
|
||||
|
||||
void gluDisk(GLUquadric* q, double i, double o, int sl, int lp)
|
||||
{
|
||||
(void)q; (void)i; (void)o; (void)sl; (void)lp;
|
||||
}
|
||||
|
||||
void gluSphere(GLUquadric* q, double r, int sl, int st)
|
||||
{
|
||||
(void)q; (void)r; (void)sl; (void)st;
|
||||
}
|
||||
|
||||
void gluPerspective(double fovy, double aspect, double zNear, double zFar)
|
||||
{
|
||||
(void)fovy; (void)aspect; (void)zNear; (void)zFar;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
Loading…
Reference in a new issue