Commit Needle production dist and nginx-only Docker image

- Track Needle/MenuScene/dist and Needle/SampleScene/dist for container deploys
- Dockerfile copies pre-built dist (no npm in image); rebuild with npm run build before commit
- Fix Needle .gitignore: /assets/ so dist/assets is not ignored
- Git LFS: track *.wasm; relax .dockerignore so dist is sent as build context

Made-with: Cursor
This commit is contained in:
pelpanagiotis
2026-04-19 23:39:59 +03:00
parent 98062ef858
commit 6947303755
169 changed files with 91778 additions and 31 deletions
+1 -4
View File
@@ -14,12 +14,9 @@ MemoryCaptures/
Recordings/ Recordings/
obj/ obj/
# Host-side Node (reinstalled in container) # Host-side Node (not needed when image serves pre-built dist)
**/node_modules/ **/node_modules/
# Build output (rebuilt in container)
**/Needle/*/dist/
# OS # OS
.DS_Store .DS_Store
Thumbs.db Thumbs.db
+1
View File
@@ -9,3 +9,4 @@
*.wav filter=lfs diff=lfs merge=lfs -text *.wav filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text *.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text *.ogg filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
+1 -1
View File
@@ -21,7 +21,7 @@
# Node / Needle web builds # Node / Needle web builds
**/node_modules/ **/node_modules/
Needle/**/dist/ # Needle/*/dist/ is committed for Docker (rebuild with npm run build before commit)
# OS # OS
.DS_Store .DS_Store
+6 -22
View File
@@ -1,29 +1,13 @@
# Needle Engine web export (Vite) → static nginx # Serves pre-built Needle/Vite output from the repo (Needle/<app>/dist).
# # Build locally first: cd Needle/MenuScene && npm run build
# Prerequisites before `docker build`: # Optional: Needle/SampleScene && npm run build (for compose profile "sample")
# - Export your scene from Unity so Needle/MenuScene (or SampleScene) contains:
# - src/generated/ (gen.js, register_types, etc.)
# - assets/ with your .glb and any files the Vite build copies
# - `npm run build` should succeed locally for the same NEEDLE_APP.
#
# Build needs network: Needle's build may verify license against needle.tools.
FROM node:22-alpine AS build
WORKDIR /build
ARG NEEDLE_APP=MenuScene ARG NEEDLE_APP=MenuScene
ENV NEEDLE_APP=${NEEDLE_APP}
COPY Needle/${NEEDLE_APP}/package.json Needle/${NEEDLE_APP}/package-lock.json ./ FROM nginx:1.27-alpine
RUN npm ci
COPY Needle/${NEEDLE_APP}/ ./ ARG NEEDLE_APP
COPY Needle/${NEEDLE_APP}/dist /usr/share/nginx/html
RUN npm run build
FROM nginx:1.27-alpine AS runtime
COPY --from=build /build/dist /usr/share/nginx/html
COPY docker/nginx-default.conf /etc/nginx/conf.d/default.conf COPY docker/nginx-default.conf /etc/nginx/conf.d/default.conf
EXPOSE 80 EXPOSE 80
+2 -2
View File
@@ -1,7 +1,7 @@
**/node_modules **/node_modules
assets/ # Only ignore exported Unity assets at project root, not dist/assets/
/assets/
src/generated/ src/generated/
dist/
include/draco/ include/draco/
include/ktx2/ include/ktx2/
include/three/ include/three/
@@ -0,0 +1 @@
import{t as e}from"./needle-engine@5.0.3.js";export{e as GenerateMeshBVHWorker};
Binary file not shown.
@@ -0,0 +1 @@
import{u as e}from"./materialx.CnHmG--Y.js";export{e as default};
@@ -0,0 +1 @@
import{c as e}from"./materialx.CnHmG--Y.js";export{e as default};
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
import{l as e}from"./materialx.CnHmG--Y.js";export{e as default};
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
import{n as e,r as t,t as n}from"./postprocessing.ao.BRYreHJS.js";export{e as DepthType,n as N8AOPass,t as N8AOPostPass};
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./register_types.C910Zu9b.js","./needle-engine-webxr.DQA26K-l.js","./materialx.CnHmG--Y.js","./rolldown-runtime.CYVBeYYp.js","./three@0.169.19.js","./needle-engine-ui.BzyLX9fs.js","./gltf-progressive.BBJGcb3s.js","./three-examples@0.169.19.js","./three-mesh-ui.CgGzZ3ml.js","./needle-engine@5.0.3.js","./needle-engine.extras.t1Dx-yNB.js","./needle-engine-particles.Bw3zzNt3.js","./three-quarks.DALdLWPD.js","./needle-engine.extras.CDv9-f43.js","./postprocessing.Wy71DpiM.js","./three-mesh-bvh.B-oXEGo2.js","./needle-engine.V_YEpsUE.js"])))=>i.map(i=>d[i]);
import{d as e}from"./materialx.CnHmG--Y.js";import{_t as t,fn as n,wi as r}from"./needle-engine-ui.BzyLX9fs.js";import"./needle-engine@5.0.3.js";import{x as i}from"./needle-engine-webxr.DQA26K-l.js";(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})(),globalThis[`needle:dependencies:ready`]=e(()=>import(`./register_types.C910Zu9b.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]),import.meta.url);var a=[];globalThis[`needle:codegen_files`]=a,a.push(`./assets/MenuScene.glb`),document.addEventListener(`DOMContentLoaded`,()=>{let e=document.querySelector(`needle-engine`);e&&e.getAttribute(`src`)===null&&(e.setAttribute(`hash`,`1776630853539`),e.setAttribute(`src`,JSON.stringify(a)))}),console.log(`Made with ♥ by 🌵 Needle - https://needle.tools — Version 5.0.3`),r(e=>{let t=e.scene.getComponentInChildren(i);t||(t=e.scene.addComponent(i),t.createARButton=!0,t.createVRButton=!0),t.autoPlace=!0,t.autoCenter=!0,t.arScale=1});function o(e){document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,()=>e(),{once:!0}):e()}function s(){let e=document.querySelector(`needle-engine`),r=document.querySelector(`#asset-picker-prev`),i=document.querySelector(`#asset-picker-next`),a=document.querySelector(`#asset-picker-ar`),s=document.querySelector(`#asset-picker-label`),c=document.querySelector(`#asset-picker-index`);if(!e||!r||!i||!a||!s||!c)return;let l=null,u=!1,d=!1,f=!1,p=()=>{l&&l.getDishSlotCount()>0?(s.textContent=l.getPickerLabel(),c.textContent=``):l?(s.textContent=`Menu (assign dishes in Unity)`,c.textContent=``):(s.textContent=`Menu scene`,c.textContent=``);let e=l!==null&&l.getDishSlotCount()>1;r.disabled=!e,i.disabled=!e,a.disabled=!d||f||u},m=async()=>{try{let n=await e.getContext();l=t(MenuController,n)}catch{l=null}p()};n.isARSupported().then(e=>{d=e,p()});let h=e=>{!l||l.getDishSlotCount()<=1||(e<0?l.selectPreviousDish():l.selectNextDish(),p())},g=async()=>{if(!(!d||f||u)){f=!0,p();try{let t=await e.getContext();await n.start(`immersive-ar`,void 0,t)}catch(e){console.warn(`[assetPicker] Failed to start AR session:`,e)}finally{f=!1,p()}}};r.addEventListener(`click`,()=>h(-1)),i.addEventListener(`click`,()=>h(1)),a.addEventListener(`click`,()=>void g()),e.addEventListener(`enter-ar`,()=>{u=!0,p()}),e.addEventListener(`exit-ar`,()=>{u=!1,p()}),e.addEventListener(`enter-vr`,()=>{u=!0,p()}),e.addEventListener(`exit-vr`,()=>{u=!1,p()}),e.addEventListener(`loadfinished`,()=>void m()),o(()=>{requestAnimationFrame(()=>void m())})}s(),e(()=>import(`./needle-engine.V_YEpsUE.js`),__vite__mapDeps([16,10,2,3,4,1,5,6,7,8,11,12,13,14,9,15]),import.meta.url);
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
html{height:-webkit-fill-available}body{margin:0;padding:0}needle-engine{width:100%;height:100%;position:absolute;top:0;left:0}#asset-picker{left:0;right:0;bottom:calc(4.75rem + env(safe-area-inset-bottom,0px));z-index:600;pointer-events:auto;box-sizing:border-box;justify-content:center;padding:12px;display:flex;position:fixed}#asset-picker .asset-picker__inner{color:#f2f2f7;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);background:#0f0f14d1;border-radius:12px;flex-wrap:wrap;align-items:center;gap:12px;max-width:min(100%,36rem);padding:10px 14px;font-family:system-ui,-apple-system,Segoe UI,sans-serif;font-size:.95rem;display:flex;box-shadow:0 4px 24px #00000059}#asset-picker button{pointer-events:auto;touch-action:manipulation;cursor:pointer;font:inherit;color:#0f0f14;background:#e8e8ed;border:none;border-radius:8px;padding:8px 14px;font-weight:600}#asset-picker button:disabled{opacity:.45;cursor:not-allowed}#asset-picker #asset-picker-ar{color:#0a0a0c;background:#34c759}#asset-picker #asset-picker-ar:disabled{color:#ffffff80;background:#3a3a3e}#asset-picker .asset-picker__label{text-overflow:ellipsis;white-space:nowrap;text-align:center;flex:1;min-width:0;overflow:hidden}#asset-picker .asset-picker__index{opacity:.85;font-variant-numeric:tabular-nums;flex-shrink:0;font-size:.85rem}
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
import{a as e,i as t,n,o as r,r as i,s as a,t as o}from"./materialx.CnHmG--Y.js";export{o as Experimental_API,e as MaterialXEnvironment,n as MaterialXLoader,t as MaterialXMaterial,r as preloadWasm,a as ready,i as useNeedleMaterialX};
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
import{u as e}from"./needle-engine@5.0.3.js";export{e as NeedleEngineWebComponent};
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -0,0 +1 @@
import{a as e,i as t,n,r}from"./three-mesh-bvh.B-oXEGo2.js";export{e as MeshBVH,n as StaticGeometryGenerator,r as acceleratedRaycast,t as computeBoundsTree};
Binary file not shown.
@@ -0,0 +1 @@
import{t as e}from"./needle-engine.D6xdy26G.js";export{e as GLTFAnimationPointerExtension};
@@ -0,0 +1 @@
import{$ as ee,$t as te,A as ne,At as e,B as t,Bt as n,C as r,Ct as re,D as ie,Dt as ae,E as oe,Et as i,F as a,Ft as o,G as s,Gt as c,H as l,Ht as u,I as d,It as f,J as p,Jt as m,K as h,Kt as g,L as _,Lt as v,M as y,Mt as b,N as x,Nt as S,O as C,Ot as w,P as T,Pt as E,Q as D,Qt as O,R as k,Rt as A,S as j,St as M,T as N,Tt as P,U as F,Ut as I,V as L,Vt as R,W as z,Wt as B,X as V,Xt as H,Y as U,Yt as W,Z as G,Zt as K,_ as q,_t as se,a as ce,an as le,at as J,b as ue,bt as Y,c as de,cn as fe,ct as pe,d as me,dt as he,en as ge,et as _e,f as ve,ft as ye,g as be,gt as xe,h as Se,ht as X,i as Ce,in as we,it as Te,j as Ee,jt as De,k as Oe,kt as ke,l as Ae,ln as je,lt as Z,m as Me,mt as Ne,n as Pe,nn as Fe,nt as Ie,o as Le,on as Re,ot as Q,p as ze,pt as Be,q as Ve,qt as He,r as Ue,rn as We,rt as Ge,s as Ke,sn as qe,st as Je,t as Ye,tn as Xe,tt as Ze,u as Qe,ut as $e,v as et,vt as tt,w as nt,wt as rt,x as $,xt as it,y as at,yt as ot,z as st,zt as ct}from"./postprocessing.Wy71DpiM.js";export{Ye as ASCIIEffect,Pe as ASCIITexture,Ue as AdaptiveLuminanceMaterial,Ce as AdaptiveLuminancePass,ce as BlendFunction,Le as BlendMode,Ke as BloomEffect,Q as BlurPass,Q as KawaseBlurPass,de as BokehEffect,Ae as BokehMaterial,Qe as BoxBlurMaterial,me as BoxBlurPass,ve as BrightnessContrastEffect,ze as ChromaticAberrationEffect,Me as CircleOfConfusionMaterial,Se as ClearMaskPass,be as ClearPass,q as ColorAverageEffect,et as ColorChannel,at as ColorDepthEffect,_ as ColorEdgesMaterial,_ as EdgeDetectionMaterial,J as ConvolutionMaterial,J as KawaseBlurMaterial,ue as CopyMaterial,$ as CopyPass,$ as SavePass,j as DepthComparisonMaterial,r as DepthCopyMaterial,nt as DepthCopyMode,N as DepthCopyPass,N as DepthSavePass,oe as DepthDownsamplingMaterial,ie as DepthDownsamplingPass,C as DepthEffect,Oe as DepthMaskMaterial,ne as DepthOfFieldEffect,Ee as DepthPass,y as DepthPickingPass,x as DepthTestStrategy,T as Disposable,a as DotScreenEffect,d as DownsamplingMaterial,k as EdgeDetectionMode,st as Effect,t as EffectAttribute,L as EffectComposer,l as EffectMaterial,F as EffectPass,z as EffectShaderData,s as EffectShaderSection,s as Section,h as FXAAEffect,Ve as GammaCorrectionEffect,p as GaussKernel,U as GaussianBlurMaterial,V as GaussianBlurPass,G as GlitchEffect,D as GlitchMode,ee as GodRaysEffect,_e as GodRaysMaterial,Ze as GridEffect,Ie as HueSaturationEffect,Ge as ImmutableTimer,Te as Initializable,Je as KernelSize,pe as LUT1DEffect,Z as LUT3DEffect,Z as LUTEffect,$e as LUT3dlLoader,he as LUTCubeLoader,ye as LUTOperation,Be as LambdaPass,Ne as LensDistortionEffect,X as LookupTexture,X as LookupTexture3D,xe as LuminanceMaterial,se as LuminancePass,tt as MaskFunction,ot as MaskMaterial,Y as MaskPass,it as MipmapBlurPass,M as NoiseEffect,re as NoiseTexture,rt as NormalPass,i as OutlineEdgesMaterial,i as OutlineMaterial,P as OutlineEffect,ae as OverrideMaterialManager,w as Pass,ke as PixelationEffect,e as PredicationMode,De as RawImageData,b as RealisticBokehEffect,S as RenderPass,E as Resizable,o as Resizer,o as Resolution,f as SMAAAreaImageData,v as SMAAEffect,A as SMAAImageGenerator,ct as SMAAImageLoader,n as SMAAPreset,R as SMAASearchImageData,u as SMAAWeightsMaterial,I as SSAOEffect,B as SSAOMaterial,c as ScanlineEffect,g as Selection,He as SelectiveBloomEffect,m as SepiaEffect,W as ShaderPass,H as ShockWaveEffect,K as TetrahedralUpscaler,O as TextureEffect,te as TiltShiftBlurMaterial,ge as TiltShiftBlurPass,Xe as TiltShiftEffect,Fe as Timer,We as ToneMappingEffect,we as ToneMappingMode,le as UpsamplingMaterial,Re as VignetteEffect,qe as VignetteTechnique,fe as WebGLExtension,je as version};
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
+41
View File
@@ -0,0 +1,41 @@
import{Ar as e,Kn as t,_n as n,fn as r,kr as i}from"./three@0.169.19.js";import{$i as a,Xi as o,ct as s,eo as c,ot as l}from"./needle-engine-ui.BzyLX9fs.js";import"./needle-engine@5.0.3.js";import{m as u}from"./needle-engine-webxr.DQA26K-l.js";import{m as d,t as f}from"./needle-engine.extras.CDv9-f43.js";var p=class extends l{raycaster=new t;touchPos=new i;plane=new n(new e(0,1,0),0);initialPinchDistance=0;initialScale=new e;isScaling=!1;onEnable(){let e=this.context.renderer.domElement;e.addEventListener(`touchstart`,this.onTouchStart),e.addEventListener(`touchmove`,this.onTouchMove),e.addEventListener(`touchend`,this.onTouchEnd)}onDisable(){let e=this.context.renderer.domElement;e.removeEventListener(`touchstart`,this.onTouchStart),e.removeEventListener(`touchmove`,this.onTouchMove),e.removeEventListener(`touchend`,this.onTouchEnd)}onTouchStart=e=>{if(e.touches.length===2){this.isScaling=!0;let t=e.touches[0],n=e.touches[1];this.initialPinchDistance=Math.hypot(n.clientX-t.clientX,n.clientY-t.clientY),this.initialScale.copy(this.gameObject.scale)}};onTouchMove=t=>{if(t.preventDefault(),this.isScaling&&t.touches.length===2){let e=t.touches[0],n=t.touches[1],r=Math.hypot(n.clientX-e.clientX,n.clientY-e.clientY)/this.initialPinchDistance,i=this.initialScale.clone().multiplyScalar(r);this.gameObject.scale.copy(i)}else if(t.touches.length===1&&!this.isScaling){let n=t.touches[0],r=this.context.renderer.domElement.getBoundingClientRect();this.touchPos.set((n.clientX-r.left)/r.width*2-1,-((n.clientY-r.top)/r.height)*2+1),this.raycaster.setFromCamera(this.touchPos,this.context.mainCamera);let i=new e;this.raycaster.ray.intersectPlane(this.plane,i)&&this.gameObject.position.copy(i)}};onTouchEnd=e=>{e.touches.length<2&&(this.isScaling=!1)}};function m(e,t,n,r){var i=arguments.length,a=i<3?t:r===null?r=Object.getOwnPropertyDescriptor(t,n):r,o;if(typeof Reflect==`object`&&typeof Reflect.decorate==`function`)a=Reflect.decorate(e,t,n,r);else for(var s=e.length-1;s>=0;s--)(o=e[s])&&(a=(i<3?o(a):i>3?o(t,n,a):o(t,n))||a);return i>3&&a&&Object.defineProperty(t,n,a),a}var h=new WeakMap,g=class extends l{isMobile=!1;isDesktop=!1;isXR=!1;dishName=``;dishes=[];webXROrigin;dishBobAmplitude=.05;dishBobSpeed=2.5;usdzExporter;arSessionBobPaused=!1;selectedDishIndex=0;onEnable(){if(this.dishName=new URLSearchParams(window.location.search).get(`dishName`)??``,this.webXROrigin&&(this.usdzExporter=this.webXROrigin.getComponent(u)??void 0),this.dishName){let e=!1;this.dishes.forEach((t,n)=>{t&&t.name===this.dishName&&(this.selectedDishIndex=n,e=!0)}),this.dishes.forEach(t=>{if(!t)return;let n=e&&t.name===this.dishName;n||this.restoreDishBaseY(t),s.setActive(t,n)}),e||this.ensureOnlySelectedDishVisible()}else this.ensureOnlySelectedDishVisible();this.updateUSDZExporterTarget(),this.checkForDeviceType().then(()=>{this.isMobile?console.log(`[MenuController] isMobile`):this.isDesktop?this.setupDesktopControls():this.isXR}),this.setupMobileControls(),this.disableDoubleTapZoom()}onEnterXR(e){e.xr.mode===`immersive-ar`&&(this.arSessionBobPaused=!0,this.snapActiveDishToBaseY())}onLeaveXR(e){this.arSessionBobPaused=!1}update(){if(this.arSessionBobPaused||this.dishBobAmplitude<=0||this.getValidDishIndices().length===0)return;let e=this.dishes[this.selectedDishIndex];if(!e)return;let t=h.get(e);t===void 0&&(t=e.position.y,h.set(e,t));let n=this.context.time.time;e.position.y=t+Math.sin(n*this.dishBobSpeed)*this.dishBobAmplitude}async checkForDeviceType(){await this.isXRDevice()?this.isXR=!0:(console.log(`DeviceUtilities.isMobileDevice()`,c.isMobileDevice()),this.isMobile=c.isMobileDevice(),this.isMobile||(this.isDesktop=c.isDesktop()))}async isXRDevice(){if(navigator.xr)try{return await navigator.xr.isSessionSupported(`immersive-vr`)}catch{return console.log(`XR check error!`),!1}return!1}setupMobileControls(){typeof document<`u`&&document.querySelector(`#asset-picker`)||this.createMenuMobileControls()}setupDesktopControls(){}createMenuMobileControls(){let e=document.createElement(`div`);e.id=`menuControlsZone`,e.style.cssText=`
position: absolute;
display: flex;
justify-content: space-between;
bottom: 10%;
left: 10%;
right: 10%;
height: 150px;
`,document.body.appendChild(e);let t=document.createElement(`button`);t.id=`previousButton`,t.style.cssText=`
height: 100px;
width: 100px;
background-color: #ffffff;
color: #111111;
border: none;
border-radius: 50px;
box-shadow: 0 8px 24px rgba(0,0,0,0.25);
display: flex;
align-items: center;
justify-content: center;
touch-action: manipulation;
`,t.setAttribute(`aria-label`,`Previous`),t.innerHTML=`
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
`,t.onclick=this.selectPreviousDish.bind(this);let n=document.createElement(`button`);n.id=`nextButton`,n.style.cssText=`
height: 100px;
width: 100px;
background-color: #ffffff;
color: #111111;
border: none;
border-radius: 50px;
box-shadow: 0 8px 24px rgba(0,0,0,0.25);
display: flex;
align-items: center;
justify-content: center;
touch-action: manipulation;
`,n.setAttribute(`aria-label`,`Next`),n.innerHTML=`
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9 6L15 12L9 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
`,n.onclick=this.selectNextDish.bind(this),this.dishName&&(t.disabled=!0,t.style.display=`none`,n.disabled=!0,n.style.display=`none`),e.appendChild(t),e.appendChild(n)}disableDoubleTapZoom(){let e=0;document.addEventListener(`touchend`,t=>{let n=Date.now();n-e<=300&&t.preventDefault(),e=n},{passive:!1})}getValidDishIndices(){return this.dishes.map((e,t)=>e==null?-1:t).filter(e=>e>=0)}ensureOnlySelectedDishVisible(){let e=this.getValidDishIndices();if(e.length===0)return;let t=this.selectedDishIndex;e.indexOf(t)<0&&(t=e[0],this.selectedDishIndex=t),e.forEach(e=>{let t=e===this.selectedDishIndex,n=this.dishes[e];t||this.restoreDishBaseY(n),s.setActive(this.dishes[e],t)})}restoreDishBaseY(e){if(!e)return;let t=h.get(e);t!==void 0&&(e.position.y=t)}snapActiveDishToBaseY(){this.restoreDishBaseY(this.dishes[this.selectedDishIndex])}getDishSlotCount(){return this.getValidDishIndices().length}getPickerLabel(){let e=this.getValidDishIndices();if(e.length===0)return`Menu`;let t=Math.max(0,e.indexOf(this.selectedDishIndex)),n=this.dishes[this.selectedDishIndex]?.name?.trim();return n?`${n} (${t+1}/${e.length})`:`Dish ${t+1} / ${e.length}`}selectPreviousDish(){let e=this.getValidDishIndices();if(e.length===0)return;let t=e.indexOf(this.selectedDishIndex);t<0&&(t=0),this.restoreDishBaseY(this.dishes[e[t]]),s.setActive(this.dishes[e[t]],!1),t=(t-1+e.length)%e.length,this.selectedDishIndex=e[t],s.setActive(this.dishes[this.selectedDishIndex],!0),this.updateUSDZExporterTarget()}selectNextDish(){let e=this.getValidDishIndices();if(e.length===0)return;let t=e.indexOf(this.selectedDishIndex);t<0&&(t=0),this.restoreDishBaseY(this.dishes[e[t]]),s.setActive(this.dishes[e[t]],!1),t=(t+1)%e.length,this.selectedDishIndex=e[t],s.setActive(this.dishes[this.selectedDishIndex],!0),this.updateUSDZExporterTarget()}updateUSDZExporterTarget(){let e=this.dishes[this.selectedDishIndex];this.usdzExporter&&e&&(this.usdzExporter.objectToExport=e)}getUrlParameter(e){return new URLSearchParams(window.location.search).get(e)}};m([o(r)],g.prototype,`dishes`,void 0),m([o(r)],g.prototype,`webXROrigin`,void 0),m([o()],g.prototype,`dishBobAmplitude`,void 0),m([o()],g.prototype,`dishBobSpeed`,void 0);var _=class extends l{volume;start(){if(!this.volume){console.warn(`No PostProcessVolume assigned`);return}this.volume.addEffect(new d({intensity:3,luminanceThreshold:.2}))}};m([o(f)],_.prototype,`volume`,void 0);function v(){a.add(`ARObjectController`,p),a.add(`MenuController`,g),a.add(`PostProcessingVolumeController`,_)}v();export{v as registerTypes};
Binary file not shown.
@@ -0,0 +1 @@
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),s=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n||t(r,Symbol.toStringTag,{value:`Module`}),r},c=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},l=(n,r,a)=>(a=n==null?{}:e(i(n)),c(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));export{s as n,l as r,o as t};
Binary file not shown.
@@ -0,0 +1 @@
import{S as e,at as t,mn as n}from"./three@0.169.19.js";var r=class{constructor(){this.isPass=!0,this.enabled=!0,this.needsSwap=!0,this.clear=!1,this.renderToScreen=!1}setSize(){}render(){console.error(`THREE.Pass: .render() must be implemented in derived pass.`)}dispose(){}};new n(-1,1,1,-1,0,1),new class extends e{constructor(){super(),this.setAttribute(`position`,new t([-1,3,0,-1,-1,0,3,-1,0],3)),this.setAttribute(`uv`,new t([0,2,0,0,2,0],2))}};export{r as t};
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
+69
View File
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="modulepreload" as="script" href="./assets/three@0.169.19.js">
<link rel="modulepreload" as="script" href="./assets/three-examples@0.169.19.js">
<link rel="modulepreload" as="script" href="./assets/needle-engine@5.0.3.js">
<link rel="preload" as="fetch" fetchpriority="low" href="./assets/MenuScene.glb" type="model/gltf+json" crossorigin>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://www.gstatic.com">
<link rel="preconnect" href="https://cdn.needle.tools">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Roboto+Flex:opsz,wght@8..144,100..1000&amp;display=swap" crossorigin="" onload="this.rel=&#39;stylesheet&#39;">
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0&amp;display=block" crossorigin="" onload="this.rel=&#39;stylesheet&#39;">
<link rel="preload" fetchpriority="high" as="image" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgMTYwIDE4Ny43NCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iODkuNjQiIHkxPSIxODQuODEiIHgyPSI5MC40OCIgeTI9IjIxLjg1IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNjJkMzk5Ii8+PHN0b3Agb2Zmc2V0PSIuNTEiIHN0b3AtY29sb3I9IiNhY2Q4NDIiLz48c3RvcCBvZmZzZXQ9Ii45IiBzdG9wLWNvbG9yPSIjZDdkYjBhIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImIiIHgxPSI2OS42OCIgeTE9IjE3OC45IiB4Mj0iNjguMDgiIHkyPSIxNi43NyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzBiYTM5OCIvPjxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiM0Y2EzNTIiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiM3NmEzMGEiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9IjM2LjYiIHkxPSIxNTIuMTciIHgyPSIzNC43IiB5Mj0iODQuMTkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xOSIgc3RvcC1jb2xvcj0iIzM2YTM4MiIvPjxzdG9wIG9mZnNldD0iLjU0IiBzdG9wLWNvbG9yPSIjNDlhNDU5Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNzZhMzBiIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSIxNS44MiIgeTE9IjE1My4yNCIgeDI9IjE4IiB5Mj0iOTAuODYiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMyNjc4ODAiLz48c3RvcCBvZmZzZXQ9Ii41MSIgc3RvcC1jb2xvcj0iIzQ1N2E1YyIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzcxNzUxNiIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJlIiB4MT0iMTM1LjA4IiB5MT0iMTM1LjQzIiB4Mj0iMTQ4LjkzIiB5Mj0iNjMuNDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiMGQ5MzkiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlYWRiMDQiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iZiIgeDE9Ii00MTYzLjI1IiB5MT0iMjI4NS4xMiIgeDI9Ii00MTYwLjgxIiB5Mj0iMjIxNS4zNCIgZ3JhZGllbnRUcmFuc2Zvcm09InJvdGF0ZSgyMCA0MDg4LjQ5IDEzMzE2LjcxMikiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj48c3RvcCBvZmZzZXQ9Ii4xNyIgc3RvcC1jb2xvcj0iIzc0YWY1MiIvPjxzdG9wIG9mZnNldD0iLjQ4IiBzdG9wLWNvbG9yPSIjOTliZTMyIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjYzBjNDBhIi8+PC9saW5lYXJHcmFkaWVudD48c3ltYm9sIGlkPSJnIiB2aWV3Qm94PSIwIDAgMTYwIDE4Ny43NCI+PHBhdGggc3R5bGU9ImZpbGw6dXJsKCNhKSIgZD0iTTc5LjMyIDM2Ljk4djE1MC43Nkw5NSAxNzQuNTRsNi41OS0xNTYuMzEtMjIuMjcgMTguNzV6Ii8+PHBhdGggc3R5bGU9ImZpbGw6dXJsKCNiKSIgZD0iTTc5LjMyIDM2Ljk4IDU3LjA1IDE4LjIzbDYuNTkgMTU2LjMxIDE1LjY4IDEzLjJWMzYuOTh6Ii8+PHBhdGggc3R5bGU9ImZpbGw6dXJsKCNjKSIgZD0ibTI1LjE5IDEwNC44MyA4LjYzIDQ5LjA0IDEyLjUtMTQuOTUtMi40Ni01Ni40Mi0xOC42NyAyMi4zM3oiLz48cGF0aCBzdHlsZT0iZmlsbDp1cmwoI2QpIiBkPSJNMjUuMTkgMTA0LjgzIDAgOTAuMjRsMTYuOTcgNTMuODYgMTYuODUgOS43Ny04LjYzLTQ5LjA0eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiM5YzMiIGQ9Ik00My44NiA4Mi41IDE4LjY5IDY3Ljk4IDAgOTAuMjRsMjUuMTggMTQuNTlMNDMuODYgODIuNXoiLz48cGF0aCBzdHlsZT0iZmlsbDp1cmwoI2UpIiBkPSJtMTM0LjgyIDc4LjY5LTkuOTcgNTYuNSAxNS41OC05LjA0TDE2MCA2NC4xbC0yNS4xOCAxNC41OXoiLz48cGF0aCBzdHlsZT0iZmlsbDp1cmwoI2YpIiBkPSJtMTM0LjgyIDc4LjY5LTE4LjY4LTIyLjMzLTIuODYgNjUgMTEuNTcgMTMuODMgOS45Ny01Ni41eiIvPjxwYXRoIHN0eWxlPSJmaWxsOiNmZmUxMTMiIGQ9Im0xNjAgNjQuMS0xOC42OS0yMi4yNi0yNS4xNyAxNC41MiAxOC42NyAyMi4zM0wxNjAgNjQuMXoiLz48cGF0aCBzdHlsZT0iZmlsbDojZjNlNjAwIiBkPSJNMTAxLjU5IDE4LjIzIDc5LjMyIDAgNTcuMDUgMTguMjNsMjIuMjcgMTguNzUgMjIuMjctMTguNzV6Ii8+PC9zeW1ib2w+PC9kZWZzPjx1c2Ugd2lkdGg9IjE2MCIgaGVpZ2h0PSIxODcuNzQiIHhsaW5rOmhyZWY9IiNnIi8+PC9zdmc+" type="image/svg+xml">
<meta name="og:title" content="Menu Scene">
<meta name="generator" content="Unity 6000.4.3f1, Needle Engine Integration @5.0.3">
<meta name="needle-engine" content="5.0.3">
<meta name="needle:buildtime" content="2026-04-19T20:37:35.867Z">
<meta charset="UTF-8" />
<link rel="icon" href="./assets/favicon.DtN_Fmav.ico">
<meta name="viewport" content="width=device-width, user-scalable=no, viewport-fit=cover">
<title>Menu Scene</title>
<meta name="description" content="🌵 Made with Needle Engine">
<meta property="og:description" content="🌵 Made with Needle Engine" />
<meta name="robots" content="index,follow">
<meta name="url" content="https://needle.tools">
<script type="module" crossorigin src="./assets/index-CkG2vTVa.js"></script>
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime.CYVBeYYp.js">
<link rel="modulepreload" crossorigin href="./assets/three@0.169.19.js">
<link rel="modulepreload" crossorigin href="./assets/materialx.CnHmG--Y.js">
<link rel="modulepreload" crossorigin href="./assets/three-examples@0.169.19.js">
<link rel="modulepreload" crossorigin href="./assets/gltf-progressive.BBJGcb3s.js">
<link rel="modulepreload" crossorigin href="./assets/three-mesh-ui.CgGzZ3ml.js">
<link rel="modulepreload" crossorigin href="./assets/needle-engine-ui.BzyLX9fs.js">
<link rel="modulepreload" crossorigin href="./assets/needle-engine-webxr.DQA26K-l.js">
<link rel="modulepreload" crossorigin href="./assets/three-quarks.DALdLWPD.js">
<link rel="modulepreload" crossorigin href="./assets/needle-engine-particles.Bw3zzNt3.js">
<link rel="modulepreload" crossorigin href="./assets/postprocessing.Wy71DpiM.js">
<link rel="modulepreload" crossorigin href="./assets/needle-engine.extras.CDv9-f43.js">
<link rel="modulepreload" crossorigin href="./assets/needle-engine.extras.t1Dx-yNB.js">
<link rel="modulepreload" crossorigin href="./assets/three-mesh-bvh.B-oXEGo2.js">
<link rel="modulepreload" crossorigin href="./assets/needle-engine@5.0.3.js">
<link rel="stylesheet" crossorigin href="./assets/index.2XwOwFJz.css">
</head>
<body>
<script>/* needle: fix for peerjs */ window.global = window; var parcelRequire;</script>
<needle-engine>
<div id="asset-picker" class="ar desktop">
<div class="asset-picker__inner">
<button type="button" id="asset-picker-prev" aria-label="Previous model">Previous</button>
<span id="asset-picker-label" class="asset-picker__label"></span>
<span id="asset-picker-index" class="asset-picker__index" aria-live="polite"></span>
<button type="button" id="asset-picker-next" aria-label="Next model">Next</button>
<button type="button" id="asset-picker-ar" aria-label="Start augmented reality">View in AR</button>
</div>
</div>
</needle-engine>
</body>
</html>
Binary file not shown.
+102
View File
@@ -0,0 +1,102 @@
// Needle Engine attributes we want to allow to be overriden
const knownAttributes = [
"src",
"background-color",
"background-image",
"environment-image",
"focus-rect",
];
const scriptUrl = new URL(import.meta.url);
const componentName = scriptUrl.searchParams.get("component-name") || 'needle-app';
if (!customElements.get(componentName)) {
console.debug(`Defining needle-app as <${componentName}>`);
customElements.define(componentName, class extends HTMLElement {
static get observedAttributes() {
return knownAttributes;
}
constructor() {
super();
this.attachShadow({ mode: 'open' });
const template = document.createElement('template');
template.innerHTML = `
<style>
:host {
position: relative;
display: block;
width: max(360px 100%);
height: max(240px, 100%);
margin: 0;
padding: 0;
}
needle-engine {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
</style>
`;
this.shadowRoot.appendChild(template.content.cloneNode(true));
const script = document.createElement('script');
script.type = 'module';
const url = new URL('.', import.meta.url);
this.basePath = this.getAttribute('base-path') || `${url.protocol}//${url.host}${url.pathname}`;
while(this.basePath.endsWith('/')) {
this.basePath = this.basePath.slice(0, -1);
}
script.src = this.getAttribute('script-src') || `${this.basePath}/assets/index-CkG2vTVa.js`;
this.shadowRoot.appendChild(script);
this.needleEngine = document.createElement('needle-engine');
this.updateAttributes();
this.shadowRoot.appendChild(this.needleEngine);
console.debug(this.basePath, script.src, this.needleEngine.getAttribute("src"));
}
onConnectedCallback() {
console.debug('NeedleEmbed connected to the DOM');
}
disconnectedCallback() {
console.debug('NeedleEmbed disconnected from the DOM');
}
attributeChangedCallback(name, oldValue, newValue) {
console.debug(`NeedleApp attribute changed: ${name} from ${oldValue} to ${newValue}`);
this.updateAttributes();
}
updateAttributes() {
console.debug("NeedleApp updating attributes");
const src = this.getAttribute('src') || null;
if(src) this.needleEngine.setAttribute("src", src);
else this.needleEngine.removeAttribute("src");
for(const attr of knownAttributes) {
if(attr === "src") continue; // already handled above
if(this.hasAttribute(attr)) {
this.needleEngine.setAttribute(attr, this.getAttribute(attr));
}
else {
this.needleEngine.removeAttribute(attr);
}
}
}
});
}
else {
console.warn(`needle-app <${componentName}> already defined.`);
}
File diff suppressed because one or more lines are too long
+1 -2
View File
@@ -1,7 +1,6 @@
**/node_modules **/node_modules
assets/ /assets/
src/generated/ src/generated/
dist/
include/draco/ include/draco/
include/ktx2/ include/ktx2/
include/three/ include/three/
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
import{t as e}from"./needle-engine@5.0.3.js";export{e as GenerateMeshBVHWorker};
Binary file not shown.
@@ -0,0 +1 @@
import{u as e}from"./materialx.CnHmG--Y.js";export{e as default};
@@ -0,0 +1 @@
import{c as e}from"./materialx.CnHmG--Y.js";export{e as default};
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
import{l as e}from"./materialx.CnHmG--Y.js";export{e as default};
+1
View File
@@ -0,0 +1 @@
import{n as e,r as t,t as n}from"./postprocessing.ao.CBPOoA3d.js";export{e as DepthType,n as N8AOPass,t as N8AOPostPass};
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More