Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__pycache__/
*.pyc
artifacts/
.venv/
.idea/
.vscode/
*.DS_Store
node_modules/
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# SEC3ENGINE

A collection of WebGL rendering demos plus a lightweight engine prototype. The repository includes multiple HTML entry points and the `Sec3Engine` source tree (core, math helpers, third-party utilities, shaders, textures, and sample models).

## Project layout
- **`index.html`** – Loads the Sec3Engine core and runs the `SEC3DEMO` scene inside a canvas.
- **`particleDemo.html`** – Alternate entry that focuses on the particle system.
- **`Sec3Engine/`** – Engine sources and assets:
- `js/core`: scene graph, renderer, and runtime helpers.
- `js/math`: small math/geometry utility layer.
- `js/3party`: third-party JavaScript helpers used by the engine.
- `shader`: GLSL programs consumed by the demos.
- `textures` and `models`: sample assets (Sponza, spheres, planes, cube world, etc.).
- `demos` / `misc`: additional experiments and supporting files.
- **`NoEngines/`** – Legacy demo without the engine abstraction; open `NoEngines/index.html` to compare behaviors.

## Running locally
Serve the repo root so the demos can load their assets via HTTP. The simplest option uses Python:

```bash
python -m http.server 8000
```

Then open one of the pages in your browser:
- Main demo: `http://localhost:8000/index.html`
- Particle demo: `http://localhost:8000/particleDemo.html`
- Engine sample page: `http://localhost:8000/Sec3Engine/sec3index.html`
- Legacy comparison: `http://localhost:8000/NoEngines/index.html`

> If you change ports, update the URLs accordingly.

## Capturing screenshots with Playwright
Automate visual checks using the helper script. It works both locally (with Playwright installed) and inside this workspace via the built-in Playwright runtime.

```bash
python scripts/playwright_capture.py --url http://localhost:8000/index.html --out artifacts/home.png --wait 1500
```

Flags:
- `--url`: page to open.
- `--out`: where to save the screenshot (directories are created automatically).
- `--wait`: optional delay in milliseconds after navigation (defaults to `0`).
- `--width` / `--height`: viewport size (defaults to `1280x720`).

### Workspace usage
If you are in the ChatGPT/Codex workspace, start the local server and call `browser_container.run_playwright_script` to reuse the Playwright runtime without installing anything:

```python
from browser_container import run_playwright_script

run_playwright_script(
ports_to_forward=[8000],
script="""
import asyncio
from playwright.async_api import async_playwright

async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True, args=[\"--no-sandbox\", \"--disable-dev-shm-usage\"])
context = await browser.new_context(viewport={"width": 1280, "height": 720})
page = await context.new_page()
await page.goto('http://127.0.0.1:8000/index.html')
await page.wait_for_timeout(1000)
await page.screenshot(path='artifacts/example.png', full_page=True)
await browser.close()

asyncio.run(main())
""",
)
```

> Note: The Python `playwright` package is optional and may not install in restricted environments. Install it (and run `playwright install`) on a machine with network access if you want to run the helper locally.

## Development notes
- Assets and shaders are loaded relative to the repository root; serving over HTTP avoids CORS and local file access issues.
- The engine JavaScript is split between `js/core` (scene and renderer plumbing) and `js/math` utilities; third-party helpers live in `js/3party`.
- Sample models (Sponza, spheres, planes, cube world) reside under `Sec3Engine/models/` and are referenced by the demo HTML pages.

## Troubleshooting
- Use a modern browser with WebGL enabled; if assets fail to load, confirm you’re serving over HTTP rather than `file://`.
- For Playwright captures, ensure the server is running and the forwarded port matches the URL passed to the script.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
playwright
47 changes: 47 additions & 0 deletions scripts/playwright_capture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import argparse
import asyncio
from pathlib import Path

from playwright.async_api import async_playwright


def parse_args():
parser = argparse.ArgumentParser(description="Capture a screenshot of a URL using Playwright")
parser.add_argument("--url", default="http://localhost:8000/index.html", help="Page URL to capture")
parser.add_argument("--out", default="artifacts/screenshot.png", help="Path to save the screenshot")
parser.add_argument("--wait", type=int, default=0, help="Delay in milliseconds after navigation")
parser.add_argument("--width", type=int, default=1280, help="Viewport width")
parser.add_argument("--height", type=int, default=720, help="Viewport height")
return parser.parse_args()


async def capture(url: str, out_path: Path, wait_ms: int, width: int, height: int) -> None:
out_path.parent.mkdir(parents=True, exist_ok=True)

async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=["--no-sandbox", "--disable-dev-shm-usage"],
)
context = await browser.new_context(viewport={"width": width, "height": height})
page = await context.new_page()

try:
await page.goto(url)

if wait_ms > 0:
await page.wait_for_timeout(wait_ms)

await page.screenshot(path=str(out_path), full_page=True)
finally:
await context.close()
await browser.close()


async def main():
args = parse_args()
await capture(args.url, Path(args.out), args.wait, args.width, args.height)


if __name__ == "__main__":
asyncio.run(main())