{"id":13773771,"url":"https://github.com/Ctrlmonster/r3f-effekseer","last_synced_at":"2025-05-11T06:30:33.135Z","repository":{"id":180155052,"uuid":"664694863","full_name":"Ctrlmonster/r3f-effekseer","owner":"Ctrlmonster","description":null,"archived":false,"fork":false,"pushed_at":"2023-07-24T16:03:57.000Z","size":2357,"stargazers_count":59,"open_issues_count":1,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-02-15T07:37:10.631Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ctrlmonster.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2023-07-10T14:39:44.000Z","updated_at":"2024-02-09T01:18:51.000Z","dependencies_parsed_at":"2024-01-13T11:57:38.035Z","dependency_job_id":"0a5f2fe0-05eb-4afc-a583-6749155178a9","html_url":"https://github.com/Ctrlmonster/r3f-effekseer","commit_stats":null,"previous_names":["ctrlmonster/r3f-effekseer"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ctrlmonster%2Fr3f-effekseer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ctrlmonster%2Fr3f-effekseer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ctrlmonster%2Fr3f-effekseer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ctrlmonster%2Fr3f-effekseer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ctrlmonster","download_url":"https://codeload.github.com/Ctrlmonster/r3f-effekseer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225017840,"owners_count":17407838,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T17:01:19.996Z","updated_at":"2024-11-17T08:31:31.085Z","avatar_url":"https://github.com/Ctrlmonster.png","language":"TypeScript","funding_links":[],"categories":["Particle System"],"sub_categories":["Visual Animation Editor"],"readme":"# Effekseer for React-Three-Fiber 🎆💥\n\nThis Library aims to provide React bindings for the **WebGL + WASM** runtime\nof [**Effekseer**](https://effekseer.github.io/en/). Effekseer is a mature **Particle Effect Creation Tool**,\nwhich supports major game engines, is used in many commercial games, and includes its\nown free to use editor, which you can use to create to your own effects!\n \n---------\n\n\u003e TODO: Section on how to install\n\n---------\n\n## Adding Effects to your Scene: `\u003cEffekt /\u003e`\nEffects are loaded from `.etf` files, which is the effects \nformat of Effekseer. You can export these yourself from the \nEffekseer Editor, or download some from the collection of\n[sample effects](https://effekseer.github.io/en/contribute.html).\n\n```tsx\nfunction MyScene() {\n  // get ref to EffectInstance for imperative control of effect\n  const effectRef = useRef\u003cEffectInstance\u003e(null!);\n\n  return (\n    // effects can be added anywhere inside parent component\n    \u003cEffekseer\u003e\n      \u003cmesh onClick={() =\u003e effectRef.current?.play()}\u003e\n        \u003csphereGeometry/\u003e\n        \u003cmeshStandardMaterial/\u003e\n\n        {/*Suspense is required for async loading of effect*/}\n        \u003cSuspense\u003e \n          \u003cEffekt ref={effectRef}\n                  name={\"Laser1\"}\n                  src={\"../assets/Laser1.efk\"}\n                  playOnMount // start playing as soon effect is ready\n                  position={[0, 1.5, 0]} // transforms are relative to parent mesh\n          /\u003e\n        \u003c/Suspense\u003e\n      \u003c/mesh\u003e\n    \u003c/Effekseer\u003e\n  )\n}\n```\n-------------\n\n## Controlling Effects imperatively via `EffectInstance`\n\nThe `\u003cEffekt /\u003e` component forwards a ref to an `EffectInstance`. This class gives you a persistent handle\nto a particular instance of this effect. You can have as many instances\nof one effect as you like. The `EffectInstance` provides you  with an\nimperative api that lets you set a variety of settings supported\nby Effekseer, as well as control playback of the effect. \n\nSome examples methods:\n\n```js\nconst effect = new EffectInstance(name, path); // or get via \u003cEffekt ref={effectRef}\u003e\neffect.setPaused(true); // pause / unpause this effect\neffect.stop(); // stop the effect from running\neffect.sendTrigger(index); // send trigger to effect (effekseer feature)\nawait effect.play(); // start a new run of this effect (returns a promise for completion)\n```\n\n\n## Effect Settings\nAll settings applied to an `EffectInstance` are **persistent** and will be applied to the current\nrun of this effect, as well as all future calls of `effect.play()`.\n```js\neffect.setSpeed(0.5); // set playback speed\neffect.setPosition(x, y, z); // set transforms relative to parent in scene tree\neffect.setColor(255, 0, 255, 255); // set rgba color\neffect.setVisible(false) // hide effect\n```\n\nTo **drop a setting**, call `effect.dropSetting(name)`. This is the full \nlist of **available settings**:\n```ts\ntype EffectInstanceSetting = \"paused\"\n  | \"position\"\n  | \"rotation\"\n  | \"scale\"\n  | \"speed\"\n  | \"randomSeed\"\n  | \"visible\"\n  | \"matrix\"\n  | \"dynamicInput\"\n  | \"targetPosition\"\n  | \"color\"\n```\nYou can also set settings via **props** on the `\u003cEffekt/\u003e` component. \nThis is full list of props available:\n\n```ts\ntype EffectProps = {\n  // required props for initialization / loading\n  name: string,\n  src: string,\n  // -----------------------------------------\n  // effect settings\n  position?: [x: number, y: number, z: number],\n  rotation?: [x: number, y: number, z: number],\n  scale?: [x: number, y: number, z: number],\n  speed?: number,\n  randomSeed?: number,\n  visible?: boolean,\n  dynamicInput?: (number | undefined)[],\n  targetPosition?: [x: number, y: number, z: number],\n  color?: [r: number, g: number, b: number, alpha: number],\n  paused?: boolean,\n  // -----------------------------------------\n  // r3f specifics\n  playOnMount?: boolean,\n  dispose?: null, // set to null to prevent unloading of effect on dismount\n  debug?: boolean,\n  // -----------------------------------------\n  // loading callbacks\n  onload?: (() =\u003e void) | undefined,\n  onerror?: ((reason: string, path: string) =\u003e void) | undefined,\n  redirect?: ((path: string) =\u003e string) | undefined,\n}\n```\n-------------\n\n## Spawning multiple Instances of the same effect\n\nAny Effect that has been loaded can be spawned multiple times. Either via the `EffectInstance` class,\nor via the `\u003cEffekt /\u003e` component. Simply re-use the same name and path, and you will get a new instance each time.\n\n```tsx\nconst instance1 = new EffectInstance(\"Laser1\", \"../assets/Laser1.efk\");\n\n// or via \u003cEffekt /\u003e component\n\u003cEffekt name={\"Laser1\"} src={\"../assets/Laser1.efk\"} speed={0.1} /\u003e\n\u003cEffekt name={\"Laser1\"} src={\"../assets/Laser1.efk\"} position={[0, 2, 0]} /\u003e\n```\n\n\n## The Parent Component: `\u003cEffekseer\u003e`\n\nThe `\u003cEffekseer\u003e` parent component provides its children with the React context to spawn effects.\nYou can access all loaded effects, as well as the manager singleton via the context.\n```ts\nconst {effects, manager} = useContext(EffekseerReactContext);\n```\n\nThe `\u003cEffekseer\u003e` component can be initialized with a set of native Effekseer settings, \na custom camera as well as prop to take over Rendering:\n```ts\ntype EffekseerSettings = {\n  instanceMaxCount?: number // default is 4000\n  squareMaxCount?: number, // default is 10000\n  enableExtensionsByDefault?: boolean, \n  enablePremultipliedAlpha?: boolean,\n  enableTimerQuery?: boolean,\n  onTimerQueryReport?: (averageDrawTime: number) =\u003e void,\n  timerQueryReportIntervalCount?: number,\n}\n```\n\n```tsx\n\u003cEffekseer settings={effekseerSettings} camera={DefaultCamera} ejectRenderer={false} /\u003e\n```\n--------\n## Loading \u0026 Rendering Effects: `effekseerManager`  \n\nThe parent component also forwards a ref to the `effekseerManager` **singleton**.\nThis object holds all loaded effects, handles the initialization of the wasm runtime, \nloading/unloading of effects and offers a limited imperative API to play effects, \nnext to the `EffectInstance` class.\n```js\nconst effectHandle = effectManager.playEffect(name); // play an effect\neffectHandle.setSpeed(0.5); // fleeting effect handle, becomes invalid once effect has finished.\n// view all loaded effects\nconsole.log(effectManager.effects) \n```\n\n### Overtaking the renderer:\nIf you decide to eject the default effekseer renderer, you can render yourself like this (it's\nwhat `\u003cEffekseer\u003e` does internally):\n\n```js\nuseFrame((state, delta) =\u003e {\n  state.gl.render();\n  effekseerManager.update(delta);\n}, 1); \n```\n**Note**: Setting `ejectRenderer` to true will also be required if\nyou plan on rendering effekseer effects as a postprocessing effect.\n\n-----------------\n## Preloading Runtime \u0026 Effects\nYou can start preloading via the manager. Preloading the runtime means it will already\nbe available when `\u003cEffekseer\u003e` mounts and preloading effects means they will already\nbe available when a `\u003cEffekt\u003e` component using this effect mounts.\u003cbr/\u003e\n```js\neffekseerManager.preload(); // Start initializing the wasm runtime before \u003cEffekseer\u003e mounts\n```\n\n**Note**: Effects can't actually start preloading before the `\u003cEffekseer\u003e` component mounts\nThis is because they rely on the `EffekseerContext` to be created, which can't be instantiated\nbefore WebGLRenderer, Camera and Scene exist. This is still useful to preload Effects\nthat don't get mounted with your initial render. Also preloading any effect will\nautomatically preload the runtime, meaning you don't have to call `preload`, if you're\ndoing `preloadEffect`.\n\n```js\neffekseerManager.preloadEffect(name, path); // will preload runtime automatically\n```\n---------------\n\n## Disable automatic Effect disposal:\nBy default, an effect will be disposed when the last `\u003cEffekt\u003e` using it unmounts. This means \nthe next time an \u003cEffekt\u003e component using that effect mounts, it will have to be loaded again. \nYou can disable this behaviour via setting the **dispose** prop to null. This way effects\nnever get disposed automatically. \n```jsx\n// Laser1.efk will not be unloaded\n\u003cEffekt name={\"Laser\"} path={\"../assets/Laser1.efk\"} dispose={null}\u003e\n```\nYou can unload the effect yourself via the `effekseerManager`. \u003cbr/\u003e\n**Note**: Since effects are stored by name, make sure to give each effect a **unique name**.\n```js\neffekseerManager.disposeEffect(\"Laser\");\n```\n\n\n---------------\n\n## Native API: `EffekseerContext`:\nThe `EffekseerManager` holds a reference to the `EffekseerContext` which\nis a class provided by Effekseer itself. If you are looking for **direct\naccess** to the **native API**, this is the place to look at. It includes methods like:\n```js\neffekseerManager.context.setProjectionMatrix();\neffekseerManager.context.setProjectionOrthographic();\neffekseerManager.context.loadEffectPackage();\n...\n```\n\n-------------------\n## Known issues / Gotchas:\n* There needs to be a background color assigned to the scene, or else \nblack parts of the particle image are not rendered transparently.\n\n## TODOs:\n* Check if all Effekseer Settings are being used in the effekseer.js \nfile, in the same that they were used in effekseer.src.js\n* Check if baseDir in manager needs to be settable\n\n## Next Steps / How to Contribute:\n* The Effekseer render pass needs to be adapted to be compatible\nwith the pmndrs PostProcessing lib (see Resources below) - check the\n`EffekseerRenderPass.tsx` for a wip.\n* Check what kind of additional methods to add to the Manager\n* Check if HMR experience can be improved\n\n--------------\n## References\n### Vanilla Three.js Demo\nI've included the Effekseer vanilla three demo for reference inside\nthe reference folder. \nJust run `python -m SimpleHTTPServer` or \n`python3 -m http.server` inside `references/html-demo/src` to view\nthe original demo in browser.\n\n### Effekseer Resources\n* website: https://effekseer.github.io/en/\n* effekseer webgl: https://github.com/effekseer/EffekseerForWebGL\n* effekseer post processing pass: https://github.com/effekseer/EffekseerForWebGL/blob/master/tests/post_processing_threejs.html\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCtrlmonster%2Fr3f-effekseer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCtrlmonster%2Fr3f-effekseer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCtrlmonster%2Fr3f-effekseer/lists"}