@geastack/native-webgl-angle 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +4 -0
- package/geatsc-plugin-instancing.mjs +4 -2
- package/geatsc-plugin.mjs +2 -1
- package/native/angle_webgl_host.mm +88 -2
- package/package.json +1 -1
package/NOTICE
ADDED
|
@@ -147,11 +147,13 @@ export function transformAutomaticInstancing(text, fileName, replaceOne, renderI
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
export function transformCanonicalInstanceHooks(text, fileName) {
|
|
150
|
-
|
|
150
|
+
// Windows paths arrive with backslashes.
|
|
151
|
+
const normalized = fileName.replaceAll('\\', '/');
|
|
152
|
+
if (normalized.endsWith('/three/src/core/Object3D.js')) {
|
|
151
153
|
return text + `\nexport const nativeInstanceBeforeRender = Object3D.prototype.onBeforeRender;
|
|
152
154
|
export const nativeInstanceAfterRender = Object3D.prototype.onAfterRender;\n`;
|
|
153
155
|
}
|
|
154
|
-
if (
|
|
156
|
+
if (normalized.endsWith('/three/src/materials/Material.js')) {
|
|
155
157
|
return text + `\nexport const nativeInstanceMaterialBeforeRender = Material.prototype.onBeforeRender;
|
|
156
158
|
export const nativeInstanceBeforeCompile = Material.prototype.onBeforeCompile;
|
|
157
159
|
export const nativeInstanceProgramCacheKey = Material.prototype.customProgramCacheKey;\n`;
|
package/geatsc-plugin.mjs
CHANGED
|
@@ -2010,7 +2010,8 @@ const transformSource = ({ fileName, text }) => {
|
|
|
2010
2010
|
}
|
|
2011
2011
|
if (normalized.endsWith("/three/src/renderers/WebGLRenderer.js")) {
|
|
2012
2012
|
transformed =
|
|
2013
|
-
|
|
2013
|
+
// Forward slashes: a Windows path's backslashes would be escapes in this literal.
|
|
2014
|
+
`import { nativeProfilePhase } from '${fileURLToPath(new URL("./src/nativeWebGLHost.ts", import.meta.url)).replaceAll("\\", "/")}';\n` +
|
|
2014
2015
|
transformed;
|
|
2015
2016
|
transformed = replaceOne(
|
|
2016
2017
|
transformed,
|
|
@@ -168,6 +168,23 @@ extern "C" bool gea_three_key_pressed(double code) {
|
|
|
168
168
|
return gGeaKeyState[c];
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
#if defined(_WIN32)
|
|
172
|
+
// The Win32 window procedure owns input on Windows; it reports into the same
|
|
173
|
+
// tables the AppKit host view fills on macOS (keys as macOS virtual key codes).
|
|
174
|
+
extern "C" void gea_three_set_key_state(int code, bool down) {
|
|
175
|
+
if (code >= 0 && code < 256) gGeaKeyState[code] = down;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
extern "C" void gea_three_set_pointer_state(double x, double y, double width, double height, bool down) {
|
|
179
|
+
gGeaPointerX = x;
|
|
180
|
+
gGeaPointerY = y;
|
|
181
|
+
gGeaPointerW = width;
|
|
182
|
+
gGeaPointerH = height;
|
|
183
|
+
gGeaPointerDown = down;
|
|
184
|
+
gGeaPointerSeq += 1.0;
|
|
185
|
+
}
|
|
186
|
+
#endif
|
|
187
|
+
|
|
171
188
|
#if !defined(_WIN32)
|
|
172
189
|
@interface GeaAngleWebGLHostView : NSView
|
|
173
190
|
@end
|
|
@@ -1064,8 +1081,22 @@ extern "C" bool gea_three_webgl_attach(
|
|
|
1064
1081
|
if (gWebGL.ready) return true;
|
|
1065
1082
|
|
|
1066
1083
|
#if defined(_WIN32)
|
|
1067
|
-
|
|
1068
|
-
|
|
1084
|
+
// An override (e.g. threejs-rendozer's GeaRendozerGLES.dll) first, then the
|
|
1085
|
+
// packaged ANGLE of a UWP app, then a plain desktop load next to the exe.
|
|
1086
|
+
auto openWin32 = [](const wchar_t *overrideEnv, const wchar_t *name) -> HMODULE {
|
|
1087
|
+
wchar_t overridePath[MAX_PATH];
|
|
1088
|
+
const DWORD n = GetEnvironmentVariableW(overrideEnv, overridePath, MAX_PATH);
|
|
1089
|
+
if (n > 0 && n < MAX_PATH) {
|
|
1090
|
+
if (HMODULE m = LoadLibraryW(overridePath)) return m;
|
|
1091
|
+
}
|
|
1092
|
+
#if defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP
|
|
1093
|
+
return LoadPackagedLibrary(name, 0);
|
|
1094
|
+
#else
|
|
1095
|
+
return LoadLibraryW(name);
|
|
1096
|
+
#endif
|
|
1097
|
+
};
|
|
1098
|
+
gWebGL.egl = openWin32(L"GEA_ANGLE_EGL_DYLIB", L"libEGL.dll");
|
|
1099
|
+
gWebGL.gles = openWin32(L"GEA_ANGLE_GLES_DYLIB", L"libGLESv2.dll");
|
|
1069
1100
|
#else
|
|
1070
1101
|
static const char *const eglPaths[] = {
|
|
1071
1102
|
"/Applications/Visual Studio Code.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libEGL.dylib",
|
|
@@ -1400,6 +1431,61 @@ extern "C" void gea_three_webgl_swap() {
|
|
|
1400
1431
|
}
|
|
1401
1432
|
}
|
|
1402
1433
|
}
|
|
1434
|
+
// Debug recording: GEA_WEBGL_RECORD=<path>[:<seconds>] appends the
|
|
1435
|
+
// framebuffer as raw top-down RGB24 at a constant 30 fps of wall-clock time
|
|
1436
|
+
// (frames repeat or drop to hold the rate), then closes the file. The size
|
|
1437
|
+
// goes to the smoke log; encode with ffmpeg -f rawvideo -pix_fmt rgb24.
|
|
1438
|
+
if (const char *recordSpec = std::getenv("GEA_WEBGL_RECORD"); recordSpec && gWebGL.glReadPixels) {
|
|
1439
|
+
static FILE *recordFile = nullptr;
|
|
1440
|
+
static bool recordDone = false;
|
|
1441
|
+
static double recordSeconds = 30.0;
|
|
1442
|
+
static long long recordWritten = 0;
|
|
1443
|
+
static int recordW = 0, recordH = 0;
|
|
1444
|
+
static std::chrono::steady_clock::time_point recordStart;
|
|
1445
|
+
if (!recordDone && !recordFile) {
|
|
1446
|
+
std::string spec(recordSpec);
|
|
1447
|
+
// A trailing ":<seconds>" sets the length; drive-letter colons stay in the path.
|
|
1448
|
+
if (const size_t colon = spec.rfind(':'); colon != std::string::npos && colon > 1) {
|
|
1449
|
+
recordSeconds = std::atof(spec.c_str() + colon + 1);
|
|
1450
|
+
spec.resize(colon);
|
|
1451
|
+
}
|
|
1452
|
+
recordW = gWebGL.widthPx;
|
|
1453
|
+
recordH = gWebGL.heightPx;
|
|
1454
|
+
recordFile = std::fopen(spec.c_str(), "wb");
|
|
1455
|
+
recordStart = std::chrono::steady_clock::now();
|
|
1456
|
+
if (recordFile) {
|
|
1457
|
+
smokeLog("[record] %dx%d rgb24 30fps %.1fs -> %s", recordW, recordH, recordSeconds, spec.c_str());
|
|
1458
|
+
} else {
|
|
1459
|
+
recordDone = true;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
if (recordFile) {
|
|
1463
|
+
const double elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() - recordStart).count();
|
|
1464
|
+
const long long due = std::min(static_cast<long long>(elapsed * 30.0) + 1,
|
|
1465
|
+
static_cast<long long>(recordSeconds * 30.0));
|
|
1466
|
+
if (due > recordWritten && gWebGL.widthPx == recordW && gWebGL.heightPx == recordH) {
|
|
1467
|
+
std::vector<unsigned char> rgba(static_cast<size_t>(recordW) * recordH * 4);
|
|
1468
|
+
std::vector<unsigned char> rgb(static_cast<size_t>(recordW) * recordH * 3);
|
|
1469
|
+
gWebGL.glReadPixels(0, 0, recordW, recordH, GL_RGBA_VALUE, GL_UNSIGNED_BYTE, rgba.data());
|
|
1470
|
+
for (int y = 0; y < recordH; ++y) {
|
|
1471
|
+
const unsigned char *src = rgba.data() + static_cast<size_t>(recordH - 1 - y) * recordW * 4;
|
|
1472
|
+
unsigned char *dst = rgb.data() + static_cast<size_t>(y) * recordW * 3;
|
|
1473
|
+
for (int x = 0; x < recordW; ++x) {
|
|
1474
|
+
dst[x * 3 + 0] = src[x * 4 + 0];
|
|
1475
|
+
dst[x * 3 + 1] = src[x * 4 + 1];
|
|
1476
|
+
dst[x * 3 + 2] = src[x * 4 + 2];
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
for (; recordWritten < due; ++recordWritten) std::fwrite(rgb.data(), 1, rgb.size(), recordFile);
|
|
1480
|
+
}
|
|
1481
|
+
if (recordWritten >= static_cast<long long>(recordSeconds * 30.0)) {
|
|
1482
|
+
std::fclose(recordFile);
|
|
1483
|
+
recordFile = nullptr;
|
|
1484
|
+
recordDone = true;
|
|
1485
|
+
smokeLog("[record] done: %lld frames", recordWritten);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1403
1489
|
// Measure presentation separately from the display-link callback. The
|
|
1404
1490
|
// latter includes this wait and cannot by itself distinguish frame work
|
|
1405
1491
|
// from drawable/GPU backpressure. No clock reads run when profiling is off.
|