codebee 0.1.104 → 0.1.106
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/CHANGELOG.md +17 -0
- package/README.md +10 -4
- package/app/core/activity.py +36 -0
- package/app/core/builtin_agent.py +6 -1
- package/app/core/checkpoints.py +48 -5
- package/app/core/jobs.py +7 -0
- package/app/core/manager.py +1 -0
- package/app/core/market_remote.py +19 -7
- package/app/core/mcp_client.py +102 -63
- package/app/core/openapi.py +29 -0
- package/app/core/pipeline.py +40 -14
- package/app/core/project_memory.py +47 -1
- package/app/core/publish/auto.py +74 -3
- package/app/core/publish/ledger.py +5 -0
- package/app/core/publish/manager.py +5 -1
- package/app/core/registry.py +35 -0
- package/app/core/review_requests.py +119 -0
- package/app/core/runner.py +5 -1
- package/app/core/runtime_lock.py +75 -0
- package/app/core/selfupdate.py +8 -0
- package/app/core/store.py +32 -2
- package/app/core/usage.py +31 -26
- package/app/core/webhooks.py +17 -13
- package/app/desktop_host.py +190 -0
- package/app/main.py +131 -4
- package/app/pet.py +1 -1
- package/app/ui/activity.css +34 -0
- package/app/ui/activity.js +199 -0
- package/app/ui/app.js +813 -66
- package/app/ui/hive3d.js +214 -72
- package/app/ui/i18n.js +162 -2
- package/app/ui/index.html +155 -75
- package/app/ui/style.css +583 -26
- package/package.json +1 -1
package/app/ui/hive3d.js
CHANGED
|
@@ -5,6 +5,11 @@ window.Hive3D = (function () {
|
|
|
5
5
|
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
6
6
|
const STATUS = { running:"运行中", done:"完成", queued:"排队中", failed:"失败", timeout:"超时", cancelled:"已取消" };
|
|
7
7
|
const STAGES = ["规划","起草","评审","执行","打磨","合成"];
|
|
8
|
+
const WING_OUTLINE=[
|
|
9
|
+
[-.50,0,.008],[-.43,.19,.035],[-.28,.30,.075],[-.06,.34,.10],
|
|
10
|
+
[.18,.30,.086],[.38,.19,.05],[.50,0,.008],[.38,-.19,.05],
|
|
11
|
+
[.18,-.30,.086],[-.06,-.34,.10],[-.28,-.30,.075],[-.43,-.19,.035]
|
|
12
|
+
];
|
|
8
13
|
const SCREENS = [
|
|
9
14
|
[163,441,127,55],[389,441,126,55],[616,441,125,55],[843,441,125,55],
|
|
10
15
|
[1073,441,122,55],[1298,441,125,55],[1531,441,120,55],[1750,441,125,55],
|
|
@@ -91,17 +96,21 @@ window.Hive3D = (function () {
|
|
|
91
96
|
function organicGeometry(gl){
|
|
92
97
|
// A polished, tapered leaf/wing silhouette with a gently domed center and two-sided normals.
|
|
93
98
|
let outline=[
|
|
94
|
-
[-.5,0
|
|
95
|
-
[.38,.13,.
|
|
96
|
-
[-.12,-.255,.
|
|
99
|
+
[-.5,0,.006],[-.34,.17,.028],[-.12,.255,.064],[.14,.225,.078],
|
|
100
|
+
[.38,.13,.038],[.5,0,.006],[.38,-.13,.038],[.14,-.225,.078],
|
|
101
|
+
[-.12,-.255,.064],[-.34,-.17,.028]
|
|
97
102
|
];
|
|
98
103
|
const area=outline.reduce((sum,p,i)=>{const q=outline[(i+1)%outline.length];return sum+p[0]*q[1]-q[0]*p[1];},0);
|
|
99
104
|
if(area<0)outline=outline.reverse();
|
|
100
105
|
const positions=[],normals=[],indices=[];
|
|
101
106
|
const emitSide=(sign)=>{
|
|
102
107
|
const centerIndex=positions.length/3;
|
|
103
|
-
positions.push(0,0,sign*.
|
|
104
|
-
for(const p of outline){
|
|
108
|
+
positions.push(0,0,sign*.105);normals.push(0,0,sign);
|
|
109
|
+
for(const p of outline){
|
|
110
|
+
positions.push(p[0],p[1],p[2]*sign);
|
|
111
|
+
const n=norm([-.20*p[0],-.28*p[1],sign]);
|
|
112
|
+
normals.push(...n);
|
|
113
|
+
}
|
|
105
114
|
for(let i=0;i<outline.length;i++){
|
|
106
115
|
const a=centerIndex+1+i,b=centerIndex+1+(i+1)%outline.length;
|
|
107
116
|
if(sign>0)indices.push(centerIndex,a,b);else indices.push(centerIndex,b,a);
|
|
@@ -110,6 +119,30 @@ window.Hive3D = (function () {
|
|
|
110
119
|
emitSide(1);emitSide(-1);
|
|
111
120
|
return geometry(gl,positions,indices,normals);
|
|
112
121
|
}
|
|
122
|
+
function wingGeometry(gl){
|
|
123
|
+
// Separate convex, oval wing mesh for the bee assistants. Plants retain organicGeometry.
|
|
124
|
+
let outline=WING_OUTLINE.map(p=>p.slice());
|
|
125
|
+
// WebGL back-face culling requires consistent counter-clockwise front faces.
|
|
126
|
+
const wingArea=outline.reduce((sum,p,i)=>{const q=outline[(i+1)%outline.length];return sum+p[0]*q[1]-q[0]*p[1];},0);
|
|
127
|
+
if(wingArea<0)outline=outline.reverse();
|
|
128
|
+
const positions=[],normals=[],indices=[],colors=[];
|
|
129
|
+
const emitSide=(sign)=>{
|
|
130
|
+
const centerIndex=positions.length/3;
|
|
131
|
+
positions.push(0,0,sign*.42);normals.push(0,0,sign);colors.push(.80,.91,.99,.74);
|
|
132
|
+
for(const p of outline){
|
|
133
|
+
positions.push(p[0],p[1],p[2]*sign);
|
|
134
|
+
normals.push(...norm([-.48*p[0],-.48*p[1],sign]));
|
|
135
|
+
const edge=Math.min(1,Math.hypot(p[0]*.82,p[1]*1.15));
|
|
136
|
+
colors.push(.70-.06*edge,.86-.04*edge,.98-.02*edge,.70-.08*edge);
|
|
137
|
+
}
|
|
138
|
+
for(let i=0;i<outline.length;i++){
|
|
139
|
+
const a=centerIndex+1+i,b=centerIndex+1+(i+1)%outline.length;
|
|
140
|
+
if(sign>0)indices.push(centerIndex,a,b);else indices.push(centerIndex,b,a);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
emitSide(1);emitSide(-1);
|
|
144
|
+
const mesh=geometry(gl,positions,indices,normals);mesh.colors=colors;return mesh;
|
|
145
|
+
}
|
|
113
146
|
function hexGroutGeometry(gl){
|
|
114
147
|
// Build the complete staggered flat-top hex lattice as one static mesh. Drawing only
|
|
115
148
|
// three consecutive edges per tile creates crosses and broken hexes; unique full edges
|
|
@@ -141,6 +174,26 @@ window.Hive3D = (function () {
|
|
|
141
174
|
}
|
|
142
175
|
return geometry(gl,positions,indices,normals);
|
|
143
176
|
}
|
|
177
|
+
function radialShadowGeometry(gl,segments=48,rings=7){
|
|
178
|
+
// Smooth, triangulated light falloff avoids the low-poly ellipsoid stamps that read
|
|
179
|
+
// like floating objects on the pale hex floor. Vertex alpha fades completely at the rim.
|
|
180
|
+
const positions=[0,0,0],normals=[0,1,0],colors=[1,1,1,.24],indices=[];
|
|
181
|
+
for(let ring=1;ring<=rings;ring++)for(let i=0;i<segments;i++){
|
|
182
|
+
const a=i/segments*Math.PI*2,r=ring/rings;
|
|
183
|
+
positions.push(Math.cos(a)*r,0,Math.sin(a)*r);normals.push(0,1,0);
|
|
184
|
+
const fade=1-r;colors.push(1,1,1,.24*fade*fade);
|
|
185
|
+
}
|
|
186
|
+
for(let i=0;i<segments;i++){
|
|
187
|
+
const next=(i+1)%segments;
|
|
188
|
+
indices.push(0,1+next,1+i);
|
|
189
|
+
}
|
|
190
|
+
for(let ring=1;ring<rings;ring++)for(let i=0;i<segments;i++){
|
|
191
|
+
const next=(i+1)%segments,a=1+(ring-1)*segments+i,b=1+(ring-1)*segments+next;
|
|
192
|
+
const c=1+ring*segments+i,d=1+ring*segments+next;
|
|
193
|
+
indices.push(a,d,c,a,b,d);
|
|
194
|
+
}
|
|
195
|
+
const mesh=geometry(gl,positions,indices,normals);mesh.colors=colors;return mesh;
|
|
196
|
+
}
|
|
144
197
|
function cylinderGeometry(gl,segments=20){
|
|
145
198
|
const p=[],n=[],ix=[];
|
|
146
199
|
// Side wall has independent normals from the flat end caps, avoiding pinched highlights.
|
|
@@ -167,7 +220,7 @@ window.Hive3D = (function () {
|
|
|
167
220
|
function Scene(opts){
|
|
168
221
|
this.opts=opts;this.canvas=opts.canvas;this.overlay=opts.overlay;this.host=this.canvas.parentElement;
|
|
169
222
|
this.onCellActivate=opts.onCellActivate||function(){};this.cellRefresh=opts.cellRefresh||function(){return{};};
|
|
170
|
-
this.model=null;this.cells=[];this.page=0;this.zoom=1;this.yaw=0;this.pitch=0.
|
|
223
|
+
this.model=null;this.cells=[];this.page=0;this.zoom=1;this.yaw=0;this.pitch=0.48;this.panX=0;this.panY=0;this.active=false;this.raf=0;this.timer=0;this.listeners=[];this.screenMeta=[];this.displayMode="reference";this.eyeDirection=norm([0,3.1+Math.sin(this.pitch)*13.88-.62,25.4-1.3]);
|
|
171
224
|
this.host.dataset.renderer="webgl";this.canvas.hidden=false;this.canvas.classList.add("hive-gl-live");
|
|
172
225
|
this.referenceImage=this.host.querySelector(".hive-reference");
|
|
173
226
|
this.layer=node("div","hive-reference-layer",this.host);
|
|
@@ -186,9 +239,9 @@ window.Hive3D = (function () {
|
|
|
186
239
|
const gl=this.canvas.getContext("webgl",{alpha:true,antialias:true,powerPreference:"high-performance"})||this.canvas.getContext("experimental-webgl");
|
|
187
240
|
if(!gl)throw Error("WebGL unavailable");this.gl=gl;
|
|
188
241
|
const vs="attribute vec3 aPosition; attribute vec3 aNormal; attribute vec4 aColor; uniform mat4 uViewProj; varying vec4 vColor; varying float vShade; varying vec3 vWorld; varying vec3 vNormal; void main(){gl_Position=uViewProj*vec4(aPosition,1.0);vColor=aColor;vWorld=aPosition;vNormal=normalize(aNormal);vec3 n=normalize(aNormal);float sky=clamp(n.y*0.5+0.5,0.0,1.0);float side=abs(n.x)*0.035;float heightShade=mix(0.94,1.0,smoothstep(-0.2,3.6,aPosition.y));vShade=(0.88+0.08*sky+side)*heightShade;}";
|
|
189
|
-
const fs="precision mediump float; varying vec4 vColor; varying float vShade; varying vec3 vWorld; varying vec3 vNormal; uniform vec3 uEyePosition; void main(){vec3 n=normalize(vNormal);vec3 v=normalize(uEyePosition-vWorld);vec3 key=normalize(vec3(-0.48,0.86,0.42));vec3 fill=normalize(vec3(0.62,0.32,-0.72));vec3 warm=normalize(vec3(0.34,0.46,0.82));float ndl=max(dot(n,key),0.0);float fillN=max(dot(n,fill),0.0);float warmN=max(dot(n,warm),0.0);float hemi=clamp(n.y*0.5+0.5,0.0,1.0);vec3 base=vColor.rgb;float wallBlue=step(0.38,base.b)*step(0.35,base.g)*step(base.r*1.65,base.g)*step(1.7,vWorld.y);float wallGradient=0.
|
|
242
|
+
const fs="precision mediump float; varying vec4 vColor; varying float vShade; varying vec3 vWorld; varying vec3 vNormal; uniform vec3 uEyePosition; void main(){vec3 n=normalize(vNormal);vec3 v=normalize(uEyePosition-vWorld);vec3 key=normalize(vec3(-0.48,0.86,0.42));vec3 fill=normalize(vec3(0.62,0.32,-0.72));vec3 warm=normalize(vec3(0.34,0.46,0.82));float ndl=max(dot(n,key),0.0);float fillN=max(dot(n,fill),0.0);float warmN=max(dot(n,warm),0.0);float hemi=clamp(n.y*0.5+0.5,0.0,1.0);vec3 base=vColor.rgb;float chroma=max(max(base.r,base.g),base.b)-min(min(base.r,base.g),base.b);float wallBlue=step(0.38,base.b)*step(0.35,base.g)*step(base.r*1.65,base.g)*step(1.7,vWorld.y);float wallGradient=0.78+0.12*smoothstep(1.0,4.5,vWorld.y)+0.24*exp(-pow((vWorld.x-0.2)*0.19,2.0));base=mix(base,base*wallGradient,wallBlue);vec3 h=normalize(key+v);vec3 hf=normalize(fill+v);float gloss=mix(0.10,0.34,smoothstep(0.10,0.82,chroma));float spec=pow(max(dot(n,h),0.0),mix(24.0,58.0,gloss))*gloss*0.42;float specFill=pow(max(dot(n,hf),0.0),36.0)*0.075;float fresnel=pow(1.0-max(dot(n,v),0.0),3.0);vec3 ambient=base*(0.55+0.13*hemi);vec3 direct=base*(0.34*ndl+0.16*fillN+0.08*warmN);vec3 highlight=vec3(1.0,0.91,0.78)*spec+vec3(0.52,0.84,1.0)*specFill+vec3(0.18,0.48,0.68)*fresnel*0.075;float cyan=max(0.0,min(base.g,base.b)-base.r)*0.65;vec3 emissive=base*cyan;float honey=step(0.68,base.r)*step(0.34,base.g)*step(base.g*0.96,base.r)*step(base.b*1.4,base.g);vec3 lit=ambient+direct+highlight+emissive+vec3(0.18,0.105,0.018)*honey;lit*=vShade;gl_FragColor=vec4(min(lit,vec3(1.0)),vColor.a);}";
|
|
190
243
|
const program=gl.createProgram();gl.attachShader(program,shader(gl,gl.VERTEX_SHADER,vs));gl.attachShader(program,shader(gl,gl.FRAGMENT_SHADER,fs));gl.linkProgram(program);if(!gl.getProgramParameter(program,gl.LINK_STATUS))throw Error(gl.getProgramInfoLog(program));this.program=program;gl.useProgram(program);
|
|
191
|
-
this.aPosition=gl.getAttribLocation(program,"aPosition");this.aNormal=gl.getAttribLocation(program,"aNormal");this.aColor=gl.getAttribLocation(program,"aColor");this.uViewProj=gl.getUniformLocation(program,"uViewProj");this.uEyePosition=gl.getUniformLocation(program,"uEyePosition");this.box=boxGeometry(gl);this.roundBox=roundedBoxGeometry(gl,6,.12);this.sphere=sphereGeometry(gl,16,24);this.cylinder=cylinderGeometry(gl,20);this.organic=organicGeometry(gl);this.grout=hexGroutGeometry(gl);this.bandGoldTop=sphereBandGeometry(gl,-.
|
|
244
|
+
this.aPosition=gl.getAttribLocation(program,"aPosition");this.aNormal=gl.getAttribLocation(program,"aNormal");this.aColor=gl.getAttribLocation(program,"aColor");this.uViewProj=gl.getUniformLocation(program,"uViewProj");this.uEyePosition=gl.getUniformLocation(program,"uEyePosition");this.box=boxGeometry(gl);this.roundBox=roundedBoxGeometry(gl,6,.12);this.sphere=sphereGeometry(gl,16,24);this.cylinder=cylinderGeometry(gl,20);this.organic=organicGeometry(gl);this.wing=wingGeometry(gl);this.grout=hexGroutGeometry(gl);this.shadow=radialShadowGeometry(gl);this.bandGoldTop=sphereBandGeometry(gl,-.08,.18);this.bandGoldBottom=sphereBandGeometry(gl,-.97,-.61);this.bandSeparator=sphereBandGeometry(gl,-.51,-.38);this.bandGoldTop.smooth=true;this.bandGoldBottom.smooth=true;this.bandSeparator.smooth=true;gl.enable(gl.DEPTH_TEST);gl.enable(gl.CULL_FACE);gl.cullFace(gl.BACK);gl.enable(gl.BLEND);gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA);gl.clearColor(0,0,0,0);
|
|
192
245
|
};
|
|
193
246
|
Scene.prototype.listen=function(el,type,fn,opts){el.addEventListener(type,fn,opts);this.listeners.push(()=>el.removeEventListener(type,fn,opts));};
|
|
194
247
|
Scene.prototype.bindSurface=function(){
|
|
@@ -204,10 +257,13 @@ window.Hive3D = (function () {
|
|
|
204
257
|
Scene.prototype.addLink=function(a,b,radius,color){const dx=b[0]-a[0],dy=b[1]-a[1],dz=b[2]-a[2],length=Math.hypot(dx,dy,dz)||.001,rotX=Math.asin(clamp(dz/length,-1,1)),rotZ=Math.atan2(-dx,dy);this.add(this.cylinder,(a[0]+b[0])/2,(a[1]+b[1])/2,(a[2]+b[2])/2,radius,length,radius,color,0,rotZ,rotX);};
|
|
205
258
|
Scene.prototype.buildWorld=function(){
|
|
206
259
|
const b=this.box,rb=this.roundBox,s=this.sphere;this.objects=[];
|
|
207
|
-
// Bright, calm blue-white studio with a continuous floor and a single clean feature wall.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
260
|
+
// Bright, calm blue-white studio with a continuous floor and a single clean feature wall. The free camera is framed wide enough to keep all 14 stations visible at desktop aspect ratios.
|
|
261
|
+
// Keep the studio bright and photographic: the reference has a nearly white
|
|
262
|
+
// floor with cool blue bounce, while the feature wall carries the saturated
|
|
263
|
+
// accent. The old values made the whole room read as a blue plastic box.
|
|
264
|
+
this.add(b,0,-.18,5.5,23,.28,34,[.86,.90,.94,1]);
|
|
265
|
+
this.add(b,0,2.70,-6.45,19,3.65,.24,[.19,.53,.83,1]);
|
|
266
|
+
this.add(b,0,4.58,-6.30,19,.075,.12,[.985,.99,1,1]);
|
|
211
267
|
// Wall seams, lower trim, and glass side windows.
|
|
212
268
|
this.add(b,0,.90,-6.28,18.7,.12,.06,[.08,.42,.78,1]);
|
|
213
269
|
this.add(b,0,.82,-6.285,18.7,.035,.035,[.25,.67,.94,1]);
|
|
@@ -222,8 +278,8 @@ window.Hive3D = (function () {
|
|
|
222
278
|
}
|
|
223
279
|
// Ceiling light fixtures and soft-colored illuminated panels.
|
|
224
280
|
for(let x=-6.3;x<=6.4;x+=6.3){
|
|
225
|
-
this.add(b,x,
|
|
226
|
-
this.add(b,x,
|
|
281
|
+
this.add(b,x,4.66,-3.35,2.65,.065,.30,[.12,.17,.23,1]);
|
|
282
|
+
this.add(b,x,4.595,-3.35,2.40,.045,.24,[1,.99,.91,1]);
|
|
227
283
|
}
|
|
228
284
|
// Six white phase controls are projected on this wall by the live HTML overlay.
|
|
229
285
|
for(let i=0;i<6;i++){
|
|
@@ -236,12 +292,12 @@ window.Hive3D = (function () {
|
|
|
236
292
|
this.add(b,x,2.34,-6.005,.035,.48,.024,[.22,.89,1,1]);
|
|
237
293
|
}
|
|
238
294
|
// Continuous, pale hex grout is one static mesh: no crossed edges and no thousands of scene objects.
|
|
239
|
-
this.add(this.grout,0,0,0,1,1,1,[.
|
|
295
|
+
this.add(this.grout,0,0,0,1,1,1,[.88,.91,.92,1]);
|
|
240
296
|
// Reference layout: eight compact rear stations and six wider front stations.
|
|
241
297
|
// Every monitor overlay is projected from these exact same coordinates below.
|
|
242
298
|
const deskRows=[
|
|
243
|
-
{row:0,z:-1.80,width:2.03,screenWidth:1.28,screenHeight:.
|
|
244
|
-
{row:1,z:5.85,width:2.42,screenWidth:1.
|
|
299
|
+
{row:0,z:-1.80,width:2.03,screenWidth:1.28,screenHeight:.54,xs:[-8.4,-6.0,-3.6,-1.2,1.2,3.6,6.0,8.4]},
|
|
300
|
+
{row:1,z:5.85,width:2.42,screenWidth:1.62,screenHeight:.64,xs:[-6.0,-3.6,-1.2,1.2,3.6,6.0]}
|
|
245
301
|
];
|
|
246
302
|
this.deskPositions=[];
|
|
247
303
|
for(const config of deskRows)for(const x of config.xs){
|
|
@@ -254,17 +310,35 @@ window.Hive3D = (function () {
|
|
|
254
310
|
this.add(b,x,.455,z-.565,width*.64,.028,.024,[.61,.70,.79,1]);
|
|
255
311
|
this.add(rb,x-width*.36,.25,z+.03,.43,.52,1.02,[.90,.93,.97,1]);
|
|
256
312
|
this.add(rb,x+width*.36,.25,z+.03,.43,.52,1.02,[.90,.93,.97,1]);
|
|
313
|
+
// Full-height acoustic partitions give every workstation the enclosed, built-in
|
|
314
|
+
// cubicle silhouette of the reference while preserving a clear sightline to screens.
|
|
315
|
+
for(const side of [-1,1]){
|
|
316
|
+
const dividerX=x+side*(width*.49);
|
|
317
|
+
this.add(rb,dividerX,.88,z+.005,.058,1.06,1.16,[.91,.93,.93,1]);
|
|
318
|
+
this.add(rb,dividerX-side*.012,.90,z-.015,.020,.96,1.08,[.98,.98,.96,1]);
|
|
319
|
+
this.add(rb,dividerX,.88,z-.595,.066,1.04,.045,[.95,.96,.94,1]);
|
|
320
|
+
this.add(rb,dividerX,.88,z+.595,.066,1.04,.045,[.95,.96,.94,1]);
|
|
321
|
+
this.add(b,dividerX-side*.014,.88,z-.55,.009,.76,.010,[.75,.81,.83,1]);
|
|
322
|
+
this.add(b,dividerX-side*.014,.88,z+.55,.009,.76,.010,[.75,.81,.83,1]);
|
|
323
|
+
}
|
|
257
324
|
// A compact three-drawer pedestal gives each station the white office furniture silhouette of the reference.
|
|
258
|
-
this.add(rb,x+width*.30,.27,z+.445,width*.30,.42,.26,[.
|
|
325
|
+
this.add(rb,x+width*.30,.27,z+.445,width*.30,.42,.26,[.97,.965,.94,1]);
|
|
259
326
|
for(const drawerY of [.18,.28,.38])this.add(b,x+width*.30,drawerY,z+.579,width*.22,.012,.012,[.62,.71,.80,1]);
|
|
260
327
|
this.add(b,x+width*.30,.41,z+.581,width*.085,.018,.014,[.45,.58,.70,1]);
|
|
328
|
+
for(const drawerY of [.18,.28,.38])this.add(rb,x+width*.30,drawerY,z+.593,width*.105,.026,.018,[.80,.86,.92,1]);
|
|
261
329
|
this.add(rb,x,.43,z-.22,width*.78,.045,.74,[.89,.93,.97,1]);
|
|
262
|
-
this.add(rb,x,screenY,z-.48,screenWidth+.
|
|
263
|
-
this.add(rb,x,screenY,z-.
|
|
264
|
-
this.add(b,x,screenY,z-.
|
|
265
|
-
this.add(this.cylinder,x,.78,z-.43,.
|
|
266
|
-
this.add(
|
|
330
|
+
this.add(rb,x,screenY,z-.48,screenWidth+.055,screenHeight+.095,.060,[.025,.040,.055,1]);
|
|
331
|
+
this.add(rb,x,screenY,z-.447,screenWidth+.028,screenHeight+.060,.020,[.12,.17,.21,1]);
|
|
332
|
+
this.add(b,x,screenY,z-.408,screenWidth,screenHeight,.018,[.025,.145,.235,1]);
|
|
333
|
+
this.add(this.cylinder,x,.78,z-.43,.034,.36,.034,[.55,.60,.63,1]);
|
|
334
|
+
this.add(rb,x,.60,z-.45,.30,.18,.028,[.76,.79,.79,1]);
|
|
335
|
+
this.add(rb,x,.935,z-.451,.20,.10,.038,[.43,.49,.52,1]);
|
|
336
|
+
this.add(b,x,.49,z-.27,.54,.045,.34,[.12,.17,.20,1]);
|
|
267
337
|
this.add(b,x,screenY-screenHeight*.42,z-.382,screenWidth*.84,.018,.014,[.10,.83,.94,1]);
|
|
338
|
+
// A tiny centered webcam and a restrained power indicator give the monitor a finished hardware read.
|
|
339
|
+
this.add(rb,x,screenY+screenHeight*.5+.064,z-.386,.070,.026,.018,[.025,.035,.045,1]);
|
|
340
|
+
this.add(s,x,screenY+screenHeight*.5+.064,z-.374,.012,.012,.008,[.14,.82,.98,1]);
|
|
341
|
+
this.add(s,x+screenWidth*.39,screenY-screenHeight*.5+.025,z-.375,.010,.010,.008,[.28,.94,.62,1]);
|
|
268
342
|
this.add(rb,x-.18,.635,z+.25,.60,.035,.20,[.11,.14,.17,1]);
|
|
269
343
|
this.add(s,x+.40,.648,z+.22,.060,.025,.075,[.055,.065,.075,1]);
|
|
270
344
|
this.add(b,x+.40,.674,z+.20,.009,.005,.024,[.30,.66,.78,1]);
|
|
@@ -272,101 +346,150 @@ window.Hive3D = (function () {
|
|
|
272
346
|
const keyColor=(row===0&&col%4===0)?[.54,.61,.68,1]:((row+col)%5===0?[.40,.48,.56,1]:[.22,.27,.32,1]);
|
|
273
347
|
this.add(b,x-.45+col*.049,.659,z+.185+row*.045,.030,.009,.024,keyColor);
|
|
274
348
|
}
|
|
275
|
-
this.add(b,x-.18,.62,z+.25,.66,.012,.24,[.
|
|
349
|
+
this.add(b,x-.18,.62,z+.25,.66,.012,.24,[.78,.82,.83,1]);
|
|
350
|
+
for(let key=0;key<4;key++)this.add(b,x-.43+key*.14,.669,z+.17,.11,.004,.012,[.54,.61,.64,1]);
|
|
351
|
+
for(let key=0;key<4;key++)this.add(b,x-.43+key*.14,.669,z+.32,.11,.004,.012,[.54,.61,.64,1]);
|
|
276
352
|
const binderStart=x+width/2-.48;
|
|
277
353
|
for(let k=0;k<4;k++)this.add(b,binderStart+k*.095,.83,z+.35,.075,.43,.22,[[.13,.43,.78,1],[.95,.42,.24,1],[.16,.66,.46,1],[.93,.72,.28,1]][k]);
|
|
278
354
|
const blueBinderStart=x-width/2+.19;
|
|
279
355
|
for(let k=0;k<3;k++)this.add(b,blueBinderStart+k*.095,.79,z+.34,.075,.35,.18,[[.08,.34,.80,1],[.10,.51,.91,1],[.06,.25,.65,1]][k]);
|
|
280
356
|
this.add(this.cylinder,x,.08,z+1.10,.075,.28,.075,[.12,.15,.18,1]);
|
|
281
357
|
this.add(rb,x,.30,z+1.10,.94,.12,.68,[.11,.14,.17,1]);
|
|
282
|
-
this.add(rb,x,.
|
|
283
|
-
this.add(rb,x,.
|
|
358
|
+
this.add(rb,x,.68,z+.82,.82,.68,.15,[.17,.20,.23,1]);
|
|
359
|
+
this.add(rb,x,.68,z+.91,.62,.56,.024,[.24,.28,.31,1]);
|
|
284
360
|
this.add(rb,x,.42,z+1.10,.48,.12,.12,[.18,.22,.25,1]);
|
|
285
361
|
// Contrasting graphite upholstery separates the chair from the bee chassis.
|
|
286
|
-
this.add(rb,x,.
|
|
287
|
-
for(const ribY of [.
|
|
288
|
-
for(const side of [-1,1])this.add(rb,x+side*.
|
|
362
|
+
this.add(rb,x,.68,z+.90,.62,.56,.050,[.22,.25,.28,1]);
|
|
363
|
+
for(const ribY of [.45,.64,.82,.94])this.add(rb,x,ribY,z+.93,.44,.018,.020,[.38,.41,.43,1]);
|
|
364
|
+
for(const side of [-1,1])this.add(rb,x+side*.40,.68,z+.93,.026,.60,.025,[.29,.34,.38,1]);
|
|
289
365
|
for(const side of [-1,1]){
|
|
290
366
|
this.add(rb,x+side*.39,.63,z+.96,.10,.075,.38,[.14,.18,.21,1]);
|
|
291
367
|
this.add(b,x+side*.39,.48,z+.97,.035,.28,.045,[.10,.13,.16,1]);
|
|
292
368
|
}
|
|
293
369
|
for(let k=0;k<5;k++){const a=k*Math.PI*2/5;const wx=x+Math.cos(a)*.48,wz=z+1.10+Math.sin(a)*.40;this.add(b,x+Math.cos(a)*.38,.035,z+1.10+Math.sin(a)*.32,.30,.06,.075,[.08,.10,.12,1],a);this.add(s,wx,.005,wz,.085,.065,.075,[.035,.045,.055,1]);}
|
|
294
|
-
const by=1.25,bz=z+.98;
|
|
370
|
+
const by=1.25,bz=z+.98,beeObjectStart=this.objects.length;
|
|
295
371
|
// Bee assistant: distinct head, dark chassis, striped abdomen, headset and translucent wings.
|
|
296
|
-
this.add(s,x,by-.
|
|
372
|
+
this.add(s,x,by-.48,bz+.005,.325,.355,.250,[.075,.09,.11,1]);
|
|
297
373
|
// Slightly enlarge the band ellipsoid to avoid depth-buffer z-fighting with the shell.
|
|
298
|
-
const shellX=.
|
|
299
|
-
this.add(this.bandGoldTop,x,shellCenterY,shellCenterZ,shellX,shellY,shellZ,[1,.
|
|
300
|
-
this.add(this.bandGoldBottom,x,shellCenterY,shellCenterZ,shellX,shellY,shellZ,[
|
|
374
|
+
const shellX=.330,shellY=.360,shellZ=.255,shellCenterY=by-.48,shellCenterZ=bz+.005;
|
|
375
|
+
this.add(this.bandGoldTop,x,shellCenterY,shellCenterZ,shellX,shellY,shellZ,[1,.86,.12,1]);
|
|
376
|
+
this.add(this.bandGoldBottom,x,shellCenterY,shellCenterZ,shellX,shellY,shellZ,[1,.70,.045,1]);
|
|
301
377
|
this.add(this.bandSeparator,x,shellCenterY,shellCenterZ,shellX,shellY,shellZ,[.045,.055,.065,1]);
|
|
302
|
-
this.add(s,x,by,bz,.
|
|
303
|
-
// The
|
|
378
|
+
this.add(s,x,by,bz,.515,.465,.415,[1,.94,.22,1]);
|
|
379
|
+
// The reference bee has a clear glossy black cheek/eye patch visible from behind.
|
|
380
|
+
// Put it on the camera-facing quarter of the head so the character reads at scene scale.
|
|
381
|
+
this.add(s,x+.340,by+.025,bz+.265,.190,.205,.074,[.008,.012,.018,1]);
|
|
382
|
+
this.add(s,x+.355,by+.085,bz+.315,.034,.038,.018,[.32,.86,.98,1]);
|
|
383
|
+
// A slim graphite cowl across the crown catches light like the reference's
|
|
384
|
+
// dark headset band and separates the head from the rounded thorax below.
|
|
385
|
+
this.add(rb,x,by+.405,bz+.004,.52,.060,.32,[.12,.14,.15,1]);
|
|
386
|
+
// A forward-facing graphite visor makes the assistant read as a character, not a plain yellow sphere.
|
|
387
|
+
// The workstation monitor is on -Z, so the faceplate and optical details sit on that side of the head.
|
|
388
|
+
this.add(rb,x,by+.012,bz-.166,.258,.142,.026,[.035,.055,.070,1]);
|
|
389
|
+
this.add(rb,x,by-.006,bz-.183,.214,.092,.012,[.055,.085,.105,1]);
|
|
390
|
+
for(const side of [-1,1]){
|
|
391
|
+
this.add(s,x+side*.066,by+.022,bz-.194,.045,.046,.020,[.20,.91,.98,1]);
|
|
392
|
+
this.add(s,x+side*.066,by+.029,bz-.205,.017,.018,.008,[.92,.99,1,1]);
|
|
393
|
+
}
|
|
394
|
+
this.add(rb,x,by-.043,bz-.197,.070,.010,.008,[.98,.61,.035,1]);
|
|
395
|
+
// The rear silhouette carries the headset details.
|
|
304
396
|
for(const side of [-1,1]){
|
|
305
397
|
this.add(s,x+side*.382,by+.015,bz+.035,.078,.118,.094,[.045,.055,.065,1]);
|
|
306
398
|
this.add(s,x+side*.407,by+.015,bz+.052,.030,.052,.048,[.99,.68,.045,1]);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
399
|
+
const antennaBase=[x+side*.125,by+.39,bz+.14];
|
|
400
|
+
const antennaBend=[x+side*.16,by+.55,bz+.16];
|
|
401
|
+
const antennaTip=[x+side*.205,by+.65,bz+.19];
|
|
402
|
+
this.addLink(antennaBase,antennaBend,.013,[.045,.055,.065,1]);
|
|
403
|
+
this.addLink(antennaBend,antennaTip,.011,[.045,.055,.065,1]);
|
|
404
|
+
this.add(s,antennaTip[0],antennaTip[1],antennaTip[2],.030,.030,.030,[.10,.12,.14,1]);
|
|
405
|
+
this.add(s,antennaTip[0],antennaTip[1]+.012,antennaTip[2]+.012,.012,.012,.012,[1,.73,.10,1]);
|
|
310
406
|
}
|
|
311
407
|
this.add(rb,x,by-.335,bz+.156,.32,.026,.018,[.045,.055,.065,1]);
|
|
312
408
|
this.add(s,x,by+.30,bz+.18,.115,.035,.06,[.045,.055,.065,1]);
|
|
313
409
|
// Short forward-projecting thighs tuck under the desktop and visually anchor the bee to its chair.
|
|
314
410
|
for(const side of [-1,1]){
|
|
315
411
|
this.add(s,x+side*.125,.465,z+.71,.075,.13,.14,[.075,.09,.11,1]);
|
|
316
|
-
this.add(rb,x+side*.13,.315,z+.66,.10,.060,.15,[
|
|
412
|
+
this.add(rb,x+side*.13,.315,z+.66,.10,.060,.15,[1,.86,.12,1]);
|
|
317
413
|
}
|
|
318
|
-
//
|
|
414
|
+
// A wider elbow arc keeps forearms visible around the torso in the rear camera view;
|
|
415
|
+
// the left hand stays over the keyboard and the right hand aligns with the mouse.
|
|
319
416
|
for(const side of [-1,1]){
|
|
320
|
-
const shoulder=[x+side*.
|
|
321
|
-
this.addLink(shoulder,elbow,.
|
|
322
|
-
this.addLink(elbow,wrist,.
|
|
417
|
+
const shoulder=[x+side*.36,.99,z+.94],elbow=[x+side*.55,.83,z+.82],wrist=[x+(side<0?-.49:.50),.675,z+.54],hand=[x+(side<0?-.42:.42),.655,z+.28];
|
|
418
|
+
this.addLink(shoulder,elbow,.042,[.12,.15,.18,1]);
|
|
419
|
+
this.addLink(elbow,wrist,.036,[.18,.22,.25,1]);
|
|
323
420
|
this.addLink(wrist,hand,.030,[.96,.63,.035,1]);
|
|
324
|
-
this.add(s,shoulder[0],shoulder[1],shoulder[2],.
|
|
325
|
-
this.add(s,elbow[0],elbow[1],elbow[2],.
|
|
326
|
-
this.add(s,wrist[0],wrist[1],wrist[2],.052,.052,.052,[.
|
|
421
|
+
this.add(s,shoulder[0],shoulder[1],shoulder[2],.080,.080,.085,[.14,.17,.20,1]);
|
|
422
|
+
this.add(s,elbow[0],elbow[1],elbow[2],.068,.068,.068,[.26,.30,.33,1]);
|
|
423
|
+
this.add(s,wrist[0],wrist[1],wrist[2],.052,.052,.052,[.21,.25,.28,1]);
|
|
327
424
|
this.add(rb,hand[0],hand[1],hand[2],.085,.045,.070,[.96,.63,.035,1]);
|
|
328
|
-
this.add(s,
|
|
329
|
-
for(let finger=0;finger<3;finger++)this.add(b,
|
|
425
|
+
this.add(s,hand[0],hand[1],hand[2],.070,.035,.055,[.98,.68,.045,1]);
|
|
426
|
+
for(let finger=0;finger<3;finger++)this.add(b,hand[0]+(finger-1)*.026,.638,hand[2]-.06,.012,.010,.046,[.22,.29,.35,1]);
|
|
330
427
|
}
|
|
331
428
|
// Small cyan service badge on the back of the chassis.
|
|
332
429
|
this.add(rb,x,by-.585,bz+.205,.13,.068,.018,[.025,.10,.14,1]);
|
|
333
430
|
this.add(rb,x,by-.585,bz+.218,.082,.016,.010,[.16,.88,.98,1]);
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
this.add(
|
|
337
|
-
this.add(
|
|
338
|
-
|
|
339
|
-
|
|
431
|
+
// Angled, translucent wings emerge from behind the head; a leading-edge highlight
|
|
432
|
+
// defines the outward/upward sweep without enclosing the wing like a headset ring.
|
|
433
|
+
this.add(this.wing,x-.49,by+.17,bz+.14,.82,.60,.10,[.88,.96,1,.96],0,-.48);
|
|
434
|
+
this.add(this.wing,x+.49,by+.17,bz+.14,.82,.60,.10,[.88,.96,1,.96],0,.48);
|
|
435
|
+
for(const side of [-1,1]){
|
|
436
|
+
const rotation=side*.48,ca=Math.cos(rotation),sa=Math.sin(rotation);
|
|
437
|
+
const edgePoints=WING_OUTLINE.map(p=>[
|
|
438
|
+
x+side*.49+ca*p[0]*.82-sa*p[1]*.60,
|
|
439
|
+
by+.17+sa*p[0]*.82+ca*p[1]*.60,
|
|
440
|
+
bz+.125
|
|
441
|
+
]);
|
|
442
|
+
for(let i=0;i<6;i++)this.addLink(edgePoints[i],edgePoints[i+1],.0052,[.59,.80,.91,.56]);
|
|
443
|
+
const root=[x+side*.38,by+.04,bz+.17];
|
|
444
|
+
const mid=[x+side*.50,by+.17,bz+.17];
|
|
445
|
+
const tip=[x+side*.76,by+.29,bz+.17];
|
|
446
|
+
this.addLink(root,mid,.0055,[.83,.93,.98,.70]);
|
|
447
|
+
this.addLink(mid,tip,.0042,[.83,.93,.98,.62]);
|
|
448
|
+
this.addLink([x+side*.43,by+.15,bz+.13],[x+side*.55,by+.22,bz+.13],.0032,[.90,.96,1,.54]);
|
|
449
|
+
}
|
|
450
|
+
// Slightly smaller assistants read as seated workers within the furniture scale.
|
|
451
|
+
for(const assistant of this.objects.slice(beeObjectStart)){
|
|
452
|
+
assistant.x=x+(assistant.x-x)*.92;
|
|
453
|
+
assistant.y=by+(assistant.y-by)*.92;
|
|
454
|
+
assistant.z=bz+(assistant.z-bz)*.92;
|
|
455
|
+
assistant.sx*=.84;assistant.sy*=.84;assistant.sz*=.84;
|
|
456
|
+
}
|
|
340
457
|
|
|
341
458
|
}
|
|
342
459
|
// Planters and stylized leaves soften the room edges.
|
|
343
460
|
for(const x of [-9.05,9.05]){
|
|
344
|
-
this.add(s,x,-.032,-4.70,.
|
|
345
|
-
this.add(rb,x,.17,-4.70,.
|
|
461
|
+
this.add(s,x,-.032,-4.70,.78,.012,.82,[.055,.09,.14,.24]);
|
|
462
|
+
this.add(rb,x,.17,-4.70,.68,.34,.68,[.70,.77,.81,1]);
|
|
346
463
|
this.add(this.cylinder,x,.70,-4.70,.065,.78,.065,[.31,.28,.19,1]);
|
|
347
464
|
// Layered foliage with varied yaw and drooping leaf tips, so the planters read as broad indoor plants.
|
|
348
465
|
for(let k=0;k<18;k++){
|
|
349
466
|
const a=k/18*Math.PI*2,spread=Math.sin(a)*.62;
|
|
350
|
-
const y
|
|
351
|
-
const leafColor=[[.
|
|
467
|
+
const y=1.00+(k%5)*.17,rad=.24+(k%4)*.115;
|
|
468
|
+
const leafColor=[[.10,.43,.15,1],[.15,.55,.18,1],[.20,.64,.22,1],[.28,.70,.28,1]][k%4];
|
|
352
469
|
this.add(this.organic,x+Math.sin(a)*rad,y,-4.70+Math.cos(a)*rad*.62,
|
|
353
|
-
.
|
|
470
|
+
.68+(k%3)*.075,.90+(k%4)*.12,.05,leafColor,a*.62-Math.PI*.18,Math.PI/2+spread);
|
|
354
471
|
}
|
|
355
472
|
for(let k=0;k<7;k++){
|
|
356
473
|
const a=k/7*Math.PI*2,spread=Math.sin(a)*.48;
|
|
357
|
-
this.add(this.organic,x+Math.sin(a)*.
|
|
358
|
-
.
|
|
474
|
+
this.add(this.organic,x+Math.sin(a)*.14,1.82+(k%3)*.12,-4.70+Math.cos(a)*.16,
|
|
475
|
+
.44,.72,.035,[.16,.64,.23,1],a*.65,Math.PI/2+spread);
|
|
359
476
|
}
|
|
360
477
|
}
|
|
361
478
|
// Low-opacity contact shadows share the same 8+6 workstation positions.
|
|
362
479
|
for(const desk of this.deskPositions){
|
|
363
480
|
const {x,z,width}=desk;
|
|
364
|
-
|
|
365
|
-
|
|
481
|
+
// Place the soft shadow just above the coplanar hex grout so depth testing can
|
|
482
|
+
// blend the contact shade without z-fighting or the grout rejecting it.
|
|
483
|
+
this.add(this.shadow,x,-.029,z+.02,width*.78,.009,.88,[.20,.29,.39,.90]);
|
|
484
|
+
this.add(this.shadow,x+.02,-.028,z+1.0,.84,.009,.54,[.20,.29,.39,.76]);
|
|
485
|
+
for(const dx of [-.36,.36])for(const dz of [-.39,.38]){
|
|
486
|
+
this.add(this.shadow,x+width*dx,-.028,z+dz,.39,.008,.38,[.20,.29,.39,.66]);
|
|
487
|
+
this.add(this.shadow,x+width*dx,.462,z+dz,.40,.009,.40,[.20,.29,.39,.36]);
|
|
488
|
+
}
|
|
366
489
|
}
|
|
367
490
|
};
|
|
368
491
|
Scene.prototype.buildBatches=function(){
|
|
369
|
-
const channels={opaque:{positions:[],normals:[],colors:[]},transparent:{positions:[],normals:[],colors:[]}};
|
|
492
|
+
const channels={opaque:{positions:[],normals:[],colors:[]},transparent:{positions:[],normals:[],colors:[],triangles:[]}};
|
|
370
493
|
const emit=(target,p,n,color)=>{
|
|
371
494
|
target.positions.push(p[0],p[1],p[2]);target.normals.push(n[0],n[1],n[2]);
|
|
372
495
|
target.colors.push(color[0],color[1],color[2],color[3]==null?1:color[3]);
|
|
@@ -382,19 +505,30 @@ window.Hive3D = (function () {
|
|
|
382
505
|
const world=local.map(v=>worldPoint(o,v));
|
|
383
506
|
const smooth=mesh===this.sphere||mesh.smooth;
|
|
384
507
|
const face=smooth?null:faceNormal(world[0],world[1],world[2]);
|
|
508
|
+
const triangle=[];
|
|
385
509
|
for(let k=0;k<3;k++){
|
|
386
510
|
const ix3=ix[i+k]*3,normal=[normals[ix3],normals[ix3+1],normals[ix3+2]];
|
|
387
|
-
|
|
511
|
+
const color=mesh.colors?mesh.colors.slice(ix[i+k]*4,ix[i+k]*4+4):o.color;
|
|
512
|
+
if(mesh.colors)color[3]*=o.color[3]==null?1:o.color[3];
|
|
513
|
+
const vertex={position:world[k],normal:face||smoothNormal(o,normal),color};
|
|
514
|
+
if(target===channels.transparent)triangle.push(vertex);else emit(target,vertex.position,vertex.normal,vertex.color);
|
|
388
515
|
}
|
|
516
|
+
if(triangle.length)channels.transparent.triangles.push({vertices:triangle,depth:triangle.reduce((sum,v)=>sum+v.position[0]*this.eyeDirection[0]+v.position[1]*this.eyeDirection[1]+v.position[2]*this.eyeDirection[2],0)/3});
|
|
389
517
|
}
|
|
390
518
|
}
|
|
519
|
+
// Transparent objects retain depth testing but disable depth writes. Keep their
|
|
520
|
+
// triangles so the packed draw batch can be depth-sorted again when the camera moves.
|
|
521
|
+
this.transparentTriangles=channels.transparent.triangles;
|
|
522
|
+
for(const triangle of this.transparentTriangles)triangle.depth=triangle.vertices.reduce((sum,v)=>sum+dot(v.position,this.eyeDirection),0)/3;
|
|
523
|
+
this.transparentTriangles.sort((a,b)=>a.depth-b.depth);
|
|
524
|
+
for(const triangle of this.transparentTriangles)for(const vertex of triangle.vertices)emit(channels.transparent,vertex.position,vertex.normal,vertex.color);
|
|
391
525
|
const gl=this.gl,upload=data=>{if(!data.length)return null;const buffer=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,buffer);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(data),gl.STATIC_DRAW);return buffer;};
|
|
392
526
|
this.batchBuffers={};
|
|
393
527
|
for(const key of ["opaque","transparent"]){
|
|
394
528
|
const channel=channels[key];
|
|
395
529
|
this.batchBuffers[key]={positions:upload(channel.positions),normals:upload(channel.normals),colors:upload(channel.colors),count:channel.positions.length/3};
|
|
396
530
|
}
|
|
397
|
-
for(const mesh of [this.box,this.roundBox,this.sphere,this.cylinder,this.organic,this.grout,this.bandGoldTop,this.bandGoldBottom,this.bandSeparator])if(mesh){if(mesh.p)gl.deleteBuffer(mesh.p);if(mesh.ix)gl.deleteBuffer(mesh.ix);mesh.p=null;mesh.ix=null;}
|
|
531
|
+
for(const mesh of [this.box,this.roundBox,this.sphere,this.cylinder,this.organic,this.wing,this.grout,this.shadow,this.bandGoldTop,this.bandGoldBottom,this.bandSeparator])if(mesh){if(mesh.p)gl.deleteBuffer(mesh.p);if(mesh.ix)gl.deleteBuffer(mesh.ix);mesh.p=null;mesh.ix=null;}
|
|
398
532
|
};
|
|
399
533
|
Scene.prototype.drawBatch=function(batch){
|
|
400
534
|
if(!batch||!batch.count)return;
|
|
@@ -404,11 +538,19 @@ window.Hive3D = (function () {
|
|
|
404
538
|
gl.bindBuffer(gl.ARRAY_BUFFER,batch.colors);gl.enableVertexAttribArray(this.aColor);gl.vertexAttribPointer(this.aColor,4,gl.FLOAT,false,0,0);
|
|
405
539
|
gl.drawArrays(gl.TRIANGLES,0,batch.count);
|
|
406
540
|
};
|
|
541
|
+
Scene.prototype.sortTransparentBatch=function(direction){
|
|
542
|
+
if(!this.transparentTriangles||!this.batchBuffers||!this.batchBuffers.transparent)return;
|
|
543
|
+
for(const triangle of this.transparentTriangles)triangle.depth=triangle.vertices.reduce((sum,v)=>sum+dot(v.position,direction),0)/3;
|
|
544
|
+
this.transparentTriangles.sort((a,b)=>a.depth-b.depth);
|
|
545
|
+
const positions=[],normals=[],colors=[];
|
|
546
|
+
for(const triangle of this.transparentTriangles)for(const vertex of triangle.vertices){positions.push(...vertex.position);normals.push(...vertex.normal);colors.push(...vertex.color);}
|
|
547
|
+
const gl=this.gl,batch=this.batchBuffers.transparent;gl.bindBuffer(gl.ARRAY_BUFFER,batch.positions);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(positions),gl.DYNAMIC_DRAW);gl.bindBuffer(gl.ARRAY_BUFFER,batch.normals);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(normals),gl.DYNAMIC_DRAW);gl.bindBuffer(gl.ARRAY_BUFFER,batch.colors);gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(colors),gl.DYNAMIC_DRAW);
|
|
548
|
+
};
|
|
407
549
|
Scene.prototype.render=function(){
|
|
408
550
|
if(!this.gl||!this.active||this.displayMode!=="live3d")return;const gl=this.gl;const w=Math.max(1,this.cssW),h=Math.max(1,this.cssH);const dpr=Math.min(window.devicePixelRatio||1,1.6);
|
|
409
551
|
const bw=Math.floor(w*dpr),bh=Math.floor(h*dpr);if(this.canvas.width!==bw||this.canvas.height!==bh){this.canvas.width=bw;this.canvas.height=bh;}
|
|
410
552
|
gl.viewport(0,0,bw,bh);gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);gl.useProgram(this.program);
|
|
411
|
-
const radius=
|
|
553
|
+
const radius=25.4/this.zoom;const eye=[Math.sin(this.yaw)*radius,3.1+Math.sin(this.pitch)*radius*.52,Math.cos(this.yaw)*radius-1.3];const view=mat4.lookAt(eye,[0,.62,0],[0,1,0]);const proj=mat4.perspective(.46,w/h,.1,80);const vp=mat4.multiply(mat4.multiply(proj,view),mat4.translate(this.panX,this.panY,0));this.viewProj=vp;this.eyeDirection=norm(sub(eye,[0,.62,0]));this.sortTransparentBatch(this.eyeDirection);
|
|
412
554
|
gl.uniformMatrix4fv(this.uViewProj,false,vp);gl.uniform3f(this.uEyePosition,eye[0],eye[1],eye[2]);
|
|
413
555
|
this.drawBatch(this.batchBuffers&&this.batchBuffers.opaque);
|
|
414
556
|
const translucent=this.batchBuffers&&this.batchBuffers.transparent;
|
|
@@ -497,14 +639,14 @@ window.Hive3D = (function () {
|
|
|
497
639
|
Scene.prototype.setActive=function(active){this.active=!!active;if(this.timer)clearInterval(this.timer);if(this.raf)cancelAnimationFrame(this.raf);this.raf=0;this.layer.hidden=!this.active;this.canvas.hidden=!this.active||this.displayMode!=="live3d";if(this.active){this.updateOverlay();if(this.displayMode==="live3d")this.render();else if(this.displayMode==="reference")this.layoutOverlayPositions();this.timer=setInterval(()=>{if(!document.hidden&&this.host.offsetParent!==null)this.updateOverlay();},1000);}};
|
|
498
640
|
Scene.prototype.resize=function(){this.cssW=this.host.clientWidth;this.cssH=this.host.clientHeight;if(!this.cssW||!this.cssH)return;if(this.displayMode==="live3d")this.render();else if(this.displayMode==="reference")this.layoutOverlayPositions();};
|
|
499
641
|
Scene.prototype.zoomAt=function(factor){this.zoom=clamp(this.zoom/factor,.72,2.4);this.applyArtboardTransform();this.render();};
|
|
500
|
-
Scene.prototype.resetView=function(){this.zoom=1;this.yaw=0;this.pitch=.
|
|
501
|
-
Scene.prototype.setDisplayMode=function(mode){const next=mode==="live3d"?"live3d":mode==="2d"?"2d":"reference";const changed=next!==this.displayMode;this.displayMode=next;if(changed){this.zoom=1;this.yaw=0;this.pitch=.
|
|
642
|
+
Scene.prototype.resetView=function(){this.zoom=1;this.yaw=0;this.pitch=.48;this.panX=0;this.panY=0;this.applyArtboardTransform();this.render();};
|
|
643
|
+
Scene.prototype.setDisplayMode=function(mode){const next=mode==="live3d"?"live3d":mode==="2d"?"2d":"reference";const changed=next!==this.displayMode;this.displayMode=next;if(changed){this.zoom=1;this.yaw=0;this.pitch=.48;this.panX=0;this.panY=0;}this.canvas.hidden=!this.active||this.displayMode!=="live3d";this.updateOverlay();this.applyArtboardTransform();if(this.displayMode==="live3d")this.render();else if(this.displayMode==="reference")this.layoutOverlayPositions();};
|
|
502
644
|
Scene.prototype.applyArtboardTransform=function(){if(!this.artboard)return;this.artboard.style.transform=this.displayMode==="reference"?"translate3d("+(this.panX*100)+"%,"+(this.panY*100)+"%,0) scale("+this.zoom+")":"none";};
|
|
503
645
|
Scene.prototype.info=function(){return{renderer:"webgl",cells:(this.cells||[]).length,screens:SCREENS.length,lanes:this.model?this.model.lanes.length:0,page:this.page,zoom:this.zoom,cssW:this.cssW,cssH:this.cssH,objects:this.objects?this.objects.length:0,deskRows:this.deskPositions?[this.deskPositions.filter(d=>d.row===0).length,this.deskPositions.filter(d=>d.row===1).length]:[]};};
|
|
504
646
|
Scene.prototype.projectCell=function(rel){const i=this.screenMeta.findIndex(c=>c.rel===rel),desk=this.deskPositions&&this.deskPositions[i];if(i<0||!desk)return null;const p=this.projectWorld(desk.x,desk.row===1?1.38:1.205,desk.z-.418);return p&&p.visible?{x:p.x,y:p.y}:null;};
|
|
505
|
-
Scene.prototype.dispose=function(){this.setActive(false);if(this.resizeObserver)this.resizeObserver.disconnect();if(this.languageObserver)this.languageObserver.disconnect();this.listeners.forEach(fn=>fn());if(this.pager)this.pager.remove();if(this.gl){const gl=this.gl;if(this.batchBuffers)for(const pass of Object.values(this.batchBuffers))for(const key of ["positions","normals","colors"])if(pass&&pass[key])gl.deleteBuffer(pass[key]);for(const o of [this.box,this.roundBox,this.sphere])if(o){if(o.p)gl.deleteBuffer(o.p);if(o.ix)gl.deleteBuffer(o.ix);}if(this.program)gl.deleteProgram(this.program);}if(window.__hive3d===this)delete window.__hive3d;};
|
|
647
|
+
Scene.prototype.dispose=function(){this.setActive(false);if(this.resizeObserver)this.resizeObserver.disconnect();if(this.languageObserver)this.languageObserver.disconnect();this.listeners.forEach(fn=>fn());if(this.pager)this.pager.remove();if(this.gl){const gl=this.gl;if(this.batchBuffers)for(const pass of Object.values(this.batchBuffers))for(const key of ["positions","normals","colors"])if(pass&&pass[key])gl.deleteBuffer(pass[key]);for(const o of [this.box,this.roundBox,this.sphere,this.cylinder,this.organic,this.wing,this.grout,this.shadow,this.bandGoldTop,this.bandGoldBottom,this.bandSeparator])if(o){if(o.p)gl.deleteBuffer(o.p);if(o.ix)gl.deleteBuffer(o.ix);}if(this.program)gl.deleteProgram(this.program);}if(window.__hive3d===this)delete window.__hive3d;};
|
|
506
648
|
const originalCreate=(opts)=>new Scene(opts);
|
|
507
649
|
// Initialize geometry after the constructor has successfully acquired WebGL.
|
|
508
650
|
const create=(opts)=>{const scene=originalCreate(opts);if(scene.failed||!scene.gl)return null;scene.buildWorld();scene.buildBatches();scene.render();return scene;};
|
|
509
651
|
return {create};
|
|
510
|
-
})();
|
|
652
|
+
})();
|