pni 1.0.0
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/dist/add-three-app.d.ts +6 -0
- package/dist/add-three-app.js +111 -0
- package/dist/app.d.ts +11 -0
- package/dist/app.js +143 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +71 -0
- package/dist/components/FeatureSelector.d.ts +21 -0
- package/dist/components/FeatureSelector.js +175 -0
- package/dist/components/ProgressIndicator.d.ts +7 -0
- package/dist/components/ProgressIndicator.js +46 -0
- package/dist/components/Summary.d.ts +8 -0
- package/dist/components/Summary.js +51 -0
- package/dist/components/WelcomeHeader.d.ts +2 -0
- package/dist/components/WelcomeHeader.js +8 -0
- package/dist/template_code/three/README.md +146 -0
- package/dist/template_code/three/World.js +133 -0
- package/dist/template_code/three/camera.js +30 -0
- package/dist/template_code/three/components/GlobeSphere.js +608 -0
- package/dist/template_code/three/components/cube.js +27 -0
- package/dist/template_code/three/components/lights.js +16 -0
- package/dist/template_code/three/components/sphere.js +26 -0
- package/dist/template_code/three/components/torus.js +25 -0
- package/dist/template_code/three/scene.js +28 -0
- package/dist/template_code/three/systems/Loop.js +43 -0
- package/dist/template_code/three/systems/Resizer.js +26 -0
- package/dist/template_code/three/systems/controls.js +19 -0
- package/dist/template_code/three/systems/post-processing.js +50 -0
- package/dist/template_code/three/systems/renderer.js +17 -0
- package/dist/template_code/three/utils/deviceDetector.js +141 -0
- package/dist/template_code/three/utils/gltfLoader.js +14 -0
- package/dist/template_code/three/utils/loadKTX2Texture.js +42 -0
- package/dist/template_code/three/utils/textureLoader.js +21 -0
- package/dist/utils/add-three.d.ts +7 -0
- package/dist/utils/add-three.js +288 -0
- package/dist/utils/app-creation.d.ts +4 -0
- package/dist/utils/app-creation.js +35 -0
- package/dist/utils/config-generator.d.ts +6 -0
- package/dist/utils/config-generator.js +508 -0
- package/dist/utils/css-variables.d.ts +4 -0
- package/dist/utils/css-variables.js +316 -0
- package/dist/utils/dependencies.d.ts +11 -0
- package/dist/utils/dependencies.js +68 -0
- package/dist/utils/package-manager.d.ts +4 -0
- package/dist/utils/package-manager.js +56 -0
- package/dist/utils/project-detection.d.ts +2 -0
- package/dist/utils/project-detection.js +60 -0
- package/dist/utils/shadcn-setup.d.ts +2 -0
- package/dist/utils/shadcn-setup.js +46 -0
- package/package.json +81 -0
- package/readme.md +119 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {Scene, Color} from 'three';
|
|
2
|
+
import {createCube} from './components/cube.js';
|
|
3
|
+
import {createTorus} from './components/torus.js';
|
|
4
|
+
import {createSphere} from './components/sphere.js';
|
|
5
|
+
import {createLights} from './components/lights.js';
|
|
6
|
+
|
|
7
|
+
function createScene() {
|
|
8
|
+
const scene = new Scene();
|
|
9
|
+
scene.background = new Color('#1a1a1a');
|
|
10
|
+
|
|
11
|
+
// Add meshes
|
|
12
|
+
const cube = createCube();
|
|
13
|
+
const torus = createTorus();
|
|
14
|
+
const sphere = createSphere();
|
|
15
|
+
|
|
16
|
+
scene.add(cube, torus, sphere);
|
|
17
|
+
|
|
18
|
+
// Add lights
|
|
19
|
+
const {ambientLight, mainLight} = createLights();
|
|
20
|
+
scene.add(ambientLight, mainLight);
|
|
21
|
+
|
|
22
|
+
// Store animatable objects
|
|
23
|
+
scene.userData.updatables = [cube, torus, sphere];
|
|
24
|
+
|
|
25
|
+
return scene;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {createScene};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {Clock} from 'three';
|
|
2
|
+
|
|
3
|
+
const clock = new Clock();
|
|
4
|
+
|
|
5
|
+
class Loop {
|
|
6
|
+
constructor(camera, scene, renderer, composer) {
|
|
7
|
+
this.camera = camera;
|
|
8
|
+
this.scene = scene;
|
|
9
|
+
this.renderer = renderer;
|
|
10
|
+
this.composer = composer;
|
|
11
|
+
this.updatables = [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
start() {
|
|
15
|
+
this.renderer.setAnimationLoop(() => {
|
|
16
|
+
// tell every animated object to tick forward one frame
|
|
17
|
+
this.tick();
|
|
18
|
+
|
|
19
|
+
// render a frame using effect composer
|
|
20
|
+
// this.composer.render();
|
|
21
|
+
this.renderer.render(this.scene, this.camera);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
stop() {
|
|
26
|
+
this.renderer.setAnimationLoop(null);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
tick() {
|
|
30
|
+
// only call the getDelta function once per frame!
|
|
31
|
+
const delta = clock.getDelta();
|
|
32
|
+
|
|
33
|
+
// console.log(
|
|
34
|
+
// `The last frame rendered in ${delta * 1000} milliseconds`,
|
|
35
|
+
// );
|
|
36
|
+
|
|
37
|
+
for (const object of this.updatables) {
|
|
38
|
+
object.tick(delta);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {Loop};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const setSize = (container, camera, renderer, composer) => {
|
|
2
|
+
camera.aspect = container.clientWidth / container.clientHeight;
|
|
3
|
+
camera.updateProjectionMatrix();
|
|
4
|
+
|
|
5
|
+
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
6
|
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
7
|
+
composer.setSize(container.clientWidth, container.clientHeight);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
class Resizer {
|
|
11
|
+
constructor(container, camera, renderer, composer) {
|
|
12
|
+
// set initial size on load
|
|
13
|
+
setSize(container, camera, renderer, composer);
|
|
14
|
+
|
|
15
|
+
window.addEventListener('resize', () => {
|
|
16
|
+
// set the size again if a resize occurs
|
|
17
|
+
setSize(container, camera, renderer, composer);
|
|
18
|
+
// perform any custom actions
|
|
19
|
+
this.onResize();
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
onResize() {}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {Resizer};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
|
|
2
|
+
|
|
3
|
+
function createControls(camera, canvas) {
|
|
4
|
+
const controls = new OrbitControls(camera, canvas);
|
|
5
|
+
|
|
6
|
+
controls.enableDamping = true;
|
|
7
|
+
controls.dampingFactor = 0.05;
|
|
8
|
+
controls.enablePan = true;
|
|
9
|
+
controls.enableZoom = true;
|
|
10
|
+
controls.autoRotate = false;
|
|
11
|
+
controls.autoRotateSpeed = 1;
|
|
12
|
+
|
|
13
|
+
// forward controls.update to our custom .tick method
|
|
14
|
+
controls.tick = () => controls.update();
|
|
15
|
+
|
|
16
|
+
return controls;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {createControls};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BloomEffect,
|
|
3
|
+
EffectComposer,
|
|
4
|
+
EffectPass,
|
|
5
|
+
RenderPass,
|
|
6
|
+
ChromaticAberrationEffect,
|
|
7
|
+
BlendFunction,
|
|
8
|
+
NoiseEffect,
|
|
9
|
+
SMAAEffect,
|
|
10
|
+
ShaderPass,
|
|
11
|
+
} from 'postprocessing';
|
|
12
|
+
import {HalfFloatType, ShaderMaterial, Uniform, Vector2} from 'three';
|
|
13
|
+
|
|
14
|
+
function createPostprocessing(scene, camera, renderer) {
|
|
15
|
+
const composer = new EffectComposer(renderer, {
|
|
16
|
+
frameBufferType: HalfFloatType,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
composer.setSize(window.innerWidth, window.innerHeight);
|
|
20
|
+
composer.addPass(new RenderPass(scene, camera));
|
|
21
|
+
|
|
22
|
+
const bloomEffect = new BloomEffect({
|
|
23
|
+
luminanceThreshold: 0.2,
|
|
24
|
+
luminanceSmoothing: 0.7,
|
|
25
|
+
intensity: 0.1,
|
|
26
|
+
mipmapBlur: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const chromaticAberrationEffect = new ChromaticAberrationEffect({
|
|
30
|
+
offset: new Vector2(0.0005, 0.0005),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const noiseEffect = new NoiseEffect({
|
|
34
|
+
blendFunction: BlendFunction.OVERLAY,
|
|
35
|
+
premultiplyAlpha: true,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
noiseEffect.blendMode.opacity.value = 0.2;
|
|
39
|
+
|
|
40
|
+
const smaaEffect = new SMAAEffect();
|
|
41
|
+
|
|
42
|
+
const smaaPass = new EffectPass(camera, smaaEffect);
|
|
43
|
+
const effectPass = new EffectPass(camera, noiseEffect);
|
|
44
|
+
composer.addPass(smaaPass);
|
|
45
|
+
composer.addPass(effectPass);
|
|
46
|
+
|
|
47
|
+
return composer;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export {createPostprocessing};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {SRGBColorSpace, WebGLRenderer} from 'three';
|
|
2
|
+
|
|
3
|
+
function createRenderer() {
|
|
4
|
+
const renderer = new WebGLRenderer({
|
|
5
|
+
powerPreference: 'high-performance',
|
|
6
|
+
// antialias: false,
|
|
7
|
+
// stencil: false,
|
|
8
|
+
// depth: false,
|
|
9
|
+
alpha: true,
|
|
10
|
+
});
|
|
11
|
+
renderer.outputColorSpace = SRGBColorSpace;
|
|
12
|
+
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
|
|
13
|
+
|
|
14
|
+
return renderer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {createRenderer};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Device detection utility with resize handling
|
|
2
|
+
|
|
3
|
+
const BREAKPOINTS = {
|
|
4
|
+
mobile: 768,
|
|
5
|
+
tablet: 1024,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
let deviceType = 'desktop';
|
|
9
|
+
let windowWidth = typeof window !== 'undefined' ? window.innerWidth : 1920;
|
|
10
|
+
let resizeHandler = null;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Determines the device type based on window width
|
|
14
|
+
* @returns {string} 'mobile' | 'tablet' | 'desktop'
|
|
15
|
+
*/
|
|
16
|
+
function getDeviceType() {
|
|
17
|
+
if (typeof window === 'undefined') {
|
|
18
|
+
return 'desktop';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const width = window.innerWidth;
|
|
22
|
+
|
|
23
|
+
if (width < BREAKPOINTS.mobile) {
|
|
24
|
+
return 'mobile';
|
|
25
|
+
} else if (width < BREAKPOINTS.tablet) {
|
|
26
|
+
return 'tablet';
|
|
27
|
+
}
|
|
28
|
+
return 'desktop';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Updates the current device type
|
|
33
|
+
*/
|
|
34
|
+
function updateDeviceType() {
|
|
35
|
+
const newDeviceType = getDeviceType();
|
|
36
|
+
const newWidth = window.innerWidth;
|
|
37
|
+
|
|
38
|
+
if (newDeviceType !== deviceType || newWidth !== windowWidth) {
|
|
39
|
+
deviceType = newDeviceType;
|
|
40
|
+
windowWidth = newWidth;
|
|
41
|
+
|
|
42
|
+
// Dispatch custom event for listeners
|
|
43
|
+
if (typeof window !== 'undefined') {
|
|
44
|
+
window.dispatchEvent(
|
|
45
|
+
new CustomEvent('devicechange', {
|
|
46
|
+
detail: {deviceType, width: windowWidth},
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initializes device detection and sets up resize listener
|
|
55
|
+
*/
|
|
56
|
+
function initDeviceDetector() {
|
|
57
|
+
if (typeof window === 'undefined') {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Initial detection
|
|
62
|
+
updateDeviceType();
|
|
63
|
+
|
|
64
|
+
// Set up resize listener with debounce
|
|
65
|
+
let resizeTimeout;
|
|
66
|
+
resizeHandler = () => {
|
|
67
|
+
clearTimeout(resizeTimeout);
|
|
68
|
+
resizeTimeout = setTimeout(() => {
|
|
69
|
+
updateDeviceType();
|
|
70
|
+
}, 150);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
window.addEventListener('resize', resizeHandler);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Cleans up the resize listener
|
|
78
|
+
*/
|
|
79
|
+
function disposeDeviceDetector() {
|
|
80
|
+
if (resizeHandler && typeof window !== 'undefined') {
|
|
81
|
+
window.removeEventListener('resize', resizeHandler);
|
|
82
|
+
resizeHandler = null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Gets the current device type
|
|
88
|
+
* @returns {string} Current device type
|
|
89
|
+
*/
|
|
90
|
+
function getCurrentDevice() {
|
|
91
|
+
return deviceType;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Gets the current window width
|
|
96
|
+
* @returns {number} Current window width
|
|
97
|
+
*/
|
|
98
|
+
function getCurrentWidth() {
|
|
99
|
+
return windowWidth;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Checks if current device is mobile
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
106
|
+
function isMobile() {
|
|
107
|
+
return deviceType === 'mobile';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Checks if current device is tablet
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
114
|
+
function isTablet() {
|
|
115
|
+
return deviceType === 'tablet';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Checks if current device is desktop
|
|
120
|
+
* @returns {boolean}
|
|
121
|
+
*/
|
|
122
|
+
function isDesktop() {
|
|
123
|
+
return deviceType === 'desktop';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Auto-initialize if in browser environment
|
|
127
|
+
if (typeof window !== 'undefined') {
|
|
128
|
+
initDeviceDetector();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
initDeviceDetector,
|
|
133
|
+
disposeDeviceDetector,
|
|
134
|
+
getCurrentDevice,
|
|
135
|
+
getCurrentWidth,
|
|
136
|
+
getDeviceType,
|
|
137
|
+
isMobile,
|
|
138
|
+
isTablet,
|
|
139
|
+
isDesktop,
|
|
140
|
+
BREAKPOINTS,
|
|
141
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {GLTFLoader} from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
2
|
+
import {DRACOLoader} from 'three/examples/jsm/loaders/DRACOLoader.js';
|
|
3
|
+
|
|
4
|
+
const gltfLoader = new GLTFLoader();
|
|
5
|
+
const dracoLoader = new DRACOLoader();
|
|
6
|
+
dracoLoader.setDecoderPath('/draco/');
|
|
7
|
+
gltfLoader.setDRACOLoader(dracoLoader);
|
|
8
|
+
|
|
9
|
+
async function loadGLTF(modelPath) {
|
|
10
|
+
const model = await gltfLoader.loadAsync(modelPath);
|
|
11
|
+
return model;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {gltfLoader, loadGLTF};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {KTX2Loader} from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
2
|
+
|
|
3
|
+
let ktx2Loader = null;
|
|
4
|
+
|
|
5
|
+
function initKTX2Loader(renderer) {
|
|
6
|
+
if (!ktx2Loader) {
|
|
7
|
+
ktx2Loader = new KTX2Loader();
|
|
8
|
+
ktx2Loader.setTranscoderPath('/basis/');
|
|
9
|
+
ktx2Loader.detectSupport(renderer);
|
|
10
|
+
}
|
|
11
|
+
return ktx2Loader;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function loadKTX2Texture(url) {
|
|
15
|
+
if (!ktx2Loader) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'KTX2Loader not initialized. Call initKTX2Loader(renderer) first.',
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
ktx2Loader.load(
|
|
23
|
+
url,
|
|
24
|
+
texture => {
|
|
25
|
+
resolve(texture);
|
|
26
|
+
},
|
|
27
|
+
undefined,
|
|
28
|
+
error => {
|
|
29
|
+
reject(error);
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function disposeKTX2Loader() {
|
|
36
|
+
if (ktx2Loader) {
|
|
37
|
+
ktx2Loader.dispose();
|
|
38
|
+
ktx2Loader = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export {initKTX2Loader, loadKTX2Texture, disposeKTX2Loader};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {TextureLoader} from 'three';
|
|
2
|
+
|
|
3
|
+
// Global texture loader instance
|
|
4
|
+
const textureLoader = new TextureLoader();
|
|
5
|
+
|
|
6
|
+
async function loadTexture(url) {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
textureLoader.load(
|
|
9
|
+
url,
|
|
10
|
+
texture => {
|
|
11
|
+
resolve(texture);
|
|
12
|
+
},
|
|
13
|
+
undefined,
|
|
14
|
+
error => {
|
|
15
|
+
reject(error);
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export {textureLoader, loadTexture};
|