@particle-academy/fancy-3d-babylon 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @particle-academy/fancy-3d-babylon
2
2
 
3
+ [![Fancified](art/fancified.svg)](https://particle.academy)
4
+
3
5
  Babylon.js adapter for [`@particle-academy/fancy-3d`](https://www.npmjs.com/package/@particle-academy/fancy-3d).
4
6
 
5
7
  `fancy-3d` ships an engine-agnostic core (Scene types + a `CanvasEngine` interface + a DOM/CSS-3D renderer). This package adds a full WebGL renderer on top, plus the React components that mount onto a Babylon `Scene`: `Stage`, `Monitor`, `Card3D`, `Screen`, primitives, and layout helpers (`placeOnGrid`, `placeOnArc`, `placeOnWall`, `placeOnSphere`, `placeOnPath`).
@@ -62,3 +64,9 @@ The core `fancy-3d` is engine-agnostic and lightweight (~80KB). Pulling in Babyl
62
64
  ## License
63
65
 
64
66
  MIT
67
+
68
+ ---
69
+
70
+ ## ⭐ Star Fancy UI
71
+
72
+ If this package is useful to you, a quick ⭐ on the repo really helps us build a better kit. Thank you!
package/dist/index.cjs CHANGED
@@ -1,6 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var core = require('@babylonjs/core');
3
+ var math_color = require('@babylonjs/core/Maths/math.color');
4
+ var math_vector = require('@babylonjs/core/Maths/math.vector');
5
+ var dynamicTexture = require('@babylonjs/core/Materials/Textures/dynamicTexture');
6
+ var meshBuilder = require('@babylonjs/core/Meshes/meshBuilder');
7
+ var standardMaterial = require('@babylonjs/core/Materials/standardMaterial');
8
+ var mesh = require('@babylonjs/core/Meshes/mesh');
4
9
 
5
10
  // src/index.ts
6
11
 
@@ -403,11 +408,11 @@ function autoPixelSize(worldWidth, worldHeight, density = DEFAULT_DENSITY) {
403
408
  return { w, h };
404
409
  }
405
410
  function buildSurface(scene, name, surface, autoSize) {
406
- const mat = new core.StandardMaterial(`${name}-mat`, scene);
407
- mat.specularColor = new core.Color3(0.05, 0.05, 0.05);
411
+ const mat = new standardMaterial.StandardMaterial(`${name}-mat`, scene);
412
+ mat.specularColor = new math_color.Color3(0.05, 0.05, 0.05);
408
413
  if (surface.type === "color") {
409
- mat.diffuseColor = core.Color3.FromHexString(surface.color);
410
- mat.emissiveColor = core.Color3.FromHexString(surface.color).scale(0.15);
414
+ mat.diffuseColor = math_color.Color3.FromHexString(surface.color);
415
+ mat.emissiveColor = math_color.Color3.FromHexString(surface.color).scale(0.15);
411
416
  return { material: mat, texture: null };
412
417
  }
413
418
  let w = surface.pixelWidth;
@@ -419,7 +424,7 @@ function buildSurface(scene, name, surface, autoSize) {
419
424
  }
420
425
  w = w ?? 512;
421
426
  h = h ?? 512;
422
- const tex = new core.DynamicTexture(`${name}-tex`, { width: w * TEX_SCALE, height: h * TEX_SCALE }, scene, true);
427
+ const tex = new dynamicTexture.DynamicTexture(`${name}-tex`, { width: w * TEX_SCALE, height: h * TEX_SCALE }, scene, true);
423
428
  tex.vScale = -1;
424
429
  tex.vOffset = 1;
425
430
  if (surface.type === "paint" && surface.transparent) {
@@ -438,13 +443,13 @@ function buildSurface(scene, name, surface, autoSize) {
438
443
  tex.update(false);
439
444
  mat.diffuseTexture = tex;
440
445
  mat.emissiveTexture = tex;
441
- mat.emissiveColor = new core.Color3(0.6, 0.6, 0.6);
446
+ mat.emissiveColor = new math_color.Color3(0.6, 0.6, 0.6);
442
447
  if (tex.hasAlpha) mat.useAlphaFromDiffuseTexture = true;
443
448
  return { material: mat, texture: tex };
444
449
  }
445
450
  function createPanel(opts) {
446
451
  const name = opts.name ?? "panel";
447
- const m = core.MeshBuilder.CreatePlane(name, { width: opts.width, height: opts.height, sideOrientation: opts.sideOrientation }, opts.scene);
452
+ const m = meshBuilder.MeshBuilder.CreatePlane(name, { width: opts.width, height: opts.height, sideOrientation: opts.sideOrientation }, opts.scene);
448
453
  const { material } = buildSurface(opts.scene, name, opts.surface, {
449
454
  worldWidth: opts.width,
450
455
  worldHeight: opts.height
@@ -455,7 +460,7 @@ function createPanel(opts) {
455
460
  function createBillboard(opts) {
456
461
  const m = createPanel({ ...opts, name: opts.name ?? "billboard" });
457
462
  if (opts.faceCamera) {
458
- m.billboardMode = core.Mesh.BILLBOARDMODE_ALL;
463
+ m.billboardMode = mesh.Mesh.BILLBOARDMODE_ALL;
459
464
  }
460
465
  return m;
461
466
  }
@@ -463,7 +468,7 @@ var BOX_FACE_ORDER = ["front", "back", "right", "left", "top", "bottom"];
463
468
  function createBuilding(opts) {
464
469
  const name = opts.name ?? "building";
465
470
  if (opts.surface && !opts.faces) {
466
- const box2 = core.MeshBuilder.CreateBox(name, { width: opts.width, height: opts.height, depth: opts.depth }, opts.scene);
471
+ const box2 = meshBuilder.MeshBuilder.CreateBox(name, { width: opts.width, height: opts.height, depth: opts.depth }, opts.scene);
467
472
  const { material } = buildSurface(opts.scene, name, opts.surface, {
468
473
  worldWidth: opts.width,
469
474
  worldHeight: opts.height
@@ -475,7 +480,7 @@ function createBuilding(opts) {
475
480
  const faceH = 256;
476
481
  const atlasW = faceW * 3;
477
482
  const atlasH = faceH * 2;
478
- const tex = new core.DynamicTexture(`${name}-atlas`, { width: atlasW * TEX_SCALE, height: atlasH * TEX_SCALE }, opts.scene, true);
483
+ const tex = new dynamicTexture.DynamicTexture(`${name}-atlas`, { width: atlasW * TEX_SCALE, height: atlasH * TEX_SCALE }, opts.scene, true);
479
484
  tex.vScale = -1;
480
485
  tex.vOffset = 1;
481
486
  const ctx = tex.getContext();
@@ -498,15 +503,15 @@ function createBuilding(opts) {
498
503
  paintWidget(ctx, surface.widget, faceW, faceH, surface.selected ?? false);
499
504
  }
500
505
  ctx.restore();
501
- faceUV.push(new core.Vector4(fx / atlasW, 1 - (fy + faceH) / atlasH, (fx + faceW) / atlasW, 1 - fy / atlasH));
506
+ faceUV.push(new math_vector.Vector4(fx / atlasW, 1 - (fy + faceH) / atlasH, (fx + faceW) / atlasW, 1 - fy / atlasH));
502
507
  });
503
508
  tex.update(false);
504
- const mat = new core.StandardMaterial(`${name}-mat`, opts.scene);
509
+ const mat = new standardMaterial.StandardMaterial(`${name}-mat`, opts.scene);
505
510
  mat.diffuseTexture = tex;
506
511
  mat.emissiveTexture = tex;
507
- mat.emissiveColor = new core.Color3(0.4, 0.4, 0.4);
508
- mat.specularColor = new core.Color3(0.05, 0.05, 0.05);
509
- const box = core.MeshBuilder.CreateBox(name, {
512
+ mat.emissiveColor = new math_color.Color3(0.4, 0.4, 0.4);
513
+ mat.specularColor = new math_color.Color3(0.05, 0.05, 0.05);
514
+ const box = meshBuilder.MeshBuilder.CreateBox(name, {
510
515
  width: opts.width,
511
516
  height: opts.height,
512
517
  depth: opts.depth,
@@ -528,7 +533,7 @@ function createPillar(opts) {
528
533
  }
529
534
  function createCylinder(opts) {
530
535
  const name = opts.name ?? "cylinder";
531
- const m = core.MeshBuilder.CreateCylinder(name, {
536
+ const m = meshBuilder.MeshBuilder.CreateCylinder(name, {
532
537
  diameter: opts.radius * 2,
533
538
  height: opts.height,
534
539
  tessellation: opts.tessellation ?? 48
@@ -545,13 +550,13 @@ function createCurvedPanel(opts) {
545
550
  const radius = opts.width / opts.arc;
546
551
  const tess = opts.tessellation ?? 48;
547
552
  const fullCircleSegments = Math.max(8, Math.ceil(tess * (Math.PI * 2) / opts.arc));
548
- const m = core.MeshBuilder.CreateCylinder(name, {
553
+ const m = meshBuilder.MeshBuilder.CreateCylinder(name, {
549
554
  diameter: radius * 2,
550
555
  height: opts.height,
551
556
  tessellation: fullCircleSegments,
552
557
  arc: opts.arc / (Math.PI * 2),
553
- cap: core.Mesh.NO_CAP,
554
- sideOrientation: core.Mesh.DOUBLESIDE
558
+ cap: mesh.Mesh.NO_CAP,
559
+ sideOrientation: mesh.Mesh.DOUBLESIDE
555
560
  }, opts.scene);
556
561
  const { material } = buildSurface(opts.scene, name, opts.surface, {
557
562
  worldWidth: opts.width,
@@ -562,7 +567,7 @@ function createCurvedPanel(opts) {
562
567
  }
563
568
  function createSphere(opts) {
564
569
  const name = opts.name ?? "sphere";
565
- const m = core.MeshBuilder.CreateSphere(name, { diameter: opts.diameter, segments: opts.segments ?? 32 }, opts.scene);
570
+ const m = meshBuilder.MeshBuilder.CreateSphere(name, { diameter: opts.diameter, segments: opts.segments ?? 32 }, opts.scene);
566
571
  const { material } = buildSurface(opts.scene, name, opts.surface, {
567
572
  worldWidth: opts.diameter * Math.PI,
568
573
  worldHeight: opts.diameter * Math.PI / 2
@@ -572,7 +577,7 @@ function createSphere(opts) {
572
577
  }
573
578
  function createDisc(opts) {
574
579
  const name = opts.name ?? "disc";
575
- const m = core.MeshBuilder.CreateDisc(name, { radius: opts.radius, tessellation: opts.tessellation ?? 64 }, opts.scene);
580
+ const m = meshBuilder.MeshBuilder.CreateDisc(name, { radius: opts.radius, tessellation: opts.tessellation ?? 64 }, opts.scene);
576
581
  const { material } = buildSurface(opts.scene, name, opts.surface, {
577
582
  worldWidth: opts.radius * 2,
578
583
  worldHeight: opts.radius * 2
@@ -605,7 +610,7 @@ function createSign(opts) {
605
610
  const postHeight = opts.postHeight ?? 1.4;
606
611
  const postThickness = opts.postThickness ?? 0.12;
607
612
  const postColor = opts.postColor ?? "#1f2937";
608
- const root = new core.Mesh(`${name}-root`, opts.scene);
613
+ const root = new mesh.Mesh(`${name}-root`, opts.scene);
609
614
  const panel = createBillboard({
610
615
  scene: opts.scene,
611
616
  name: `${name}-panel`,
@@ -613,16 +618,16 @@ function createSign(opts) {
613
618
  height: opts.height,
614
619
  surface: opts.surface
615
620
  });
616
- panel.position = new core.Vector3(0, postHeight + opts.height / 2, 0);
621
+ panel.position = new math_vector.Vector3(0, postHeight + opts.height / 2, 0);
617
622
  panel.parent = root;
618
- const post = core.MeshBuilder.CreateBox(`${name}-post`, {
623
+ const post = meshBuilder.MeshBuilder.CreateBox(`${name}-post`, {
619
624
  width: postThickness,
620
625
  height: postHeight,
621
626
  depth: postThickness
622
627
  }, opts.scene);
623
- post.position = new core.Vector3(0, postHeight / 2, 0);
624
- const postMat = new core.StandardMaterial(`${name}-post-mat`, opts.scene);
625
- postMat.diffuseColor = core.Color3.FromHexString(postColor);
628
+ post.position = new math_vector.Vector3(0, postHeight / 2, 0);
629
+ const postMat = new standardMaterial.StandardMaterial(`${name}-post-mat`, opts.scene);
630
+ postMat.diffuseColor = math_color.Color3.FromHexString(postColor);
626
631
  post.material = postMat;
627
632
  post.parent = root;
628
633
  return { root, panel, post };
@@ -631,15 +636,15 @@ function createMonitor(opts) {
631
636
  const name = opts.name ?? "monitor";
632
637
  const depth = opts.depth ?? 0.18;
633
638
  const bezelColor = opts.bezel ?? "#0b0f17";
634
- const root = new core.Mesh(`${name}-root`, opts.scene);
635
- const body = core.MeshBuilder.CreateBox(`${name}-body`, {
639
+ const root = new mesh.Mesh(`${name}-root`, opts.scene);
640
+ const body = meshBuilder.MeshBuilder.CreateBox(`${name}-body`, {
636
641
  width: opts.width,
637
642
  height: opts.height,
638
643
  depth
639
644
  }, opts.scene);
640
- const bodyMat = new core.StandardMaterial(`${name}-body-mat`, opts.scene);
641
- bodyMat.diffuseColor = core.Color3.FromHexString(bezelColor);
642
- bodyMat.specularColor = new core.Color3(0.08, 0.08, 0.08);
645
+ const bodyMat = new standardMaterial.StandardMaterial(`${name}-body-mat`, opts.scene);
646
+ bodyMat.diffuseColor = math_color.Color3.FromHexString(bezelColor);
647
+ bodyMat.specularColor = new math_color.Color3(0.08, 0.08, 0.08);
643
648
  body.material = bodyMat;
644
649
  body.parent = root;
645
650
  const screenInset = 0.04;
@@ -654,16 +659,16 @@ function createMonitor(opts) {
654
659
  front: opts.screen,
655
660
  edge: { type: "color", color: bezelColor }
656
661
  });
657
- screen.position = new core.Vector3(0, 0, depth / 2 + 5e-3);
662
+ screen.position = new math_vector.Vector3(0, 0, depth / 2 + 5e-3);
658
663
  screen.parent = root;
659
664
  let stand;
660
665
  if (opts.standHeight && opts.standHeight > 0) {
661
- stand = core.MeshBuilder.CreateBox(`${name}-stand`, {
666
+ stand = meshBuilder.MeshBuilder.CreateBox(`${name}-stand`, {
662
667
  width: opts.width * 0.3,
663
668
  height: opts.standHeight,
664
669
  depth: depth * 1.5
665
670
  }, opts.scene);
666
- stand.position = new core.Vector3(0, -opts.height / 2 - opts.standHeight / 2, 0);
671
+ stand.position = new math_vector.Vector3(0, -opts.height / 2 - opts.standHeight / 2, 0);
667
672
  stand.material = bodyMat;
668
673
  stand.parent = root;
669
674
  }
@@ -671,9 +676,9 @@ function createMonitor(opts) {
671
676
  }
672
677
  function createDecal(opts) {
673
678
  const name = opts.name ?? "decal";
674
- const normal = opts.normal ?? new core.Vector3(0, 0, -1);
675
- const size = new core.Vector3(opts.size.width, opts.size.height, opts.size.depth ?? 1);
676
- const decal = core.MeshBuilder.CreateDecal(name, opts.target, {
679
+ const normal = opts.normal ?? new math_vector.Vector3(0, 0, -1);
680
+ const size = new math_vector.Vector3(opts.size.width, opts.size.height, opts.size.depth ?? 1);
681
+ const decal = meshBuilder.MeshBuilder.CreateDecal(name, opts.target, {
677
682
  position: opts.position,
678
683
  normal,
679
684
  size,
@@ -690,7 +695,7 @@ function createDecal(opts) {
690
695
  return decal;
691
696
  }
692
697
  function createWidgetTexture(scene, name, widget, pixelWidth, pixelHeight, selected = false) {
693
- const tex = new core.DynamicTexture(name, { width: pixelWidth * TEX_SCALE, height: pixelHeight * TEX_SCALE }, scene, true);
698
+ const tex = new dynamicTexture.DynamicTexture(name, { width: pixelWidth * TEX_SCALE, height: pixelHeight * TEX_SCALE }, scene, true);
694
699
  tex.vScale = -1;
695
700
  tex.vOffset = 1;
696
701
  tex.hasAlpha = true;
@@ -711,11 +716,11 @@ function normalizedCenter(node, bounds) {
711
716
  }
712
717
  function placeOnGrid(node, mesh, bounds, opts = {}) {
713
718
  const scale = opts.scale ?? 1 / 120;
714
- const origin = opts.origin ?? new core.Vector3(0, 0, 0);
719
+ const origin = opts.origin ?? new math_vector.Vector3(0, 0, 0);
715
720
  const { w, h } = sizeOf(node);
716
721
  const cx = node.position.x + w / 2 - (bounds.minX + bounds.maxX) / 2;
717
722
  const cy = node.position.y + h / 2 - (bounds.minY + bounds.maxY) / 2;
718
- mesh.position = new core.Vector3(origin.x + cx * scale, origin.y - cy * scale, origin.z);
723
+ mesh.position = new math_vector.Vector3(origin.x + cx * scale, origin.y - cy * scale, origin.z);
719
724
  }
720
725
  function placeOnWall(node, mesh, bounds, opts = {}) {
721
726
  const spreadX = opts.spreadX ?? 10;
@@ -723,7 +728,7 @@ function placeOnWall(node, mesh, bounds, opts = {}) {
723
728
  const centerY = opts.centerY ?? 1.5;
724
729
  const z = opts.z ?? 0;
725
730
  const { cx, cy } = normalizedCenter(node, bounds);
726
- mesh.position = new core.Vector3((cx - 0.5) * spreadX, centerY + (0.5 - cy) * spreadY, z);
731
+ mesh.position = new math_vector.Vector3((cx - 0.5) * spreadX, centerY + (0.5 - cy) * spreadY, z);
727
732
  mesh.rotation.y = 0;
728
733
  }
729
734
  function placeOnArc(node, mesh, bounds, opts = {}) {
@@ -733,19 +738,19 @@ function placeOnArc(node, mesh, bounds, opts = {}) {
733
738
  const baseY = opts.baseY ?? 0;
734
739
  const { cx, cy } = normalizedCenter(node, bounds);
735
740
  const angle = (cx - 0.5) * arc;
736
- mesh.position = new core.Vector3(Math.sin(angle) * radius, baseY + (0.5 - cy) * height, Math.cos(angle) * radius);
741
+ mesh.position = new math_vector.Vector3(Math.sin(angle) * radius, baseY + (0.5 - cy) * height, Math.cos(angle) * radius);
737
742
  mesh.rotation.y = angle;
738
743
  }
739
744
  function lerpPath(waypoints, t) {
740
- if (waypoints.length === 0) return { pos: new core.Vector3(0, 0, 0), tangent: new core.Vector3(0, 0, 1) };
741
- if (waypoints.length === 1) return { pos: waypoints[0].clone(), tangent: new core.Vector3(0, 0, 1) };
745
+ if (waypoints.length === 0) return { pos: new math_vector.Vector3(0, 0, 0), tangent: new math_vector.Vector3(0, 0, 1) };
746
+ if (waypoints.length === 1) return { pos: waypoints[0].clone(), tangent: new math_vector.Vector3(0, 0, 1) };
742
747
  const segments = waypoints.length - 1;
743
748
  const scaled = Math.max(0, Math.min(1, t)) * segments;
744
749
  const i = Math.min(segments - 1, Math.floor(scaled));
745
750
  const local = scaled - i;
746
751
  const a = waypoints[i];
747
752
  const b = waypoints[i + 1];
748
- const pos = core.Vector3.Lerp(a, b, local);
753
+ const pos = math_vector.Vector3.Lerp(a, b, local);
749
754
  const tangent = b.subtract(a).normalize();
750
755
  return { pos, tangent };
751
756
  }
@@ -768,7 +773,7 @@ function placeOnPath(node, mesh, bounds, opts) {
768
773
  }
769
774
  function placeOnSphere(node, mesh, bounds, opts = {}) {
770
775
  const radius = opts.radius ?? 5;
771
- const center = opts.center ?? new core.Vector3(0, 0, 0);
776
+ const center = opts.center ?? new math_vector.Vector3(0, 0, 0);
772
777
  const latSpan = opts.latSpan ?? Math.PI * 0.7;
773
778
  const lonSpan = opts.lonSpan ?? Math.PI * 2;
774
779
  const { cx, cy } = normalizedCenter(node, bounds);
@@ -778,7 +783,7 @@ function placeOnSphere(node, mesh, bounds, opts = {}) {
778
783
  const x = r * Math.cos(lat) * Math.sin(lon);
779
784
  const y = r * Math.sin(lat);
780
785
  const z = r * Math.cos(lat) * Math.cos(lon);
781
- mesh.position = new core.Vector3(center.x + x, center.y + y, center.z + z);
786
+ mesh.position = new math_vector.Vector3(center.x + x, center.y + y, center.z + z);
782
787
  mesh.rotation.y = lon;
783
788
  }
784
789
 
@@ -788,7 +793,7 @@ function createBabylonAdapter(deps) {
788
793
  return {
789
794
  render(spec, ctx) {
790
795
  const { w, h } = deps.sizeFor(spec);
791
- const tex = new core.DynamicTexture(`tex-${ctx.nodeId}`, { width: w * TEX_SCALE, height: h * TEX_SCALE }, deps.scene3D, true);
796
+ const tex = new dynamicTexture.DynamicTexture(`tex-${ctx.nodeId}`, { width: w * TEX_SCALE, height: h * TEX_SCALE }, deps.scene3D, true);
792
797
  tex.hasAlpha = true;
793
798
  tex.vScale = -1;
794
799
  tex.vOffset = 1;
@@ -796,13 +801,13 @@ function createBabylonAdapter(deps) {
796
801
  c.scale(TEX_SCALE, TEX_SCALE);
797
802
  paintWidget(c, spec, w, h, ctx.selected);
798
803
  tex.update(false);
799
- const mat = new core.StandardMaterial(`mat-${ctx.nodeId}`, deps.scene3D);
804
+ const mat = new standardMaterial.StandardMaterial(`mat-${ctx.nodeId}`, deps.scene3D);
800
805
  mat.diffuseTexture = tex;
801
806
  mat.emissiveTexture = tex;
802
- mat.emissiveColor = new core.Color3(1, 1, 1);
807
+ mat.emissiveColor = new math_color.Color3(1, 1, 1);
803
808
  mat.disableLighting = false;
804
809
  mat.useAlphaFromDiffuseTexture = true;
805
- const plane = core.MeshBuilder.CreatePlane(`node-${ctx.nodeId}`, { width: w * WORLD_SCALE, height: h * WORLD_SCALE }, deps.scene3D);
810
+ const plane = meshBuilder.MeshBuilder.CreatePlane(`node-${ctx.nodeId}`, { width: w * WORLD_SCALE, height: h * WORLD_SCALE }, deps.scene3D);
806
811
  plane.material = mat;
807
812
  plane.metadata = { nodeId: ctx.nodeId, kind: spec.kind };
808
813
  return plane;
@@ -818,8 +823,8 @@ function placeOnCylinder(node, mesh, bounds, opts = {}) {
818
823
  const cy = (node.position.y + (node.size?.h ?? 120) / 2 - bounds.minY) / rangeY;
819
824
  const angle = (cx - 0.5) * arc;
820
825
  const height = (0.5 - cy) * 6;
821
- mesh.position = new core.Vector3(Math.sin(angle) * radius, height, Math.cos(angle) * radius);
822
- mesh.rotation = new core.Vector3(0, angle, 0);
826
+ mesh.position = new math_vector.Vector3(Math.sin(angle) * radius, height, Math.cos(angle) * radius);
827
+ mesh.rotation = new math_vector.Vector3(0, angle, 0);
823
828
  }
824
829
  function sceneBounds(scene) {
825
830
  const xs = scene.nodes.map((n) => n.position.x);