Spaces:
Runtime error
Runtime error
Duplicate from temp-org/ldm3d-gradio
Browse filesCo-authored-by: Radamés Ajna <[email protected]>
- .gitattributes +37 -0
- .gitignore +5 -0
- README.md +11 -0
- app.py +113 -0
- requirements.txt +9 -0
- static/depthmap.html +211 -0
- static/public/images/depth.png +3 -0
- static/public/images/equirectangular/kandao3.jpg +3 -0
- static/public/images/equirectangular/kandao3_depthmap.jpg +3 -0
- static/public/images/favicon/android-chrome-192x192.png +3 -0
- static/public/images/favicon/android-chrome-512x512.png +3 -0
- static/public/images/favicon/apple-touch-icon.png +3 -0
- static/public/images/favicon/favicon-16x16.png +3 -0
- static/public/images/favicon/favicon-32x32.png +3 -0
- static/public/images/favicon/favicon.ico +0 -0
- static/public/images/rgb.png +3 -0
- static/public/images/ruins_rgbd.png +3 -0
- static/public/js/GUIHelper.js +34 -0
- static/public/js/WebVR.js +261 -0
- static/public/js/three-6dof.min.js +2 -0
- static/public/js/three-6dof.min.js.map +1 -0
- static/public/site.webmanifest +19 -0
- static/three6dof.html +180 -0
.gitattributes
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
gradio_cached_examples
|
2 |
+
tmp
|
3 |
+
venv
|
4 |
+
__pycache__
|
5 |
+
*.py[cod]
|
README.md
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: LDM3D Gradio Three.js
|
3 |
+
emoji: 🔵🔴🟢🧊
|
4 |
+
sdk: gradio
|
5 |
+
sdk_version: 3.35.2
|
6 |
+
app_file: app.py
|
7 |
+
pinned: false
|
8 |
+
duplicated_from: temp-org/ldm3d-gradio
|
9 |
+
---
|
10 |
+
|
11 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from diffusers import StableDiffusionLDM3DPipeline
|
2 |
+
import gradio as gr
|
3 |
+
import torch
|
4 |
+
from PIL import Image
|
5 |
+
import base64
|
6 |
+
from io import BytesIO
|
7 |
+
from tempfile import NamedTemporaryFile
|
8 |
+
from pathlib import Path
|
9 |
+
|
10 |
+
Path("tmp").mkdir(exist_ok=True)
|
11 |
+
|
12 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
13 |
+
pipe = StableDiffusionLDM3DPipeline.from_pretrained(
|
14 |
+
"Intel/ldm3d-4c",
|
15 |
+
torch_dtype=torch.float16
|
16 |
+
# , safety_checker=None
|
17 |
+
)
|
18 |
+
pipe.to(device)
|
19 |
+
pipe.enable_xformers_memory_efficient_attention()
|
20 |
+
pipe.enable_model_cpu_offload()
|
21 |
+
|
22 |
+
|
23 |
+
def get_iframe(rgb_path: str, depth_path: str, viewer_mode: str = "6DOF"):
|
24 |
+
# buffered = BytesIO()
|
25 |
+
# rgb.convert("RGB").save(buffered, format="JPEG")
|
26 |
+
# rgb_base64 = base64.b64encode(buffered.getvalue())
|
27 |
+
# buffered = BytesIO()
|
28 |
+
# depth.convert("RGB").save(buffered, format="JPEG")
|
29 |
+
# depth_base64 = base64.b64encode(buffered.getvalue())
|
30 |
+
|
31 |
+
# rgb_base64 = "data:image/jpeg;base64," + rgb_base64.decode("utf-8")
|
32 |
+
# depth_base64 = "data:image/jpeg;base64," + depth_base64.decode("utf-8")
|
33 |
+
rgb_base64 = f"/file={rgb_path}"
|
34 |
+
depth_base64 = f"/file={depth_path}"
|
35 |
+
if viewer_mode == "6DOF":
|
36 |
+
return f"""<iframe src="file=static/three6dof.html" width="100%" height="500px" data-rgb="{rgb_base64}" data-depth="{depth_base64}"></iframe>"""
|
37 |
+
else:
|
38 |
+
return f"""<iframe src="file=static/depthmap.html" width="100%" height="500px" data-rgb="{rgb_base64}" data-depth="{depth_base64}"></iframe>"""
|
39 |
+
|
40 |
+
|
41 |
+
def predict(
|
42 |
+
prompt: str,
|
43 |
+
negative_prompt: str,
|
44 |
+
guidance_scale: float = 5.0,
|
45 |
+
seed: int = 0,
|
46 |
+
randomize_seed: bool = True,
|
47 |
+
):
|
48 |
+
generator = torch.Generator() if randomize_seed else torch.manual_seed(seed)
|
49 |
+
output = pipe(
|
50 |
+
prompt,
|
51 |
+
width=1024,
|
52 |
+
height=512,
|
53 |
+
negative_prompt=negative_prompt,
|
54 |
+
guidance_scale=guidance_scale,
|
55 |
+
generator=generator,
|
56 |
+
num_inference_steps=40,
|
57 |
+
) # type: ignore
|
58 |
+
rgb_image, depth_image = output.rgb[0], output.depth[0] # type: ignore
|
59 |
+
with NamedTemporaryFile(suffix=".png", delete=False, dir="tmp") as rgb_file:
|
60 |
+
rgb_image.save(rgb_file.name)
|
61 |
+
rgb_image = rgb_file.name
|
62 |
+
with NamedTemporaryFile(suffix=".png", delete=False, dir="tmp") as depth_file:
|
63 |
+
depth_image.save(depth_file.name)
|
64 |
+
depth_image = depth_file.name
|
65 |
+
|
66 |
+
iframe = get_iframe(rgb_image, depth_image)
|
67 |
+
return rgb_image, depth_image, generator.seed(), iframe
|
68 |
+
|
69 |
+
|
70 |
+
with gr.Blocks() as block:
|
71 |
+
gr.Markdown(
|
72 |
+
"""
|
73 |
+
## LDM3d Demo
|
74 |
+
|
75 |
+
model: https://huggingface.co/Intel/ldm3d<br>
|
76 |
+
[Diffusers docs](https://huggingface.co/docs/diffusers/main/en/api/pipelines/stable_diffusion/ldm3d_diffusion)
|
77 |
+
|
78 |
+
"""
|
79 |
+
)
|
80 |
+
with gr.Row():
|
81 |
+
with gr.Column(scale=1):
|
82 |
+
prompt = gr.Textbox(label="Prompt")
|
83 |
+
negative_prompt = gr.Textbox(label="Negative Prompt")
|
84 |
+
guidance_scale = gr.Slider(
|
85 |
+
label="Guidance Scale", minimum=0, maximum=10, step=0.1, value=5.0
|
86 |
+
)
|
87 |
+
randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
|
88 |
+
seed = gr.Slider(label="Seed", minimum=0,
|
89 |
+
maximum=2**64 - 1, step=1)
|
90 |
+
generated_seed = gr.Number(label="Generated Seed")
|
91 |
+
markdown = gr.Markdown(label="Output Box")
|
92 |
+
with gr.Row():
|
93 |
+
new_btn = gr.Button("New Image")
|
94 |
+
with gr.Column(scale=2):
|
95 |
+
html = gr.HTML()
|
96 |
+
with gr.Row():
|
97 |
+
rgb = gr.Image(label="RGB Image", type="filepath")
|
98 |
+
depth = gr.Image(label="Depth Image", type="filepath")
|
99 |
+
gr.Examples(
|
100 |
+
examples=[
|
101 |
+
["A picture of some lemons on a table panoramic view", "", 5.0, 0, True]],
|
102 |
+
inputs=[prompt, negative_prompt, guidance_scale, seed, randomize_seed],
|
103 |
+
outputs=[rgb, depth, generated_seed, html],
|
104 |
+
fn=predict,
|
105 |
+
cache_examples=True)
|
106 |
+
|
107 |
+
new_btn.click(
|
108 |
+
fn=predict,
|
109 |
+
inputs=[prompt, negative_prompt, guidance_scale, seed, randomize_seed],
|
110 |
+
outputs=[rgb, depth, generated_seed, html],
|
111 |
+
)
|
112 |
+
|
113 |
+
block.launch()
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
git+https://github.com/huggingface/diffusers@07c9a08e67feb4d05ba22ba6b1a2d39468d68225
|
2 |
+
gradio
|
3 |
+
transformers
|
4 |
+
accelerate
|
5 |
+
Pillow
|
6 |
+
numpy
|
7 |
+
xformers
|
8 |
+
# --index-url https://download.pytorch.org/whl/cu118
|
9 |
+
torch
|
static/depthmap.html
ADDED
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="utf-8" />
|
6 |
+
<title>depthmap-viewer-three</title>
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
font-family: sans-serif;
|
10 |
+
margin: 0;
|
11 |
+
}
|
12 |
+
|
13 |
+
.dropzone {
|
14 |
+
box-sizing: border-box;
|
15 |
+
display: none;
|
16 |
+
position: fixed;
|
17 |
+
width: 100%;
|
18 |
+
height: 100%;
|
19 |
+
left: 0;
|
20 |
+
top: 0;
|
21 |
+
z-index: 99999;
|
22 |
+
background: rgba(#60a7dc, .8);
|
23 |
+
border: 11px dashed #60a7dc;
|
24 |
+
}
|
25 |
+
</style>
|
26 |
+
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
|
27 |
+
<script type="importmap">
|
28 |
+
{
|
29 |
+
"imports": {
|
30 |
+
"three": "https://unpkg.com/[email protected]/build/three.module.js",
|
31 |
+
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
|
32 |
+
}
|
33 |
+
}
|
34 |
+
</script>
|
35 |
+
<script type="module">
|
36 |
+
import * as THREE from 'three';
|
37 |
+
import { OrbitControls } from "three/addons/controls/OrbitControls";
|
38 |
+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
39 |
+
|
40 |
+
var rgbBase64Img = window.frameElement?.getAttribute('data-rgb') || "public/images/rgb.png"
|
41 |
+
var depthBase64Img = window.frameElement?.getAttribute('data-depth') || "public/images/depth.png"
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
let mesh;
|
46 |
+
let material;
|
47 |
+
let stopAnimation = false;
|
48 |
+
|
49 |
+
const settings = {
|
50 |
+
metalness: 0.0,
|
51 |
+
roughness: 0.5,
|
52 |
+
ambientIntensity: 0.85,
|
53 |
+
displacementScale: 100,
|
54 |
+
displacementBias: -0.5,
|
55 |
+
};
|
56 |
+
const meshSettings = {
|
57 |
+
rotation: {
|
58 |
+
x: 0,
|
59 |
+
y: 0,
|
60 |
+
z: 0
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
// init
|
65 |
+
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);
|
66 |
+
camera.position.z = 3;
|
67 |
+
|
68 |
+
const scene = new THREE.Scene();
|
69 |
+
|
70 |
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
|
71 |
+
scene.add(ambientLight);
|
72 |
+
|
73 |
+
const pointLight = new THREE.PointLight(0xff0000, 0.5);
|
74 |
+
pointLight.position.z = 2500;
|
75 |
+
scene.add(pointLight);
|
76 |
+
|
77 |
+
|
78 |
+
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
79 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
80 |
+
renderer.setAnimationLoop(animation);
|
81 |
+
// renderer.xr.enabled = true;
|
82 |
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
83 |
+
renderer.toneMappingExposure = 1;
|
84 |
+
renderer.outputEncoding = THREE.sRGBEncoding;
|
85 |
+
document.body.appendChild(renderer.domElement);
|
86 |
+
|
87 |
+
// animation
|
88 |
+
function animation(time) {
|
89 |
+
if (mesh && !stopAnimation) {
|
90 |
+
mesh.rotation.x = 0.5 * Math.sin(time / 2000);
|
91 |
+
mesh.rotation.y = 0.5 * Math.sin(time / 2000);
|
92 |
+
meshSettings.rotation.x = mesh.rotation.x;
|
93 |
+
meshSettings.rotation.y = mesh.rotation.y;
|
94 |
+
}
|
95 |
+
renderer.render(scene, camera);
|
96 |
+
|
97 |
+
}
|
98 |
+
|
99 |
+
function onWindowResize() {
|
100 |
+
|
101 |
+
const aspect = window.innerWidth / window.innerHeight;
|
102 |
+
camera.aspect = aspect;
|
103 |
+
camera.updateProjectionMatrix();
|
104 |
+
|
105 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
106 |
+
|
107 |
+
}
|
108 |
+
window.addEventListener('resize', onWindowResize);
|
109 |
+
|
110 |
+
|
111 |
+
// orbit controls
|
112 |
+
const controls = new OrbitControls(camera, renderer.domElement);
|
113 |
+
controls.enableZoom = true;
|
114 |
+
controls.enableDamping = true;
|
115 |
+
|
116 |
+
async function getCanvasTexture(imageSrc) {
|
117 |
+
return new Promise((resolve, reject) => {
|
118 |
+
const image = new Image();
|
119 |
+
image.src = imageSrc;
|
120 |
+
image.onload = () => {
|
121 |
+
const ctx = document.createElement('canvas').getContext('2d');
|
122 |
+
ctx.canvas.width = image.width;
|
123 |
+
ctx.canvas.height = image.height;
|
124 |
+
ctx.drawImage(image, 0, 0, image.width, image.height);
|
125 |
+
const texture = new THREE.CanvasTexture(ctx.canvas);
|
126 |
+
resolve(texture);
|
127 |
+
}
|
128 |
+
})
|
129 |
+
|
130 |
+
}
|
131 |
+
(async () => {
|
132 |
+
const rgbTexture = await getCanvasTexture(rgbBase64Img);
|
133 |
+
const depthTexture = await getCanvasTexture(depthBase64Img);
|
134 |
+
|
135 |
+
if (mesh) {
|
136 |
+
mesh.geometry.dispose();
|
137 |
+
mesh.material.dispose();
|
138 |
+
scene.remove(mesh);
|
139 |
+
}
|
140 |
+
// material
|
141 |
+
material = new THREE.MeshStandardMaterial({
|
142 |
+
|
143 |
+
color: 0xaaaaaa,
|
144 |
+
roughness: settings.roughness,
|
145 |
+
metalness: settings.metalness,
|
146 |
+
|
147 |
+
map: rgbTexture,
|
148 |
+
|
149 |
+
displacementMap: depthTexture,
|
150 |
+
displacementScale: settings.displacementScale,
|
151 |
+
displacementBias: settings.displacementBias,
|
152 |
+
|
153 |
+
side: THREE.DoubleSide
|
154 |
+
|
155 |
+
});
|
156 |
+
// generating geometry and add mesh to scene
|
157 |
+
const geometry = new THREE.PlaneGeometry(rgbTexture.image.width, rgbTexture.image.height, 512, 512);
|
158 |
+
mesh = new THREE.Mesh(geometry, material);
|
159 |
+
const scale = 1 / Math.max(rgbTexture.image.width, rgbTexture.image.height);
|
160 |
+
mesh.scale.set(scale, scale, scale);
|
161 |
+
scene.add(mesh);
|
162 |
+
|
163 |
+
})()
|
164 |
+
|
165 |
+
|
166 |
+
|
167 |
+
// setup gui
|
168 |
+
const gui = new GUI();
|
169 |
+
gui.close();
|
170 |
+
const sceneGUI = gui.addFolder('Scene');
|
171 |
+
sceneGUI.add(settings, 'metalness').min(0).max(1).onChange(function (value) {
|
172 |
+
material.metalness = value;
|
173 |
+
});
|
174 |
+
sceneGUI.add(settings, 'roughness').min(0).max(1).onChange(function (value) {
|
175 |
+
material.roughness = value;
|
176 |
+
});
|
177 |
+
sceneGUI.add(settings, 'ambientIntensity').min(0).max(1).onChange(function (value) {
|
178 |
+
ambientLight.intensity = value;
|
179 |
+
});
|
180 |
+
sceneGUI.add(settings, 'displacementScale').min(0).max(500.0).onChange(function (value) {
|
181 |
+
material.displacementScale = value;
|
182 |
+
});
|
183 |
+
sceneGUI.add(settings, 'displacementBias').min(-500).max(500).onChange(function (value) {
|
184 |
+
material.displacementBias = value;
|
185 |
+
});
|
186 |
+
const meshGUI = gui.addFolder('Mesh');
|
187 |
+
meshGUI.add(meshSettings.rotation, 'x').min(-Math.PI).max(Math.PI).step(0.0001).onChange(function (value) {
|
188 |
+
mesh.rotation.x = value;
|
189 |
+
stopAnimation = true;
|
190 |
+
}).listen()
|
191 |
+
|
192 |
+
|
193 |
+
meshGUI.add(meshSettings.rotation, 'y').min(-Math.PI).max(Math.PI).step(0.0001).onChange(function (value) {
|
194 |
+
mesh.rotation.y = value;
|
195 |
+
stopAnimation = true;
|
196 |
+
}).listen()
|
197 |
+
|
198 |
+
|
199 |
+
meshGUI.add(meshSettings.rotation, 'z').min(-Math.PI).max(Math.PI).step(0.0001).onChange(function (value) {
|
200 |
+
mesh.rotation.z = value;
|
201 |
+
stopAnimation = true;
|
202 |
+
}).listen()
|
203 |
+
|
204 |
+
|
205 |
+
</script>
|
206 |
+
</head>
|
207 |
+
|
208 |
+
<body>
|
209 |
+
</body>
|
210 |
+
|
211 |
+
</html>
|
static/public/images/depth.png
ADDED
![]() |
Git LFS Details
|
static/public/images/equirectangular/kandao3.jpg
ADDED
![]() |
Git LFS Details
|
static/public/images/equirectangular/kandao3_depthmap.jpg
ADDED
![]() |
Git LFS Details
|
static/public/images/favicon/android-chrome-192x192.png
ADDED
![]() |
Git LFS Details
|
static/public/images/favicon/android-chrome-512x512.png
ADDED
![]() |
Git LFS Details
|
static/public/images/favicon/apple-touch-icon.png
ADDED
![]() |
Git LFS Details
|
static/public/images/favicon/favicon-16x16.png
ADDED
![]() |
Git LFS Details
|
static/public/images/favicon/favicon-32x32.png
ADDED
![]() |
Git LFS Details
|
static/public/images/favicon/favicon.ico
ADDED
|
static/public/images/rgb.png
ADDED
![]() |
Git LFS Details
|
static/public/images/ruins_rgbd.png
ADDED
![]() |
Git LFS Details
|
static/public/js/GUIHelper.js
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
THREE.GUI = {
|
2 |
+
create: (viewer, scene) => {
|
3 |
+
var gui = new dat.GUI();
|
4 |
+
|
5 |
+
// Set some GUI params
|
6 |
+
var shaderParams = gui.addFolder('Shader');
|
7 |
+
shaderParams.add(sixDofViewer, 'displacement', 0, 7).name('Displacement');
|
8 |
+
shaderParams.add(sixDofViewer, 'opacity', 0, 1).name('Opacity');
|
9 |
+
shaderParams.add(sixDofViewer, 'pointSize', 0, 10).name('Point Size');
|
10 |
+
shaderParams.add({ 'debugDepth': false }, 'debugDepth')
|
11 |
+
.name('Debug Depth')
|
12 |
+
.onChange(val => {
|
13 |
+
sixDofViewer.toggleDepthDebug(val);
|
14 |
+
});
|
15 |
+
shaderParams.add({
|
16 |
+
'changeStyle': () => { }
|
17 |
+
}, 'changeStyle', {
|
18 |
+
'Mesh': SixDOF.Style[SixDOF.Style.MESH],
|
19 |
+
'Wireframe': SixDOF.Style[SixDOF.Style.WIRE],
|
20 |
+
'Pointcloud': SixDOF.Style[SixDOF.Style.POINTS]
|
21 |
+
})
|
22 |
+
.name('Rendering Style')
|
23 |
+
.onChange(val => {
|
24 |
+
scene.remove(sixDofViewer);
|
25 |
+
sixDofViewer = new SixDOF.Viewer(colorTexture, depthTexture, {
|
26 |
+
'style': SixDOF.Style[val]
|
27 |
+
});
|
28 |
+
scene.add(sixDofViewer);
|
29 |
+
});
|
30 |
+
|
31 |
+
|
32 |
+
return gui;
|
33 |
+
}
|
34 |
+
}
|
static/public/js/WebVR.js
ADDED
@@ -0,0 +1,261 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* @author mrdoob / http://mrdoob.com
|
3 |
+
* @author Mugen87 / https://github.com/Mugen87
|
4 |
+
*
|
5 |
+
* Based on @tojiro's vr-samples-utils.js
|
6 |
+
*/
|
7 |
+
|
8 |
+
THREE.WEBVR = {
|
9 |
+
|
10 |
+
createButton: function ( renderer, options ) {
|
11 |
+
|
12 |
+
if ( options && options.referenceSpaceType ) {
|
13 |
+
|
14 |
+
renderer.vr.setReferenceSpaceType( options.referenceSpaceType );
|
15 |
+
|
16 |
+
}
|
17 |
+
|
18 |
+
function showEnterVR( device ) {
|
19 |
+
|
20 |
+
button.style.display = '';
|
21 |
+
|
22 |
+
button.style.cursor = 'pointer';
|
23 |
+
button.style.left = 'calc(50% - 50px)';
|
24 |
+
button.style.width = '100px';
|
25 |
+
|
26 |
+
button.textContent = 'ENTER VR';
|
27 |
+
|
28 |
+
button.onmouseenter = function () {
|
29 |
+
|
30 |
+
button.style.opacity = '1.0';
|
31 |
+
|
32 |
+
};
|
33 |
+
|
34 |
+
button.onmouseleave = function () {
|
35 |
+
|
36 |
+
button.style.opacity = '0.5';
|
37 |
+
|
38 |
+
};
|
39 |
+
|
40 |
+
button.onclick = function () {
|
41 |
+
|
42 |
+
device.isPresenting ? device.exitPresent() : device.requestPresent( [ { source: renderer.domElement } ] );
|
43 |
+
|
44 |
+
};
|
45 |
+
|
46 |
+
renderer.vr.setDevice( device );
|
47 |
+
|
48 |
+
}
|
49 |
+
|
50 |
+
function showEnterXR( /*device*/ ) {
|
51 |
+
|
52 |
+
var currentSession = null;
|
53 |
+
|
54 |
+
function onSessionStarted( session ) {
|
55 |
+
|
56 |
+
session.addEventListener( 'end', onSessionEnded );
|
57 |
+
|
58 |
+
renderer.vr.setSession( session );
|
59 |
+
button.textContent = 'EXIT XR';
|
60 |
+
|
61 |
+
currentSession = session;
|
62 |
+
|
63 |
+
}
|
64 |
+
|
65 |
+
function onSessionEnded( /*event*/ ) {
|
66 |
+
|
67 |
+
currentSession.removeEventListener( 'end', onSessionEnded );
|
68 |
+
|
69 |
+
renderer.vr.setSession( null );
|
70 |
+
button.textContent = 'ENTER XR';
|
71 |
+
|
72 |
+
currentSession = null;
|
73 |
+
|
74 |
+
}
|
75 |
+
|
76 |
+
//
|
77 |
+
|
78 |
+
button.style.display = '';
|
79 |
+
|
80 |
+
button.style.cursor = 'pointer';
|
81 |
+
button.style.left = 'calc(50% - 50px)';
|
82 |
+
button.style.width = '100px';
|
83 |
+
|
84 |
+
button.textContent = 'ENTER XR';
|
85 |
+
|
86 |
+
button.onmouseenter = function () {
|
87 |
+
|
88 |
+
button.style.opacity = '1.0';
|
89 |
+
|
90 |
+
};
|
91 |
+
|
92 |
+
button.onmouseleave = function () {
|
93 |
+
|
94 |
+
button.style.opacity = '0.5';
|
95 |
+
|
96 |
+
};
|
97 |
+
|
98 |
+
button.onclick = function () {
|
99 |
+
|
100 |
+
if ( currentSession === null ) {
|
101 |
+
|
102 |
+
// WebXR's requestReferenceSpace only works if the corresponding feature
|
103 |
+
// was requested at session creation time. For simplicity, just ask for
|
104 |
+
// the interesting ones as optional features, but be aware that the
|
105 |
+
// requestReferenceSpace call will fail if it turns out to be unavailable.
|
106 |
+
// ('local' is always available for immersive sessions and doesn't need to
|
107 |
+
// be requested separately.)
|
108 |
+
|
109 |
+
var sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor' ] };
|
110 |
+
navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted );
|
111 |
+
|
112 |
+
} else {
|
113 |
+
|
114 |
+
currentSession.end();
|
115 |
+
|
116 |
+
}
|
117 |
+
|
118 |
+
};
|
119 |
+
|
120 |
+
}
|
121 |
+
|
122 |
+
function disableButton() {
|
123 |
+
|
124 |
+
button.style.display = '';
|
125 |
+
|
126 |
+
button.style.cursor = 'auto';
|
127 |
+
button.style.left = 'calc(50% - 75px)';
|
128 |
+
button.style.width = '150px';
|
129 |
+
|
130 |
+
button.onmouseenter = null;
|
131 |
+
button.onmouseleave = null;
|
132 |
+
|
133 |
+
button.onclick = null;
|
134 |
+
|
135 |
+
}
|
136 |
+
|
137 |
+
function showVRNotFound() {
|
138 |
+
|
139 |
+
disableButton();
|
140 |
+
|
141 |
+
button.textContent = 'VR NOT FOUND';
|
142 |
+
|
143 |
+
renderer.vr.setDevice( null );
|
144 |
+
|
145 |
+
}
|
146 |
+
|
147 |
+
function showXRNotFound() {
|
148 |
+
|
149 |
+
disableButton();
|
150 |
+
|
151 |
+
button.textContent = 'XR NOT FOUND';
|
152 |
+
|
153 |
+
}
|
154 |
+
|
155 |
+
function stylizeElement( element ) {
|
156 |
+
|
157 |
+
element.style.position = 'absolute';
|
158 |
+
element.style.bottom = '20px';
|
159 |
+
element.style.padding = '12px 6px';
|
160 |
+
element.style.border = '1px solid #fff';
|
161 |
+
element.style.borderRadius = '4px';
|
162 |
+
element.style.background = 'rgba(0,0,0,0.1)';
|
163 |
+
element.style.color = '#fff';
|
164 |
+
element.style.font = 'normal 13px sans-serif';
|
165 |
+
element.style.textAlign = 'center';
|
166 |
+
element.style.opacity = '0.5';
|
167 |
+
element.style.outline = 'none';
|
168 |
+
element.style.zIndex = '999';
|
169 |
+
|
170 |
+
}
|
171 |
+
|
172 |
+
if ( 'xr' in navigator ) {
|
173 |
+
|
174 |
+
var button = document.createElement( 'button' );
|
175 |
+
button.style.display = 'none';
|
176 |
+
|
177 |
+
stylizeElement( button );
|
178 |
+
|
179 |
+
navigator.xr.isSessionSupported( 'immersive-vr' ).then( function ( supported ) {
|
180 |
+
|
181 |
+
if ( supported ) {
|
182 |
+
|
183 |
+
showEnterXR();
|
184 |
+
|
185 |
+
} else {
|
186 |
+
|
187 |
+
showXRNotFound();
|
188 |
+
|
189 |
+
}
|
190 |
+
|
191 |
+
} );
|
192 |
+
|
193 |
+
return button;
|
194 |
+
|
195 |
+
} else if ( 'getVRDisplays' in navigator ) {
|
196 |
+
|
197 |
+
var button = document.createElement( 'button' );
|
198 |
+
button.style.display = 'none';
|
199 |
+
|
200 |
+
stylizeElement( button );
|
201 |
+
|
202 |
+
window.addEventListener( 'vrdisplayconnect', function ( event ) {
|
203 |
+
|
204 |
+
showEnterVR( event.display );
|
205 |
+
|
206 |
+
}, false );
|
207 |
+
|
208 |
+
window.addEventListener( 'vrdisplaydisconnect', function ( /*event*/ ) {
|
209 |
+
|
210 |
+
showVRNotFound();
|
211 |
+
|
212 |
+
}, false );
|
213 |
+
|
214 |
+
window.addEventListener( 'vrdisplaypresentchange', function ( event ) {
|
215 |
+
|
216 |
+
button.textContent = event.display.isPresenting ? 'EXIT VR' : 'ENTER VR';
|
217 |
+
|
218 |
+
}, false );
|
219 |
+
|
220 |
+
window.addEventListener( 'vrdisplayactivate', function ( event ) {
|
221 |
+
|
222 |
+
event.display.requestPresent( [ { source: renderer.domElement } ] );
|
223 |
+
|
224 |
+
}, false );
|
225 |
+
|
226 |
+
navigator.getVRDisplays()
|
227 |
+
.then( function ( displays ) {
|
228 |
+
|
229 |
+
if ( displays.length > 0 ) {
|
230 |
+
|
231 |
+
showEnterVR( displays[ 0 ] );
|
232 |
+
|
233 |
+
} else {
|
234 |
+
|
235 |
+
showVRNotFound();
|
236 |
+
|
237 |
+
}
|
238 |
+
|
239 |
+
} ).catch( showVRNotFound );
|
240 |
+
|
241 |
+
return button;
|
242 |
+
|
243 |
+
} else {
|
244 |
+
|
245 |
+
var message = document.createElement( 'a' );
|
246 |
+
message.href = 'https://webvr.info';
|
247 |
+
message.innerHTML = 'WEBVR NOT SUPPORTED';
|
248 |
+
|
249 |
+
message.style.left = 'calc(50% - 90px)';
|
250 |
+
message.style.width = '180px';
|
251 |
+
message.style.textDecoration = 'none';
|
252 |
+
|
253 |
+
stylizeElement( message );
|
254 |
+
|
255 |
+
return message;
|
256 |
+
|
257 |
+
}
|
258 |
+
|
259 |
+
}
|
260 |
+
|
261 |
+
};
|
static/public/js/three-6dof.min.js
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("three")):"function"==typeof define&&define.amd?define(["exports","three"],t):t((e=e||self).SixDOF={},e.THREE)}(this,(function(e,t){"use strict";var r,n,i,o="#define GLSLIFY 1\n\n/** Small util to get the depth texture */\nvec3 getDepth(sampler2D depth, vec2 uvs) {\n\n /** Return the depth texture */\n return texture2D(depth, uvs).rgb;\n}\n\n/** Small util to get the lower half of a texture (in our case the depthmap) */\nvec3 getDepthFromBottomHalf(sampler2D tex, vec2 uvs) {\n \n /** Chop the uvs to the lower half of the texture (i.e top-bottom) */\n vec2 lower_half_uvs = vec2(uvs.x, uvs.y * 0.5);\n\n /** Return the depth texture */\n return texture2D(tex, lower_half_uvs).rgb;\n}\n\n/** Small util to get the upper half of a texture (in our case the color texture) */\nvec3 getColorFromUpperHalf(sampler2D tex, vec2 uvs) {\n \n /** Chop the uvs to the lower half of the texture (i.e top-bottom) */\n vec2 upper_half_uvs = vec2(uvs.x, (uvs.y * 0.5) + 0.5);\n\n /** Return the depth texture */\n return texture2D(tex, upper_half_uvs).rgb;\n}\n\n// Uniforms\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\nuniform float debugDepth;\nuniform float opacity;\n\n// Varyings from vertex program\nvarying vec2 vUv;\n\n// Internal\nvec3 depth;\nvec3 color;\n\nvoid main() {\n\n/** Use compiler definitions to know which method to pick */\n#ifdef TOP_BOTTOM\n depth = getDepthFromBottomHalf(colorTexture, vUv);\n color = getColorFromUpperHalf(colorTexture, vUv);\n#endif\n\n#ifdef SEPERATE\n depth = getDepth(depthTexture, vUv);\n color = texture2D(colorTexture, vUv).rgb;\n#endif\n\n // Mix the depth and color based on debugDepth value\n vec3 depthColorMixer = mix(color, depth , debugDepth);\n\n // Render dat fragment\n gl_FragColor = vec4(depthColorMixer, opacity);\n}\n",s="#define GLSLIFY 1\n\n/** Small util to get the depth texture */\nvec3 getDepth(sampler2D depth, vec2 uvs) {\n\n /** Return the depth texture */\n return texture2D(depth, uvs).rgb;\n}\n\n/** Small util to get the lower half of a texture (in our case the depthmap) */\nvec3 getDepthFromBottomHalf(sampler2D tex, vec2 uvs) {\n \n /** Chop the uvs to the lower half of the texture (i.e top-bottom) */\n vec2 lower_half_uvs = vec2(uvs.x, uvs.y * 0.5);\n\n /** Return the depth texture */\n return texture2D(tex, lower_half_uvs).rgb;\n}\n\n// Uniforms\nuniform sampler2D colorTexture;\nuniform sampler2D depthTexture;\nuniform float pointSize;\nuniform float displacement;\n\n// Varyings passed to fragment\nvarying vec2 vUv;\n\n// Internal\nfloat depth;\n\nvoid main() {\n\n /** Transform and pass to fragment shader */\n vUv = uv;\n\n /** Set the GL point size for when rendering points, ignored otherwise */\n gl_PointSize = pointSize;\n\n/** Use compiler definitions to know which method to pick */\n#ifdef TOP_BOTTOM\n depth = getDepthFromBottomHalf(colorTexture, vUv).r;\n#endif\n\n#ifdef SEPERATE\n depth = getDepth(depthTexture, vUv).r;\n#endif\n\n /** \n * Invert the normals (since they are pointing outwards) and \n * move the position on the normal direction scaled by the \n * displacement which is the depth for the current vertex\n * multiplied by a `displacement` scalaer\n **/\n float disp = displacement * depth;\n vec3 offset = position + (-normal) * disp;\n\n /** Transform */\n gl_Position = projectionMatrix *\n modelViewMatrix *\n vec4(offset, 1.0);\n}",a={colorTexture:{type:"t",value:null},depthTexture:{type:"t",value:null},time:{type:"f",value:0},opacity:{type:"f",value:1},pointSize:{type:"f",value:3},debugDepth:{type:"f",value:0},displacement:{type:"f",value:1}};(r=e.MeshDensity||(e.MeshDensity={}))[r.LOW=64]="LOW",r[r.MEDIUM=128]="MEDIUM",r[r.HIGH=256]="HIGH",r[r.EXTRA_HIGH=512]="EXTRA_HIGH",r[r.EPIC=1024]="EPIC",(n=e.Style||(e.Style={}))[n.WIRE=0]="WIRE",n[n.POINTS=1]="POINTS",n[n.MESH=2]="MESH",(i=e.TextureType||(e.TextureType={}))[i.TOP_BOTTOM=0]="TOP_BOTTOM",i[i.SEPERATE=1]="SEPERATE";class u{constructor(){this.type=e.TextureType.SEPERATE,this.density=e.MeshDensity.HIGH,this.style=e.Style.MESH,this.displacement=4,this.radius=6}}class p extends t.Object3D{constructor(r,n,i){super(),this.props=new u,this.material=new t.ShaderMaterial({uniforms:a,vertexShader:s,fragmentShader:o,transparent:!0,side:t.BackSide}),this.setProps(this.props,i),this.setShaderDefines(this.material,[e.TextureType[this.props.type]]),p.geometry||(p.geometry=this.createSphereGeometry(this.props.radius,this.props.density)),this.assignTexture(this.props.type,r,n),this.displacement=this.props.displacement,super.add(this.createMesh(p.geometry,this.material,this.props.style))}setShaderDefines(e,t){t.forEach((function(t){return e.defines[t]=""}))}createSphereGeometry(e,r){return new t.SphereBufferGeometry(e,r,r)}setProps(e,t){if(t)for(var r in t)e[r]?e[r]=t[r]:console.warn("THREE.SixDOF: Provided ".concat(r," in config but it is not a valid property and being ignored"))}assignTexture(t,r,n){if(t===e.TextureType.SEPERATE){if(!n)throw new Error("When using seperate texture type, depthmap must be provided");this.depth=this.setDefaultTextureProps(n)}this.texture=this.setDefaultTextureProps(r)}setDefaultTextureProps(e){return e.minFilter=t.NearestFilter,e.magFilter=t.LinearFilter,e.format=t.RGBFormat,e.generateMipmaps=!1,e}createMesh(r,n,i){switch(i){case e.Style.WIRE:return this.material.wireframe||(this.material.wireframe=!0),new t.Mesh(r,n);case e.Style.MESH:return this.material.wireframe&&(this.material.wireframe=!1),new t.Mesh(r,n);case e.Style.POINTS:return new t.Points(r,n)}}toggleDepthDebug(e){this.material.uniforms.debugDepth.value=null!=e?e:!this.material.uniforms.debugDepth.value}set displacement(e){this.material.uniforms.displacement.value=e}set depth(e){this.material.uniforms.depthTexture.value=e}set texture(e){this.material.uniforms.colorTexture.value=e}set opacity(e){this.material.uniforms.opacity.value=e}set pointSize(e){this.material.uniforms.pointSize.value=e}get config(){return this.props}get opacity(){return this.material.uniforms.opacity.value}get pointSize(){return this.material.uniforms.pointSize.value}get displacement(){return this.material.uniforms.displacement.value}get texture(){return this.material.uniforms.colorTexture.value}get depth(){return this.material.uniforms.opacity.value}}p.geometry=void 0,e.Viewer=p,Object.defineProperty(e,"__esModule",{value:!0})}));
|
2 |
+
//# sourceMappingURL=three-6dof.min.js.map
|
static/public/js/three-6dof.min.js.map
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"version":3,"file":"three-6dof.min.js","sources":["../src/components/constants.ts","../src/components/uniforms.ts","../src/components/viewer.ts"],"sourcesContent":["enum MeshDensity {\n LOW = 64,\n MEDIUM = 128,\n HIGH = 256,\n EXTRA_HIGH = 512,\n EPIC = 1024,\n}\n\nenum Style {\n WIRE = 0,\n POINTS = 1,\n MESH = 2,\n}\n\nenum TextureType {\n TOP_BOTTOM,\n SEPERATE,\n}\n\nclass Props {\n public type: TextureType = TextureType.SEPERATE\n public density: MeshDensity = MeshDensity.HIGH\n public style: Style = Style.MESH\n public displacement: number = 4.0\n public radius: number = 6\n}\n\nexport { MeshDensity, Style, TextureType, Props }\n","export const Uniforms: object = {\n colorTexture: {\n type: 't',\n value: null,\n },\n depthTexture: {\n type: 't',\n value: null,\n },\n time: {\n type: 'f',\n value: 0.0,\n },\n opacity: {\n type: 'f',\n value: 1.0,\n },\n pointSize: {\n type: 'f',\n value: 3.0,\n },\n debugDepth: {\n type: 'f',\n value: 0.0,\n },\n displacement: {\n type: 'f',\n value: 1.0,\n },\n}\n","import {\n Object3D,\n ShaderMaterial,\n BackSide,\n Mesh,\n Points,\n SphereBufferGeometry,\n Texture,\n NearestFilter,\n LinearFilter,\n RGBFormat,\n} from './three'\n\n// @ts-ignore\nimport frag from '../shaders/sixdof.frag'\n// @ts-ignore\nimport vert from '../shaders/sixdof.vert'\n\nimport { Uniforms } from './uniforms'\nimport { Style, MeshDensity, TextureType, Props } from './constants'\n\nexport default class Viewer extends Object3D {\n /** Default props if not provided */\n private props: Props = new Props()\n\n private static geometry: SphereBufferGeometry\n private material: ShaderMaterial = new ShaderMaterial({\n uniforms: Uniforms,\n vertexShader: vert,\n fragmentShader: frag,\n transparent: true,\n side: BackSide,\n })\n\n constructor(texture: Texture, depth?: Texture, props?: object) {\n super()\n\n /** Assign the user provided props, if any */\n this.setProps(this.props, props)\n\n // /** Add the compiler definitions needed to pick the right GLSL methods */\n this.setShaderDefines(this.material, [TextureType[this.props.type]])\n\n /**\n * Create the geometry only once, it can be shared between instances\n * of the viewer since it's kept as a static class member\n **/\n if (!Viewer.geometry) {\n Viewer.geometry = this.createSphereGeometry(\n this.props.radius,\n this.props.density,\n )\n }\n\n /** Assign the textures and update the shader uniforms */\n this.assignTexture(this.props.type, texture, depth)\n\n /** Set the displacement using the public setter */\n this.displacement = this.props.displacement\n\n /** Create the Mesh/Points and add it to the viewer object */\n super.add(this.createMesh(Viewer.geometry, this.material, this.props.style))\n }\n\n /** Small util to set the defines of the GLSL program based on textureType */\n private setShaderDefines(\n material: ShaderMaterial,\n defines: Array<string>,\n ): void {\n defines.forEach(define => (material.defines[define] = ''))\n }\n\n /** Internal util to create buffer geometry */\n private createSphereGeometry(\n radius: number,\n meshDensity: MeshDensity,\n ): SphereBufferGeometry {\n return new SphereBufferGeometry(radius, meshDensity, meshDensity)\n }\n\n /** Internal util to set viewer props from config object */\n private setProps(viewerProps: Props, userProps?: object): void {\n if (!userProps) return\n\n /** Iterate over user provided props and assign to viewer props */\n for (let prop in userProps) {\n if (viewerProps[prop]) {\n viewerProps[prop] = userProps[prop]\n } else {\n console.warn(\n `THREE.SixDOF: Provided ${prop} in config but it is not a valid property and being ignored`,\n )\n }\n }\n }\n\n /** Internal util to assign the textures to the shader uniforms */\n private assignTexture(\n type: TextureType,\n colorTexture: Texture,\n depthTexture?: Texture,\n ): void {\n /** Check wheter we are rendering top bottom or just single textures */\n if (type === TextureType.SEPERATE) {\n if (!depthTexture)\n throw new Error(\n 'When using seperate texture type, depthmap must be provided',\n )\n this.depth = this.setDefaultTextureProps(depthTexture)\n }\n\n /** Assign the main texture */\n this.texture = this.setDefaultTextureProps(colorTexture)\n }\n\n private setDefaultTextureProps(texture: Texture): Texture {\n texture.minFilter = NearestFilter\n texture.magFilter = LinearFilter\n texture.format = RGBFormat\n texture.generateMipmaps = false\n return texture\n }\n\n /** An internal util to create the Mesh Object3D */\n private createMesh(\n geo: SphereBufferGeometry,\n mat: ShaderMaterial,\n style: Style,\n ): Object3D {\n switch (style) {\n case Style.WIRE:\n if (!this.material.wireframe) this.material.wireframe = true\n return new Mesh(geo, mat)\n case Style.MESH:\n if (this.material.wireframe) this.material.wireframe = false\n return new Mesh(geo, mat)\n case Style.POINTS:\n return new Points(geo, mat)\n }\n }\n\n /** Toggle vieweing texture or depthmap in viewer */\n public toggleDepthDebug(state?: boolean): void {\n this.material.uniforms.debugDepth.value =\n state != undefined ? state : !this.material.uniforms.debugDepth.value\n }\n\n /** Setter for displacement amount */\n public set displacement(val: number) {\n this.material.uniforms.displacement.value = val\n }\n\n /** Setter for depthmap uniform */\n public set depth(map: Texture) {\n this.material.uniforms.depthTexture.value = map\n }\n\n /** Setter for depthmap uniform */\n public set texture(map: Texture) {\n this.material.uniforms.colorTexture.value = map\n }\n\n /** Setter for the opacity */\n public set opacity(val: number) {\n this.material.uniforms.opacity.value = val\n }\n\n /** Setter for the point size */\n public set pointSize(val: number) {\n this.material.uniforms.pointSize.value = val\n }\n\n /** Getter for the current viewer props */\n public get config(): Props {\n return this.props\n }\n\n /** Getter for the opacity */\n public get opacity(): number {\n return this.material.uniforms.opacity.value\n }\n\n /** Getter for the point size */\n public get pointSize(): number {\n return this.material.uniforms.pointSize.value\n }\n\n /** Getter for displacement amount */\n public get displacement(): number {\n return this.material.uniforms.displacement.value\n }\n\n /** Getter for texture */\n public get texture(): Texture {\n return this.material.uniforms.colorTexture.value\n }\n\n /** Getter for the depth texture */\n public get depth(): Texture {\n return this.material.uniforms.opacity.value\n }\n}\n"],"names":["MeshDensity","Style","TextureType","Uniforms","colorTexture","type","value","depthTexture","time","opacity","pointSize","debugDepth","displacement","Props","SEPERATE","density","HIGH","style","MESH","radius","Viewer","Object3D","constructor","texture","depth","props","material","ShaderMaterial","uniforms","vertexShader","vert","fragmentShader","frag","transparent","side","BackSide","setProps","this","setShaderDefines","geometry","createSphereGeometry","assignTexture","add","createMesh","defines","forEach","define","meshDensity","SphereBufferGeometry","viewerProps","userProps","prop","console","warn","Error","setDefaultTextureProps","minFilter","NearestFilter","magFilter","LinearFilter","format","RGBFormat","generateMipmaps","geo","mat","WIRE","wireframe","Mesh","POINTS","Points","toggleDepthDebug","state","undefined","val","map"],"mappings":"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"}
|
static/public/site.webmanifest
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "",
|
3 |
+
"short_name": "",
|
4 |
+
"icons": [
|
5 |
+
{
|
6 |
+
"src": "/public/images/favicon/android-chrome-192x192.png",
|
7 |
+
"sizes": "192x192",
|
8 |
+
"type": "image/png"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"src": "/public/images/favicon/android-chrome-512x512.png",
|
12 |
+
"sizes": "512x512",
|
13 |
+
"type": "image/png"
|
14 |
+
}
|
15 |
+
],
|
16 |
+
"theme_color": "#ffffff",
|
17 |
+
"background_color": "#ffffff",
|
18 |
+
"display": "standalone"
|
19 |
+
}
|
static/three6dof.html
ADDED
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<html>
|
2 |
+
|
3 |
+
<head>
|
4 |
+
<title>THREE.6DOF - Image Viewer Example</title>
|
5 |
+
<meta charset="utf-8">
|
6 |
+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
background: #000;
|
10 |
+
color: #fff;
|
11 |
+
padding: 0;
|
12 |
+
margin: 0;
|
13 |
+
overflow: hidden;
|
14 |
+
font-family: georgia;
|
15 |
+
text-align: center;
|
16 |
+
}
|
17 |
+
|
18 |
+
a {
|
19 |
+
color: skyblue;
|
20 |
+
text-decoration: none
|
21 |
+
}
|
22 |
+
|
23 |
+
video {
|
24 |
+
display: none;
|
25 |
+
}
|
26 |
+
|
27 |
+
#info {
|
28 |
+
position: absolute;
|
29 |
+
top: 15px;
|
30 |
+
width: 100%;
|
31 |
+
}
|
32 |
+
|
33 |
+
#info_wrapper {
|
34 |
+
background: rgba(0, 0, 0, 0.7);
|
35 |
+
}
|
36 |
+
</style>
|
37 |
+
|
38 |
+
<!-- Favicon -->
|
39 |
+
<link rel="apple-touch-icon" sizes="180x180" href="public/images/favicon/apple-touch-icon.png">
|
40 |
+
<link rel="icon" type="image/png" sizes="32x32" href="public/images/favicon/favicon-32x32.png">
|
41 |
+
<link rel="icon" type="image/png" sizes="16x16" href="public/images/favicon/favicon-16x16.png">
|
42 |
+
<link rel="manifest" href="public/site.webmanifest">
|
43 |
+
|
44 |
+
<!-- Libraries -->
|
45 |
+
<script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
|
46 |
+
<script src="https://unpkg.com/[email protected]/examples/js/controls/OrbitControls.js"></script>
|
47 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
|
48 |
+
<!-- <script src="public/js/WebVR.js"></script> -->
|
49 |
+
<script src="public/js/GUIHelper.js"></script>
|
50 |
+
|
51 |
+
<!-- THREE-6DOF -->
|
52 |
+
<script src="public/js/three-6dof.min.js"></script>
|
53 |
+
</head>
|
54 |
+
|
55 |
+
<body>
|
56 |
+
<script>
|
57 |
+
'use strict';
|
58 |
+
// get data-rgb from parent iframe tag
|
59 |
+
var rgbBase64Img = window.frameElement?.getAttribute('data-rgb');
|
60 |
+
var depthBase64Img = window.frameElement?.getAttribute('data-depth');
|
61 |
+
|
62 |
+
// debug GUI
|
63 |
+
var gui = new dat.GUI({ closed: true, closeOnTop: true });
|
64 |
+
|
65 |
+
// We will create the viewer once the textures are loaded
|
66 |
+
var sixDofViewer;
|
67 |
+
|
68 |
+
// Keep track of time
|
69 |
+
var clock = new THREE.Clock();
|
70 |
+
|
71 |
+
let enableAnimation = true;
|
72 |
+
// Create the scene, renderer and camera
|
73 |
+
var scene = new THREE.Scene();
|
74 |
+
const w = 1024
|
75 |
+
const h = 512
|
76 |
+
var renderer = new THREE.WebGLRenderer({ antialias: true });
|
77 |
+
// renderer.setSize(w, h); //window.innerWidth, window.innerHeight);
|
78 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
79 |
+
// renderer.vr.enabled = true;
|
80 |
+
document.body.appendChild(renderer.domElement);
|
81 |
+
// document.body.appendChild(THREE.WEBVR.createButton(renderer));
|
82 |
+
|
83 |
+
var camera = new THREE.PerspectiveCamera(55, w / h, 0.001, 1000);
|
84 |
+
var cameraDolly = new THREE.Object3D(); // We use a camera dolly since WebVR/XR will override camera transform
|
85 |
+
cameraDolly.position.y = -1.7;
|
86 |
+
|
87 |
+
cameraDolly.add(camera);
|
88 |
+
scene.add(cameraDolly);
|
89 |
+
|
90 |
+
|
91 |
+
var loadingManager = new THREE.LoadingManager();
|
92 |
+
var textureLoader = new THREE.TextureLoader(loadingManager);
|
93 |
+
|
94 |
+
// Load the textures and store them
|
95 |
+
var colorTexture, depthTexture;
|
96 |
+
// three.js load texture from base64
|
97 |
+
if (rgbBase64Img && depthBase64Img) {
|
98 |
+
|
99 |
+
textureLoader.load(rgbBase64Img, texture => { colorTexture = texture });
|
100 |
+
textureLoader.load(depthBase64Img, texture => { depthTexture = texture });
|
101 |
+
} else {
|
102 |
+
textureLoader.load('public/images/equirectangular/kandao3.jpg', texture => { colorTexture = texture });
|
103 |
+
textureLoader.load('public/images/equirectangular/kandao3_depthmap.jpg', texture => { depthTexture = texture });
|
104 |
+
}
|
105 |
+
|
106 |
+
// On finish loading create the viewer with the textures
|
107 |
+
loadingManager.onLoad = () => {
|
108 |
+
sixDofViewer = new SixDOF.Viewer(colorTexture, depthTexture,
|
109 |
+
{
|
110 |
+
'type': SixDOF.TextureType.SEPERATE, // For seperate depth and texture (for single top bottom use TextureType.TOP_BOTTOM)
|
111 |
+
'style': SixDOF.Style.MESH, // Chooses the rendering style (defaults to Style.MESH)
|
112 |
+
'density': SixDOF.MeshDensity.EXTRA_HIGH, // Chooses geometry tesselation level
|
113 |
+
'displacement': 4.0, // Defaults to 4.0
|
114 |
+
'radius': 6 // Defaults to 6
|
115 |
+
})
|
116 |
+
scene.add(sixDofViewer);
|
117 |
+
|
118 |
+
// Create the debug GUI and add some debug params
|
119 |
+
var shaderParams = gui.addFolder('Shader');
|
120 |
+
shaderParams.add(sixDofViewer, 'displacement', 0, 7).name('Displacement');
|
121 |
+
shaderParams.add(sixDofViewer, 'opacity', 0, 1).name('Opacity');
|
122 |
+
shaderParams.add(sixDofViewer, 'pointSize', 0, 10).name('Point Size');
|
123 |
+
shaderParams.add(camera, 'fov', 1, 100).name('Camera FOV').onChange(val => {
|
124 |
+
camera.updateProjectionMatrix();
|
125 |
+
});
|
126 |
+
shaderParams.add(camera.position, 'x', -10, 10).name('Camera X');
|
127 |
+
shaderParams.add(camera.position, 'y', -10, 10).name('Camera Y');
|
128 |
+
shaderParams.add(camera.position, 'z', -10, 10).name('Camera Z');
|
129 |
+
|
130 |
+
shaderParams.add({ 'debugDepth': false }, 'debugDepth')
|
131 |
+
.name('Debug Depth')
|
132 |
+
.onChange(val => {
|
133 |
+
sixDofViewer.toggleDepthDebug(val);
|
134 |
+
});
|
135 |
+
shaderParams.add({
|
136 |
+
'changeStyle': () => { }
|
137 |
+
}, 'changeStyle', {
|
138 |
+
'Mesh': SixDOF.Style[SixDOF.Style.MESH],
|
139 |
+
'Wireframe': SixDOF.Style[SixDOF.Style.WIRE],
|
140 |
+
'Pointcloud': SixDOF.Style[SixDOF.Style.POINTS]
|
141 |
+
})
|
142 |
+
.name('Rendering Style')
|
143 |
+
.onChange(val => {
|
144 |
+
scene.remove(sixDofViewer);
|
145 |
+
sixDofViewer = new SixDOF.Viewer(colorTexture, depthTexture, {
|
146 |
+
'style': SixDOF.Style[val]
|
147 |
+
});
|
148 |
+
scene.add(sixDofViewer);
|
149 |
+
});
|
150 |
+
|
151 |
+
shaderParams.open();
|
152 |
+
}
|
153 |
+
const controls = new THREE.OrbitControls(cameraDolly, renderer.domElement);
|
154 |
+
controls.enableZoom = true
|
155 |
+
controls.enableDamping = true;
|
156 |
+
camera.rotation.x = Math.PI / 2;
|
157 |
+
|
158 |
+
controls.autoRotate = true;
|
159 |
+
controls.addEventListener('start', function () {
|
160 |
+
controls.autoRotate = false;
|
161 |
+
});
|
162 |
+
|
163 |
+
|
164 |
+
renderer.setAnimationLoop((time) => {
|
165 |
+
|
166 |
+
controls.update();
|
167 |
+
renderer.render(scene, camera);
|
168 |
+
});
|
169 |
+
|
170 |
+
|
171 |
+
|
172 |
+
window.addEventListener('resize', ev => {
|
173 |
+
camera.aspect = window.innerWidth / window.innerHeight;
|
174 |
+
camera.updateProjectionMatrix();
|
175 |
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
176 |
+
});
|
177 |
+
</script>
|
178 |
+
</body>
|
179 |
+
|
180 |
+
</html>
|