添加关照、全局等高线、修改图层问题
BIN
dist/electron/static/icon/flyLine.png
vendored
Normal file
|
After Width: | Height: | Size: 276 B |
BIN
dist/electron/static/sdk.zip
vendored
8
dist/electron/static/sdk/3rdparty/libgif.js
vendored
@ -926,8 +926,14 @@
|
||||
get_current_frame: function() { return player.current_frame() },
|
||||
load_url: function(src,callback){
|
||||
if (!load_setup(callback)) return;
|
||||
|
||||
var h = new XMLHttpRequest();
|
||||
h.onreadystatechange = function() {
|
||||
if (h.readyState == 4) {
|
||||
if (h.status == 404) {
|
||||
callback(404)
|
||||
}
|
||||
}
|
||||
};
|
||||
// new browsers (XMLHttpRequest2-compliant)
|
||||
h.open('GET', src, true);
|
||||
|
||||
|
||||
BIN
dist/electron/static/sdk/3rdparty/liveplayer.zip
vendored
Normal file
7
dist/electron/static/sdk/Cesium/Cesium.js.map
vendored
Normal file
16078
dist/electron/static/sdk/Cesium/index.cjs
vendored
Normal file
16078
dist/electron/static/sdk/Cesium/index.js
vendored
Normal file
7
dist/electron/static/sdk/Cesium/index.js.map
vendored
Normal file
12
dist/electron/static/sdk/YJEarth.min.js
vendored
209
dist/electron/static/sdk/custom/css/index.css
vendored
@ -1515,6 +1515,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog>.content .cy_datalist dl dd:hover {
|
||||
@ -1599,6 +1600,10 @@
|
||||
width: 23px;
|
||||
height: 26px;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(var(--color-sdk-base-rgb), 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.billboard-object>.content div .image {
|
||||
@ -1780,6 +1785,89 @@
|
||||
.YJ-custom-base-dialog.water-surface>.content>div .row .label {
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
/* 流光飞线 */
|
||||
.YJ-custom-base-dialog.flow-line-surface>.content {
|
||||
width: 586px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.flow-line-surface>.content>div .row .label {
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
|
||||
/* 光照 */
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content {
|
||||
width: 586px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .row .label {
|
||||
flex: 0 0 60px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .timeline-container {
|
||||
width: 100%;
|
||||
padding: 20px 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .timeline {
|
||||
height: 8px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 15px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .progress {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: rgba(var(--color-sdk-base-rgb), 1);
|
||||
border-radius: 15px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .handle {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: white;
|
||||
/* border: 3px solid #4285f4; */
|
||||
background: rgba(var(--color-sdk-base-rgb), 1);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .time-marks {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .time-mark {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .controls {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div .current-time {
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
text-align: center;
|
||||
right: -25px;
|
||||
top: -200%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.sun-shine-surface>.content>div #timePause {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 电子围墙 */
|
||||
.YJ-custom-base-dialog.wall-stereoscopic>.content {
|
||||
@ -2511,7 +2599,9 @@
|
||||
.YJ-custom-base-dialog.polyline>.content {
|
||||
width: 580px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div #dashTextureDom {
|
||||
display: none;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .row .col {
|
||||
margin: 0 10px;
|
||||
}
|
||||
@ -2560,6 +2650,117 @@
|
||||
max-height: 185px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-unit-box textarea {
|
||||
border-radius: unset!important;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-unit .datalist {
|
||||
background-color:rgba(var(--color-sdk-base-rgb), 0.1)!important;
|
||||
border-radius: 4px 0px 0px 4px !important;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-unit:nth-of-type(1) .datalist {
|
||||
background-color:rgba(var(--color-sdk-base-rgb), 0.1)!important;
|
||||
border-radius: 4px 0px, 0px, 4px!important;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-unit:nth-of-type(2) .datalist {
|
||||
background-color:rgba(var(--color-sdk-base-rgb), 0.1)!important;
|
||||
border-radius: 0px 4px 4px 0px!important;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-unit:nth-of-type(1) input {
|
||||
border: 1px solid rgba(var(--color-sdk-base-rgb), 0.5)!important;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-unit:nth-of-type(2) input {
|
||||
border: 1px solid rgba(var(--color-sdk-base-rgb), 0.5)!important;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content input.input-text{
|
||||
background-color: rgba(0, 0, 0, 0.5)!important;
|
||||
border-radius: unset!important;
|
||||
border-top: 1px solid rgba(var(--color-sdk-base-rgb), 0.5)!important;
|
||||
border-bottom: 1px solid rgba(var(--color-sdk-base-rgb), 0.5) !important;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist input {
|
||||
padding-left: 35px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .icon-active {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
left: 10px;
|
||||
-webkit-pointer-events: none;
|
||||
-moz-pointer-events: none;
|
||||
-ms-pointer-events: none;
|
||||
-o-pointer-events: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.icon {
|
||||
display: inline-block;
|
||||
width: 22px;
|
||||
height: 10px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.line {
|
||||
border: 1px solid rgba(255, 255, 255, 1);
|
||||
height: 0px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.dash-line {
|
||||
border: 1px dashed rgba(255, 255, 255, 1);
|
||||
height: 0px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.light-line {
|
||||
border: 1px solid rgba(255, 255, 255, 1);
|
||||
height: 0px;
|
||||
margin-top: 4px;
|
||||
box-shadow: 0 0 3px #fff
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.tail-line {
|
||||
background: url(../../img/arrow/tail.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.mult-tail-line {
|
||||
background: url(../../img/arrow/tail.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.flow-dash-line1 {
|
||||
border: 1px dashed rgba(255, 255, 255, 1);
|
||||
height: 0px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.flow-dash-line2 {
|
||||
border: 1px dashed rgba(255, 255, 255, 1);
|
||||
height: 0px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.pic-line1 {
|
||||
background: url(../../img/arrow/1.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.pic-line2 {
|
||||
background: url(../../img/arrow/2.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.pic-line3 {
|
||||
background: url(../../img/arrow/3.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.pic-line4 {
|
||||
background: url(../../img/arrow/4.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.pic-line5 {
|
||||
background: url(../../img/arrow/5.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.YJ-custom-base-dialog.polyline>.content>div .input-select-line-type-box .cy_datalist i.pic-line6 {
|
||||
background: url(../../img/arrow/6.png) 100% 100% no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
/* 贴地图片 */
|
||||
.YJ-custom-base-dialog.ground-image>.content {
|
||||
width: 500px;
|
||||
@ -2978,9 +3179,9 @@
|
||||
|
||||
/* 等高线 */
|
||||
.YJ-custom-base-dialog.contour>.content {
|
||||
width: 350px;
|
||||
width: 525px;
|
||||
}
|
||||
|
||||
.YJ-custom-base-dialog.contour>.content .label {
|
||||
flex: 0 0 56px;
|
||||
}
|
||||
flex: unset;
|
||||
}
|
||||
|
||||
6
dist/electron/static/sdk/custom/img/icon-cross.svg
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
|
||||
<path d="M11.0843 0.555313L11.0843 0.555313C10.6784 0.149383 10.0203 0.149383 9.61434 0.555313L5.81934 4.35031L2.02434 0.555313C1.61841 0.149383 0.960265 0.149383 0.554336 0.555313L0.554336 0.555313C0.148407 0.961242 0.148407 1.61938 0.554336 2.02531L4.34934 5.82031L0.554336 9.61531C0.148407 10.0212 0.148407 10.6794 0.554336 11.0853L0.554336 11.0853C0.960265 11.4912 1.61841 11.4912 2.02434 11.0853L5.81934 7.29031L9.61434 11.0853C10.0203 11.4912 10.6784 11.4912 11.0843 11.0853L11.0843 11.0853C11.4903 10.6794 11.4903 10.0212 11.0843 9.61531L7.28934 5.82031L11.0843 2.02531C11.4903 1.61938 11.4903 0.961242 11.0843 0.555313Z">
|
||||
</path>
|
||||
<path fill="rgba(0, 0, 0, 0.5)" d="M10.9076 0.732089M10.9076 0.732089Q10.6763 0.500866 10.3493 0.500866Q10.0223 0.500866 9.79111 0.732089L5.81934 4.70387L1.84756 0.732089C1.53926 0.423791 1.03941 0.423791 0.731113 0.732089C0.422814 1.04039 0.422814 1.54024 0.731113 1.84854L4.70289 5.82031L0.731113 9.79209Q0.499889 10.0233 0.499889 10.3503Q0.499889 10.6773 0.731113 10.9085Q0.962336 11.1398 1.28934 11.1398Q1.61634 11.1398 1.84756 10.9085L5.81934 6.93676L9.79111 10.9085Q10.0223 11.1398 10.3493 11.1398Q10.6763 11.1398 10.9076 10.9085Q11.1388 10.6773 11.1388 10.3503Q11.1388 10.0233 10.9076 9.79209L6.93578 5.82031L10.9076 1.84854Q11.1388 1.61731 11.1388 1.29031Q11.1388 0.963312 10.9076 0.732089ZM11.2611 0.378536Q11.6388 0.756205 11.6388 1.29031Q11.6388 1.82442 11.2611 2.20209L7.46611 5.99709L7.28934 5.82031L7.46611 5.64354L11.2611 9.43854Q11.6388 9.8162 11.6388 10.3503Q11.6388 10.8844 11.2611 11.2621Q10.8834 11.6398 10.3493 11.6398Q9.81523 11.6398 9.43756 11.2621L5.64256 7.46709L5.81934 7.29031L5.99611 7.46709L2.20111 11.2621Q1.82344 11.6398 1.28934 11.6398Q0.755229 11.6398 0.377559 11.2621Q-0.000110999 10.8844 -0.000111014 10.3503Q-0.000110984 9.81621 0.377559 9.43854L4.17256 5.64354L4.34934 5.82031L4.17256 5.99709L0.377559 2.20209C-0.126001 1.69853 -0.126001 0.882096 0.377559 0.378536C0.88112 -0.125024 1.69755 -0.125025 2.20111 0.378536L5.99611 4.17354L5.81934 4.35031L5.64256 4.17354L9.43756 0.378536Q9.81523 0.000865549 10.3493 0.000865549Q10.8834 0.000865579 11.2611 0.378536L11.2611 0.378536Z">
|
||||
</path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
4
dist/electron/static/sdk/custom/img/icon-pen.svg
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16" >
|
||||
<path d="M15.5219 2.5188L13.4796 0.476487C13.1732 0.170059 12.7657 0 12.3309 0C11.8977 0 11.4886 0.168455 11.1822 0.476487L1.60273 10.0864L1.57866 10.1105L0 16L5.92801 14.4117L15.5219 4.81781C15.8299 4.50978 16 4.10228 16 3.66911C15.9984 3.23433 15.8299 2.82683 15.5219 2.5188ZM2.79154 14.2818L1.71824 13.2085L2.24125 11.2576L4.74401 13.7604L2.79154 14.2818ZM5.66169 13.3529L2.65998 10.3511L9.72546 3.2584L12.7416 6.27294L5.66169 13.3529ZM14.8593 4.15361L13.9208 5.09215L10.9046 2.07761L11.8448 1.13747C11.9667 1.01394 12.1448 0.943347 12.3293 0.943347C12.5154 0.943347 12.6935 1.01394 12.8154 1.13747L14.8577 3.18139C14.9877 3.31134 15.0599 3.48461 15.0599 3.6675C15.0615 3.8504 14.9909 4.02366 14.8593 4.15361Z" >
|
||||
</path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 837 B |
BIN
dist/electron/static/sdk/img/A-blu-blank.png
vendored
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
dist/electron/static/sdk/img/arrow/1.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
dist/electron/static/sdk/img/arrow/11.png
vendored
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
dist/electron/static/sdk/img/arrow/2.png
vendored
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
dist/electron/static/sdk/img/arrow/21.png
vendored
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
dist/electron/static/sdk/img/arrow/222.png
vendored
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
dist/electron/static/sdk/img/arrow/3.png
vendored
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
dist/electron/static/sdk/img/arrow/31.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
dist/electron/static/sdk/img/arrow/33.png
vendored
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
dist/electron/static/sdk/img/arrow/4.png
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
dist/electron/static/sdk/img/arrow/41.png
vendored
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
dist/electron/static/sdk/img/arrow/44.png
vendored
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
dist/electron/static/sdk/img/arrow/5.png
vendored
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
dist/electron/static/sdk/img/arrow/55.png
vendored
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
dist/electron/static/sdk/img/arrow/6.png
vendored
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
dist/electron/static/sdk/img/arrow/tail.png
vendored
Normal file
|
After Width: | Height: | Size: 511 B |
BIN
dist/electron/static/sdk/img/arrowRoad.jpg
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
dist/electron/static/sdk/img/bubble.png
vendored
Normal file
|
After Width: | Height: | Size: 883 B |
BIN
dist/electron/static/sdk/img/bubble.zip
vendored
Normal file
BIN
dist/electron/static/sdk/img/bubble/卫星.png
vendored
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
dist/electron/static/sdk/img/cluster.zip
vendored
Normal file
BIN
dist/electron/static/sdk/img/material.zip
vendored
Normal file
BIN
dist/electron/static/sdk/img/material/brick4.png
vendored
Normal file
|
After Width: | Height: | Size: 5.2 MiB |
BIN
dist/electron/static/sdk/img/particlesystem/d79e774380ccd613c5e7300659e822e.png
vendored
Normal file
|
After Width: | Height: | Size: 34 KiB |
48623
dist/electron/static/sdk/three/fonts/FZZongYi-M05S_regular.typeface.json
vendored
Normal file
295
dist/electron/static/sdk/three/jsm/Addons.js
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
export * from './animation/AnimationClipCreator.js';
|
||||
export * from './animation/CCDIKSolver.js';
|
||||
export * from './animation/MMDAnimationHelper.js';
|
||||
export * from './animation/MMDPhysics.js';
|
||||
|
||||
export * from './cameras/CinematicCamera.js';
|
||||
|
||||
export { default as WebGL } from './capabilities/WebGL.js';
|
||||
|
||||
export * from './controls/ArcballControls.js';
|
||||
export * from './controls/DragControls.js';
|
||||
export * from './controls/FirstPersonControls.js';
|
||||
export * from './controls/FlyControls.js';
|
||||
export * from './controls/MapControls.js';
|
||||
export * from './controls/OrbitControls.js';
|
||||
export * from './controls/PointerLockControls.js';
|
||||
export * from './controls/TrackballControls.js';
|
||||
export * from './controls/TransformControls.js';
|
||||
|
||||
export * from './csm/CSM.js';
|
||||
export * from './csm/CSMFrustum.js';
|
||||
export * from './csm/CSMHelper.js';
|
||||
export * from './csm/CSMShader.js';
|
||||
|
||||
export * as Curves from './curves/CurveExtras.js';
|
||||
export * from './curves/NURBSCurve.js';
|
||||
export * from './curves/NURBSSurface.js';
|
||||
export * from './curves/NURBSVolume.js';
|
||||
export * as NURBSUtils from './curves/NURBSUtils.js';
|
||||
|
||||
export * from './effects/AnaglyphEffect.js';
|
||||
export * from './effects/AsciiEffect.js';
|
||||
export * from './effects/OutlineEffect.js';
|
||||
export * from './effects/ParallaxBarrierEffect.js';
|
||||
export * from './effects/PeppersGhostEffect.js';
|
||||
export * from './effects/StereoEffect.js';
|
||||
|
||||
export * from './environments/DebugEnvironment.js';
|
||||
export * from './environments/RoomEnvironment.js';
|
||||
|
||||
export * from './exporters/DRACOExporter.js';
|
||||
export * from './exporters/EXRExporter.js';
|
||||
export * from './exporters/GLTFExporter.js';
|
||||
export * from './exporters/KTX2Exporter.js';
|
||||
export * from './exporters/MMDExporter.js';
|
||||
export * from './exporters/OBJExporter.js';
|
||||
export * from './exporters/PLYExporter.js';
|
||||
export * from './exporters/STLExporter.js';
|
||||
export * from './exporters/USDZExporter.js';
|
||||
|
||||
export * from './geometries/BoxLineGeometry.js';
|
||||
export * from './geometries/ConvexGeometry.js';
|
||||
export * from './geometries/DecalGeometry.js';
|
||||
export * from './geometries/ParametricGeometries.js';
|
||||
export * from './geometries/ParametricGeometry.js';
|
||||
export * from './geometries/RoundedBoxGeometry.js';
|
||||
export * from './geometries/TeapotGeometry.js';
|
||||
export * from './geometries/TextGeometry.js';
|
||||
|
||||
export * from './helpers/LightProbeHelper.js';
|
||||
export * from './helpers/OctreeHelper.js';
|
||||
export * from './helpers/PositionalAudioHelper.js';
|
||||
export * from './helpers/RectAreaLightHelper.js';
|
||||
export * from './helpers/TextureHelper.js';
|
||||
export * from './helpers/VertexNormalsHelper.js';
|
||||
export * from './helpers/VertexTangentsHelper.js';
|
||||
export * from './helpers/ViewHelper.js';
|
||||
|
||||
export * from './interactive/HTMLMesh.js';
|
||||
export * from './interactive/InteractiveGroup.js';
|
||||
export * from './interactive/SelectionBox.js';
|
||||
export * from './interactive/SelectionHelper.js';
|
||||
|
||||
export { default as IESSpotLight } from './lights/IESSpotLight.js';
|
||||
export * from './lights/LightProbeGenerator.js';
|
||||
export * from './lights/RectAreaLightUniformsLib.js';
|
||||
|
||||
export * from './lines/Line2.js';
|
||||
export * from './lines/LineGeometry.js';
|
||||
export * from './lines/LineMaterial.js';
|
||||
export * from './lines/LineSegments2.js';
|
||||
export * from './lines/LineSegmentsGeometry.js';
|
||||
export * from './lines/Wireframe.js';
|
||||
export * from './lines/WireframeGeometry2.js';
|
||||
|
||||
export * from './loaders/3DMLoader.js';
|
||||
export * from './loaders/3MFLoader.js';
|
||||
export * from './loaders/AMFLoader.js';
|
||||
export * from './loaders/BVHLoader.js';
|
||||
export * from './loaders/ColladaLoader.js';
|
||||
export * from './loaders/DDSLoader.js';
|
||||
export * from './loaders/DRACOLoader.js';
|
||||
export * from './loaders/EXRLoader.js';
|
||||
export * from './loaders/FBXLoader.js';
|
||||
export * from './loaders/FontLoader.js';
|
||||
export * from './loaders/GCodeLoader.js';
|
||||
export * from './loaders/GLTFLoader.js';
|
||||
export * from './loaders/HDRCubeTextureLoader.js';
|
||||
export * from './loaders/IESLoader.js';
|
||||
export * from './loaders/KMZLoader.js';
|
||||
export * from './loaders/KTX2Loader.js';
|
||||
export * from './loaders/KTXLoader.js';
|
||||
export * from './loaders/LDrawLoader.js';
|
||||
export * from './loaders/LUT3dlLoader.js';
|
||||
export * from './loaders/LUTCubeLoader.js';
|
||||
export * from './loaders/LWOLoader.js';
|
||||
export * from './loaders/LogLuvLoader.js';
|
||||
export * from './loaders/LottieLoader.js';
|
||||
export * from './loaders/MD2Loader.js';
|
||||
export * from './loaders/MDDLoader.js';
|
||||
export * from './loaders/MMDLoader.js';
|
||||
export * from './loaders/MTLLoader.js';
|
||||
export * from './loaders/NRRDLoader.js';
|
||||
export * from './loaders/OBJLoader.js';
|
||||
export * from './loaders/PCDLoader.js';
|
||||
export * from './loaders/PDBLoader.js';
|
||||
export * from './loaders/PLYLoader.js';
|
||||
export * from './loaders/PVRLoader.js';
|
||||
export * from './loaders/RGBELoader.js';
|
||||
export * from './loaders/RGBMLoader.js';
|
||||
export * from './loaders/STLLoader.js';
|
||||
export * from './loaders/SVGLoader.js';
|
||||
export * from './loaders/TDSLoader.js';
|
||||
export * from './loaders/TGALoader.js';
|
||||
export * from './loaders/TIFFLoader.js';
|
||||
export * from './loaders/TTFLoader.js';
|
||||
export * from './loaders/TiltLoader.js';
|
||||
export * from './loaders/USDZLoader.js';
|
||||
export * from './loaders/VOXLoader.js';
|
||||
export * from './loaders/VRMLLoader.js';
|
||||
export * from './loaders/VTKLoader.js';
|
||||
export * from './loaders/XYZLoader.js';
|
||||
|
||||
export * from './materials/MeshGouraudMaterial.js';
|
||||
|
||||
export * from './math/Capsule.js';
|
||||
export * from './math/ColorConverter.js';
|
||||
export * from './math/ConvexHull.js';
|
||||
export * from './math/ImprovedNoise.js';
|
||||
export * from './math/Lut.js';
|
||||
export * from './math/MeshSurfaceSampler.js';
|
||||
export * from './math/OBB.js';
|
||||
export * from './math/Octree.js';
|
||||
export * from './math/SimplexNoise.js';
|
||||
|
||||
export * from './misc/ConvexObjectBreaker.js';
|
||||
export * from './misc/GPUComputationRenderer.js';
|
||||
export * from './misc/Gyroscope.js';
|
||||
export * from './misc/MD2Character.js';
|
||||
export * from './misc/MD2CharacterComplex.js';
|
||||
export * from './misc/MorphAnimMesh.js';
|
||||
export * from './misc/MorphBlendMesh.js';
|
||||
export * from './misc/ProgressiveLightMap.js';
|
||||
export * from './misc/RollerCoaster.js';
|
||||
export * from './misc/Timer.js';
|
||||
export * from './misc/TubePainter.js';
|
||||
export * from './misc/Volume.js';
|
||||
export * from './misc/VolumeSlice.js';
|
||||
|
||||
export * from './modifiers/CurveModifier.js';
|
||||
export * from './modifiers/EdgeSplitModifier.js';
|
||||
export * from './modifiers/SimplifyModifier.js';
|
||||
export * from './modifiers/TessellateModifier.js';
|
||||
|
||||
export * from './objects/GroundedSkybox.js';
|
||||
export * from './objects/Lensflare.js';
|
||||
export * from './objects/MarchingCubes.js';
|
||||
export * from './objects/Reflector.js';
|
||||
export * from './objects/ReflectorForSSRPass.js';
|
||||
export * from './objects/Refractor.js';
|
||||
export * from './objects/ShadowMesh.js';
|
||||
export * from './objects/Sky.js';
|
||||
export * from './objects/Water.js';
|
||||
export { Water as Water2 } from './objects/Water2.js';
|
||||
|
||||
export * from './physics/AmmoPhysics.js';
|
||||
export * from './physics/RapierPhysics.js';
|
||||
|
||||
export * from './postprocessing/AfterimagePass.js';
|
||||
export * from './postprocessing/BloomPass.js';
|
||||
export * from './postprocessing/BokehPass.js';
|
||||
export * from './postprocessing/ClearPass.js';
|
||||
export * from './postprocessing/CubeTexturePass.js';
|
||||
export * from './postprocessing/DotScreenPass.js';
|
||||
export * from './postprocessing/EffectComposer.js';
|
||||
export * from './postprocessing/FilmPass.js';
|
||||
export * from './postprocessing/GlitchPass.js';
|
||||
export * from './postprocessing/GTAOPass.js';
|
||||
export * from './postprocessing/HalftonePass.js';
|
||||
export * from './postprocessing/LUTPass.js';
|
||||
export * from './postprocessing/MaskPass.js';
|
||||
export * from './postprocessing/OutlinePass.js';
|
||||
export * from './postprocessing/OutputPass.js';
|
||||
export * from './postprocessing/Pass.js';
|
||||
export * from './postprocessing/RenderPass.js';
|
||||
export * from './postprocessing/RenderPixelatedPass.js';
|
||||
export * from './postprocessing/SAOPass.js';
|
||||
export * from './postprocessing/SMAAPass.js';
|
||||
export * from './postprocessing/SSAARenderPass.js';
|
||||
export * from './postprocessing/SSAOPass.js';
|
||||
export * from './postprocessing/SSRPass.js';
|
||||
export * from './postprocessing/SavePass.js';
|
||||
export * from './postprocessing/ShaderPass.js';
|
||||
export * from './postprocessing/TAARenderPass.js';
|
||||
export * from './postprocessing/TexturePass.js';
|
||||
export * from './postprocessing/UnrealBloomPass.js';
|
||||
|
||||
export * from './renderers/CSS2DRenderer.js';
|
||||
export * from './renderers/CSS3DRenderer.js';
|
||||
export * from './renderers/Projector.js';
|
||||
export * from './renderers/SVGRenderer.js';
|
||||
|
||||
export * from './shaders/ACESFilmicToneMappingShader.js';
|
||||
export * from './shaders/AfterimageShader.js';
|
||||
export * from './shaders/BasicShader.js';
|
||||
export * from './shaders/BleachBypassShader.js';
|
||||
export * from './shaders/BlendShader.js';
|
||||
export * from './shaders/BokehShader.js';
|
||||
export { BokehShader as BokehShader2 } from './shaders/BokehShader2.js';
|
||||
export * from './shaders/BrightnessContrastShader.js';
|
||||
export * from './shaders/ColorCorrectionShader.js';
|
||||
export * from './shaders/ColorifyShader.js';
|
||||
export * from './shaders/ConvolutionShader.js';
|
||||
export * from './shaders/CopyShader.js';
|
||||
export * from './shaders/DOFMipMapShader.js';
|
||||
export * from './shaders/DepthLimitedBlurShader.js';
|
||||
export * from './shaders/DigitalGlitch.js';
|
||||
export * from './shaders/DotScreenShader.js';
|
||||
export * from './shaders/ExposureShader.js';
|
||||
export * from './shaders/FXAAShader.js';
|
||||
export * from './shaders/FilmShader.js';
|
||||
export * from './shaders/FocusShader.js';
|
||||
export * from './shaders/FreiChenShader.js';
|
||||
export * from './shaders/GammaCorrectionShader.js';
|
||||
export * from './shaders/GodRaysShader.js';
|
||||
export * from './shaders/GTAOShader.js';
|
||||
export * from './shaders/HalftoneShader.js';
|
||||
export * from './shaders/HorizontalBlurShader.js';
|
||||
export * from './shaders/HorizontalTiltShiftShader.js';
|
||||
export * from './shaders/HueSaturationShader.js';
|
||||
export * from './shaders/KaleidoShader.js';
|
||||
export * from './shaders/LuminosityHighPassShader.js';
|
||||
export * from './shaders/LuminosityShader.js';
|
||||
export * from './shaders/MMDToonShader.js';
|
||||
export * from './shaders/MirrorShader.js';
|
||||
export * from './shaders/NormalMapShader.js';
|
||||
export * from './shaders/OutputShader.js';
|
||||
export * from './shaders/RGBShiftShader.js';
|
||||
export * from './shaders/SAOShader.js';
|
||||
export * from './shaders/SMAAShader.js';
|
||||
export * from './shaders/SSAOShader.js';
|
||||
export * from './shaders/SSRShader.js';
|
||||
export * from './shaders/SepiaShader.js';
|
||||
export * from './shaders/SobelOperatorShader.js';
|
||||
export * from './shaders/SubsurfaceScatteringShader.js';
|
||||
export * from './shaders/TechnicolorShader.js';
|
||||
export * from './shaders/ToonShader.js';
|
||||
export * from './shaders/TriangleBlurShader.js';
|
||||
export * from './shaders/UnpackDepthRGBAShader.js';
|
||||
export * from './shaders/VelocityShader.js';
|
||||
export * from './shaders/VerticalBlurShader.js';
|
||||
export * from './shaders/VerticalTiltShiftShader.js';
|
||||
export * from './shaders/VignetteShader.js';
|
||||
export * from './shaders/VolumeShader.js';
|
||||
export * from './shaders/WaterRefractionShader.js';
|
||||
|
||||
export * from './textures/FlakesTexture.js';
|
||||
|
||||
export * as BufferGeometryUtils from './utils/BufferGeometryUtils.js';
|
||||
export * as CameraUtils from './utils/CameraUtils.js';
|
||||
export * from './utils/GPUStatsPanel.js';
|
||||
export * as GeometryCompressionUtils from './utils/GeometryCompressionUtils.js';
|
||||
export * as GeometryUtils from './utils/GeometryUtils.js';
|
||||
export * from './utils/LDrawUtils.js';
|
||||
export * from './utils/PackedPhongMaterial.js';
|
||||
export * as SceneUtils from './utils/SceneUtils.js';
|
||||
export * from './utils/ShadowMapViewer.js';
|
||||
export * as SkeletonUtils from './utils/SkeletonUtils.js';
|
||||
export * as SortUtils from './utils/SortUtils.js';
|
||||
export * from './utils/TextureUtils.js';
|
||||
export * from './utils/UVsDebug.js';
|
||||
export * from './utils/WorkerPool.js';
|
||||
|
||||
export * from './webxr/ARButton.js';
|
||||
export * from './webxr/OculusHandModel.js';
|
||||
export * from './webxr/OculusHandPointerModel.js';
|
||||
export * from './webxr/Text2D.js';
|
||||
export * from './webxr/VRButton.js';
|
||||
export * from './webxr/XRButton.js';
|
||||
export * from './webxr/XRControllerModelFactory.js';
|
||||
export * from './webxr/XREstimatedLight.js';
|
||||
export * from './webxr/XRHandMeshModel.js';
|
||||
export * from './webxr/XRHandModelFactory.js';
|
||||
export * from './webxr/XRHandPrimitiveModel.js';
|
||||
export * from './webxr/XRPlanes.js';
|
||||
116
dist/electron/static/sdk/three/jsm/animation/AnimationClipCreator.js
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
import {
|
||||
AnimationClip,
|
||||
BooleanKeyframeTrack,
|
||||
ColorKeyframeTrack,
|
||||
NumberKeyframeTrack,
|
||||
Vector3,
|
||||
VectorKeyframeTrack
|
||||
} from 'three';
|
||||
|
||||
class AnimationClipCreator {
|
||||
|
||||
static CreateRotationAnimation( period, axis = 'x' ) {
|
||||
|
||||
const times = [ 0, period ], values = [ 0, 360 ];
|
||||
|
||||
const trackName = '.rotation[' + axis + ']';
|
||||
|
||||
const track = new NumberKeyframeTrack( trackName, times, values );
|
||||
|
||||
return new AnimationClip( null, period, [ track ] );
|
||||
|
||||
}
|
||||
|
||||
static CreateScaleAxisAnimation( period, axis = 'x' ) {
|
||||
|
||||
const times = [ 0, period ], values = [ 0, 1 ];
|
||||
|
||||
const trackName = '.scale[' + axis + ']';
|
||||
|
||||
const track = new NumberKeyframeTrack( trackName, times, values );
|
||||
|
||||
return new AnimationClip( null, period, [ track ] );
|
||||
|
||||
}
|
||||
|
||||
static CreateShakeAnimation( duration, shakeScale ) {
|
||||
|
||||
const times = [], values = [], tmp = new Vector3();
|
||||
|
||||
for ( let i = 0; i < duration * 10; i ++ ) {
|
||||
|
||||
times.push( i / 10 );
|
||||
|
||||
tmp.set( Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0, Math.random() * 2.0 - 1.0 ).
|
||||
multiply( shakeScale ).
|
||||
toArray( values, values.length );
|
||||
|
||||
}
|
||||
|
||||
const trackName = '.position';
|
||||
|
||||
const track = new VectorKeyframeTrack( trackName, times, values );
|
||||
|
||||
return new AnimationClip( null, duration, [ track ] );
|
||||
|
||||
}
|
||||
|
||||
static CreatePulsationAnimation( duration, pulseScale ) {
|
||||
|
||||
const times = [], values = [], tmp = new Vector3();
|
||||
|
||||
for ( let i = 0; i < duration * 10; i ++ ) {
|
||||
|
||||
times.push( i / 10 );
|
||||
|
||||
const scaleFactor = Math.random() * pulseScale;
|
||||
tmp.set( scaleFactor, scaleFactor, scaleFactor ).
|
||||
toArray( values, values.length );
|
||||
|
||||
}
|
||||
|
||||
const trackName = '.scale';
|
||||
|
||||
const track = new VectorKeyframeTrack( trackName, times, values );
|
||||
|
||||
return new AnimationClip( null, duration, [ track ] );
|
||||
|
||||
}
|
||||
|
||||
static CreateVisibilityAnimation( duration ) {
|
||||
|
||||
const times = [ 0, duration / 2, duration ], values = [ true, false, true ];
|
||||
|
||||
const trackName = '.visible';
|
||||
|
||||
const track = new BooleanKeyframeTrack( trackName, times, values );
|
||||
|
||||
return new AnimationClip( null, duration, [ track ] );
|
||||
|
||||
}
|
||||
|
||||
static CreateMaterialColorAnimation( duration, colors ) {
|
||||
|
||||
const times = [], values = [],
|
||||
timeStep = duration / colors.length;
|
||||
|
||||
for ( let i = 0; i < colors.length; i ++ ) {
|
||||
|
||||
times.push( i * timeStep );
|
||||
|
||||
const color = colors[ i ];
|
||||
values.push( color.r, color.g, color.b );
|
||||
|
||||
}
|
||||
|
||||
const trackName = '.material.color';
|
||||
|
||||
const track = new ColorKeyframeTrack( trackName, times, values );
|
||||
|
||||
return new AnimationClip( null, duration, [ track ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { AnimationClipCreator };
|
||||
484
dist/electron/static/sdk/three/jsm/animation/CCDIKSolver.js
vendored
Normal file
@ -0,0 +1,484 @@
|
||||
import {
|
||||
BufferAttribute,
|
||||
BufferGeometry,
|
||||
Color,
|
||||
Line,
|
||||
LineBasicMaterial,
|
||||
Matrix4,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
Object3D,
|
||||
Quaternion,
|
||||
SphereGeometry,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _q = new Quaternion();
|
||||
const _targetPos = new Vector3();
|
||||
const _targetVec = new Vector3();
|
||||
const _effectorPos = new Vector3();
|
||||
const _effectorVec = new Vector3();
|
||||
const _linkPos = new Vector3();
|
||||
const _invLinkQ = new Quaternion();
|
||||
const _linkScale = new Vector3();
|
||||
const _axis = new Vector3();
|
||||
const _vector = new Vector3();
|
||||
const _matrix = new Matrix4();
|
||||
|
||||
|
||||
/**
|
||||
* CCD Algorithm
|
||||
* - https://sites.google.com/site/auraliusproject/ccd-algorithm
|
||||
*
|
||||
* // ik parameter example
|
||||
* //
|
||||
* // target, effector, index in links are bone index in skeleton.bones.
|
||||
* // the bones relation should be
|
||||
* // <-- parent child -->
|
||||
* // links[ n ], links[ n - 1 ], ..., links[ 0 ], effector
|
||||
* iks = [ {
|
||||
* target: 1,
|
||||
* effector: 2,
|
||||
* links: [ { index: 5, limitation: new Vector3( 1, 0, 0 ) }, { index: 4, enabled: false }, { index : 3 } ],
|
||||
* iteration: 10,
|
||||
* minAngle: 0.0,
|
||||
* maxAngle: 1.0,
|
||||
* } ];
|
||||
*/
|
||||
|
||||
class CCDIKSolver {
|
||||
|
||||
/**
|
||||
* @param {THREE.SkinnedMesh} mesh
|
||||
* @param {Array<Object>} iks
|
||||
*/
|
||||
constructor( mesh, iks = [] ) {
|
||||
|
||||
this.mesh = mesh;
|
||||
this.iks = iks;
|
||||
|
||||
this._valid();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all IK bones.
|
||||
*
|
||||
* @return {CCDIKSolver}
|
||||
*/
|
||||
update() {
|
||||
|
||||
const iks = this.iks;
|
||||
|
||||
for ( let i = 0, il = iks.length; i < il; i ++ ) {
|
||||
|
||||
this.updateOne( iks[ i ] );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Update one IK bone
|
||||
*
|
||||
* @param {Object} ik parameter
|
||||
* @return {CCDIKSolver}
|
||||
*/
|
||||
updateOne( ik ) {
|
||||
|
||||
const bones = this.mesh.skeleton.bones;
|
||||
|
||||
// for reference overhead reduction in loop
|
||||
const math = Math;
|
||||
|
||||
const effector = bones[ ik.effector ];
|
||||
const target = bones[ ik.target ];
|
||||
|
||||
// don't use getWorldPosition() here for the performance
|
||||
// because it calls updateMatrixWorld( true ) inside.
|
||||
_targetPos.setFromMatrixPosition( target.matrixWorld );
|
||||
|
||||
const links = ik.links;
|
||||
const iteration = ik.iteration !== undefined ? ik.iteration : 1;
|
||||
|
||||
for ( let i = 0; i < iteration; i ++ ) {
|
||||
|
||||
let rotated = false;
|
||||
|
||||
for ( let j = 0, jl = links.length; j < jl; j ++ ) {
|
||||
|
||||
const link = bones[ links[ j ].index ];
|
||||
|
||||
// skip this link and following links.
|
||||
// this skip is used for MMD performance optimization.
|
||||
if ( links[ j ].enabled === false ) break;
|
||||
|
||||
const limitation = links[ j ].limitation;
|
||||
const rotationMin = links[ j ].rotationMin;
|
||||
const rotationMax = links[ j ].rotationMax;
|
||||
|
||||
// don't use getWorldPosition/Quaternion() here for the performance
|
||||
// because they call updateMatrixWorld( true ) inside.
|
||||
link.matrixWorld.decompose( _linkPos, _invLinkQ, _linkScale );
|
||||
_invLinkQ.invert();
|
||||
_effectorPos.setFromMatrixPosition( effector.matrixWorld );
|
||||
|
||||
// work in link world
|
||||
_effectorVec.subVectors( _effectorPos, _linkPos );
|
||||
_effectorVec.applyQuaternion( _invLinkQ );
|
||||
_effectorVec.normalize();
|
||||
|
||||
_targetVec.subVectors( _targetPos, _linkPos );
|
||||
_targetVec.applyQuaternion( _invLinkQ );
|
||||
_targetVec.normalize();
|
||||
|
||||
let angle = _targetVec.dot( _effectorVec );
|
||||
|
||||
if ( angle > 1.0 ) {
|
||||
|
||||
angle = 1.0;
|
||||
|
||||
} else if ( angle < - 1.0 ) {
|
||||
|
||||
angle = - 1.0;
|
||||
|
||||
}
|
||||
|
||||
angle = math.acos( angle );
|
||||
|
||||
// skip if changing angle is too small to prevent vibration of bone
|
||||
if ( angle < 1e-5 ) continue;
|
||||
|
||||
if ( ik.minAngle !== undefined && angle < ik.minAngle ) {
|
||||
|
||||
angle = ik.minAngle;
|
||||
|
||||
}
|
||||
|
||||
if ( ik.maxAngle !== undefined && angle > ik.maxAngle ) {
|
||||
|
||||
angle = ik.maxAngle;
|
||||
|
||||
}
|
||||
|
||||
_axis.crossVectors( _effectorVec, _targetVec );
|
||||
_axis.normalize();
|
||||
|
||||
_q.setFromAxisAngle( _axis, angle );
|
||||
link.quaternion.multiply( _q );
|
||||
|
||||
// TODO: re-consider the limitation specification
|
||||
if ( limitation !== undefined ) {
|
||||
|
||||
let c = link.quaternion.w;
|
||||
|
||||
if ( c > 1.0 ) c = 1.0;
|
||||
|
||||
const c2 = math.sqrt( 1 - c * c );
|
||||
link.quaternion.set( limitation.x * c2,
|
||||
limitation.y * c2,
|
||||
limitation.z * c2,
|
||||
c );
|
||||
|
||||
}
|
||||
|
||||
if ( rotationMin !== undefined ) {
|
||||
|
||||
link.rotation.setFromVector3( _vector.setFromEuler( link.rotation ).max( rotationMin ) );
|
||||
|
||||
}
|
||||
|
||||
if ( rotationMax !== undefined ) {
|
||||
|
||||
link.rotation.setFromVector3( _vector.setFromEuler( link.rotation ).min( rotationMax ) );
|
||||
|
||||
}
|
||||
|
||||
link.updateMatrixWorld( true );
|
||||
|
||||
rotated = true;
|
||||
|
||||
}
|
||||
|
||||
if ( ! rotated ) break;
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates Helper
|
||||
*
|
||||
* @param {number} sphereSize
|
||||
* @return {CCDIKHelper}
|
||||
*/
|
||||
createHelper( sphereSize ) {
|
||||
|
||||
return new CCDIKHelper( this.mesh, this.iks, sphereSize );
|
||||
|
||||
}
|
||||
|
||||
// private methods
|
||||
|
||||
_valid() {
|
||||
|
||||
const iks = this.iks;
|
||||
const bones = this.mesh.skeleton.bones;
|
||||
|
||||
for ( let i = 0, il = iks.length; i < il; i ++ ) {
|
||||
|
||||
const ik = iks[ i ];
|
||||
const effector = bones[ ik.effector ];
|
||||
const links = ik.links;
|
||||
let link0, link1;
|
||||
|
||||
link0 = effector;
|
||||
|
||||
for ( let j = 0, jl = links.length; j < jl; j ++ ) {
|
||||
|
||||
link1 = bones[ links[ j ].index ];
|
||||
|
||||
if ( link0.parent !== link1 ) {
|
||||
|
||||
console.warn( 'THREE.CCDIKSolver: bone ' + link0.name + ' is not the child of bone ' + link1.name );
|
||||
|
||||
}
|
||||
|
||||
link0 = link1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getPosition( bone, matrixWorldInv ) {
|
||||
|
||||
return _vector
|
||||
.setFromMatrixPosition( bone.matrixWorld )
|
||||
.applyMatrix4( matrixWorldInv );
|
||||
|
||||
}
|
||||
|
||||
function setPositionOfBoneToAttributeArray( array, index, bone, matrixWorldInv ) {
|
||||
|
||||
const v = getPosition( bone, matrixWorldInv );
|
||||
|
||||
array[ index * 3 + 0 ] = v.x;
|
||||
array[ index * 3 + 1 ] = v.y;
|
||||
array[ index * 3 + 2 ] = v.z;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Visualize IK bones
|
||||
*
|
||||
* @param {SkinnedMesh} mesh
|
||||
* @param {Array<Object>} iks
|
||||
* @param {number} sphereSize
|
||||
*/
|
||||
class CCDIKHelper extends Object3D {
|
||||
|
||||
constructor( mesh, iks = [], sphereSize = 0.25 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.root = mesh;
|
||||
this.iks = iks;
|
||||
|
||||
this.matrix.copy( mesh.matrixWorld );
|
||||
this.matrixAutoUpdate = false;
|
||||
|
||||
this.sphereGeometry = new SphereGeometry( sphereSize, 16, 8 );
|
||||
|
||||
this.targetSphereMaterial = new MeshBasicMaterial( {
|
||||
color: new Color( 0xff8888 ),
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: true
|
||||
} );
|
||||
|
||||
this.effectorSphereMaterial = new MeshBasicMaterial( {
|
||||
color: new Color( 0x88ff88 ),
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: true
|
||||
} );
|
||||
|
||||
this.linkSphereMaterial = new MeshBasicMaterial( {
|
||||
color: new Color( 0x8888ff ),
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: true
|
||||
} );
|
||||
|
||||
this.lineMaterial = new LineBasicMaterial( {
|
||||
color: new Color( 0xff0000 ),
|
||||
depthTest: false,
|
||||
depthWrite: false,
|
||||
transparent: true
|
||||
} );
|
||||
|
||||
this._init();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates IK bones visualization.
|
||||
*/
|
||||
updateMatrixWorld( force ) {
|
||||
|
||||
const mesh = this.root;
|
||||
|
||||
if ( this.visible ) {
|
||||
|
||||
let offset = 0;
|
||||
|
||||
const iks = this.iks;
|
||||
const bones = mesh.skeleton.bones;
|
||||
|
||||
_matrix.copy( mesh.matrixWorld ).invert();
|
||||
|
||||
for ( let i = 0, il = iks.length; i < il; i ++ ) {
|
||||
|
||||
const ik = iks[ i ];
|
||||
|
||||
const targetBone = bones[ ik.target ];
|
||||
const effectorBone = bones[ ik.effector ];
|
||||
|
||||
const targetMesh = this.children[ offset ++ ];
|
||||
const effectorMesh = this.children[ offset ++ ];
|
||||
|
||||
targetMesh.position.copy( getPosition( targetBone, _matrix ) );
|
||||
effectorMesh.position.copy( getPosition( effectorBone, _matrix ) );
|
||||
|
||||
for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
|
||||
|
||||
const link = ik.links[ j ];
|
||||
const linkBone = bones[ link.index ];
|
||||
|
||||
const linkMesh = this.children[ offset ++ ];
|
||||
|
||||
linkMesh.position.copy( getPosition( linkBone, _matrix ) );
|
||||
|
||||
}
|
||||
|
||||
const line = this.children[ offset ++ ];
|
||||
const array = line.geometry.attributes.position.array;
|
||||
|
||||
setPositionOfBoneToAttributeArray( array, 0, targetBone, _matrix );
|
||||
setPositionOfBoneToAttributeArray( array, 1, effectorBone, _matrix );
|
||||
|
||||
for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
|
||||
|
||||
const link = ik.links[ j ];
|
||||
const linkBone = bones[ link.index ];
|
||||
setPositionOfBoneToAttributeArray( array, j + 2, linkBone, _matrix );
|
||||
|
||||
}
|
||||
|
||||
line.geometry.attributes.position.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.matrix.copy( mesh.matrixWorld );
|
||||
|
||||
super.updateMatrixWorld( force );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees the GPU-related resources allocated by this instance. Call this method whenever this instance is no longer used in your app.
|
||||
*/
|
||||
dispose() {
|
||||
|
||||
this.sphereGeometry.dispose();
|
||||
|
||||
this.targetSphereMaterial.dispose();
|
||||
this.effectorSphereMaterial.dispose();
|
||||
this.linkSphereMaterial.dispose();
|
||||
this.lineMaterial.dispose();
|
||||
|
||||
const children = this.children;
|
||||
|
||||
for ( let i = 0; i < children.length; i ++ ) {
|
||||
|
||||
const child = children[ i ];
|
||||
|
||||
if ( child.isLine ) child.geometry.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// private method
|
||||
|
||||
_init() {
|
||||
|
||||
const scope = this;
|
||||
const iks = this.iks;
|
||||
|
||||
function createLineGeometry( ik ) {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
const vertices = new Float32Array( ( 2 + ik.links.length ) * 3 );
|
||||
geometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
function createTargetMesh() {
|
||||
|
||||
return new Mesh( scope.sphereGeometry, scope.targetSphereMaterial );
|
||||
|
||||
}
|
||||
|
||||
function createEffectorMesh() {
|
||||
|
||||
return new Mesh( scope.sphereGeometry, scope.effectorSphereMaterial );
|
||||
|
||||
}
|
||||
|
||||
function createLinkMesh() {
|
||||
|
||||
return new Mesh( scope.sphereGeometry, scope.linkSphereMaterial );
|
||||
|
||||
}
|
||||
|
||||
function createLine( ik ) {
|
||||
|
||||
return new Line( createLineGeometry( ik ), scope.lineMaterial );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0, il = iks.length; i < il; i ++ ) {
|
||||
|
||||
const ik = iks[ i ];
|
||||
|
||||
this.add( createTargetMesh() );
|
||||
this.add( createEffectorMesh() );
|
||||
|
||||
for ( let j = 0, jl = ik.links.length; j < jl; j ++ ) {
|
||||
|
||||
this.add( createLinkMesh() );
|
||||
|
||||
}
|
||||
|
||||
this.add( createLine( ik ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CCDIKSolver, CCDIKHelper };
|
||||
1207
dist/electron/static/sdk/three/jsm/animation/MMDAnimationHelper.js
vendored
Normal file
1406
dist/electron/static/sdk/three/jsm/animation/MMDPhysics.js
vendored
Normal file
208
dist/electron/static/sdk/three/jsm/cameras/CinematicCamera.js
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
import {
|
||||
Mesh,
|
||||
OrthographicCamera,
|
||||
PerspectiveCamera,
|
||||
PlaneGeometry,
|
||||
Scene,
|
||||
ShaderMaterial,
|
||||
UniformsUtils,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
|
||||
import { BokehShader, BokehDepthShader } from '../shaders/BokehShader2.js';
|
||||
|
||||
class CinematicCamera extends PerspectiveCamera {
|
||||
|
||||
constructor( fov, aspect, near, far ) {
|
||||
|
||||
super( fov, aspect, near, far );
|
||||
|
||||
this.type = 'CinematicCamera';
|
||||
|
||||
this.postprocessing = { enabled: true };
|
||||
this.shaderSettings = {
|
||||
rings: 3,
|
||||
samples: 4
|
||||
};
|
||||
|
||||
const depthShader = BokehDepthShader;
|
||||
|
||||
this.materialDepth = new ShaderMaterial( {
|
||||
uniforms: depthShader.uniforms,
|
||||
vertexShader: depthShader.vertexShader,
|
||||
fragmentShader: depthShader.fragmentShader
|
||||
} );
|
||||
|
||||
this.materialDepth.uniforms[ 'mNear' ].value = near;
|
||||
this.materialDepth.uniforms[ 'mFar' ].value = far;
|
||||
|
||||
// In case of cinematicCamera, having a default lens set is important
|
||||
this.setLens();
|
||||
|
||||
this.initPostProcessing();
|
||||
|
||||
}
|
||||
|
||||
// providing fnumber and coc(Circle of Confusion) as extra arguments
|
||||
// In case of cinematicCamera, having a default lens set is important
|
||||
// if fnumber and coc are not provided, cinematicCamera tries to act as a basic PerspectiveCamera
|
||||
setLens( focalLength = 35, filmGauge = 35, fNumber = 8, coc = 0.019 ) {
|
||||
|
||||
this.filmGauge = filmGauge;
|
||||
|
||||
this.setFocalLength( focalLength );
|
||||
|
||||
this.fNumber = fNumber;
|
||||
this.coc = coc;
|
||||
|
||||
// fNumber is focalLength by aperture
|
||||
this.aperture = focalLength / this.fNumber;
|
||||
|
||||
// hyperFocal is required to calculate depthOfField when a lens tries to focus at a distance with given fNumber and focalLength
|
||||
this.hyperFocal = ( focalLength * focalLength ) / ( this.aperture * this.coc );
|
||||
|
||||
}
|
||||
|
||||
linearize( depth ) {
|
||||
|
||||
const zfar = this.far;
|
||||
const znear = this.near;
|
||||
return - zfar * znear / ( depth * ( zfar - znear ) - zfar );
|
||||
|
||||
}
|
||||
|
||||
smoothstep( near, far, depth ) {
|
||||
|
||||
const x = this.saturate( ( depth - near ) / ( far - near ) );
|
||||
return x * x * ( 3 - 2 * x );
|
||||
|
||||
}
|
||||
|
||||
saturate( x ) {
|
||||
|
||||
return Math.max( 0, Math.min( 1, x ) );
|
||||
|
||||
}
|
||||
|
||||
// function for focusing at a distance from the camera
|
||||
focusAt( focusDistance = 20 ) {
|
||||
|
||||
const focalLength = this.getFocalLength();
|
||||
|
||||
// distance from the camera (normal to frustrum) to focus on
|
||||
this.focus = focusDistance;
|
||||
|
||||
// the nearest point from the camera which is in focus (unused)
|
||||
this.nearPoint = ( this.hyperFocal * this.focus ) / ( this.hyperFocal + ( this.focus - focalLength ) );
|
||||
|
||||
// the farthest point from the camera which is in focus (unused)
|
||||
this.farPoint = ( this.hyperFocal * this.focus ) / ( this.hyperFocal - ( this.focus - focalLength ) );
|
||||
|
||||
// the gap or width of the space in which is everything is in focus (unused)
|
||||
this.depthOfField = this.farPoint - this.nearPoint;
|
||||
|
||||
// Considering minimum distance of focus for a standard lens (unused)
|
||||
if ( this.depthOfField < 0 ) this.depthOfField = 0;
|
||||
|
||||
this.sdistance = this.smoothstep( this.near, this.far, this.focus );
|
||||
|
||||
this.ldistance = this.linearize( 1 - this.sdistance );
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'focalDepth' ].value = this.ldistance;
|
||||
|
||||
}
|
||||
|
||||
initPostProcessing() {
|
||||
|
||||
if ( this.postprocessing.enabled ) {
|
||||
|
||||
this.postprocessing.scene = new Scene();
|
||||
|
||||
this.postprocessing.camera = new OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, - 10000, 10000 );
|
||||
|
||||
this.postprocessing.scene.add( this.postprocessing.camera );
|
||||
|
||||
this.postprocessing.rtTextureDepth = new WebGLRenderTarget( window.innerWidth, window.innerHeight );
|
||||
this.postprocessing.rtTextureColor = new WebGLRenderTarget( window.innerWidth, window.innerHeight );
|
||||
|
||||
const bokeh_shader = BokehShader;
|
||||
|
||||
this.postprocessing.bokeh_uniforms = UniformsUtils.clone( bokeh_shader.uniforms );
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'tColor' ].value = this.postprocessing.rtTextureColor.texture;
|
||||
this.postprocessing.bokeh_uniforms[ 'tDepth' ].value = this.postprocessing.rtTextureDepth.texture;
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'manualdof' ].value = 0;
|
||||
this.postprocessing.bokeh_uniforms[ 'shaderFocus' ].value = 0;
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'fstop' ].value = 2.8;
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'showFocus' ].value = 1;
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'focalDepth' ].value = 0.1;
|
||||
|
||||
//console.log( this.postprocessing.bokeh_uniforms[ "focalDepth" ].value );
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'znear' ].value = this.near;
|
||||
this.postprocessing.bokeh_uniforms[ 'zfar' ].value = this.near;
|
||||
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'textureWidth' ].value = window.innerWidth;
|
||||
|
||||
this.postprocessing.bokeh_uniforms[ 'textureHeight' ].value = window.innerHeight;
|
||||
|
||||
this.postprocessing.materialBokeh = new ShaderMaterial( {
|
||||
uniforms: this.postprocessing.bokeh_uniforms,
|
||||
vertexShader: bokeh_shader.vertexShader,
|
||||
fragmentShader: bokeh_shader.fragmentShader,
|
||||
defines: {
|
||||
RINGS: this.shaderSettings.rings,
|
||||
SAMPLES: this.shaderSettings.samples,
|
||||
DEPTH_PACKING: 1
|
||||
}
|
||||
} );
|
||||
|
||||
this.postprocessing.quad = new Mesh( new PlaneGeometry( window.innerWidth, window.innerHeight ), this.postprocessing.materialBokeh );
|
||||
this.postprocessing.quad.position.z = - 500;
|
||||
this.postprocessing.scene.add( this.postprocessing.quad );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderCinematic( scene, renderer ) {
|
||||
|
||||
if ( this.postprocessing.enabled ) {
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
|
||||
renderer.clear();
|
||||
|
||||
// Render scene into texture
|
||||
|
||||
scene.overrideMaterial = null;
|
||||
renderer.setRenderTarget( this.postprocessing.rtTextureColor );
|
||||
renderer.clear();
|
||||
renderer.render( scene, this );
|
||||
|
||||
// Render depth into texture
|
||||
|
||||
scene.overrideMaterial = this.materialDepth;
|
||||
renderer.setRenderTarget( this.postprocessing.rtTextureDepth );
|
||||
renderer.clear();
|
||||
renderer.render( scene, this );
|
||||
|
||||
// Render bokeh composite
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
renderer.render( this.postprocessing.scene, this.postprocessing.camera );
|
||||
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CinematicCamera };
|
||||
108
dist/electron/static/sdk/three/jsm/capabilities/WebGL.js
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
class WebGL {
|
||||
|
||||
static isWebGLAvailable() {
|
||||
|
||||
try {
|
||||
|
||||
const canvas = document.createElement( 'canvas' );
|
||||
return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
|
||||
|
||||
} catch ( e ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static isWebGL2Available() {
|
||||
|
||||
try {
|
||||
|
||||
const canvas = document.createElement( 'canvas' );
|
||||
return !! ( window.WebGL2RenderingContext && canvas.getContext( 'webgl2' ) );
|
||||
|
||||
} catch ( e ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static isColorSpaceAvailable( colorSpace ) {
|
||||
|
||||
try {
|
||||
|
||||
const canvas = document.createElement( 'canvas' );
|
||||
const ctx = window.WebGL2RenderingContext && canvas.getContext( 'webgl2' );
|
||||
ctx.drawingBufferColorSpace = colorSpace;
|
||||
return ctx.drawingBufferColorSpace === colorSpace; // deepscan-disable-line SAME_OPERAND_VALUE
|
||||
|
||||
} catch ( e ) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static getWebGLErrorMessage() {
|
||||
|
||||
return this.getErrorMessage( 1 );
|
||||
|
||||
}
|
||||
|
||||
static getWebGL2ErrorMessage() {
|
||||
|
||||
return this.getErrorMessage( 2 );
|
||||
|
||||
}
|
||||
|
||||
static getErrorMessage( version ) {
|
||||
|
||||
const names = {
|
||||
1: 'WebGL',
|
||||
2: 'WebGL 2'
|
||||
};
|
||||
|
||||
const contexts = {
|
||||
1: window.WebGLRenderingContext,
|
||||
2: window.WebGL2RenderingContext
|
||||
};
|
||||
|
||||
let message = 'Your $0 does not seem to support <a href="http://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation" style="color:#000">$1</a>';
|
||||
|
||||
const element = document.createElement( 'div' );
|
||||
element.id = 'webglmessage';
|
||||
element.style.fontFamily = 'monospace';
|
||||
element.style.fontSize = '13px';
|
||||
element.style.fontWeight = 'normal';
|
||||
element.style.textAlign = 'center';
|
||||
element.style.background = '#fff';
|
||||
element.style.color = '#000';
|
||||
element.style.padding = '1.5em';
|
||||
element.style.width = '400px';
|
||||
element.style.margin = '5em auto 0';
|
||||
|
||||
if ( contexts[ version ] ) {
|
||||
|
||||
message = message.replace( '$0', 'graphics card' );
|
||||
|
||||
} else {
|
||||
|
||||
message = message.replace( '$0', 'browser' );
|
||||
|
||||
}
|
||||
|
||||
message = message.replace( '$1', names[ version ] );
|
||||
|
||||
element.innerHTML = message;
|
||||
|
||||
return element;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default WebGL;
|
||||
57
dist/electron/static/sdk/three/jsm/capabilities/WebGPU.js
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
if ( self.GPUShaderStage === undefined ) {
|
||||
|
||||
self.GPUShaderStage = { VERTEX: 1, FRAGMENT: 2, COMPUTE: 4 };
|
||||
|
||||
}
|
||||
|
||||
// statics
|
||||
|
||||
let isAvailable = navigator.gpu !== undefined;
|
||||
|
||||
|
||||
if ( typeof window !== 'undefined' && isAvailable ) {
|
||||
|
||||
isAvailable = await navigator.gpu.requestAdapter();
|
||||
|
||||
}
|
||||
|
||||
class WebGPU {
|
||||
|
||||
static isAvailable() {
|
||||
|
||||
return Boolean( isAvailable );
|
||||
|
||||
}
|
||||
|
||||
static getStaticAdapter() {
|
||||
|
||||
return isAvailable;
|
||||
|
||||
}
|
||||
|
||||
static getErrorMessage() {
|
||||
|
||||
const message = 'Your browser does not support <a href="https://gpuweb.github.io/gpuweb/" style="color:blue">WebGPU</a> yet';
|
||||
|
||||
const element = document.createElement( 'div' );
|
||||
element.id = 'webgpumessage';
|
||||
element.style.fontFamily = 'monospace';
|
||||
element.style.fontSize = '13px';
|
||||
element.style.fontWeight = 'normal';
|
||||
element.style.textAlign = 'center';
|
||||
element.style.background = '#fff';
|
||||
element.style.color = '#000';
|
||||
element.style.padding = '1.5em';
|
||||
element.style.maxWidth = '400px';
|
||||
element.style.margin = '5em auto 0';
|
||||
|
||||
element.innerHTML = message;
|
||||
|
||||
return element;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default WebGPU;
|
||||
3224
dist/electron/static/sdk/three/jsm/controls/ArcballControls.js
vendored
Normal file
282
dist/electron/static/sdk/three/jsm/controls/DragControls.js
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
import {
|
||||
EventDispatcher,
|
||||
Matrix4,
|
||||
Plane,
|
||||
Raycaster,
|
||||
Vector2,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _plane = new Plane();
|
||||
const _raycaster = new Raycaster();
|
||||
|
||||
const _pointer = new Vector2();
|
||||
const _offset = new Vector3();
|
||||
const _diff = new Vector2();
|
||||
const _previousPointer = new Vector2();
|
||||
const _intersection = new Vector3();
|
||||
const _worldPosition = new Vector3();
|
||||
const _inverseMatrix = new Matrix4();
|
||||
|
||||
const _up = new Vector3();
|
||||
const _right = new Vector3();
|
||||
|
||||
class DragControls extends EventDispatcher {
|
||||
|
||||
constructor( _objects, _camera, _domElement ) {
|
||||
|
||||
super();
|
||||
|
||||
_domElement.style.touchAction = 'none'; // disable touch scroll
|
||||
|
||||
let _selected = null, _hovered = null;
|
||||
|
||||
const _intersections = [];
|
||||
|
||||
this.mode = 'translate';
|
||||
|
||||
this.rotateSpeed = 1;
|
||||
|
||||
//
|
||||
|
||||
const scope = this;
|
||||
|
||||
function activate() {
|
||||
|
||||
_domElement.addEventListener( 'pointermove', onPointerMove );
|
||||
_domElement.addEventListener( 'pointerdown', onPointerDown );
|
||||
_domElement.addEventListener( 'pointerup', onPointerCancel );
|
||||
_domElement.addEventListener( 'pointerleave', onPointerCancel );
|
||||
|
||||
}
|
||||
|
||||
function deactivate() {
|
||||
|
||||
_domElement.removeEventListener( 'pointermove', onPointerMove );
|
||||
_domElement.removeEventListener( 'pointerdown', onPointerDown );
|
||||
_domElement.removeEventListener( 'pointerup', onPointerCancel );
|
||||
_domElement.removeEventListener( 'pointerleave', onPointerCancel );
|
||||
|
||||
_domElement.style.cursor = '';
|
||||
|
||||
}
|
||||
|
||||
function dispose() {
|
||||
|
||||
deactivate();
|
||||
|
||||
}
|
||||
|
||||
function getObjects() {
|
||||
|
||||
return _objects;
|
||||
|
||||
}
|
||||
|
||||
function setObjects( objects ) {
|
||||
|
||||
_objects = objects;
|
||||
|
||||
}
|
||||
|
||||
function getRaycaster() {
|
||||
|
||||
return _raycaster;
|
||||
|
||||
}
|
||||
|
||||
function onPointerMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
updatePointer( event );
|
||||
|
||||
_raycaster.setFromCamera( _pointer, _camera );
|
||||
|
||||
if ( _selected ) {
|
||||
|
||||
if ( scope.mode === 'translate' ) {
|
||||
|
||||
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
|
||||
|
||||
_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) );
|
||||
|
||||
}
|
||||
|
||||
} else if ( scope.mode === 'rotate' ) {
|
||||
|
||||
_diff.subVectors( _pointer, _previousPointer ).multiplyScalar( scope.rotateSpeed );
|
||||
_selected.rotateOnWorldAxis( _up, _diff.x );
|
||||
_selected.rotateOnWorldAxis( _right.normalize(), - _diff.y );
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( { type: 'drag', object: _selected } );
|
||||
|
||||
_previousPointer.copy( _pointer );
|
||||
|
||||
} else {
|
||||
|
||||
// hover support
|
||||
|
||||
if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) {
|
||||
|
||||
_intersections.length = 0;
|
||||
|
||||
_raycaster.setFromCamera( _pointer, _camera );
|
||||
_raycaster.intersectObjects( _objects, scope.recursive, _intersections );
|
||||
|
||||
if ( _intersections.length > 0 ) {
|
||||
|
||||
const object = _intersections[ 0 ].object;
|
||||
|
||||
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) );
|
||||
|
||||
if ( _hovered !== object && _hovered !== null ) {
|
||||
|
||||
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
|
||||
|
||||
_domElement.style.cursor = 'auto';
|
||||
_hovered = null;
|
||||
|
||||
}
|
||||
|
||||
if ( _hovered !== object ) {
|
||||
|
||||
scope.dispatchEvent( { type: 'hoveron', object: object } );
|
||||
|
||||
_domElement.style.cursor = 'pointer';
|
||||
_hovered = object;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if ( _hovered !== null ) {
|
||||
|
||||
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } );
|
||||
|
||||
_domElement.style.cursor = 'auto';
|
||||
_hovered = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_previousPointer.copy( _pointer );
|
||||
|
||||
}
|
||||
|
||||
function onPointerDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
updatePointer( event );
|
||||
|
||||
_intersections.length = 0;
|
||||
|
||||
_raycaster.setFromCamera( _pointer, _camera );
|
||||
_raycaster.intersectObjects( _objects, scope.recursive, _intersections );
|
||||
|
||||
if ( _intersections.length > 0 ) {
|
||||
|
||||
if ( scope.transformGroup === true ) {
|
||||
|
||||
// look for the outermost group in the object's upper hierarchy
|
||||
|
||||
_selected = findGroup( _intersections[ 0 ].object );
|
||||
|
||||
} else {
|
||||
|
||||
_selected = _intersections[ 0 ].object;
|
||||
|
||||
}
|
||||
|
||||
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
|
||||
|
||||
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) {
|
||||
|
||||
if ( scope.mode === 'translate' ) {
|
||||
|
||||
_inverseMatrix.copy( _selected.parent.matrixWorld ).invert();
|
||||
_offset.copy( _intersection ).sub( _worldPosition.setFromMatrixPosition( _selected.matrixWorld ) );
|
||||
|
||||
} else if ( scope.mode === 'rotate' ) {
|
||||
|
||||
// the controls only support Y+ up
|
||||
_up.set( 0, 1, 0 ).applyQuaternion( _camera.quaternion ).normalize();
|
||||
_right.set( 1, 0, 0 ).applyQuaternion( _camera.quaternion ).normalize();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_domElement.style.cursor = 'move';
|
||||
|
||||
scope.dispatchEvent( { type: 'dragstart', object: _selected } );
|
||||
|
||||
}
|
||||
|
||||
_previousPointer.copy( _pointer );
|
||||
|
||||
}
|
||||
|
||||
function onPointerCancel() {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
if ( _selected ) {
|
||||
|
||||
scope.dispatchEvent( { type: 'dragend', object: _selected } );
|
||||
|
||||
_selected = null;
|
||||
|
||||
}
|
||||
|
||||
_domElement.style.cursor = _hovered ? 'pointer' : 'auto';
|
||||
|
||||
}
|
||||
|
||||
function updatePointer( event ) {
|
||||
|
||||
const rect = _domElement.getBoundingClientRect();
|
||||
|
||||
_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
|
||||
_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
|
||||
|
||||
}
|
||||
|
||||
function findGroup( obj, group = null ) {
|
||||
|
||||
if ( obj.isGroup ) group = obj;
|
||||
|
||||
if ( obj.parent === null ) return group;
|
||||
|
||||
return findGroup( obj.parent, group );
|
||||
|
||||
}
|
||||
|
||||
activate();
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
this.recursive = true;
|
||||
this.transformGroup = false;
|
||||
|
||||
this.activate = activate;
|
||||
this.deactivate = deactivate;
|
||||
this.dispose = dispose;
|
||||
this.getObjects = getObjects;
|
||||
this.getRaycaster = getRaycaster;
|
||||
this.setObjects = setObjects;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { DragControls };
|
||||
325
dist/electron/static/sdk/three/jsm/controls/FirstPersonControls.js
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
import {
|
||||
MathUtils,
|
||||
Spherical,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _lookDirection = new Vector3();
|
||||
const _spherical = new Spherical();
|
||||
const _target = new Vector3();
|
||||
|
||||
class FirstPersonControls {
|
||||
|
||||
constructor( object, domElement ) {
|
||||
|
||||
this.object = object;
|
||||
this.domElement = domElement;
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.movementSpeed = 1.0;
|
||||
this.lookSpeed = 0.005;
|
||||
|
||||
this.lookVertical = true;
|
||||
this.autoForward = false;
|
||||
|
||||
this.activeLook = true;
|
||||
|
||||
this.heightSpeed = false;
|
||||
this.heightCoef = 1.0;
|
||||
this.heightMin = 0.0;
|
||||
this.heightMax = 1.0;
|
||||
|
||||
this.constrainVertical = false;
|
||||
this.verticalMin = 0;
|
||||
this.verticalMax = Math.PI;
|
||||
|
||||
this.mouseDragOn = false;
|
||||
|
||||
// internals
|
||||
|
||||
this.autoSpeedFactor = 0.0;
|
||||
|
||||
this.pointerX = 0;
|
||||
this.pointerY = 0;
|
||||
|
||||
this.moveForward = false;
|
||||
this.moveBackward = false;
|
||||
this.moveLeft = false;
|
||||
this.moveRight = false;
|
||||
|
||||
this.viewHalfX = 0;
|
||||
this.viewHalfY = 0;
|
||||
|
||||
// private variables
|
||||
|
||||
let lat = 0;
|
||||
let lon = 0;
|
||||
|
||||
//
|
||||
|
||||
this.handleResize = function () {
|
||||
|
||||
if ( this.domElement === document ) {
|
||||
|
||||
this.viewHalfX = window.innerWidth / 2;
|
||||
this.viewHalfY = window.innerHeight / 2;
|
||||
|
||||
} else {
|
||||
|
||||
this.viewHalfX = this.domElement.offsetWidth / 2;
|
||||
this.viewHalfY = this.domElement.offsetHeight / 2;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.onPointerDown = function ( event ) {
|
||||
|
||||
if ( this.domElement !== document ) {
|
||||
|
||||
this.domElement.focus();
|
||||
|
||||
}
|
||||
|
||||
if ( this.activeLook ) {
|
||||
|
||||
switch ( event.button ) {
|
||||
|
||||
case 0: this.moveForward = true; break;
|
||||
case 2: this.moveBackward = true; break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.mouseDragOn = true;
|
||||
|
||||
};
|
||||
|
||||
this.onPointerUp = function ( event ) {
|
||||
|
||||
if ( this.activeLook ) {
|
||||
|
||||
switch ( event.button ) {
|
||||
|
||||
case 0: this.moveForward = false; break;
|
||||
case 2: this.moveBackward = false; break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.mouseDragOn = false;
|
||||
|
||||
};
|
||||
|
||||
this.onPointerMove = function ( event ) {
|
||||
|
||||
if ( this.domElement === document ) {
|
||||
|
||||
this.pointerX = event.pageX - this.viewHalfX;
|
||||
this.pointerY = event.pageY - this.viewHalfY;
|
||||
|
||||
} else {
|
||||
|
||||
this.pointerX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
|
||||
this.pointerY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.onKeyDown = function ( event ) {
|
||||
|
||||
switch ( event.code ) {
|
||||
|
||||
case 'ArrowUp':
|
||||
case 'KeyW': this.moveForward = true; break;
|
||||
|
||||
case 'ArrowLeft':
|
||||
case 'KeyA': this.moveLeft = true; break;
|
||||
|
||||
case 'ArrowDown':
|
||||
case 'KeyS': this.moveBackward = true; break;
|
||||
|
||||
case 'ArrowRight':
|
||||
case 'KeyD': this.moveRight = true; break;
|
||||
|
||||
case 'KeyR': this.moveUp = true; break;
|
||||
case 'KeyF': this.moveDown = true; break;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.onKeyUp = function ( event ) {
|
||||
|
||||
switch ( event.code ) {
|
||||
|
||||
case 'ArrowUp':
|
||||
case 'KeyW': this.moveForward = false; break;
|
||||
|
||||
case 'ArrowLeft':
|
||||
case 'KeyA': this.moveLeft = false; break;
|
||||
|
||||
case 'ArrowDown':
|
||||
case 'KeyS': this.moveBackward = false; break;
|
||||
|
||||
case 'ArrowRight':
|
||||
case 'KeyD': this.moveRight = false; break;
|
||||
|
||||
case 'KeyR': this.moveUp = false; break;
|
||||
case 'KeyF': this.moveDown = false; break;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.lookAt = function ( x, y, z ) {
|
||||
|
||||
if ( x.isVector3 ) {
|
||||
|
||||
_target.copy( x );
|
||||
|
||||
} else {
|
||||
|
||||
_target.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
this.object.lookAt( _target );
|
||||
|
||||
setOrientation( this );
|
||||
|
||||
return this;
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
const targetPosition = new Vector3();
|
||||
|
||||
return function update( delta ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
if ( this.heightSpeed ) {
|
||||
|
||||
const y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
|
||||
const heightDelta = y - this.heightMin;
|
||||
|
||||
this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
|
||||
|
||||
} else {
|
||||
|
||||
this.autoSpeedFactor = 0.0;
|
||||
|
||||
}
|
||||
|
||||
const actualMoveSpeed = delta * this.movementSpeed;
|
||||
|
||||
if ( this.moveForward || ( this.autoForward && ! this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
|
||||
if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
|
||||
|
||||
if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
|
||||
if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
|
||||
|
||||
if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
|
||||
if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
|
||||
|
||||
let actualLookSpeed = delta * this.lookSpeed;
|
||||
|
||||
if ( ! this.activeLook ) {
|
||||
|
||||
actualLookSpeed = 0;
|
||||
|
||||
}
|
||||
|
||||
let verticalLookRatio = 1;
|
||||
|
||||
if ( this.constrainVertical ) {
|
||||
|
||||
verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
|
||||
|
||||
}
|
||||
|
||||
lon -= this.pointerX * actualLookSpeed;
|
||||
if ( this.lookVertical ) lat -= this.pointerY * actualLookSpeed * verticalLookRatio;
|
||||
|
||||
lat = Math.max( - 85, Math.min( 85, lat ) );
|
||||
|
||||
let phi = MathUtils.degToRad( 90 - lat );
|
||||
const theta = MathUtils.degToRad( lon );
|
||||
|
||||
if ( this.constrainVertical ) {
|
||||
|
||||
phi = MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
|
||||
|
||||
}
|
||||
|
||||
const position = this.object.position;
|
||||
|
||||
targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
|
||||
|
||||
this.object.lookAt( targetPosition );
|
||||
|
||||
};
|
||||
|
||||
}();
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
this.domElement.removeEventListener( 'contextmenu', contextmenu );
|
||||
this.domElement.removeEventListener( 'pointerdown', _onPointerDown );
|
||||
this.domElement.removeEventListener( 'pointermove', _onPointerMove );
|
||||
this.domElement.removeEventListener( 'pointerup', _onPointerUp );
|
||||
|
||||
window.removeEventListener( 'keydown', _onKeyDown );
|
||||
window.removeEventListener( 'keyup', _onKeyUp );
|
||||
|
||||
};
|
||||
|
||||
const _onPointerMove = this.onPointerMove.bind( this );
|
||||
const _onPointerDown = this.onPointerDown.bind( this );
|
||||
const _onPointerUp = this.onPointerUp.bind( this );
|
||||
const _onKeyDown = this.onKeyDown.bind( this );
|
||||
const _onKeyUp = this.onKeyUp.bind( this );
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', contextmenu );
|
||||
this.domElement.addEventListener( 'pointerdown', _onPointerDown );
|
||||
this.domElement.addEventListener( 'pointermove', _onPointerMove );
|
||||
this.domElement.addEventListener( 'pointerup', _onPointerUp );
|
||||
|
||||
window.addEventListener( 'keydown', _onKeyDown );
|
||||
window.addEventListener( 'keyup', _onKeyUp );
|
||||
|
||||
function setOrientation( controls ) {
|
||||
|
||||
const quaternion = controls.object.quaternion;
|
||||
|
||||
_lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
|
||||
_spherical.setFromVector3( _lookDirection );
|
||||
|
||||
lat = 90 - MathUtils.radToDeg( _spherical.phi );
|
||||
lon = MathUtils.radToDeg( _spherical.theta );
|
||||
|
||||
}
|
||||
|
||||
this.handleResize();
|
||||
|
||||
setOrientation( this );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function contextmenu( event ) {
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
}
|
||||
|
||||
export { FirstPersonControls };
|
||||
326
dist/electron/static/sdk/three/jsm/controls/FlyControls.js
vendored
Normal file
@ -0,0 +1,326 @@
|
||||
import {
|
||||
EventDispatcher,
|
||||
Quaternion,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _changeEvent = { type: 'change' };
|
||||
|
||||
class FlyControls extends EventDispatcher {
|
||||
|
||||
constructor( object, domElement ) {
|
||||
|
||||
super();
|
||||
|
||||
this.object = object;
|
||||
this.domElement = domElement;
|
||||
|
||||
// API
|
||||
|
||||
// Set to false to disable this control
|
||||
this.enabled = true;
|
||||
|
||||
this.movementSpeed = 1.0;
|
||||
this.rollSpeed = 0.005;
|
||||
|
||||
this.dragToLook = false;
|
||||
this.autoForward = false;
|
||||
|
||||
// disable default target object behavior
|
||||
|
||||
// internals
|
||||
|
||||
const scope = this;
|
||||
|
||||
const EPS = 0.000001;
|
||||
|
||||
const lastQuaternion = new Quaternion();
|
||||
const lastPosition = new Vector3();
|
||||
|
||||
this.tmpQuaternion = new Quaternion();
|
||||
|
||||
this.status = 0;
|
||||
|
||||
this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 };
|
||||
this.moveVector = new Vector3( 0, 0, 0 );
|
||||
this.rotationVector = new Vector3( 0, 0, 0 );
|
||||
|
||||
this.keydown = function ( event ) {
|
||||
|
||||
if ( event.altKey || this.enabled === false ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
switch ( event.code ) {
|
||||
|
||||
case 'ShiftLeft':
|
||||
case 'ShiftRight': this.movementSpeedMultiplier = .1; break;
|
||||
|
||||
case 'KeyW': this.moveState.forward = 1; break;
|
||||
case 'KeyS': this.moveState.back = 1; break;
|
||||
|
||||
case 'KeyA': this.moveState.left = 1; break;
|
||||
case 'KeyD': this.moveState.right = 1; break;
|
||||
|
||||
case 'KeyR': this.moveState.up = 1; break;
|
||||
case 'KeyF': this.moveState.down = 1; break;
|
||||
|
||||
case 'ArrowUp': this.moveState.pitchUp = 1; break;
|
||||
case 'ArrowDown': this.moveState.pitchDown = 1; break;
|
||||
|
||||
case 'ArrowLeft': this.moveState.yawLeft = 1; break;
|
||||
case 'ArrowRight': this.moveState.yawRight = 1; break;
|
||||
|
||||
case 'KeyQ': this.moveState.rollLeft = 1; break;
|
||||
case 'KeyE': this.moveState.rollRight = 1; break;
|
||||
|
||||
}
|
||||
|
||||
this.updateMovementVector();
|
||||
this.updateRotationVector();
|
||||
|
||||
};
|
||||
|
||||
this.keyup = function ( event ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
switch ( event.code ) {
|
||||
|
||||
case 'ShiftLeft':
|
||||
case 'ShiftRight': this.movementSpeedMultiplier = 1; break;
|
||||
|
||||
case 'KeyW': this.moveState.forward = 0; break;
|
||||
case 'KeyS': this.moveState.back = 0; break;
|
||||
|
||||
case 'KeyA': this.moveState.left = 0; break;
|
||||
case 'KeyD': this.moveState.right = 0; break;
|
||||
|
||||
case 'KeyR': this.moveState.up = 0; break;
|
||||
case 'KeyF': this.moveState.down = 0; break;
|
||||
|
||||
case 'ArrowUp': this.moveState.pitchUp = 0; break;
|
||||
case 'ArrowDown': this.moveState.pitchDown = 0; break;
|
||||
|
||||
case 'ArrowLeft': this.moveState.yawLeft = 0; break;
|
||||
case 'ArrowRight': this.moveState.yawRight = 0; break;
|
||||
|
||||
case 'KeyQ': this.moveState.rollLeft = 0; break;
|
||||
case 'KeyE': this.moveState.rollRight = 0; break;
|
||||
|
||||
}
|
||||
|
||||
this.updateMovementVector();
|
||||
this.updateRotationVector();
|
||||
|
||||
};
|
||||
|
||||
this.pointerdown = function ( event ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
if ( this.dragToLook ) {
|
||||
|
||||
this.status ++;
|
||||
|
||||
} else {
|
||||
|
||||
switch ( event.button ) {
|
||||
|
||||
case 0: this.moveState.forward = 1; break;
|
||||
case 2: this.moveState.back = 1; break;
|
||||
|
||||
}
|
||||
|
||||
this.updateMovementVector();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.pointermove = function ( event ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
if ( ! this.dragToLook || this.status > 0 ) {
|
||||
|
||||
const container = this.getContainerDimensions();
|
||||
const halfWidth = container.size[ 0 ] / 2;
|
||||
const halfHeight = container.size[ 1 ] / 2;
|
||||
|
||||
this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth;
|
||||
this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight;
|
||||
|
||||
this.updateRotationVector();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.pointerup = function ( event ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
if ( this.dragToLook ) {
|
||||
|
||||
this.status --;
|
||||
|
||||
this.moveState.yawLeft = this.moveState.pitchDown = 0;
|
||||
|
||||
} else {
|
||||
|
||||
switch ( event.button ) {
|
||||
|
||||
case 0: this.moveState.forward = 0; break;
|
||||
case 2: this.moveState.back = 0; break;
|
||||
|
||||
}
|
||||
|
||||
this.updateMovementVector();
|
||||
|
||||
}
|
||||
|
||||
this.updateRotationVector();
|
||||
|
||||
};
|
||||
|
||||
this.pointercancel = function () {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
if ( this.dragToLook ) {
|
||||
|
||||
this.status = 0;
|
||||
|
||||
this.moveState.yawLeft = this.moveState.pitchDown = 0;
|
||||
|
||||
} else {
|
||||
|
||||
this.moveState.forward = 0;
|
||||
this.moveState.back = 0;
|
||||
|
||||
this.updateMovementVector();
|
||||
|
||||
}
|
||||
|
||||
this.updateRotationVector();
|
||||
|
||||
};
|
||||
|
||||
this.contextMenu = function ( event ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
};
|
||||
|
||||
this.update = function ( delta ) {
|
||||
|
||||
if ( this.enabled === false ) return;
|
||||
|
||||
const moveMult = delta * scope.movementSpeed;
|
||||
const rotMult = delta * scope.rollSpeed;
|
||||
|
||||
scope.object.translateX( scope.moveVector.x * moveMult );
|
||||
scope.object.translateY( scope.moveVector.y * moveMult );
|
||||
scope.object.translateZ( scope.moveVector.z * moveMult );
|
||||
|
||||
scope.tmpQuaternion.set( scope.rotationVector.x * rotMult, scope.rotationVector.y * rotMult, scope.rotationVector.z * rotMult, 1 ).normalize();
|
||||
scope.object.quaternion.multiply( scope.tmpQuaternion );
|
||||
|
||||
if (
|
||||
lastPosition.distanceToSquared( scope.object.position ) > EPS ||
|
||||
8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS
|
||||
) {
|
||||
|
||||
scope.dispatchEvent( _changeEvent );
|
||||
lastQuaternion.copy( scope.object.quaternion );
|
||||
lastPosition.copy( scope.object.position );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.updateMovementVector = function () {
|
||||
|
||||
const forward = ( this.moveState.forward || ( this.autoForward && ! this.moveState.back ) ) ? 1 : 0;
|
||||
|
||||
this.moveVector.x = ( - this.moveState.left + this.moveState.right );
|
||||
this.moveVector.y = ( - this.moveState.down + this.moveState.up );
|
||||
this.moveVector.z = ( - forward + this.moveState.back );
|
||||
|
||||
//console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] );
|
||||
|
||||
};
|
||||
|
||||
this.updateRotationVector = function () {
|
||||
|
||||
this.rotationVector.x = ( - this.moveState.pitchDown + this.moveState.pitchUp );
|
||||
this.rotationVector.y = ( - this.moveState.yawRight + this.moveState.yawLeft );
|
||||
this.rotationVector.z = ( - this.moveState.rollRight + this.moveState.rollLeft );
|
||||
|
||||
//console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] );
|
||||
|
||||
};
|
||||
|
||||
this.getContainerDimensions = function () {
|
||||
|
||||
if ( this.domElement != document ) {
|
||||
|
||||
return {
|
||||
size: [ this.domElement.offsetWidth, this.domElement.offsetHeight ],
|
||||
offset: [ this.domElement.offsetLeft, this.domElement.offsetTop ]
|
||||
};
|
||||
|
||||
} else {
|
||||
|
||||
return {
|
||||
size: [ window.innerWidth, window.innerHeight ],
|
||||
offset: [ 0, 0 ]
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
this.domElement.removeEventListener( 'contextmenu', _contextmenu );
|
||||
this.domElement.removeEventListener( 'pointerdown', _pointerdown );
|
||||
this.domElement.removeEventListener( 'pointermove', _pointermove );
|
||||
this.domElement.removeEventListener( 'pointerup', _pointerup );
|
||||
this.domElement.removeEventListener( 'pointercancel', _pointercancel );
|
||||
|
||||
window.removeEventListener( 'keydown', _keydown );
|
||||
window.removeEventListener( 'keyup', _keyup );
|
||||
|
||||
};
|
||||
|
||||
const _contextmenu = this.contextMenu.bind( this );
|
||||
const _pointermove = this.pointermove.bind( this );
|
||||
const _pointerdown = this.pointerdown.bind( this );
|
||||
const _pointerup = this.pointerup.bind( this );
|
||||
const _pointercancel = this.pointercancel.bind( this );
|
||||
const _keydown = this.keydown.bind( this );
|
||||
const _keyup = this.keyup.bind( this );
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', _contextmenu );
|
||||
this.domElement.addEventListener( 'pointerdown', _pointerdown );
|
||||
this.domElement.addEventListener( 'pointermove', _pointermove );
|
||||
this.domElement.addEventListener( 'pointerup', _pointerup );
|
||||
this.domElement.addEventListener( 'pointercancel', _pointercancel );
|
||||
|
||||
window.addEventListener( 'keydown', _keydown );
|
||||
window.addEventListener( 'keyup', _keyup );
|
||||
|
||||
this.updateMovementVector();
|
||||
this.updateRotationVector();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { FlyControls };
|
||||
28
dist/electron/static/sdk/three/jsm/controls/MapControls.js
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
import { MOUSE, TOUCH } from 'three';
|
||||
|
||||
import { OrbitControls } from './OrbitControls.js';
|
||||
|
||||
// MapControls performs orbiting, dollying (zooming), and panning.
|
||||
// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
|
||||
//
|
||||
// Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
|
||||
// Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
|
||||
// Pan - left mouse, or arrow keys / touch: one-finger move
|
||||
|
||||
class MapControls extends OrbitControls {
|
||||
|
||||
constructor( object, domElement ) {
|
||||
|
||||
super( object, domElement );
|
||||
|
||||
this.screenSpacePanning = false; // pan orthogonal to world-space direction camera.up
|
||||
|
||||
this.mouseButtons = { LEFT: MOUSE.PAN, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.ROTATE };
|
||||
|
||||
this.touches = { ONE: TOUCH.PAN, TWO: TOUCH.DOLLY_ROTATE };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { MapControls };
|
||||
1532
dist/electron/static/sdk/three/jsm/controls/OrbitControls.js
vendored
Normal file
162
dist/electron/static/sdk/three/jsm/controls/PointerLockControls.js
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
import {
|
||||
Euler,
|
||||
EventDispatcher,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _euler = new Euler( 0, 0, 0, 'YXZ' );
|
||||
const _vector = new Vector3();
|
||||
|
||||
const _changeEvent = { type: 'change' };
|
||||
const _lockEvent = { type: 'lock' };
|
||||
const _unlockEvent = { type: 'unlock' };
|
||||
|
||||
const _PI_2 = Math.PI / 2;
|
||||
|
||||
class PointerLockControls extends EventDispatcher {
|
||||
|
||||
constructor( camera, domElement ) {
|
||||
|
||||
super();
|
||||
|
||||
this.camera = camera;
|
||||
this.domElement = domElement;
|
||||
|
||||
this.isLocked = false;
|
||||
|
||||
// Set to constrain the pitch of the camera
|
||||
// Range is 0 to Math.PI radians
|
||||
this.minPolarAngle = 0; // radians
|
||||
this.maxPolarAngle = Math.PI; // radians
|
||||
|
||||
this.pointerSpeed = 1.0;
|
||||
|
||||
this._onMouseMove = onMouseMove.bind( this );
|
||||
this._onPointerlockChange = onPointerlockChange.bind( this );
|
||||
this._onPointerlockError = onPointerlockError.bind( this );
|
||||
|
||||
this.connect();
|
||||
|
||||
}
|
||||
|
||||
connect() {
|
||||
|
||||
this.domElement.ownerDocument.addEventListener( 'mousemove', this._onMouseMove );
|
||||
this.domElement.ownerDocument.addEventListener( 'pointerlockchange', this._onPointerlockChange );
|
||||
this.domElement.ownerDocument.addEventListener( 'pointerlockerror', this._onPointerlockError );
|
||||
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
|
||||
this.domElement.ownerDocument.removeEventListener( 'mousemove', this._onMouseMove );
|
||||
this.domElement.ownerDocument.removeEventListener( 'pointerlockchange', this._onPointerlockChange );
|
||||
this.domElement.ownerDocument.removeEventListener( 'pointerlockerror', this._onPointerlockError );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.disconnect();
|
||||
|
||||
}
|
||||
|
||||
getObject() { // retaining this method for backward compatibility
|
||||
|
||||
return this.camera;
|
||||
|
||||
}
|
||||
|
||||
getDirection( v ) {
|
||||
|
||||
return v.set( 0, 0, - 1 ).applyQuaternion( this.camera.quaternion );
|
||||
|
||||
}
|
||||
|
||||
moveForward( distance ) {
|
||||
|
||||
// move forward parallel to the xz-plane
|
||||
// assumes camera.up is y-up
|
||||
|
||||
const camera = this.camera;
|
||||
|
||||
_vector.setFromMatrixColumn( camera.matrix, 0 );
|
||||
|
||||
_vector.crossVectors( camera.up, _vector );
|
||||
|
||||
camera.position.addScaledVector( _vector, distance );
|
||||
|
||||
}
|
||||
|
||||
moveRight( distance ) {
|
||||
|
||||
const camera = this.camera;
|
||||
|
||||
_vector.setFromMatrixColumn( camera.matrix, 0 );
|
||||
|
||||
camera.position.addScaledVector( _vector, distance );
|
||||
|
||||
}
|
||||
|
||||
lock() {
|
||||
|
||||
this.domElement.requestPointerLock();
|
||||
|
||||
}
|
||||
|
||||
unlock() {
|
||||
|
||||
this.domElement.ownerDocument.exitPointerLock();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// event listeners
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
if ( this.isLocked === false ) return;
|
||||
|
||||
const movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
|
||||
const movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
|
||||
|
||||
const camera = this.camera;
|
||||
_euler.setFromQuaternion( camera.quaternion );
|
||||
|
||||
_euler.y -= movementX * 0.002 * this.pointerSpeed;
|
||||
_euler.x -= movementY * 0.002 * this.pointerSpeed;
|
||||
|
||||
_euler.x = Math.max( _PI_2 - this.maxPolarAngle, Math.min( _PI_2 - this.minPolarAngle, _euler.x ) );
|
||||
|
||||
camera.quaternion.setFromEuler( _euler );
|
||||
|
||||
this.dispatchEvent( _changeEvent );
|
||||
|
||||
}
|
||||
|
||||
function onPointerlockChange() {
|
||||
|
||||
if ( this.domElement.ownerDocument.pointerLockElement === this.domElement ) {
|
||||
|
||||
this.dispatchEvent( _lockEvent );
|
||||
|
||||
this.isLocked = true;
|
||||
|
||||
} else {
|
||||
|
||||
this.dispatchEvent( _unlockEvent );
|
||||
|
||||
this.isLocked = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onPointerlockError() {
|
||||
|
||||
console.error( 'THREE.PointerLockControls: Unable to use Pointer Lock API' );
|
||||
|
||||
}
|
||||
|
||||
export { PointerLockControls };
|
||||
828
dist/electron/static/sdk/three/jsm/controls/TrackballControls.js
vendored
Normal file
@ -0,0 +1,828 @@
|
||||
import {
|
||||
EventDispatcher,
|
||||
MathUtils,
|
||||
MOUSE,
|
||||
Quaternion,
|
||||
Vector2,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _changeEvent = { type: 'change' };
|
||||
const _startEvent = { type: 'start' };
|
||||
const _endEvent = { type: 'end' };
|
||||
|
||||
class TrackballControls extends EventDispatcher {
|
||||
|
||||
constructor( object, domElement ) {
|
||||
|
||||
super();
|
||||
|
||||
const scope = this;
|
||||
const STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
|
||||
|
||||
this.object = object;
|
||||
this.domElement = domElement;
|
||||
this.domElement.style.touchAction = 'none'; // disable touch scroll
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.screen = { left: 0, top: 0, width: 0, height: 0 };
|
||||
|
||||
this.rotateSpeed = 1.0;
|
||||
this.zoomSpeed = 1.2;
|
||||
this.panSpeed = 0.3;
|
||||
|
||||
this.noRotate = false;
|
||||
this.noZoom = false;
|
||||
this.noPan = false;
|
||||
|
||||
this.staticMoving = false;
|
||||
this.dynamicDampingFactor = 0.2;
|
||||
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
this.minZoom = 0;
|
||||
this.maxZoom = Infinity;
|
||||
|
||||
this.keys = [ 'KeyA' /*A*/, 'KeyS' /*S*/, 'KeyD' /*D*/ ];
|
||||
|
||||
this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
|
||||
|
||||
// internals
|
||||
|
||||
this.target = new Vector3();
|
||||
|
||||
const EPS = 0.000001;
|
||||
|
||||
const lastPosition = new Vector3();
|
||||
let lastZoom = 1;
|
||||
|
||||
let _state = STATE.NONE,
|
||||
_keyState = STATE.NONE,
|
||||
|
||||
_touchZoomDistanceStart = 0,
|
||||
_touchZoomDistanceEnd = 0,
|
||||
|
||||
_lastAngle = 0;
|
||||
|
||||
const _eye = new Vector3(),
|
||||
|
||||
_movePrev = new Vector2(),
|
||||
_moveCurr = new Vector2(),
|
||||
|
||||
_lastAxis = new Vector3(),
|
||||
|
||||
_zoomStart = new Vector2(),
|
||||
_zoomEnd = new Vector2(),
|
||||
|
||||
_panStart = new Vector2(),
|
||||
_panEnd = new Vector2(),
|
||||
|
||||
_pointers = [],
|
||||
_pointerPositions = {};
|
||||
|
||||
// for reset
|
||||
|
||||
this.target0 = this.target.clone();
|
||||
this.position0 = this.object.position.clone();
|
||||
this.up0 = this.object.up.clone();
|
||||
this.zoom0 = this.object.zoom;
|
||||
|
||||
// methods
|
||||
|
||||
this.handleResize = function () {
|
||||
|
||||
const box = scope.domElement.getBoundingClientRect();
|
||||
// adjustments come from similar code in the jquery offset() function
|
||||
const d = scope.domElement.ownerDocument.documentElement;
|
||||
scope.screen.left = box.left + window.pageXOffset - d.clientLeft;
|
||||
scope.screen.top = box.top + window.pageYOffset - d.clientTop;
|
||||
scope.screen.width = box.width;
|
||||
scope.screen.height = box.height;
|
||||
|
||||
};
|
||||
|
||||
const getMouseOnScreen = ( function () {
|
||||
|
||||
const vector = new Vector2();
|
||||
|
||||
return function getMouseOnScreen( pageX, pageY ) {
|
||||
|
||||
vector.set(
|
||||
( pageX - scope.screen.left ) / scope.screen.width,
|
||||
( pageY - scope.screen.top ) / scope.screen.height
|
||||
);
|
||||
|
||||
return vector;
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
const getMouseOnCircle = ( function () {
|
||||
|
||||
const vector = new Vector2();
|
||||
|
||||
return function getMouseOnCircle( pageX, pageY ) {
|
||||
|
||||
vector.set(
|
||||
( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ) ),
|
||||
( ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width ) // screen.width intentional
|
||||
);
|
||||
|
||||
return vector;
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
this.rotateCamera = ( function () {
|
||||
|
||||
const axis = new Vector3(),
|
||||
quaternion = new Quaternion(),
|
||||
eyeDirection = new Vector3(),
|
||||
objectUpDirection = new Vector3(),
|
||||
objectSidewaysDirection = new Vector3(),
|
||||
moveDirection = new Vector3();
|
||||
|
||||
return function rotateCamera() {
|
||||
|
||||
moveDirection.set( _moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0 );
|
||||
let angle = moveDirection.length();
|
||||
|
||||
if ( angle ) {
|
||||
|
||||
_eye.copy( scope.object.position ).sub( scope.target );
|
||||
|
||||
eyeDirection.copy( _eye ).normalize();
|
||||
objectUpDirection.copy( scope.object.up ).normalize();
|
||||
objectSidewaysDirection.crossVectors( objectUpDirection, eyeDirection ).normalize();
|
||||
|
||||
objectUpDirection.setLength( _moveCurr.y - _movePrev.y );
|
||||
objectSidewaysDirection.setLength( _moveCurr.x - _movePrev.x );
|
||||
|
||||
moveDirection.copy( objectUpDirection.add( objectSidewaysDirection ) );
|
||||
|
||||
axis.crossVectors( moveDirection, _eye ).normalize();
|
||||
|
||||
angle *= scope.rotateSpeed;
|
||||
quaternion.setFromAxisAngle( axis, angle );
|
||||
|
||||
_eye.applyQuaternion( quaternion );
|
||||
scope.object.up.applyQuaternion( quaternion );
|
||||
|
||||
_lastAxis.copy( axis );
|
||||
_lastAngle = angle;
|
||||
|
||||
} else if ( ! scope.staticMoving && _lastAngle ) {
|
||||
|
||||
_lastAngle *= Math.sqrt( 1.0 - scope.dynamicDampingFactor );
|
||||
_eye.copy( scope.object.position ).sub( scope.target );
|
||||
quaternion.setFromAxisAngle( _lastAxis, _lastAngle );
|
||||
_eye.applyQuaternion( quaternion );
|
||||
scope.object.up.applyQuaternion( quaternion );
|
||||
|
||||
}
|
||||
|
||||
_movePrev.copy( _moveCurr );
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
|
||||
this.zoomCamera = function () {
|
||||
|
||||
let factor;
|
||||
|
||||
if ( _state === STATE.TOUCH_ZOOM_PAN ) {
|
||||
|
||||
factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
|
||||
_touchZoomDistanceStart = _touchZoomDistanceEnd;
|
||||
|
||||
if ( scope.object.isPerspectiveCamera ) {
|
||||
|
||||
_eye.multiplyScalar( factor );
|
||||
|
||||
} else if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
scope.object.zoom = MathUtils.clamp( scope.object.zoom / factor, scope.minZoom, scope.maxZoom );
|
||||
|
||||
if ( lastZoom !== scope.object.zoom ) {
|
||||
|
||||
scope.object.updateProjectionMatrix();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * scope.zoomSpeed;
|
||||
|
||||
if ( factor !== 1.0 && factor > 0.0 ) {
|
||||
|
||||
if ( scope.object.isPerspectiveCamera ) {
|
||||
|
||||
_eye.multiplyScalar( factor );
|
||||
|
||||
} else if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
scope.object.zoom = MathUtils.clamp( scope.object.zoom / factor, scope.minZoom, scope.maxZoom );
|
||||
|
||||
if ( lastZoom !== scope.object.zoom ) {
|
||||
|
||||
scope.object.updateProjectionMatrix();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( scope.staticMoving ) {
|
||||
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
} else {
|
||||
|
||||
_zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.panCamera = ( function () {
|
||||
|
||||
const mouseChange = new Vector2(),
|
||||
objectUp = new Vector3(),
|
||||
pan = new Vector3();
|
||||
|
||||
return function panCamera() {
|
||||
|
||||
mouseChange.copy( _panEnd ).sub( _panStart );
|
||||
|
||||
if ( mouseChange.lengthSq() ) {
|
||||
|
||||
if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
const scale_x = ( scope.object.right - scope.object.left ) / scope.object.zoom / scope.domElement.clientWidth;
|
||||
const scale_y = ( scope.object.top - scope.object.bottom ) / scope.object.zoom / scope.domElement.clientWidth;
|
||||
|
||||
mouseChange.x *= scale_x;
|
||||
mouseChange.y *= scale_y;
|
||||
|
||||
}
|
||||
|
||||
mouseChange.multiplyScalar( _eye.length() * scope.panSpeed );
|
||||
|
||||
pan.copy( _eye ).cross( scope.object.up ).setLength( mouseChange.x );
|
||||
pan.add( objectUp.copy( scope.object.up ).setLength( mouseChange.y ) );
|
||||
|
||||
scope.object.position.add( pan );
|
||||
scope.target.add( pan );
|
||||
|
||||
if ( scope.staticMoving ) {
|
||||
|
||||
_panStart.copy( _panEnd );
|
||||
|
||||
} else {
|
||||
|
||||
_panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( scope.dynamicDampingFactor ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}() );
|
||||
|
||||
this.checkDistances = function () {
|
||||
|
||||
if ( ! scope.noZoom || ! scope.noPan ) {
|
||||
|
||||
if ( _eye.lengthSq() > scope.maxDistance * scope.maxDistance ) {
|
||||
|
||||
scope.object.position.addVectors( scope.target, _eye.setLength( scope.maxDistance ) );
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
}
|
||||
|
||||
if ( _eye.lengthSq() < scope.minDistance * scope.minDistance ) {
|
||||
|
||||
scope.object.position.addVectors( scope.target, _eye.setLength( scope.minDistance ) );
|
||||
_zoomStart.copy( _zoomEnd );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
_eye.subVectors( scope.object.position, scope.target );
|
||||
|
||||
if ( ! scope.noRotate ) {
|
||||
|
||||
scope.rotateCamera();
|
||||
|
||||
}
|
||||
|
||||
if ( ! scope.noZoom ) {
|
||||
|
||||
scope.zoomCamera();
|
||||
|
||||
}
|
||||
|
||||
if ( ! scope.noPan ) {
|
||||
|
||||
scope.panCamera();
|
||||
|
||||
}
|
||||
|
||||
scope.object.position.addVectors( scope.target, _eye );
|
||||
|
||||
if ( scope.object.isPerspectiveCamera ) {
|
||||
|
||||
scope.checkDistances();
|
||||
|
||||
scope.object.lookAt( scope.target );
|
||||
|
||||
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS ) {
|
||||
|
||||
scope.dispatchEvent( _changeEvent );
|
||||
|
||||
lastPosition.copy( scope.object.position );
|
||||
|
||||
}
|
||||
|
||||
} else if ( scope.object.isOrthographicCamera ) {
|
||||
|
||||
scope.object.lookAt( scope.target );
|
||||
|
||||
if ( lastPosition.distanceToSquared( scope.object.position ) > EPS || lastZoom !== scope.object.zoom ) {
|
||||
|
||||
scope.dispatchEvent( _changeEvent );
|
||||
|
||||
lastPosition.copy( scope.object.position );
|
||||
lastZoom = scope.object.zoom;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.TrackballControls: Unsupported camera type' );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.reset = function () {
|
||||
|
||||
_state = STATE.NONE;
|
||||
_keyState = STATE.NONE;
|
||||
|
||||
scope.target.copy( scope.target0 );
|
||||
scope.object.position.copy( scope.position0 );
|
||||
scope.object.up.copy( scope.up0 );
|
||||
scope.object.zoom = scope.zoom0;
|
||||
|
||||
scope.object.updateProjectionMatrix();
|
||||
|
||||
_eye.subVectors( scope.object.position, scope.target );
|
||||
|
||||
scope.object.lookAt( scope.target );
|
||||
|
||||
scope.dispatchEvent( _changeEvent );
|
||||
|
||||
lastPosition.copy( scope.object.position );
|
||||
lastZoom = scope.object.zoom;
|
||||
|
||||
};
|
||||
|
||||
// listeners
|
||||
|
||||
function onPointerDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
if ( _pointers.length === 0 ) {
|
||||
|
||||
scope.domElement.setPointerCapture( event.pointerId );
|
||||
|
||||
scope.domElement.addEventListener( 'pointermove', onPointerMove );
|
||||
scope.domElement.addEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
addPointer( event );
|
||||
|
||||
if ( event.pointerType === 'touch' ) {
|
||||
|
||||
onTouchStart( event );
|
||||
|
||||
} else {
|
||||
|
||||
onMouseDown( event );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onPointerMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
if ( event.pointerType === 'touch' ) {
|
||||
|
||||
onTouchMove( event );
|
||||
|
||||
} else {
|
||||
|
||||
onMouseMove( event );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onPointerUp( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
if ( event.pointerType === 'touch' ) {
|
||||
|
||||
onTouchEnd( event );
|
||||
|
||||
} else {
|
||||
|
||||
onMouseUp();
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
removePointer( event );
|
||||
|
||||
if ( _pointers.length === 0 ) {
|
||||
|
||||
scope.domElement.releasePointerCapture( event.pointerId );
|
||||
|
||||
scope.domElement.removeEventListener( 'pointermove', onPointerMove );
|
||||
scope.domElement.removeEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function onPointerCancel( event ) {
|
||||
|
||||
removePointer( event );
|
||||
|
||||
}
|
||||
|
||||
function keydown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
window.removeEventListener( 'keydown', keydown );
|
||||
|
||||
if ( _keyState !== STATE.NONE ) {
|
||||
|
||||
return;
|
||||
|
||||
} else if ( event.code === scope.keys[ STATE.ROTATE ] && ! scope.noRotate ) {
|
||||
|
||||
_keyState = STATE.ROTATE;
|
||||
|
||||
} else if ( event.code === scope.keys[ STATE.ZOOM ] && ! scope.noZoom ) {
|
||||
|
||||
_keyState = STATE.ZOOM;
|
||||
|
||||
} else if ( event.code === scope.keys[ STATE.PAN ] && ! scope.noPan ) {
|
||||
|
||||
_keyState = STATE.PAN;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function keyup() {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
_keyState = STATE.NONE;
|
||||
|
||||
window.addEventListener( 'keydown', keydown );
|
||||
|
||||
}
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
if ( _state === STATE.NONE ) {
|
||||
|
||||
switch ( event.button ) {
|
||||
|
||||
case scope.mouseButtons.LEFT:
|
||||
_state = STATE.ROTATE;
|
||||
break;
|
||||
|
||||
case scope.mouseButtons.MIDDLE:
|
||||
_state = STATE.ZOOM;
|
||||
break;
|
||||
|
||||
case scope.mouseButtons.RIGHT:
|
||||
_state = STATE.PAN;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
|
||||
|
||||
if ( state === STATE.ROTATE && ! scope.noRotate ) {
|
||||
|
||||
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
||||
_movePrev.copy( _moveCurr );
|
||||
|
||||
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
|
||||
|
||||
_zoomStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
_zoomEnd.copy( _zoomStart );
|
||||
|
||||
} else if ( state === STATE.PAN && ! scope.noPan ) {
|
||||
|
||||
_panStart.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
_panEnd.copy( _panStart );
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( _startEvent );
|
||||
|
||||
}
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
const state = ( _keyState !== STATE.NONE ) ? _keyState : _state;
|
||||
|
||||
if ( state === STATE.ROTATE && ! scope.noRotate ) {
|
||||
|
||||
_movePrev.copy( _moveCurr );
|
||||
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
||||
|
||||
} else if ( state === STATE.ZOOM && ! scope.noZoom ) {
|
||||
|
||||
_zoomEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
|
||||
} else if ( state === STATE.PAN && ! scope.noPan ) {
|
||||
|
||||
_panEnd.copy( getMouseOnScreen( event.pageX, event.pageY ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onMouseUp() {
|
||||
|
||||
_state = STATE.NONE;
|
||||
|
||||
scope.dispatchEvent( _endEvent );
|
||||
|
||||
}
|
||||
|
||||
function onMouseWheel( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
if ( scope.noZoom === true ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
switch ( event.deltaMode ) {
|
||||
|
||||
case 2:
|
||||
// Zoom in pages
|
||||
_zoomStart.y -= event.deltaY * 0.025;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Zoom in lines
|
||||
_zoomStart.y -= event.deltaY * 0.01;
|
||||
break;
|
||||
|
||||
default:
|
||||
// undefined, 0, assume pixels
|
||||
_zoomStart.y -= event.deltaY * 0.00025;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( _startEvent );
|
||||
scope.dispatchEvent( _endEvent );
|
||||
|
||||
}
|
||||
|
||||
function onTouchStart( event ) {
|
||||
|
||||
trackPointer( event );
|
||||
|
||||
switch ( _pointers.length ) {
|
||||
|
||||
case 1:
|
||||
_state = STATE.TOUCH_ROTATE;
|
||||
_moveCurr.copy( getMouseOnCircle( _pointers[ 0 ].pageX, _pointers[ 0 ].pageY ) );
|
||||
_movePrev.copy( _moveCurr );
|
||||
break;
|
||||
|
||||
default: // 2 or more
|
||||
_state = STATE.TOUCH_ZOOM_PAN;
|
||||
const dx = _pointers[ 0 ].pageX - _pointers[ 1 ].pageX;
|
||||
const dy = _pointers[ 0 ].pageY - _pointers[ 1 ].pageY;
|
||||
_touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
|
||||
|
||||
const x = ( _pointers[ 0 ].pageX + _pointers[ 1 ].pageX ) / 2;
|
||||
const y = ( _pointers[ 0 ].pageY + _pointers[ 1 ].pageY ) / 2;
|
||||
_panStart.copy( getMouseOnScreen( x, y ) );
|
||||
_panEnd.copy( _panStart );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( _startEvent );
|
||||
|
||||
}
|
||||
|
||||
function onTouchMove( event ) {
|
||||
|
||||
trackPointer( event );
|
||||
|
||||
switch ( _pointers.length ) {
|
||||
|
||||
case 1:
|
||||
_movePrev.copy( _moveCurr );
|
||||
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
||||
break;
|
||||
|
||||
default: // 2 or more
|
||||
|
||||
const position = getSecondPointerPosition( event );
|
||||
|
||||
const dx = event.pageX - position.x;
|
||||
const dy = event.pageY - position.y;
|
||||
_touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy );
|
||||
|
||||
const x = ( event.pageX + position.x ) / 2;
|
||||
const y = ( event.pageY + position.y ) / 2;
|
||||
_panEnd.copy( getMouseOnScreen( x, y ) );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onTouchEnd( event ) {
|
||||
|
||||
switch ( _pointers.length ) {
|
||||
|
||||
case 0:
|
||||
_state = STATE.NONE;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
_state = STATE.TOUCH_ROTATE;
|
||||
_moveCurr.copy( getMouseOnCircle( event.pageX, event.pageY ) );
|
||||
_movePrev.copy( _moveCurr );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_state = STATE.TOUCH_ZOOM_PAN;
|
||||
|
||||
for ( let i = 0; i < _pointers.length; i ++ ) {
|
||||
|
||||
if ( _pointers[ i ].pointerId !== event.pointerId ) {
|
||||
|
||||
const position = _pointerPositions[ _pointers[ i ].pointerId ];
|
||||
_moveCurr.copy( getMouseOnCircle( position.x, position.y ) );
|
||||
_movePrev.copy( _moveCurr );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
scope.dispatchEvent( _endEvent );
|
||||
|
||||
}
|
||||
|
||||
function contextmenu( event ) {
|
||||
|
||||
if ( scope.enabled === false ) return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
}
|
||||
|
||||
function addPointer( event ) {
|
||||
|
||||
_pointers.push( event );
|
||||
|
||||
}
|
||||
|
||||
function removePointer( event ) {
|
||||
|
||||
delete _pointerPositions[ event.pointerId ];
|
||||
|
||||
for ( let i = 0; i < _pointers.length; i ++ ) {
|
||||
|
||||
if ( _pointers[ i ].pointerId == event.pointerId ) {
|
||||
|
||||
_pointers.splice( i, 1 );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function trackPointer( event ) {
|
||||
|
||||
let position = _pointerPositions[ event.pointerId ];
|
||||
|
||||
if ( position === undefined ) {
|
||||
|
||||
position = new Vector2();
|
||||
_pointerPositions[ event.pointerId ] = position;
|
||||
|
||||
}
|
||||
|
||||
position.set( event.pageX, event.pageY );
|
||||
|
||||
}
|
||||
|
||||
function getSecondPointerPosition( event ) {
|
||||
|
||||
const pointer = ( event.pointerId === _pointers[ 0 ].pointerId ) ? _pointers[ 1 ] : _pointers[ 0 ];
|
||||
|
||||
return _pointerPositions[ pointer.pointerId ];
|
||||
|
||||
}
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
scope.domElement.removeEventListener( 'contextmenu', contextmenu );
|
||||
|
||||
scope.domElement.removeEventListener( 'pointerdown', onPointerDown );
|
||||
scope.domElement.removeEventListener( 'pointercancel', onPointerCancel );
|
||||
scope.domElement.removeEventListener( 'wheel', onMouseWheel );
|
||||
|
||||
scope.domElement.removeEventListener( 'pointermove', onPointerMove );
|
||||
scope.domElement.removeEventListener( 'pointerup', onPointerUp );
|
||||
|
||||
window.removeEventListener( 'keydown', keydown );
|
||||
window.removeEventListener( 'keyup', keyup );
|
||||
|
||||
};
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', contextmenu );
|
||||
|
||||
this.domElement.addEventListener( 'pointerdown', onPointerDown );
|
||||
this.domElement.addEventListener( 'pointercancel', onPointerCancel );
|
||||
this.domElement.addEventListener( 'wheel', onMouseWheel, { passive: false } );
|
||||
|
||||
|
||||
window.addEventListener( 'keydown', keydown );
|
||||
window.addEventListener( 'keyup', keyup );
|
||||
|
||||
this.handleResize();
|
||||
|
||||
// force an update at start
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { TrackballControls };
|
||||
1573
dist/electron/static/sdk/three/jsm/controls/TransformControls.js
vendored
Normal file
384
dist/electron/static/sdk/three/jsm/csm/CSM.js
vendored
Normal file
@ -0,0 +1,384 @@
|
||||
import {
|
||||
Vector2,
|
||||
Vector3,
|
||||
DirectionalLight,
|
||||
MathUtils,
|
||||
ShaderChunk,
|
||||
Matrix4,
|
||||
Box3
|
||||
} from 'three';
|
||||
import { CSMFrustum } from './CSMFrustum.js';
|
||||
import { CSMShader } from './CSMShader.js';
|
||||
|
||||
const _cameraToLightMatrix = new Matrix4();
|
||||
const _lightSpaceFrustum = new CSMFrustum();
|
||||
const _center = new Vector3();
|
||||
const _bbox = new Box3();
|
||||
const _uniformArray = [];
|
||||
const _logArray = [];
|
||||
const _lightOrientationMatrix = new Matrix4();
|
||||
const _lightOrientationMatrixInverse = new Matrix4();
|
||||
const _up = new Vector3( 0, 1, 0 );
|
||||
|
||||
export class CSM {
|
||||
|
||||
constructor( data ) {
|
||||
|
||||
this.camera = data.camera;
|
||||
this.parent = data.parent;
|
||||
this.cascades = data.cascades || 3;
|
||||
this.maxFar = data.maxFar || 100000;
|
||||
this.mode = data.mode || 'practical';
|
||||
this.shadowMapSize = data.shadowMapSize || 2048;
|
||||
this.shadowBias = data.shadowBias || 0.000001;
|
||||
this.lightDirection = data.lightDirection || new Vector3( 1, - 1, 1 ).normalize();
|
||||
this.lightIntensity = data.lightIntensity || 3;
|
||||
this.lightNear = data.lightNear || 1;
|
||||
this.lightFar = data.lightFar || 2000;
|
||||
this.lightMargin = data.lightMargin || 200;
|
||||
this.customSplitsCallback = data.customSplitsCallback;
|
||||
this.fade = false;
|
||||
this.mainFrustum = new CSMFrustum();
|
||||
this.frustums = [];
|
||||
this.breaks = [];
|
||||
|
||||
this.lights = [];
|
||||
this.shaders = new Map();
|
||||
|
||||
this.createLights();
|
||||
this.updateFrustums();
|
||||
this.injectInclude();
|
||||
|
||||
}
|
||||
|
||||
createLights() {
|
||||
|
||||
for ( let i = 0; i < this.cascades; i ++ ) {
|
||||
|
||||
const light = new DirectionalLight( 0xffffff, this.lightIntensity );
|
||||
light.castShadow = true;
|
||||
light.shadow.mapSize.width = this.shadowMapSize;
|
||||
light.shadow.mapSize.height = this.shadowMapSize;
|
||||
|
||||
light.shadow.camera.near = this.lightNear;
|
||||
light.shadow.camera.far = this.lightFar;
|
||||
light.shadow.bias = this.shadowBias;
|
||||
|
||||
this.parent.add( light );
|
||||
this.parent.add( light.target );
|
||||
this.lights.push( light );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
initCascades() {
|
||||
|
||||
const camera = this.camera;
|
||||
camera.updateProjectionMatrix();
|
||||
this.mainFrustum.setFromProjectionMatrix( camera.projectionMatrix, this.maxFar );
|
||||
this.mainFrustum.split( this.breaks, this.frustums );
|
||||
|
||||
}
|
||||
|
||||
updateShadowBounds() {
|
||||
|
||||
const frustums = this.frustums;
|
||||
for ( let i = 0; i < frustums.length; i ++ ) {
|
||||
|
||||
const light = this.lights[ i ];
|
||||
const shadowCam = light.shadow.camera;
|
||||
const frustum = this.frustums[ i ];
|
||||
|
||||
// Get the two points that represent that furthest points on the frustum assuming
|
||||
// that's either the diagonal across the far plane or the diagonal across the whole
|
||||
// frustum itself.
|
||||
const nearVerts = frustum.vertices.near;
|
||||
const farVerts = frustum.vertices.far;
|
||||
const point1 = farVerts[ 0 ];
|
||||
let point2;
|
||||
if ( point1.distanceTo( farVerts[ 2 ] ) > point1.distanceTo( nearVerts[ 2 ] ) ) {
|
||||
|
||||
point2 = farVerts[ 2 ];
|
||||
|
||||
} else {
|
||||
|
||||
point2 = nearVerts[ 2 ];
|
||||
|
||||
}
|
||||
|
||||
let squaredBBWidth = point1.distanceTo( point2 );
|
||||
if ( this.fade ) {
|
||||
|
||||
// expand the shadow extents by the fade margin if fade is enabled.
|
||||
const camera = this.camera;
|
||||
const far = Math.max( camera.far, this.maxFar );
|
||||
const linearDepth = frustum.vertices.far[ 0 ].z / ( far - camera.near );
|
||||
const margin = 0.25 * Math.pow( linearDepth, 2.0 ) * ( far - camera.near );
|
||||
|
||||
squaredBBWidth += margin;
|
||||
|
||||
}
|
||||
|
||||
shadowCam.left = - squaredBBWidth / 2;
|
||||
shadowCam.right = squaredBBWidth / 2;
|
||||
shadowCam.top = squaredBBWidth / 2;
|
||||
shadowCam.bottom = - squaredBBWidth / 2;
|
||||
shadowCam.updateProjectionMatrix();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getBreaks() {
|
||||
|
||||
const camera = this.camera;
|
||||
const far = Math.min( camera.far, this.maxFar );
|
||||
this.breaks.length = 0;
|
||||
|
||||
switch ( this.mode ) {
|
||||
|
||||
case 'uniform':
|
||||
uniformSplit( this.cascades, camera.near, far, this.breaks );
|
||||
break;
|
||||
case 'logarithmic':
|
||||
logarithmicSplit( this.cascades, camera.near, far, this.breaks );
|
||||
break;
|
||||
case 'practical':
|
||||
practicalSplit( this.cascades, camera.near, far, 0.5, this.breaks );
|
||||
break;
|
||||
case 'custom':
|
||||
if ( this.customSplitsCallback === undefined ) console.error( 'CSM: Custom split scheme callback not defined.' );
|
||||
this.customSplitsCallback( this.cascades, camera.near, far, this.breaks );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
function uniformSplit( amount, near, far, target ) {
|
||||
|
||||
for ( let i = 1; i < amount; i ++ ) {
|
||||
|
||||
target.push( ( near + ( far - near ) * i / amount ) / far );
|
||||
|
||||
}
|
||||
|
||||
target.push( 1 );
|
||||
|
||||
}
|
||||
|
||||
function logarithmicSplit( amount, near, far, target ) {
|
||||
|
||||
for ( let i = 1; i < amount; i ++ ) {
|
||||
|
||||
target.push( ( near * ( far / near ) ** ( i / amount ) ) / far );
|
||||
|
||||
}
|
||||
|
||||
target.push( 1 );
|
||||
|
||||
}
|
||||
|
||||
function practicalSplit( amount, near, far, lambda, target ) {
|
||||
|
||||
_uniformArray.length = 0;
|
||||
_logArray.length = 0;
|
||||
logarithmicSplit( amount, near, far, _logArray );
|
||||
uniformSplit( amount, near, far, _uniformArray );
|
||||
|
||||
for ( let i = 1; i < amount; i ++ ) {
|
||||
|
||||
target.push( MathUtils.lerp( _uniformArray[ i - 1 ], _logArray[ i - 1 ], lambda ) );
|
||||
|
||||
}
|
||||
|
||||
target.push( 1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const camera = this.camera;
|
||||
const frustums = this.frustums;
|
||||
|
||||
// for each frustum we need to find its min-max box aligned with the light orientation
|
||||
// the position in _lightOrientationMatrix does not matter, as we transform there and back
|
||||
_lightOrientationMatrix.lookAt( new Vector3(), this.lightDirection, _up );
|
||||
_lightOrientationMatrixInverse.copy( _lightOrientationMatrix ).invert();
|
||||
|
||||
for ( let i = 0; i < frustums.length; i ++ ) {
|
||||
|
||||
const light = this.lights[ i ];
|
||||
const shadowCam = light.shadow.camera;
|
||||
const texelWidth = ( shadowCam.right - shadowCam.left ) / this.shadowMapSize;
|
||||
const texelHeight = ( shadowCam.top - shadowCam.bottom ) / this.shadowMapSize;
|
||||
_cameraToLightMatrix.multiplyMatrices( _lightOrientationMatrixInverse, camera.matrixWorld );
|
||||
frustums[ i ].toSpace( _cameraToLightMatrix, _lightSpaceFrustum );
|
||||
|
||||
const nearVerts = _lightSpaceFrustum.vertices.near;
|
||||
const farVerts = _lightSpaceFrustum.vertices.far;
|
||||
_bbox.makeEmpty();
|
||||
for ( let j = 0; j < 4; j ++ ) {
|
||||
|
||||
_bbox.expandByPoint( nearVerts[ j ] );
|
||||
_bbox.expandByPoint( farVerts[ j ] );
|
||||
|
||||
}
|
||||
|
||||
_bbox.getCenter( _center );
|
||||
_center.z = _bbox.max.z + this.lightMargin;
|
||||
_center.x = Math.floor( _center.x / texelWidth ) * texelWidth;
|
||||
_center.y = Math.floor( _center.y / texelHeight ) * texelHeight;
|
||||
_center.applyMatrix4( _lightOrientationMatrix );
|
||||
|
||||
light.position.copy( _center );
|
||||
light.target.position.copy( _center );
|
||||
|
||||
light.target.position.x += this.lightDirection.x;
|
||||
light.target.position.y += this.lightDirection.y;
|
||||
light.target.position.z += this.lightDirection.z;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
injectInclude() {
|
||||
|
||||
ShaderChunk.lights_fragment_begin = CSMShader.lights_fragment_begin;
|
||||
ShaderChunk.lights_pars_begin = CSMShader.lights_pars_begin;
|
||||
|
||||
}
|
||||
|
||||
setupMaterial( material ) {
|
||||
|
||||
material.defines = material.defines || {};
|
||||
material.defines.USE_CSM = 1;
|
||||
material.defines.CSM_CASCADES = this.cascades;
|
||||
|
||||
if ( this.fade ) {
|
||||
|
||||
material.defines.CSM_FADE = '';
|
||||
|
||||
}
|
||||
|
||||
const breaksVec2 = [];
|
||||
const scope = this;
|
||||
const shaders = this.shaders;
|
||||
|
||||
material.onBeforeCompile = function ( shader ) {
|
||||
|
||||
const far = Math.min( scope.camera.far, scope.maxFar );
|
||||
scope.getExtendedBreaks( breaksVec2 );
|
||||
|
||||
shader.uniforms.CSM_cascades = { value: breaksVec2 };
|
||||
shader.uniforms.cameraNear = { value: scope.camera.near };
|
||||
shader.uniforms.shadowFar = { value: far };
|
||||
|
||||
shaders.set( material, shader );
|
||||
|
||||
};
|
||||
|
||||
shaders.set( material, null );
|
||||
|
||||
}
|
||||
|
||||
updateUniforms() {
|
||||
|
||||
const far = Math.min( this.camera.far, this.maxFar );
|
||||
const shaders = this.shaders;
|
||||
|
||||
shaders.forEach( function ( shader, material ) {
|
||||
|
||||
if ( shader !== null ) {
|
||||
|
||||
const uniforms = shader.uniforms;
|
||||
this.getExtendedBreaks( uniforms.CSM_cascades.value );
|
||||
uniforms.cameraNear.value = this.camera.near;
|
||||
uniforms.shadowFar.value = far;
|
||||
|
||||
}
|
||||
|
||||
if ( ! this.fade && 'CSM_FADE' in material.defines ) {
|
||||
|
||||
delete material.defines.CSM_FADE;
|
||||
material.needsUpdate = true;
|
||||
|
||||
} else if ( this.fade && ! ( 'CSM_FADE' in material.defines ) ) {
|
||||
|
||||
material.defines.CSM_FADE = '';
|
||||
material.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
}, this );
|
||||
|
||||
}
|
||||
|
||||
getExtendedBreaks( target ) {
|
||||
|
||||
while ( target.length < this.breaks.length ) {
|
||||
|
||||
target.push( new Vector2() );
|
||||
|
||||
}
|
||||
|
||||
target.length = this.breaks.length;
|
||||
|
||||
for ( let i = 0; i < this.cascades; i ++ ) {
|
||||
|
||||
const amount = this.breaks[ i ];
|
||||
const prev = this.breaks[ i - 1 ] || 0;
|
||||
target[ i ].x = prev;
|
||||
target[ i ].y = amount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateFrustums() {
|
||||
|
||||
this.getBreaks();
|
||||
this.initCascades();
|
||||
this.updateShadowBounds();
|
||||
this.updateUniforms();
|
||||
|
||||
}
|
||||
|
||||
remove() {
|
||||
|
||||
for ( let i = 0; i < this.lights.length; i ++ ) {
|
||||
|
||||
this.parent.remove( this.lights[ i ].target );
|
||||
this.parent.remove( this.lights[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
const shaders = this.shaders;
|
||||
shaders.forEach( function ( shader, material ) {
|
||||
|
||||
delete material.onBeforeCompile;
|
||||
delete material.defines.USE_CSM;
|
||||
delete material.defines.CSM_CASCADES;
|
||||
delete material.defines.CSM_FADE;
|
||||
|
||||
if ( shader !== null ) {
|
||||
|
||||
delete shader.uniforms.CSM_cascades;
|
||||
delete shader.uniforms.cameraNear;
|
||||
delete shader.uniforms.shadowFar;
|
||||
|
||||
}
|
||||
|
||||
material.needsUpdate = true;
|
||||
|
||||
} );
|
||||
shaders.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
152
dist/electron/static/sdk/three/jsm/csm/CSMFrustum.js
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
import { Vector3, Matrix4 } from 'three';
|
||||
|
||||
const inverseProjectionMatrix = new Matrix4();
|
||||
|
||||
class CSMFrustum {
|
||||
|
||||
constructor( data ) {
|
||||
|
||||
data = data || {};
|
||||
|
||||
this.vertices = {
|
||||
near: [
|
||||
new Vector3(),
|
||||
new Vector3(),
|
||||
new Vector3(),
|
||||
new Vector3()
|
||||
],
|
||||
far: [
|
||||
new Vector3(),
|
||||
new Vector3(),
|
||||
new Vector3(),
|
||||
new Vector3()
|
||||
]
|
||||
};
|
||||
|
||||
if ( data.projectionMatrix !== undefined ) {
|
||||
|
||||
this.setFromProjectionMatrix( data.projectionMatrix, data.maxFar || 10000 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
setFromProjectionMatrix( projectionMatrix, maxFar ) {
|
||||
|
||||
const isOrthographic = projectionMatrix.elements[ 2 * 4 + 3 ] === 0;
|
||||
|
||||
inverseProjectionMatrix.copy( projectionMatrix ).invert();
|
||||
|
||||
// 3 --- 0 vertices.near/far order
|
||||
// | |
|
||||
// 2 --- 1
|
||||
// clip space spans from [-1, 1]
|
||||
|
||||
this.vertices.near[ 0 ].set( 1, 1, - 1 );
|
||||
this.vertices.near[ 1 ].set( 1, - 1, - 1 );
|
||||
this.vertices.near[ 2 ].set( - 1, - 1, - 1 );
|
||||
this.vertices.near[ 3 ].set( - 1, 1, - 1 );
|
||||
this.vertices.near.forEach( function ( v ) {
|
||||
|
||||
v.applyMatrix4( inverseProjectionMatrix );
|
||||
|
||||
} );
|
||||
|
||||
this.vertices.far[ 0 ].set( 1, 1, 1 );
|
||||
this.vertices.far[ 1 ].set( 1, - 1, 1 );
|
||||
this.vertices.far[ 2 ].set( - 1, - 1, 1 );
|
||||
this.vertices.far[ 3 ].set( - 1, 1, 1 );
|
||||
this.vertices.far.forEach( function ( v ) {
|
||||
|
||||
v.applyMatrix4( inverseProjectionMatrix );
|
||||
|
||||
const absZ = Math.abs( v.z );
|
||||
if ( isOrthographic ) {
|
||||
|
||||
v.z *= Math.min( maxFar / absZ, 1.0 );
|
||||
|
||||
} else {
|
||||
|
||||
v.multiplyScalar( Math.min( maxFar / absZ, 1.0 ) );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return this.vertices;
|
||||
|
||||
}
|
||||
|
||||
split( breaks, target ) {
|
||||
|
||||
while ( breaks.length > target.length ) {
|
||||
|
||||
target.push( new CSMFrustum() );
|
||||
|
||||
}
|
||||
|
||||
target.length = breaks.length;
|
||||
|
||||
for ( let i = 0; i < breaks.length; i ++ ) {
|
||||
|
||||
const cascade = target[ i ];
|
||||
|
||||
if ( i === 0 ) {
|
||||
|
||||
for ( let j = 0; j < 4; j ++ ) {
|
||||
|
||||
cascade.vertices.near[ j ].copy( this.vertices.near[ j ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let j = 0; j < 4; j ++ ) {
|
||||
|
||||
cascade.vertices.near[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i - 1 ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( i === breaks.length - 1 ) {
|
||||
|
||||
for ( let j = 0; j < 4; j ++ ) {
|
||||
|
||||
cascade.vertices.far[ j ].copy( this.vertices.far[ j ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let j = 0; j < 4; j ++ ) {
|
||||
|
||||
cascade.vertices.far[ j ].lerpVectors( this.vertices.near[ j ], this.vertices.far[ j ], breaks[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toSpace( cameraMatrix, target ) {
|
||||
|
||||
for ( let i = 0; i < 4; i ++ ) {
|
||||
|
||||
target.vertices.near[ i ]
|
||||
.copy( this.vertices.near[ i ] )
|
||||
.applyMatrix4( cameraMatrix );
|
||||
|
||||
target.vertices.far[ i ]
|
||||
.copy( this.vertices.far[ i ] )
|
||||
.applyMatrix4( cameraMatrix );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CSMFrustum };
|
||||
193
dist/electron/static/sdk/three/jsm/csm/CSMHelper.js
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
import {
|
||||
Group,
|
||||
Mesh,
|
||||
LineSegments,
|
||||
BufferGeometry,
|
||||
LineBasicMaterial,
|
||||
Box3Helper,
|
||||
Box3,
|
||||
PlaneGeometry,
|
||||
MeshBasicMaterial,
|
||||
BufferAttribute,
|
||||
DoubleSide
|
||||
} from 'three';
|
||||
|
||||
class CSMHelper extends Group {
|
||||
|
||||
constructor( csm ) {
|
||||
|
||||
super();
|
||||
this.csm = csm;
|
||||
this.displayFrustum = true;
|
||||
this.displayPlanes = true;
|
||||
this.displayShadowBounds = true;
|
||||
|
||||
const indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );
|
||||
const positions = new Float32Array( 24 );
|
||||
const frustumGeometry = new BufferGeometry();
|
||||
frustumGeometry.setIndex( new BufferAttribute( indices, 1 ) );
|
||||
frustumGeometry.setAttribute( 'position', new BufferAttribute( positions, 3, false ) );
|
||||
const frustumLines = new LineSegments( frustumGeometry, new LineBasicMaterial() );
|
||||
this.add( frustumLines );
|
||||
|
||||
this.frustumLines = frustumLines;
|
||||
this.cascadeLines = [];
|
||||
this.cascadePlanes = [];
|
||||
this.shadowLines = [];
|
||||
|
||||
}
|
||||
|
||||
updateVisibility() {
|
||||
|
||||
const displayFrustum = this.displayFrustum;
|
||||
const displayPlanes = this.displayPlanes;
|
||||
const displayShadowBounds = this.displayShadowBounds;
|
||||
|
||||
const frustumLines = this.frustumLines;
|
||||
const cascadeLines = this.cascadeLines;
|
||||
const cascadePlanes = this.cascadePlanes;
|
||||
const shadowLines = this.shadowLines;
|
||||
for ( let i = 0, l = cascadeLines.length; i < l; i ++ ) {
|
||||
|
||||
const cascadeLine = cascadeLines[ i ];
|
||||
const cascadePlane = cascadePlanes[ i ];
|
||||
const shadowLineGroup = shadowLines[ i ];
|
||||
|
||||
cascadeLine.visible = displayFrustum;
|
||||
cascadePlane.visible = displayFrustum && displayPlanes;
|
||||
shadowLineGroup.visible = displayShadowBounds;
|
||||
|
||||
}
|
||||
|
||||
frustumLines.visible = displayFrustum;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const csm = this.csm;
|
||||
const camera = csm.camera;
|
||||
const cascades = csm.cascades;
|
||||
const mainFrustum = csm.mainFrustum;
|
||||
const frustums = csm.frustums;
|
||||
const lights = csm.lights;
|
||||
|
||||
const frustumLines = this.frustumLines;
|
||||
const frustumLinePositions = frustumLines.geometry.getAttribute( 'position' );
|
||||
const cascadeLines = this.cascadeLines;
|
||||
const cascadePlanes = this.cascadePlanes;
|
||||
const shadowLines = this.shadowLines;
|
||||
|
||||
this.position.copy( camera.position );
|
||||
this.quaternion.copy( camera.quaternion );
|
||||
this.scale.copy( camera.scale );
|
||||
this.updateMatrixWorld( true );
|
||||
|
||||
while ( cascadeLines.length > cascades ) {
|
||||
|
||||
this.remove( cascadeLines.pop() );
|
||||
this.remove( cascadePlanes.pop() );
|
||||
this.remove( shadowLines.pop() );
|
||||
|
||||
}
|
||||
|
||||
while ( cascadeLines.length < cascades ) {
|
||||
|
||||
const cascadeLine = new Box3Helper( new Box3(), 0xffffff );
|
||||
const planeMat = new MeshBasicMaterial( { transparent: true, opacity: 0.1, depthWrite: false, side: DoubleSide } );
|
||||
const cascadePlane = new Mesh( new PlaneGeometry(), planeMat );
|
||||
const shadowLineGroup = new Group();
|
||||
const shadowLine = new Box3Helper( new Box3(), 0xffff00 );
|
||||
shadowLineGroup.add( shadowLine );
|
||||
|
||||
this.add( cascadeLine );
|
||||
this.add( cascadePlane );
|
||||
this.add( shadowLineGroup );
|
||||
|
||||
cascadeLines.push( cascadeLine );
|
||||
cascadePlanes.push( cascadePlane );
|
||||
shadowLines.push( shadowLineGroup );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < cascades; i ++ ) {
|
||||
|
||||
const frustum = frustums[ i ];
|
||||
const light = lights[ i ];
|
||||
const shadowCam = light.shadow.camera;
|
||||
const farVerts = frustum.vertices.far;
|
||||
|
||||
const cascadeLine = cascadeLines[ i ];
|
||||
const cascadePlane = cascadePlanes[ i ];
|
||||
const shadowLineGroup = shadowLines[ i ];
|
||||
const shadowLine = shadowLineGroup.children[ 0 ];
|
||||
|
||||
cascadeLine.box.min.copy( farVerts[ 2 ] );
|
||||
cascadeLine.box.max.copy( farVerts[ 0 ] );
|
||||
cascadeLine.box.max.z += 1e-4;
|
||||
|
||||
cascadePlane.position.addVectors( farVerts[ 0 ], farVerts[ 2 ] );
|
||||
cascadePlane.position.multiplyScalar( 0.5 );
|
||||
cascadePlane.scale.subVectors( farVerts[ 0 ], farVerts[ 2 ] );
|
||||
cascadePlane.scale.z = 1e-4;
|
||||
|
||||
this.remove( shadowLineGroup );
|
||||
shadowLineGroup.position.copy( shadowCam.position );
|
||||
shadowLineGroup.quaternion.copy( shadowCam.quaternion );
|
||||
shadowLineGroup.scale.copy( shadowCam.scale );
|
||||
shadowLineGroup.updateMatrixWorld( true );
|
||||
this.attach( shadowLineGroup );
|
||||
|
||||
shadowLine.box.min.set( shadowCam.bottom, shadowCam.left, - shadowCam.far );
|
||||
shadowLine.box.max.set( shadowCam.top, shadowCam.right, - shadowCam.near );
|
||||
|
||||
}
|
||||
|
||||
const nearVerts = mainFrustum.vertices.near;
|
||||
const farVerts = mainFrustum.vertices.far;
|
||||
frustumLinePositions.setXYZ( 0, farVerts[ 0 ].x, farVerts[ 0 ].y, farVerts[ 0 ].z );
|
||||
frustumLinePositions.setXYZ( 1, farVerts[ 3 ].x, farVerts[ 3 ].y, farVerts[ 3 ].z );
|
||||
frustumLinePositions.setXYZ( 2, farVerts[ 2 ].x, farVerts[ 2 ].y, farVerts[ 2 ].z );
|
||||
frustumLinePositions.setXYZ( 3, farVerts[ 1 ].x, farVerts[ 1 ].y, farVerts[ 1 ].z );
|
||||
|
||||
frustumLinePositions.setXYZ( 4, nearVerts[ 0 ].x, nearVerts[ 0 ].y, nearVerts[ 0 ].z );
|
||||
frustumLinePositions.setXYZ( 5, nearVerts[ 3 ].x, nearVerts[ 3 ].y, nearVerts[ 3 ].z );
|
||||
frustumLinePositions.setXYZ( 6, nearVerts[ 2 ].x, nearVerts[ 2 ].y, nearVerts[ 2 ].z );
|
||||
frustumLinePositions.setXYZ( 7, nearVerts[ 1 ].x, nearVerts[ 1 ].y, nearVerts[ 1 ].z );
|
||||
frustumLinePositions.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
const frustumLines = this.frustumLines;
|
||||
const cascadeLines = this.cascadeLines;
|
||||
const cascadePlanes = this.cascadePlanes;
|
||||
const shadowLines = this.shadowLines;
|
||||
|
||||
frustumLines.geometry.dispose();
|
||||
frustumLines.material.dispose();
|
||||
|
||||
const cascades = this.csm.cascades;
|
||||
|
||||
for ( let i = 0; i < cascades; i ++ ) {
|
||||
|
||||
const cascadeLine = cascadeLines[ i ];
|
||||
const cascadePlane = cascadePlanes[ i ];
|
||||
const shadowLineGroup = shadowLines[ i ];
|
||||
const shadowLine = shadowLineGroup.children[ 0 ];
|
||||
|
||||
cascadeLine.dispose(); // Box3Helper
|
||||
|
||||
cascadePlane.geometry.dispose();
|
||||
cascadePlane.material.dispose();
|
||||
|
||||
shadowLine.dispose(); // Box3Helper
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { CSMHelper };
|
||||
295
dist/electron/static/sdk/three/jsm/csm/CSMShader.js
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
import { ShaderChunk } from 'three';
|
||||
|
||||
const CSMShader = {
|
||||
lights_fragment_begin: /* glsl */`
|
||||
vec3 geometryPosition = - vViewPosition;
|
||||
vec3 geometryNormal = normal;
|
||||
vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition );
|
||||
|
||||
vec3 geometryClearcoatNormal = vec3( 0.0 );
|
||||
|
||||
#ifdef USE_CLEARCOAT
|
||||
|
||||
geometryClearcoatNormal = clearcoatNormal;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_IRIDESCENCE
|
||||
float dotNVi = saturate( dot( normal, geometryViewDir ) );
|
||||
if ( material.iridescenceThickness == 0.0 ) {
|
||||
material.iridescence = 0.0;
|
||||
} else {
|
||||
material.iridescence = saturate( material.iridescence );
|
||||
}
|
||||
if ( material.iridescence > 0.0 ) {
|
||||
material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor );
|
||||
// Iridescence F0 approximation
|
||||
material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi );
|
||||
}
|
||||
#endif
|
||||
|
||||
IncidentLight directLight;
|
||||
|
||||
#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )
|
||||
|
||||
PointLight pointLight;
|
||||
#if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0
|
||||
PointLightShadow pointLightShadow;
|
||||
#endif
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {
|
||||
|
||||
pointLight = pointLights[ i ];
|
||||
|
||||
getPointLightInfo( pointLight, geometryPosition, directLight );
|
||||
|
||||
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
|
||||
pointLightShadow = pointLightShadows[ i ];
|
||||
directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0;
|
||||
#endif
|
||||
|
||||
RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#endif
|
||||
|
||||
#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )
|
||||
|
||||
SpotLight spotLight;
|
||||
vec4 spotColor;
|
||||
vec3 spotLightCoord;
|
||||
bool inSpotLightMap;
|
||||
|
||||
#if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0
|
||||
SpotLightShadow spotLightShadow;
|
||||
#endif
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {
|
||||
|
||||
spotLight = spotLights[ i ];
|
||||
|
||||
getSpotLightInfo( spotLight, geometryPosition, directLight );
|
||||
|
||||
// spot lights are ordered [shadows with maps, shadows without maps, maps without shadows, none]
|
||||
#if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )
|
||||
#define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX
|
||||
#elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
|
||||
#define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS
|
||||
#else
|
||||
#define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS )
|
||||
#endif
|
||||
#if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS )
|
||||
spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w;
|
||||
inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) );
|
||||
spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy );
|
||||
directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color;
|
||||
#endif
|
||||
#undef SPOT_LIGHT_MAP_INDEX
|
||||
|
||||
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
|
||||
spotLightShadow = spotLightShadows[ i ];
|
||||
directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0;
|
||||
|
||||
#endif
|
||||
|
||||
RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#endif
|
||||
|
||||
#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && defined( USE_CSM ) && defined( CSM_CASCADES )
|
||||
|
||||
DirectionalLight directionalLight;
|
||||
float linearDepth = (vViewPosition.z) / (shadowFar - cameraNear);
|
||||
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
||||
DirectionalLightShadow directionalLightShadow;
|
||||
#endif
|
||||
|
||||
#if defined( USE_SHADOWMAP ) && defined( CSM_FADE )
|
||||
vec2 cascade;
|
||||
float cascadeCenter;
|
||||
float closestEdge;
|
||||
float margin;
|
||||
float csmx;
|
||||
float csmy;
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
|
||||
|
||||
directionalLight = directionalLights[ i ];
|
||||
getDirectionalLightInfo( directionalLight, directLight );
|
||||
|
||||
#if ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
|
||||
// NOTE: Depth gets larger away from the camera.
|
||||
// cascade.x is closer, cascade.y is further
|
||||
cascade = CSM_cascades[ i ];
|
||||
cascadeCenter = ( cascade.x + cascade.y ) / 2.0;
|
||||
closestEdge = linearDepth < cascadeCenter ? cascade.x : cascade.y;
|
||||
margin = 0.25 * pow( closestEdge, 2.0 );
|
||||
csmx = cascade.x - margin / 2.0;
|
||||
csmy = cascade.y + margin / 2.0;
|
||||
if( linearDepth >= csmx && ( linearDepth < csmy || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 ) ) {
|
||||
|
||||
float dist = min( linearDepth - csmx, csmy - linearDepth );
|
||||
float ratio = clamp( dist / margin, 0.0, 1.0 );
|
||||
|
||||
vec3 prevColor = directLight.color;
|
||||
directionalLightShadow = directionalLightShadows[ i ];
|
||||
directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
|
||||
|
||||
bool shouldFadeLastCascade = UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth > cascadeCenter;
|
||||
directLight.color = mix( prevColor, directLight.color, shouldFadeLastCascade ? ratio : 1.0 );
|
||||
|
||||
ReflectedLight prevLight = reflectedLight;
|
||||
RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
bool shouldBlend = UNROLLED_LOOP_INDEX != CSM_CASCADES - 1 || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1 && linearDepth < cascadeCenter;
|
||||
float blendRatio = shouldBlend ? ratio : 1.0;
|
||||
|
||||
reflectedLight.directDiffuse = mix( prevLight.directDiffuse, reflectedLight.directDiffuse, blendRatio );
|
||||
reflectedLight.directSpecular = mix( prevLight.directSpecular, reflectedLight.directSpecular, blendRatio );
|
||||
reflectedLight.indirectDiffuse = mix( prevLight.indirectDiffuse, reflectedLight.indirectDiffuse, blendRatio );
|
||||
reflectedLight.indirectSpecular = mix( prevLight.indirectSpecular, reflectedLight.indirectSpecular, blendRatio );
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
#elif defined (USE_SHADOWMAP)
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
|
||||
|
||||
directionalLight = directionalLights[ i ];
|
||||
getDirectionalLightInfo( directionalLight, directLight );
|
||||
|
||||
#if ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
|
||||
|
||||
directionalLightShadow = directionalLightShadows[ i ];
|
||||
if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y) directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
|
||||
|
||||
if(linearDepth >= CSM_cascades[UNROLLED_LOOP_INDEX].x && (linearDepth < CSM_cascades[UNROLLED_LOOP_INDEX].y || UNROLLED_LOOP_INDEX == CSM_CASCADES - 1)) RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#elif ( NUM_DIR_LIGHT_SHADOWS > 0 )
|
||||
// note: no loop here - all CSM lights are in fact one light only
|
||||
getDirectionalLightInfo( directionalLights[0], directLight );
|
||||
RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
#endif
|
||||
|
||||
#if ( NUM_DIR_LIGHTS > NUM_DIR_LIGHT_SHADOWS)
|
||||
// compute the lights not casting shadows (if any)
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = NUM_DIR_LIGHT_SHADOWS; i < NUM_DIR_LIGHTS; i ++ ) {
|
||||
|
||||
directionalLight = directionalLights[ i ];
|
||||
|
||||
getDirectionalLightInfo( directionalLight, directLight );
|
||||
|
||||
RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) && !defined( USE_CSM ) && !defined( CSM_CASCADES )
|
||||
|
||||
DirectionalLight directionalLight;
|
||||
#if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0
|
||||
DirectionalLightShadow directionalLightShadow;
|
||||
#endif
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
|
||||
|
||||
directionalLight = directionalLights[ i ];
|
||||
|
||||
getDirectionalLightInfo( directionalLight, directLight );
|
||||
|
||||
#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
|
||||
directionalLightShadow = directionalLightShadows[ i ];
|
||||
directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;
|
||||
#endif
|
||||
|
||||
RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#endif
|
||||
|
||||
#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )
|
||||
|
||||
RectAreaLight rectAreaLight;
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {
|
||||
|
||||
rectAreaLight = rectAreaLights[ i ];
|
||||
RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight );
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#endif
|
||||
|
||||
#if defined( RE_IndirectDiffuse )
|
||||
|
||||
vec3 iblIrradiance = vec3( 0.0 );
|
||||
|
||||
vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );
|
||||
|
||||
#if defined( USE_LIGHT_PROBES )
|
||||
|
||||
irradiance += getLightProbeIrradiance( lightProbe, geometryNormal );
|
||||
|
||||
#endif
|
||||
|
||||
#if ( NUM_HEMI_LIGHTS > 0 )
|
||||
|
||||
#pragma unroll_loop_start
|
||||
for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {
|
||||
|
||||
irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal );
|
||||
|
||||
}
|
||||
#pragma unroll_loop_end
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined( RE_IndirectSpecular )
|
||||
|
||||
vec3 radiance = vec3( 0.0 );
|
||||
vec3 clearcoatRadiance = vec3( 0.0 );
|
||||
|
||||
#endif
|
||||
`,
|
||||
lights_pars_begin: /* glsl */`
|
||||
#if defined( USE_CSM ) && defined( CSM_CASCADES )
|
||||
uniform vec2 CSM_cascades[CSM_CASCADES];
|
||||
uniform float cameraNear;
|
||||
uniform float shadowFar;
|
||||
#endif
|
||||
` + ShaderChunk.lights_pars_begin
|
||||
};
|
||||
|
||||
export { CSMShader };
|
||||
422
dist/electron/static/sdk/three/jsm/curves/CurveExtras.js
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
import {
|
||||
Curve,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* A bunch of parametric curves
|
||||
*
|
||||
* Formulas collected from various sources
|
||||
* http://mathworld.wolfram.com/HeartCurve.html
|
||||
* http://en.wikipedia.org/wiki/Viviani%27s_curve
|
||||
* http://www.mi.sanu.ac.rs/vismath/taylorapril2011/Taylor.pdf
|
||||
* https://prideout.net/blog/old/blog/index.html@p=44.html
|
||||
*/
|
||||
|
||||
// GrannyKnot
|
||||
|
||||
class GrannyKnot extends Curve {
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t = 2 * Math.PI * t;
|
||||
|
||||
const x = - 0.22 * Math.cos( t ) - 1.28 * Math.sin( t ) - 0.44 * Math.cos( 3 * t ) - 0.78 * Math.sin( 3 * t );
|
||||
const y = - 0.1 * Math.cos( 2 * t ) - 0.27 * Math.sin( 2 * t ) + 0.38 * Math.cos( 4 * t ) + 0.46 * Math.sin( 4 * t );
|
||||
const z = 0.7 * Math.cos( 3 * t ) - 0.4 * Math.sin( 3 * t );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( 20 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// HeartCurve
|
||||
|
||||
class HeartCurve extends Curve {
|
||||
|
||||
constructor( scale = 5 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t *= 2 * Math.PI;
|
||||
|
||||
const x = 16 * Math.pow( Math.sin( t ), 3 );
|
||||
const y = 13 * Math.cos( t ) - 5 * Math.cos( 2 * t ) - 2 * Math.cos( 3 * t ) - Math.cos( 4 * t );
|
||||
const z = 0;
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Viviani's Curve
|
||||
|
||||
class VivianiCurve extends Curve {
|
||||
|
||||
constructor( scale = 70 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t = t * 4 * Math.PI; // normalized to 0..1
|
||||
const a = this.scale / 2;
|
||||
|
||||
const x = a * ( 1 + Math.cos( t ) );
|
||||
const y = a * Math.sin( t );
|
||||
const z = 2 * a * Math.sin( t / 2 );
|
||||
|
||||
return point.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// KnotCurve
|
||||
|
||||
class KnotCurve extends Curve {
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t *= 2 * Math.PI;
|
||||
|
||||
const R = 10;
|
||||
const s = 50;
|
||||
|
||||
const x = s * Math.sin( t );
|
||||
const y = Math.cos( t ) * ( R + s * Math.cos( t ) );
|
||||
const z = Math.sin( t ) * ( R + s * Math.cos( t ) );
|
||||
|
||||
return point.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// HelixCurve
|
||||
|
||||
class HelixCurve extends Curve {
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const a = 30; // radius
|
||||
const b = 150; // height
|
||||
|
||||
const t2 = 2 * Math.PI * t * b / 30;
|
||||
|
||||
const x = Math.cos( t2 ) * a;
|
||||
const y = Math.sin( t2 ) * a;
|
||||
const z = b * t;
|
||||
|
||||
return point.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TrefoilKnot
|
||||
|
||||
class TrefoilKnot extends Curve {
|
||||
|
||||
constructor( scale = 10 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t *= Math.PI * 2;
|
||||
|
||||
const x = ( 2 + Math.cos( 3 * t ) ) * Math.cos( 2 * t );
|
||||
const y = ( 2 + Math.cos( 3 * t ) ) * Math.sin( 2 * t );
|
||||
const z = Math.sin( 3 * t );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TorusKnot
|
||||
|
||||
class TorusKnot extends Curve {
|
||||
|
||||
constructor( scale = 10 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const p = 3;
|
||||
const q = 4;
|
||||
|
||||
t *= Math.PI * 2;
|
||||
|
||||
const x = ( 2 + Math.cos( q * t ) ) * Math.cos( p * t );
|
||||
const y = ( 2 + Math.cos( q * t ) ) * Math.sin( p * t );
|
||||
const z = Math.sin( q * t );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CinquefoilKnot
|
||||
|
||||
class CinquefoilKnot extends Curve {
|
||||
|
||||
constructor( scale = 10 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const p = 2;
|
||||
const q = 5;
|
||||
|
||||
t *= Math.PI * 2;
|
||||
|
||||
const x = ( 2 + Math.cos( q * t ) ) * Math.cos( p * t );
|
||||
const y = ( 2 + Math.cos( q * t ) ) * Math.sin( p * t );
|
||||
const z = Math.sin( q * t );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// TrefoilPolynomialKnot
|
||||
|
||||
class TrefoilPolynomialKnot extends Curve {
|
||||
|
||||
constructor( scale = 10 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t = t * 4 - 2;
|
||||
|
||||
const x = Math.pow( t, 3 ) - 3 * t;
|
||||
const y = Math.pow( t, 4 ) - 4 * t * t;
|
||||
const z = 1 / 5 * Math.pow( t, 5 ) - 2 * t;
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function scaleTo( x, y, t ) {
|
||||
|
||||
const r = y - x;
|
||||
return t * r + x;
|
||||
|
||||
}
|
||||
|
||||
// FigureEightPolynomialKnot
|
||||
|
||||
class FigureEightPolynomialKnot extends Curve {
|
||||
|
||||
constructor( scale = 1 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t = scaleTo( - 4, 4, t );
|
||||
|
||||
const x = 2 / 5 * t * ( t * t - 7 ) * ( t * t - 10 );
|
||||
const y = Math.pow( t, 4 ) - 13 * t * t;
|
||||
const z = 1 / 10 * t * ( t * t - 4 ) * ( t * t - 9 ) * ( t * t - 12 );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DecoratedTorusKnot4a
|
||||
|
||||
class DecoratedTorusKnot4a extends Curve {
|
||||
|
||||
constructor( scale = 40 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t *= Math.PI * 2;
|
||||
|
||||
const x = Math.cos( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
|
||||
const y = Math.sin( 2 * t ) * ( 1 + 0.6 * ( Math.cos( 5 * t ) + 0.75 * Math.cos( 10 * t ) ) );
|
||||
const z = 0.35 * Math.sin( 5 * t );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DecoratedTorusKnot4b
|
||||
|
||||
class DecoratedTorusKnot4b extends Curve {
|
||||
|
||||
constructor( scale = 40 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const fi = t * Math.PI * 2;
|
||||
|
||||
const x = Math.cos( 2 * fi ) * ( 1 + 0.45 * Math.cos( 3 * fi ) + 0.4 * Math.cos( 9 * fi ) );
|
||||
const y = Math.sin( 2 * fi ) * ( 1 + 0.45 * Math.cos( 3 * fi ) + 0.4 * Math.cos( 9 * fi ) );
|
||||
const z = 0.2 * Math.sin( 9 * fi );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// DecoratedTorusKnot5a
|
||||
|
||||
class DecoratedTorusKnot5a extends Curve {
|
||||
|
||||
constructor( scale = 40 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const fi = t * Math.PI * 2;
|
||||
|
||||
const x = Math.cos( 3 * fi ) * ( 1 + 0.3 * Math.cos( 5 * fi ) + 0.5 * Math.cos( 10 * fi ) );
|
||||
const y = Math.sin( 3 * fi ) * ( 1 + 0.3 * Math.cos( 5 * fi ) + 0.5 * Math.cos( 10 * fi ) );
|
||||
const z = 0.2 * Math.sin( 20 * fi );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// DecoratedTorusKnot5c
|
||||
|
||||
class DecoratedTorusKnot5c extends Curve {
|
||||
|
||||
constructor( scale = 40 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.scale = scale;
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const fi = t * Math.PI * 2;
|
||||
|
||||
const x = Math.cos( 4 * fi ) * ( 1 + 0.5 * ( Math.cos( 5 * fi ) + 0.4 * Math.cos( 20 * fi ) ) );
|
||||
const y = Math.sin( 4 * fi ) * ( 1 + 0.5 * ( Math.cos( 5 * fi ) + 0.4 * Math.cos( 20 * fi ) ) );
|
||||
const z = 0.35 * Math.sin( 15 * fi );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( this.scale );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {
|
||||
GrannyKnot,
|
||||
HeartCurve,
|
||||
VivianiCurve,
|
||||
KnotCurve,
|
||||
HelixCurve,
|
||||
TrefoilKnot,
|
||||
TorusKnot,
|
||||
CinquefoilKnot,
|
||||
TrefoilPolynomialKnot,
|
||||
FigureEightPolynomialKnot,
|
||||
DecoratedTorusKnot4a,
|
||||
DecoratedTorusKnot4b,
|
||||
DecoratedTorusKnot5a,
|
||||
DecoratedTorusKnot5c
|
||||
};
|
||||
80
dist/electron/static/sdk/three/jsm/curves/NURBSCurve.js
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
import {
|
||||
Curve,
|
||||
Vector3,
|
||||
Vector4
|
||||
} from 'three';
|
||||
import * as NURBSUtils from '../curves/NURBSUtils.js';
|
||||
|
||||
/**
|
||||
* NURBS curve object
|
||||
*
|
||||
* Derives from Curve, overriding getPoint and getTangent.
|
||||
*
|
||||
* Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
|
||||
*
|
||||
**/
|
||||
|
||||
class NURBSCurve extends Curve {
|
||||
|
||||
constructor(
|
||||
degree,
|
||||
knots /* array of reals */,
|
||||
controlPoints /* array of Vector(2|3|4) */,
|
||||
startKnot /* index in knots */,
|
||||
endKnot /* index in knots */
|
||||
) {
|
||||
|
||||
super();
|
||||
|
||||
this.degree = degree;
|
||||
this.knots = knots;
|
||||
this.controlPoints = [];
|
||||
// Used by periodic NURBS to remove hidden spans
|
||||
this.startKnot = startKnot || 0;
|
||||
this.endKnot = endKnot || ( this.knots.length - 1 );
|
||||
|
||||
for ( let i = 0; i < controlPoints.length; ++ i ) {
|
||||
|
||||
// ensure Vector4 for control points
|
||||
const point = controlPoints[ i ];
|
||||
this.controlPoints[ i ] = new Vector4( point.x, point.y, point.z, point.w );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
const u = this.knots[ this.startKnot ] + t * ( this.knots[ this.endKnot ] - this.knots[ this.startKnot ] ); // linear mapping t->u
|
||||
|
||||
// following results in (wx, wy, wz, w) homogeneous point
|
||||
const hpoint = NURBSUtils.calcBSplinePoint( this.degree, this.knots, this.controlPoints, u );
|
||||
|
||||
if ( hpoint.w !== 1.0 ) {
|
||||
|
||||
// project to 3D space: (wx, wy, wz, w) -> (x, y, z, 1)
|
||||
hpoint.divideScalar( hpoint.w );
|
||||
|
||||
}
|
||||
|
||||
return point.set( hpoint.x, hpoint.y, hpoint.z );
|
||||
|
||||
}
|
||||
|
||||
getTangent( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const tangent = optionalTarget;
|
||||
|
||||
const u = this.knots[ 0 ] + t * ( this.knots[ this.knots.length - 1 ] - this.knots[ 0 ] );
|
||||
const ders = NURBSUtils.calcNURBSDerivatives( this.degree, this.knots, this.controlPoints, u, 1 );
|
||||
tangent.copy( ders[ 1 ] ).normalize();
|
||||
|
||||
return tangent;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { NURBSCurve };
|
||||
52
dist/electron/static/sdk/three/jsm/curves/NURBSSurface.js
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
import {
|
||||
Vector4
|
||||
} from 'three';
|
||||
import * as NURBSUtils from '../curves/NURBSUtils.js';
|
||||
|
||||
/**
|
||||
* NURBS surface object
|
||||
*
|
||||
* Implementation is based on (x, y [, z=0 [, w=1]]) control points with w=weight.
|
||||
**/
|
||||
|
||||
class NURBSSurface {
|
||||
|
||||
constructor( degree1, degree2, knots1, knots2 /* arrays of reals */, controlPoints /* array^2 of Vector(2|3|4) */ ) {
|
||||
|
||||
this.degree1 = degree1;
|
||||
this.degree2 = degree2;
|
||||
this.knots1 = knots1;
|
||||
this.knots2 = knots2;
|
||||
this.controlPoints = [];
|
||||
|
||||
const len1 = knots1.length - degree1 - 1;
|
||||
const len2 = knots2.length - degree2 - 1;
|
||||
|
||||
// ensure Vector4 for control points
|
||||
for ( let i = 0; i < len1; ++ i ) {
|
||||
|
||||
this.controlPoints[ i ] = [];
|
||||
|
||||
for ( let j = 0; j < len2; ++ j ) {
|
||||
|
||||
const point = controlPoints[ i ][ j ];
|
||||
this.controlPoints[ i ][ j ] = new Vector4( point.x, point.y, point.z, point.w );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPoint( t1, t2, target ) {
|
||||
|
||||
const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
|
||||
const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->u
|
||||
|
||||
NURBSUtils.calcSurfacePoint( this.degree1, this.degree2, this.knots1, this.knots2, this.controlPoints, u, v, target );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { NURBSSurface };
|
||||
542
dist/electron/static/sdk/three/jsm/curves/NURBSUtils.js
vendored
Normal file
@ -0,0 +1,542 @@
|
||||
import {
|
||||
Vector3,
|
||||
Vector4
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* NURBS utils
|
||||
*
|
||||
* See NURBSCurve and NURBSSurface.
|
||||
**/
|
||||
|
||||
|
||||
/**************************************************************
|
||||
* NURBS Utils
|
||||
**************************************************************/
|
||||
|
||||
/*
|
||||
Finds knot vector span.
|
||||
|
||||
p : degree
|
||||
u : parametric value
|
||||
U : knot vector
|
||||
|
||||
returns the span
|
||||
*/
|
||||
function findSpan( p, u, U ) {
|
||||
|
||||
const n = U.length - p - 1;
|
||||
|
||||
if ( u >= U[ n ] ) {
|
||||
|
||||
return n - 1;
|
||||
|
||||
}
|
||||
|
||||
if ( u <= U[ p ] ) {
|
||||
|
||||
return p;
|
||||
|
||||
}
|
||||
|
||||
let low = p;
|
||||
let high = n;
|
||||
let mid = Math.floor( ( low + high ) / 2 );
|
||||
|
||||
while ( u < U[ mid ] || u >= U[ mid + 1 ] ) {
|
||||
|
||||
if ( u < U[ mid ] ) {
|
||||
|
||||
high = mid;
|
||||
|
||||
} else {
|
||||
|
||||
low = mid;
|
||||
|
||||
}
|
||||
|
||||
mid = Math.floor( ( low + high ) / 2 );
|
||||
|
||||
}
|
||||
|
||||
return mid;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate basis functions. See The NURBS Book, page 70, algorithm A2.2
|
||||
|
||||
span : span in which u lies
|
||||
u : parametric point
|
||||
p : degree
|
||||
U : knot vector
|
||||
|
||||
returns array[p+1] with basis functions values.
|
||||
*/
|
||||
function calcBasisFunctions( span, u, p, U ) {
|
||||
|
||||
const N = [];
|
||||
const left = [];
|
||||
const right = [];
|
||||
N[ 0 ] = 1.0;
|
||||
|
||||
for ( let j = 1; j <= p; ++ j ) {
|
||||
|
||||
left[ j ] = u - U[ span + 1 - j ];
|
||||
right[ j ] = U[ span + j ] - u;
|
||||
|
||||
let saved = 0.0;
|
||||
|
||||
for ( let r = 0; r < j; ++ r ) {
|
||||
|
||||
const rv = right[ r + 1 ];
|
||||
const lv = left[ j - r ];
|
||||
const temp = N[ r ] / ( rv + lv );
|
||||
N[ r ] = saved + rv * temp;
|
||||
saved = lv * temp;
|
||||
|
||||
}
|
||||
|
||||
N[ j ] = saved;
|
||||
|
||||
}
|
||||
|
||||
return N;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate B-Spline curve points. See The NURBS Book, page 82, algorithm A3.1.
|
||||
|
||||
p : degree of B-Spline
|
||||
U : knot vector
|
||||
P : control points (x, y, z, w)
|
||||
u : parametric point
|
||||
|
||||
returns point for given u
|
||||
*/
|
||||
function calcBSplinePoint( p, U, P, u ) {
|
||||
|
||||
const span = findSpan( p, u, U );
|
||||
const N = calcBasisFunctions( span, u, p, U );
|
||||
const C = new Vector4( 0, 0, 0, 0 );
|
||||
|
||||
for ( let j = 0; j <= p; ++ j ) {
|
||||
|
||||
const point = P[ span - p + j ];
|
||||
const Nj = N[ j ];
|
||||
const wNj = point.w * Nj;
|
||||
C.x += point.x * wNj;
|
||||
C.y += point.y * wNj;
|
||||
C.z += point.z * wNj;
|
||||
C.w += point.w * Nj;
|
||||
|
||||
}
|
||||
|
||||
return C;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate basis functions derivatives. See The NURBS Book, page 72, algorithm A2.3.
|
||||
|
||||
span : span in which u lies
|
||||
u : parametric point
|
||||
p : degree
|
||||
n : number of derivatives to calculate
|
||||
U : knot vector
|
||||
|
||||
returns array[n+1][p+1] with basis functions derivatives
|
||||
*/
|
||||
function calcBasisFunctionDerivatives( span, u, p, n, U ) {
|
||||
|
||||
const zeroArr = [];
|
||||
for ( let i = 0; i <= p; ++ i )
|
||||
zeroArr[ i ] = 0.0;
|
||||
|
||||
const ders = [];
|
||||
|
||||
for ( let i = 0; i <= n; ++ i )
|
||||
ders[ i ] = zeroArr.slice( 0 );
|
||||
|
||||
const ndu = [];
|
||||
|
||||
for ( let i = 0; i <= p; ++ i )
|
||||
ndu[ i ] = zeroArr.slice( 0 );
|
||||
|
||||
ndu[ 0 ][ 0 ] = 1.0;
|
||||
|
||||
const left = zeroArr.slice( 0 );
|
||||
const right = zeroArr.slice( 0 );
|
||||
|
||||
for ( let j = 1; j <= p; ++ j ) {
|
||||
|
||||
left[ j ] = u - U[ span + 1 - j ];
|
||||
right[ j ] = U[ span + j ] - u;
|
||||
|
||||
let saved = 0.0;
|
||||
|
||||
for ( let r = 0; r < j; ++ r ) {
|
||||
|
||||
const rv = right[ r + 1 ];
|
||||
const lv = left[ j - r ];
|
||||
ndu[ j ][ r ] = rv + lv;
|
||||
|
||||
const temp = ndu[ r ][ j - 1 ] / ndu[ j ][ r ];
|
||||
ndu[ r ][ j ] = saved + rv * temp;
|
||||
saved = lv * temp;
|
||||
|
||||
}
|
||||
|
||||
ndu[ j ][ j ] = saved;
|
||||
|
||||
}
|
||||
|
||||
for ( let j = 0; j <= p; ++ j ) {
|
||||
|
||||
ders[ 0 ][ j ] = ndu[ j ][ p ];
|
||||
|
||||
}
|
||||
|
||||
for ( let r = 0; r <= p; ++ r ) {
|
||||
|
||||
let s1 = 0;
|
||||
let s2 = 1;
|
||||
|
||||
const a = [];
|
||||
for ( let i = 0; i <= p; ++ i ) {
|
||||
|
||||
a[ i ] = zeroArr.slice( 0 );
|
||||
|
||||
}
|
||||
|
||||
a[ 0 ][ 0 ] = 1.0;
|
||||
|
||||
for ( let k = 1; k <= n; ++ k ) {
|
||||
|
||||
let d = 0.0;
|
||||
const rk = r - k;
|
||||
const pk = p - k;
|
||||
|
||||
if ( r >= k ) {
|
||||
|
||||
a[ s2 ][ 0 ] = a[ s1 ][ 0 ] / ndu[ pk + 1 ][ rk ];
|
||||
d = a[ s2 ][ 0 ] * ndu[ rk ][ pk ];
|
||||
|
||||
}
|
||||
|
||||
const j1 = ( rk >= - 1 ) ? 1 : - rk;
|
||||
const j2 = ( r - 1 <= pk ) ? k - 1 : p - r;
|
||||
|
||||
for ( let j = j1; j <= j2; ++ j ) {
|
||||
|
||||
a[ s2 ][ j ] = ( a[ s1 ][ j ] - a[ s1 ][ j - 1 ] ) / ndu[ pk + 1 ][ rk + j ];
|
||||
d += a[ s2 ][ j ] * ndu[ rk + j ][ pk ];
|
||||
|
||||
}
|
||||
|
||||
if ( r <= pk ) {
|
||||
|
||||
a[ s2 ][ k ] = - a[ s1 ][ k - 1 ] / ndu[ pk + 1 ][ r ];
|
||||
d += a[ s2 ][ k ] * ndu[ r ][ pk ];
|
||||
|
||||
}
|
||||
|
||||
ders[ k ][ r ] = d;
|
||||
|
||||
const j = s1;
|
||||
s1 = s2;
|
||||
s2 = j;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let r = p;
|
||||
|
||||
for ( let k = 1; k <= n; ++ k ) {
|
||||
|
||||
for ( let j = 0; j <= p; ++ j ) {
|
||||
|
||||
ders[ k ][ j ] *= r;
|
||||
|
||||
}
|
||||
|
||||
r *= p - k;
|
||||
|
||||
}
|
||||
|
||||
return ders;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate derivatives of a B-Spline. See The NURBS Book, page 93, algorithm A3.2.
|
||||
|
||||
p : degree
|
||||
U : knot vector
|
||||
P : control points
|
||||
u : Parametric points
|
||||
nd : number of derivatives
|
||||
|
||||
returns array[d+1] with derivatives
|
||||
*/
|
||||
function calcBSplineDerivatives( p, U, P, u, nd ) {
|
||||
|
||||
const du = nd < p ? nd : p;
|
||||
const CK = [];
|
||||
const span = findSpan( p, u, U );
|
||||
const nders = calcBasisFunctionDerivatives( span, u, p, du, U );
|
||||
const Pw = [];
|
||||
|
||||
for ( let i = 0; i < P.length; ++ i ) {
|
||||
|
||||
const point = P[ i ].clone();
|
||||
const w = point.w;
|
||||
|
||||
point.x *= w;
|
||||
point.y *= w;
|
||||
point.z *= w;
|
||||
|
||||
Pw[ i ] = point;
|
||||
|
||||
}
|
||||
|
||||
for ( let k = 0; k <= du; ++ k ) {
|
||||
|
||||
const point = Pw[ span - p ].clone().multiplyScalar( nders[ k ][ 0 ] );
|
||||
|
||||
for ( let j = 1; j <= p; ++ j ) {
|
||||
|
||||
point.add( Pw[ span - p + j ].clone().multiplyScalar( nders[ k ][ j ] ) );
|
||||
|
||||
}
|
||||
|
||||
CK[ k ] = point;
|
||||
|
||||
}
|
||||
|
||||
for ( let k = du + 1; k <= nd + 1; ++ k ) {
|
||||
|
||||
CK[ k ] = new Vector4( 0, 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
return CK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate "K over I"
|
||||
|
||||
returns k!/(i!(k-i)!)
|
||||
*/
|
||||
function calcKoverI( k, i ) {
|
||||
|
||||
let nom = 1;
|
||||
|
||||
for ( let j = 2; j <= k; ++ j ) {
|
||||
|
||||
nom *= j;
|
||||
|
||||
}
|
||||
|
||||
let denom = 1;
|
||||
|
||||
for ( let j = 2; j <= i; ++ j ) {
|
||||
|
||||
denom *= j;
|
||||
|
||||
}
|
||||
|
||||
for ( let j = 2; j <= k - i; ++ j ) {
|
||||
|
||||
denom *= j;
|
||||
|
||||
}
|
||||
|
||||
return nom / denom;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate derivatives (0-nd) of rational curve. See The NURBS Book, page 127, algorithm A4.2.
|
||||
|
||||
Pders : result of function calcBSplineDerivatives
|
||||
|
||||
returns array with derivatives for rational curve.
|
||||
*/
|
||||
function calcRationalCurveDerivatives( Pders ) {
|
||||
|
||||
const nd = Pders.length;
|
||||
const Aders = [];
|
||||
const wders = [];
|
||||
|
||||
for ( let i = 0; i < nd; ++ i ) {
|
||||
|
||||
const point = Pders[ i ];
|
||||
Aders[ i ] = new Vector3( point.x, point.y, point.z );
|
||||
wders[ i ] = point.w;
|
||||
|
||||
}
|
||||
|
||||
const CK = [];
|
||||
|
||||
for ( let k = 0; k < nd; ++ k ) {
|
||||
|
||||
const v = Aders[ k ].clone();
|
||||
|
||||
for ( let i = 1; i <= k; ++ i ) {
|
||||
|
||||
v.sub( CK[ k - i ].clone().multiplyScalar( calcKoverI( k, i ) * wders[ i ] ) );
|
||||
|
||||
}
|
||||
|
||||
CK[ k ] = v.divideScalar( wders[ 0 ] );
|
||||
|
||||
}
|
||||
|
||||
return CK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate NURBS curve derivatives. See The NURBS Book, page 127, algorithm A4.2.
|
||||
|
||||
p : degree
|
||||
U : knot vector
|
||||
P : control points in homogeneous space
|
||||
u : parametric points
|
||||
nd : number of derivatives
|
||||
|
||||
returns array with derivatives.
|
||||
*/
|
||||
function calcNURBSDerivatives( p, U, P, u, nd ) {
|
||||
|
||||
const Pders = calcBSplineDerivatives( p, U, P, u, nd );
|
||||
return calcRationalCurveDerivatives( Pders );
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Calculate rational B-Spline surface point. See The NURBS Book, page 134, algorithm A4.3.
|
||||
|
||||
p, q : degrees of B-Spline surface
|
||||
U, V : knot vectors
|
||||
P : control points (x, y, z, w)
|
||||
u, v : parametric values
|
||||
|
||||
returns point for given (u, v)
|
||||
*/
|
||||
function calcSurfacePoint( p, q, U, V, P, u, v, target ) {
|
||||
|
||||
const uspan = findSpan( p, u, U );
|
||||
const vspan = findSpan( q, v, V );
|
||||
const Nu = calcBasisFunctions( uspan, u, p, U );
|
||||
const Nv = calcBasisFunctions( vspan, v, q, V );
|
||||
const temp = [];
|
||||
|
||||
for ( let l = 0; l <= q; ++ l ) {
|
||||
|
||||
temp[ l ] = new Vector4( 0, 0, 0, 0 );
|
||||
for ( let k = 0; k <= p; ++ k ) {
|
||||
|
||||
const point = P[ uspan - p + k ][ vspan - q + l ].clone();
|
||||
const w = point.w;
|
||||
point.x *= w;
|
||||
point.y *= w;
|
||||
point.z *= w;
|
||||
temp[ l ].add( point.multiplyScalar( Nu[ k ] ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Sw = new Vector4( 0, 0, 0, 0 );
|
||||
for ( let l = 0; l <= q; ++ l ) {
|
||||
|
||||
Sw.add( temp[ l ].multiplyScalar( Nv[ l ] ) );
|
||||
|
||||
}
|
||||
|
||||
Sw.divideScalar( Sw.w );
|
||||
target.set( Sw.x, Sw.y, Sw.z );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate rational B-Spline volume point. See The NURBS Book, page 134, algorithm A4.3.
|
||||
|
||||
p, q, r : degrees of B-Splinevolume
|
||||
U, V, W : knot vectors
|
||||
P : control points (x, y, z, w)
|
||||
u, v, w : parametric values
|
||||
|
||||
returns point for given (u, v, w)
|
||||
*/
|
||||
function calcVolumePoint( p, q, r, U, V, W, P, u, v, w, target ) {
|
||||
|
||||
const uspan = findSpan( p, u, U );
|
||||
const vspan = findSpan( q, v, V );
|
||||
const wspan = findSpan( r, w, W );
|
||||
const Nu = calcBasisFunctions( uspan, u, p, U );
|
||||
const Nv = calcBasisFunctions( vspan, v, q, V );
|
||||
const Nw = calcBasisFunctions( wspan, w, r, W );
|
||||
const temp = [];
|
||||
|
||||
for ( let m = 0; m <= r; ++ m ) {
|
||||
|
||||
temp[ m ] = [];
|
||||
|
||||
for ( let l = 0; l <= q; ++ l ) {
|
||||
|
||||
temp[ m ][ l ] = new Vector4( 0, 0, 0, 0 );
|
||||
for ( let k = 0; k <= p; ++ k ) {
|
||||
|
||||
const point = P[ uspan - p + k ][ vspan - q + l ][ wspan - r + m ].clone();
|
||||
const w = point.w;
|
||||
point.x *= w;
|
||||
point.y *= w;
|
||||
point.z *= w;
|
||||
temp[ m ][ l ].add( point.multiplyScalar( Nu[ k ] ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
const Sw = new Vector4( 0, 0, 0, 0 );
|
||||
for ( let m = 0; m <= r; ++ m ) {
|
||||
for ( let l = 0; l <= q; ++ l ) {
|
||||
|
||||
Sw.add( temp[ m ][ l ].multiplyScalar( Nw[ m ] ).multiplyScalar( Nv[ l ] ) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Sw.divideScalar( Sw.w );
|
||||
target.set( Sw.x, Sw.y, Sw.z );
|
||||
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
findSpan,
|
||||
calcBasisFunctions,
|
||||
calcBSplinePoint,
|
||||
calcBasisFunctionDerivatives,
|
||||
calcBSplineDerivatives,
|
||||
calcKoverI,
|
||||
calcRationalCurveDerivatives,
|
||||
calcNURBSDerivatives,
|
||||
calcSurfacePoint,
|
||||
calcVolumePoint,
|
||||
};
|
||||
62
dist/electron/static/sdk/three/jsm/curves/NURBSVolume.js
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
import {
|
||||
Vector4
|
||||
} from 'three';
|
||||
import * as NURBSUtils from '../curves/NURBSUtils.js';
|
||||
|
||||
/**
|
||||
* NURBS volume object
|
||||
*
|
||||
* Implementation is based on (x, y, z [, w=1]]) control points with w=weight.
|
||||
**/
|
||||
|
||||
class NURBSVolume {
|
||||
|
||||
constructor( degree1, degree2, degree3, knots1, knots2, knots3 /* arrays of reals */, controlPoints /* array^3 of Vector(2|3|4) */ ) {
|
||||
|
||||
this.degree1 = degree1;
|
||||
this.degree2 = degree2;
|
||||
this.degree3 = degree3;
|
||||
this.knots1 = knots1;
|
||||
this.knots2 = knots2;
|
||||
this.knots3 = knots3;
|
||||
this.controlPoints = [];
|
||||
|
||||
const len1 = knots1.length - degree1 - 1;
|
||||
const len2 = knots2.length - degree2 - 1;
|
||||
const len3 = knots3.length - degree3 - 1;
|
||||
|
||||
// ensure Vector4 for control points
|
||||
for ( let i = 0; i < len1; ++ i ) {
|
||||
|
||||
this.controlPoints[ i ] = [];
|
||||
|
||||
for ( let j = 0; j < len2; ++ j ) {
|
||||
|
||||
this.controlPoints[ i ][ j ] = [];
|
||||
|
||||
for ( let k = 0; k < len3; ++ k ) {
|
||||
|
||||
const point = controlPoints[ i ][ j ][ k ];
|
||||
this.controlPoints[ i ][ j ][ k ] = new Vector4( point.x, point.y, point.z, point.w );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPoint( t1, t2, t3, target ) {
|
||||
|
||||
const u = this.knots1[ 0 ] + t1 * ( this.knots1[ this.knots1.length - 1 ] - this.knots1[ 0 ] ); // linear mapping t1->u
|
||||
const v = this.knots2[ 0 ] + t2 * ( this.knots2[ this.knots2.length - 1 ] - this.knots2[ 0 ] ); // linear mapping t2->v
|
||||
const w = this.knots3[ 0 ] + t3 * ( this.knots3[ this.knots3.length - 1 ] - this.knots3[ 0 ] ); // linear mapping t3->w
|
||||
|
||||
NURBSUtils.calcVolumePoint( this.degree1, this.degree2, this.degree3, this.knots1, this.knots2, this.knots3, this.controlPoints, u, v, w, target );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { NURBSVolume };
|
||||
154
dist/electron/static/sdk/three/jsm/effects/AnaglyphEffect.js
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
import {
|
||||
LinearFilter,
|
||||
Matrix3,
|
||||
Mesh,
|
||||
NearestFilter,
|
||||
OrthographicCamera,
|
||||
PlaneGeometry,
|
||||
RGBAFormat,
|
||||
Scene,
|
||||
ShaderMaterial,
|
||||
StereoCamera,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
|
||||
class AnaglyphEffect {
|
||||
|
||||
constructor( renderer, width = 512, height = 512 ) {
|
||||
|
||||
// Dubois matrices from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.7.6968&rep=rep1&type=pdf#page=4
|
||||
|
||||
this.colorMatrixLeft = new Matrix3().fromArray( [
|
||||
0.456100, - 0.0400822, - 0.0152161,
|
||||
0.500484, - 0.0378246, - 0.0205971,
|
||||
0.176381, - 0.0157589, - 0.00546856
|
||||
] );
|
||||
|
||||
this.colorMatrixRight = new Matrix3().fromArray( [
|
||||
- 0.0434706, 0.378476, - 0.0721527,
|
||||
- 0.0879388, 0.73364, - 0.112961,
|
||||
- 0.00155529, - 0.0184503, 1.2264
|
||||
] );
|
||||
|
||||
const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
|
||||
const _scene = new Scene();
|
||||
|
||||
const _stereo = new StereoCamera();
|
||||
|
||||
const _params = { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat };
|
||||
|
||||
const _renderTargetL = new WebGLRenderTarget( width, height, _params );
|
||||
const _renderTargetR = new WebGLRenderTarget( width, height, _params );
|
||||
|
||||
const _material = new ShaderMaterial( {
|
||||
|
||||
uniforms: {
|
||||
|
||||
'mapLeft': { value: _renderTargetL.texture },
|
||||
'mapRight': { value: _renderTargetR.texture },
|
||||
|
||||
'colorMatrixLeft': { value: this.colorMatrixLeft },
|
||||
'colorMatrixRight': { value: this.colorMatrixRight }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
'varying vec2 vUv;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vUv = vec2( uv.x, uv.y );',
|
||||
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
'uniform sampler2D mapLeft;',
|
||||
'uniform sampler2D mapRight;',
|
||||
'varying vec2 vUv;',
|
||||
|
||||
'uniform mat3 colorMatrixLeft;',
|
||||
'uniform mat3 colorMatrixRight;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vec2 uv = vUv;',
|
||||
|
||||
' vec4 colorL = texture2D( mapLeft, uv );',
|
||||
' vec4 colorR = texture2D( mapRight, uv );',
|
||||
|
||||
' vec3 color = clamp(',
|
||||
' colorMatrixLeft * colorL.rgb +',
|
||||
' colorMatrixRight * colorR.rgb, 0., 1. );',
|
||||
|
||||
' gl_FragColor = vec4(',
|
||||
' color.r, color.g, color.b,',
|
||||
' max( colorL.a, colorR.a ) );',
|
||||
|
||||
' #include <tonemapping_fragment>',
|
||||
' #include <colorspace_fragment>',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' )
|
||||
|
||||
} );
|
||||
|
||||
const _mesh = new Mesh( new PlaneGeometry( 2, 2 ), _material );
|
||||
_scene.add( _mesh );
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
renderer.setSize( width, height );
|
||||
|
||||
const pixelRatio = renderer.getPixelRatio();
|
||||
|
||||
_renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
|
||||
_renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
const currentRenderTarget = renderer.getRenderTarget();
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
_stereo.update( camera );
|
||||
|
||||
renderer.setRenderTarget( _renderTargetL );
|
||||
renderer.clear();
|
||||
renderer.render( scene, _stereo.cameraL );
|
||||
|
||||
renderer.setRenderTarget( _renderTargetR );
|
||||
renderer.clear();
|
||||
renderer.render( scene, _stereo.cameraR );
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
renderer.render( _scene, _camera );
|
||||
|
||||
renderer.setRenderTarget( currentRenderTarget );
|
||||
|
||||
};
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
_renderTargetL.dispose();
|
||||
_renderTargetR.dispose();
|
||||
_mesh.geometry.dispose();
|
||||
_mesh.material.dispose();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { AnaglyphEffect };
|
||||
263
dist/electron/static/sdk/three/jsm/effects/AsciiEffect.js
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
/**
|
||||
* Ascii generation is based on https://github.com/hassadee/jsascii/blob/master/jsascii.js
|
||||
*
|
||||
* 16 April 2012 - @blurspline
|
||||
*/
|
||||
|
||||
class AsciiEffect {
|
||||
|
||||
constructor( renderer, charSet = ' .:-=+*#%@', options = {} ) {
|
||||
|
||||
// ' .,:;=|iI+hHOE#`$';
|
||||
// darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/
|
||||
// ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split('');
|
||||
|
||||
// Some ASCII settings
|
||||
|
||||
const fResolution = options[ 'resolution' ] || 0.15; // Higher for more details
|
||||
const iScale = options[ 'scale' ] || 1;
|
||||
const bColor = options[ 'color' ] || false; // nice but slows down rendering!
|
||||
const bAlpha = options[ 'alpha' ] || false; // Transparency
|
||||
const bBlock = options[ 'block' ] || false; // blocked characters. like good O dos
|
||||
const bInvert = options[ 'invert' ] || false; // black is white, white is black
|
||||
const strResolution = options[ 'strResolution' ] || 'low';
|
||||
|
||||
let width, height;
|
||||
|
||||
const domElement = document.createElement( 'div' );
|
||||
domElement.style.cursor = 'default';
|
||||
|
||||
const oAscii = document.createElement( 'table' );
|
||||
domElement.appendChild( oAscii );
|
||||
|
||||
let iWidth, iHeight;
|
||||
let oImg;
|
||||
|
||||
this.setSize = function ( w, h ) {
|
||||
|
||||
width = w;
|
||||
height = h;
|
||||
|
||||
renderer.setSize( w, h );
|
||||
|
||||
initAsciiSize();
|
||||
|
||||
};
|
||||
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
renderer.render( scene, camera );
|
||||
asciifyImage( oAscii );
|
||||
|
||||
};
|
||||
|
||||
this.domElement = domElement;
|
||||
|
||||
|
||||
// Throw in ascii library from https://github.com/hassadee/jsascii/blob/master/jsascii.js (MIT License)
|
||||
|
||||
function initAsciiSize() {
|
||||
|
||||
iWidth = Math.floor( width * fResolution );
|
||||
iHeight = Math.floor( height * fResolution );
|
||||
|
||||
oCanvas.width = iWidth;
|
||||
oCanvas.height = iHeight;
|
||||
// oCanvas.style.display = "none";
|
||||
// oCanvas.style.width = iWidth;
|
||||
// oCanvas.style.height = iHeight;
|
||||
|
||||
oImg = renderer.domElement;
|
||||
|
||||
if ( oImg.style.backgroundColor ) {
|
||||
|
||||
oAscii.rows[ 0 ].cells[ 0 ].style.backgroundColor = oImg.style.backgroundColor;
|
||||
oAscii.rows[ 0 ].cells[ 0 ].style.color = oImg.style.color;
|
||||
|
||||
}
|
||||
|
||||
oAscii.cellSpacing = 0;
|
||||
oAscii.cellPadding = 0;
|
||||
|
||||
const oStyle = oAscii.style;
|
||||
oStyle.whiteSpace = 'pre';
|
||||
oStyle.margin = '0px';
|
||||
oStyle.padding = '0px';
|
||||
oStyle.letterSpacing = fLetterSpacing + 'px';
|
||||
oStyle.fontFamily = strFont;
|
||||
oStyle.fontSize = fFontSize + 'px';
|
||||
oStyle.lineHeight = fLineHeight + 'px';
|
||||
oStyle.textAlign = 'left';
|
||||
oStyle.textDecoration = 'none';
|
||||
|
||||
}
|
||||
|
||||
|
||||
const aDefaultCharList = ( ' .,:;i1tfLCG08@' ).split( '' );
|
||||
const aDefaultColorCharList = ( ' CGO08@' ).split( '' );
|
||||
const strFont = 'courier new, monospace';
|
||||
|
||||
const oCanvasImg = renderer.domElement;
|
||||
|
||||
const oCanvas = document.createElement( 'canvas' );
|
||||
if ( ! oCanvas.getContext ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const oCtx = oCanvas.getContext( '2d' );
|
||||
if ( ! oCtx.getImageData ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
let aCharList = ( bColor ? aDefaultColorCharList : aDefaultCharList );
|
||||
|
||||
if ( charSet ) aCharList = charSet;
|
||||
|
||||
// Setup dom
|
||||
|
||||
const fFontSize = ( 2 / fResolution ) * iScale;
|
||||
const fLineHeight = ( 2 / fResolution ) * iScale;
|
||||
|
||||
// adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width.
|
||||
|
||||
let fLetterSpacing = 0;
|
||||
|
||||
if ( strResolution == 'low' ) {
|
||||
|
||||
switch ( iScale ) {
|
||||
|
||||
case 1 : fLetterSpacing = - 1; break;
|
||||
case 2 :
|
||||
case 3 : fLetterSpacing = - 2.1; break;
|
||||
case 4 : fLetterSpacing = - 3.1; break;
|
||||
case 5 : fLetterSpacing = - 4.15; break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( strResolution == 'medium' ) {
|
||||
|
||||
switch ( iScale ) {
|
||||
|
||||
case 1 : fLetterSpacing = 0; break;
|
||||
case 2 : fLetterSpacing = - 1; break;
|
||||
case 3 : fLetterSpacing = - 1.04; break;
|
||||
case 4 :
|
||||
case 5 : fLetterSpacing = - 2.1; break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( strResolution == 'high' ) {
|
||||
|
||||
switch ( iScale ) {
|
||||
|
||||
case 1 :
|
||||
case 2 : fLetterSpacing = 0; break;
|
||||
case 3 :
|
||||
case 4 :
|
||||
case 5 : fLetterSpacing = - 1; break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// can't get a span or div to flow like an img element, but a table works?
|
||||
|
||||
|
||||
// convert img element to ascii
|
||||
|
||||
function asciifyImage( oAscii ) {
|
||||
|
||||
oCtx.clearRect( 0, 0, iWidth, iHeight );
|
||||
oCtx.drawImage( oCanvasImg, 0, 0, iWidth, iHeight );
|
||||
const oImgData = oCtx.getImageData( 0, 0, iWidth, iHeight ).data;
|
||||
|
||||
// Coloring loop starts now
|
||||
let strChars = '';
|
||||
|
||||
// console.time('rendering');
|
||||
|
||||
for ( let y = 0; y < iHeight; y += 2 ) {
|
||||
|
||||
for ( let x = 0; x < iWidth; x ++ ) {
|
||||
|
||||
const iOffset = ( y * iWidth + x ) * 4;
|
||||
|
||||
const iRed = oImgData[ iOffset ];
|
||||
const iGreen = oImgData[ iOffset + 1 ];
|
||||
const iBlue = oImgData[ iOffset + 2 ];
|
||||
const iAlpha = oImgData[ iOffset + 3 ];
|
||||
let iCharIdx;
|
||||
|
||||
let fBrightness;
|
||||
|
||||
fBrightness = ( 0.3 * iRed + 0.59 * iGreen + 0.11 * iBlue ) / 255;
|
||||
// fBrightness = (0.3*iRed + 0.5*iGreen + 0.3*iBlue) / 255;
|
||||
|
||||
if ( iAlpha == 0 ) {
|
||||
|
||||
// should calculate alpha instead, but quick hack :)
|
||||
//fBrightness *= (iAlpha / 255);
|
||||
fBrightness = 1;
|
||||
|
||||
}
|
||||
|
||||
iCharIdx = Math.floor( ( 1 - fBrightness ) * ( aCharList.length - 1 ) );
|
||||
|
||||
if ( bInvert ) {
|
||||
|
||||
iCharIdx = aCharList.length - iCharIdx - 1;
|
||||
|
||||
}
|
||||
|
||||
// good for debugging
|
||||
//fBrightness = Math.floor(fBrightness * 10);
|
||||
//strThisChar = fBrightness;
|
||||
|
||||
let strThisChar = aCharList[ iCharIdx ];
|
||||
|
||||
if ( strThisChar === undefined || strThisChar == ' ' )
|
||||
strThisChar = ' ';
|
||||
|
||||
if ( bColor ) {
|
||||
|
||||
strChars += '<span style=\''
|
||||
+ 'color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');'
|
||||
+ ( bBlock ? 'background-color:rgb(' + iRed + ',' + iGreen + ',' + iBlue + ');' : '' )
|
||||
+ ( bAlpha ? 'opacity:' + ( iAlpha / 255 ) + ';' : '' )
|
||||
+ '\'>' + strThisChar + '</span>';
|
||||
|
||||
} else {
|
||||
|
||||
strChars += strThisChar;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
strChars += '<br/>';
|
||||
|
||||
}
|
||||
|
||||
oAscii.innerHTML = `<tr><td style="display:block;width:${width}px;height:${height}px;overflow:hidden">${strChars}</td></tr>`;
|
||||
|
||||
// console.timeEnd('rendering');
|
||||
|
||||
// return oAscii;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { AsciiEffect };
|
||||
539
dist/electron/static/sdk/three/jsm/effects/OutlineEffect.js
vendored
Normal file
@ -0,0 +1,539 @@
|
||||
import {
|
||||
BackSide,
|
||||
Color,
|
||||
ShaderMaterial,
|
||||
UniformsLib,
|
||||
UniformsUtils
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* Reference: https://en.wikipedia.org/wiki/Cel_shading
|
||||
*
|
||||
* API
|
||||
*
|
||||
* 1. Traditional
|
||||
*
|
||||
* const effect = new OutlineEffect( renderer );
|
||||
*
|
||||
* function render() {
|
||||
*
|
||||
* effect.render( scene, camera );
|
||||
*
|
||||
* }
|
||||
*
|
||||
* 2. VR compatible
|
||||
*
|
||||
* const effect = new OutlineEffect( renderer );
|
||||
* let renderingOutline = false;
|
||||
*
|
||||
* scene.onAfterRender = function () {
|
||||
*
|
||||
* if ( renderingOutline ) return;
|
||||
*
|
||||
* renderingOutline = true;
|
||||
*
|
||||
* effect.renderOutline( scene, camera );
|
||||
*
|
||||
* renderingOutline = false;
|
||||
*
|
||||
* };
|
||||
*
|
||||
* function render() {
|
||||
*
|
||||
* renderer.render( scene, camera );
|
||||
*
|
||||
* }
|
||||
*
|
||||
* // How to set default outline parameters
|
||||
* new OutlineEffect( renderer, {
|
||||
* defaultThickness: 0.01,
|
||||
* defaultColor: [ 0, 0, 0 ],
|
||||
* defaultAlpha: 0.8,
|
||||
* defaultKeepAlive: true // keeps outline material in cache even if material is removed from scene
|
||||
* } );
|
||||
*
|
||||
* // How to set outline parameters for each material
|
||||
* material.userData.outlineParameters = {
|
||||
* thickness: 0.01,
|
||||
* color: [ 0, 0, 0 ],
|
||||
* alpha: 0.8,
|
||||
* visible: true,
|
||||
* keepAlive: true
|
||||
* };
|
||||
*/
|
||||
|
||||
class OutlineEffect {
|
||||
|
||||
constructor( renderer, parameters = {} ) {
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
const defaultThickness = parameters.defaultThickness !== undefined ? parameters.defaultThickness : 0.003;
|
||||
const defaultColor = new Color().fromArray( parameters.defaultColor !== undefined ? parameters.defaultColor : [ 0, 0, 0 ] );
|
||||
const defaultAlpha = parameters.defaultAlpha !== undefined ? parameters.defaultAlpha : 1.0;
|
||||
const defaultKeepAlive = parameters.defaultKeepAlive !== undefined ? parameters.defaultKeepAlive : false;
|
||||
|
||||
// object.material.uuid -> outlineMaterial or
|
||||
// object.material[ n ].uuid -> outlineMaterial
|
||||
// save at the outline material creation and release
|
||||
// if it's unused removeThresholdCount frames
|
||||
// unless keepAlive is true.
|
||||
const cache = {};
|
||||
|
||||
const removeThresholdCount = 60;
|
||||
|
||||
// outlineMaterial.uuid -> object.material or
|
||||
// outlineMaterial.uuid -> object.material[ n ]
|
||||
// save before render and release after render.
|
||||
const originalMaterials = {};
|
||||
|
||||
// object.uuid -> originalOnBeforeRender
|
||||
// save before render and release after render.
|
||||
const originalOnBeforeRenders = {};
|
||||
|
||||
//this.cache = cache; // for debug
|
||||
|
||||
const uniformsOutline = {
|
||||
outlineThickness: { value: defaultThickness },
|
||||
outlineColor: { value: defaultColor },
|
||||
outlineAlpha: { value: defaultAlpha }
|
||||
};
|
||||
|
||||
const vertexShader = [
|
||||
'#include <common>',
|
||||
'#include <uv_pars_vertex>',
|
||||
'#include <displacementmap_pars_vertex>',
|
||||
'#include <fog_pars_vertex>',
|
||||
'#include <morphtarget_pars_vertex>',
|
||||
'#include <skinning_pars_vertex>',
|
||||
'#include <logdepthbuf_pars_vertex>',
|
||||
'#include <clipping_planes_pars_vertex>',
|
||||
|
||||
'uniform float outlineThickness;',
|
||||
|
||||
'vec4 calculateOutline( vec4 pos, vec3 normal, vec4 skinned ) {',
|
||||
' float thickness = outlineThickness;',
|
||||
' const float ratio = 1.0;', // TODO: support outline thickness ratio for each vertex
|
||||
' vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + normal, 1.0 );',
|
||||
// NOTE: subtract pos2 from pos because BackSide objectNormal is negative
|
||||
' vec4 norm = normalize( pos - pos2 );',
|
||||
' return pos + norm * thickness * pos.w * ratio;',
|
||||
'}',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' #include <uv_vertex>',
|
||||
|
||||
' #include <beginnormal_vertex>',
|
||||
' #include <morphnormal_vertex>',
|
||||
' #include <skinbase_vertex>',
|
||||
' #include <skinnormal_vertex>',
|
||||
|
||||
' #include <begin_vertex>',
|
||||
' #include <morphtarget_vertex>',
|
||||
' #include <skinning_vertex>',
|
||||
' #include <displacementmap_vertex>',
|
||||
' #include <project_vertex>',
|
||||
|
||||
' vec3 outlineNormal = - objectNormal;', // the outline material is always rendered with BackSide
|
||||
|
||||
' gl_Position = calculateOutline( gl_Position, outlineNormal, vec4( transformed, 1.0 ) );',
|
||||
|
||||
' #include <logdepthbuf_vertex>',
|
||||
' #include <clipping_planes_vertex>',
|
||||
' #include <fog_vertex>',
|
||||
|
||||
'}',
|
||||
|
||||
].join( '\n' );
|
||||
|
||||
const fragmentShader = [
|
||||
|
||||
'#include <common>',
|
||||
'#include <fog_pars_fragment>',
|
||||
'#include <logdepthbuf_pars_fragment>',
|
||||
'#include <clipping_planes_pars_fragment>',
|
||||
|
||||
'uniform vec3 outlineColor;',
|
||||
'uniform float outlineAlpha;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' #include <clipping_planes_fragment>',
|
||||
' #include <logdepthbuf_fragment>',
|
||||
|
||||
' gl_FragColor = vec4( outlineColor, outlineAlpha );',
|
||||
|
||||
' #include <tonemapping_fragment>',
|
||||
' #include <colorspace_fragment>',
|
||||
' #include <fog_fragment>',
|
||||
' #include <premultiplied_alpha_fragment>',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' );
|
||||
|
||||
function createMaterial() {
|
||||
|
||||
return new ShaderMaterial( {
|
||||
type: 'OutlineEffect',
|
||||
uniforms: UniformsUtils.merge( [
|
||||
UniformsLib[ 'fog' ],
|
||||
UniformsLib[ 'displacementmap' ],
|
||||
uniformsOutline
|
||||
] ),
|
||||
vertexShader: vertexShader,
|
||||
fragmentShader: fragmentShader,
|
||||
side: BackSide
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
function getOutlineMaterialFromCache( originalMaterial ) {
|
||||
|
||||
let data = cache[ originalMaterial.uuid ];
|
||||
|
||||
if ( data === undefined ) {
|
||||
|
||||
data = {
|
||||
material: createMaterial(),
|
||||
used: true,
|
||||
keepAlive: defaultKeepAlive,
|
||||
count: 0
|
||||
};
|
||||
|
||||
cache[ originalMaterial.uuid ] = data;
|
||||
|
||||
}
|
||||
|
||||
data.used = true;
|
||||
|
||||
return data.material;
|
||||
|
||||
}
|
||||
|
||||
function getOutlineMaterial( originalMaterial ) {
|
||||
|
||||
const outlineMaterial = getOutlineMaterialFromCache( originalMaterial );
|
||||
|
||||
originalMaterials[ outlineMaterial.uuid ] = originalMaterial;
|
||||
|
||||
updateOutlineMaterial( outlineMaterial, originalMaterial );
|
||||
|
||||
return outlineMaterial;
|
||||
|
||||
}
|
||||
|
||||
function isCompatible( object ) {
|
||||
|
||||
const geometry = object.geometry;
|
||||
const hasNormals = ( geometry !== undefined ) && ( geometry.attributes.normal !== undefined );
|
||||
|
||||
return ( object.isMesh === true && object.material !== undefined && hasNormals === true );
|
||||
|
||||
}
|
||||
|
||||
function setOutlineMaterial( object ) {
|
||||
|
||||
if ( isCompatible( object ) === false ) return;
|
||||
|
||||
if ( Array.isArray( object.material ) ) {
|
||||
|
||||
for ( let i = 0, il = object.material.length; i < il; i ++ ) {
|
||||
|
||||
object.material[ i ] = getOutlineMaterial( object.material[ i ] );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
object.material = getOutlineMaterial( object.material );
|
||||
|
||||
}
|
||||
|
||||
originalOnBeforeRenders[ object.uuid ] = object.onBeforeRender;
|
||||
object.onBeforeRender = onBeforeRender;
|
||||
|
||||
}
|
||||
|
||||
function restoreOriginalMaterial( object ) {
|
||||
|
||||
if ( isCompatible( object ) === false ) return;
|
||||
|
||||
if ( Array.isArray( object.material ) ) {
|
||||
|
||||
for ( let i = 0, il = object.material.length; i < il; i ++ ) {
|
||||
|
||||
object.material[ i ] = originalMaterials[ object.material[ i ].uuid ];
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
object.material = originalMaterials[ object.material.uuid ];
|
||||
|
||||
}
|
||||
|
||||
object.onBeforeRender = originalOnBeforeRenders[ object.uuid ];
|
||||
|
||||
}
|
||||
|
||||
function onBeforeRender( renderer, scene, camera, geometry, material ) {
|
||||
|
||||
const originalMaterial = originalMaterials[ material.uuid ];
|
||||
|
||||
// just in case
|
||||
if ( originalMaterial === undefined ) return;
|
||||
|
||||
updateUniforms( material, originalMaterial );
|
||||
|
||||
}
|
||||
|
||||
function updateUniforms( material, originalMaterial ) {
|
||||
|
||||
const outlineParameters = originalMaterial.userData.outlineParameters;
|
||||
|
||||
material.uniforms.outlineAlpha.value = originalMaterial.opacity;
|
||||
|
||||
if ( outlineParameters !== undefined ) {
|
||||
|
||||
if ( outlineParameters.thickness !== undefined ) material.uniforms.outlineThickness.value = outlineParameters.thickness;
|
||||
if ( outlineParameters.color !== undefined ) material.uniforms.outlineColor.value.fromArray( outlineParameters.color );
|
||||
if ( outlineParameters.alpha !== undefined ) material.uniforms.outlineAlpha.value = outlineParameters.alpha;
|
||||
|
||||
}
|
||||
|
||||
if ( originalMaterial.displacementMap ) {
|
||||
|
||||
material.uniforms.displacementMap.value = originalMaterial.displacementMap;
|
||||
material.uniforms.displacementScale.value = originalMaterial.displacementScale;
|
||||
material.uniforms.displacementBias.value = originalMaterial.displacementBias;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function updateOutlineMaterial( material, originalMaterial ) {
|
||||
|
||||
if ( material.name === 'invisible' ) return;
|
||||
|
||||
const outlineParameters = originalMaterial.userData.outlineParameters;
|
||||
|
||||
material.fog = originalMaterial.fog;
|
||||
material.toneMapped = originalMaterial.toneMapped;
|
||||
material.premultipliedAlpha = originalMaterial.premultipliedAlpha;
|
||||
material.displacementMap = originalMaterial.displacementMap;
|
||||
|
||||
if ( outlineParameters !== undefined ) {
|
||||
|
||||
if ( originalMaterial.visible === false ) {
|
||||
|
||||
material.visible = false;
|
||||
|
||||
} else {
|
||||
|
||||
material.visible = ( outlineParameters.visible !== undefined ) ? outlineParameters.visible : true;
|
||||
|
||||
}
|
||||
|
||||
material.transparent = ( outlineParameters.alpha !== undefined && outlineParameters.alpha < 1.0 ) ? true : originalMaterial.transparent;
|
||||
|
||||
if ( outlineParameters.keepAlive !== undefined ) cache[ originalMaterial.uuid ].keepAlive = outlineParameters.keepAlive;
|
||||
|
||||
} else {
|
||||
|
||||
material.transparent = originalMaterial.transparent;
|
||||
material.visible = originalMaterial.visible;
|
||||
|
||||
}
|
||||
|
||||
if ( originalMaterial.wireframe === true || originalMaterial.depthTest === false ) material.visible = false;
|
||||
|
||||
if ( originalMaterial.clippingPlanes ) {
|
||||
|
||||
material.clipping = true;
|
||||
|
||||
material.clippingPlanes = originalMaterial.clippingPlanes;
|
||||
material.clipIntersection = originalMaterial.clipIntersection;
|
||||
material.clipShadows = originalMaterial.clipShadows;
|
||||
|
||||
}
|
||||
|
||||
material.version = originalMaterial.version; // update outline material if necessary
|
||||
|
||||
}
|
||||
|
||||
function cleanupCache() {
|
||||
|
||||
let keys;
|
||||
|
||||
// clear originialMaterials
|
||||
keys = Object.keys( originalMaterials );
|
||||
|
||||
for ( let i = 0, il = keys.length; i < il; i ++ ) {
|
||||
|
||||
originalMaterials[ keys[ i ] ] = undefined;
|
||||
|
||||
}
|
||||
|
||||
// clear originalOnBeforeRenders
|
||||
keys = Object.keys( originalOnBeforeRenders );
|
||||
|
||||
for ( let i = 0, il = keys.length; i < il; i ++ ) {
|
||||
|
||||
originalOnBeforeRenders[ keys[ i ] ] = undefined;
|
||||
|
||||
}
|
||||
|
||||
// remove unused outlineMaterial from cache
|
||||
keys = Object.keys( cache );
|
||||
|
||||
for ( let i = 0, il = keys.length; i < il; i ++ ) {
|
||||
|
||||
const key = keys[ i ];
|
||||
|
||||
if ( cache[ key ].used === false ) {
|
||||
|
||||
cache[ key ].count ++;
|
||||
|
||||
if ( cache[ key ].keepAlive === false && cache[ key ].count > removeThresholdCount ) {
|
||||
|
||||
delete cache[ key ];
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
cache[ key ].used = false;
|
||||
cache[ key ].count = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( this.enabled === false ) {
|
||||
|
||||
renderer.render( scene, camera );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
const currentAutoClear = renderer.autoClear;
|
||||
renderer.autoClear = this.autoClear;
|
||||
|
||||
renderer.render( scene, camera );
|
||||
|
||||
renderer.autoClear = currentAutoClear;
|
||||
|
||||
this.renderOutline( scene, camera );
|
||||
|
||||
};
|
||||
|
||||
this.renderOutline = function ( scene, camera ) {
|
||||
|
||||
const currentAutoClear = renderer.autoClear;
|
||||
const currentSceneAutoUpdate = scene.matrixWorldAutoUpdate;
|
||||
const currentSceneBackground = scene.background;
|
||||
const currentShadowMapEnabled = renderer.shadowMap.enabled;
|
||||
|
||||
scene.matrixWorldAutoUpdate = false;
|
||||
scene.background = null;
|
||||
renderer.autoClear = false;
|
||||
renderer.shadowMap.enabled = false;
|
||||
|
||||
scene.traverse( setOutlineMaterial );
|
||||
|
||||
renderer.render( scene, camera );
|
||||
|
||||
scene.traverse( restoreOriginalMaterial );
|
||||
|
||||
cleanupCache();
|
||||
|
||||
scene.matrixWorldAutoUpdate = currentSceneAutoUpdate;
|
||||
scene.background = currentSceneBackground;
|
||||
renderer.autoClear = currentAutoClear;
|
||||
renderer.shadowMap.enabled = currentShadowMapEnabled;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* See #9918
|
||||
*
|
||||
* The following property copies and wrapper methods enable
|
||||
* OutlineEffect to be called from other *Effect, like
|
||||
*
|
||||
* effect = new StereoEffect( new OutlineEffect( renderer ) );
|
||||
*
|
||||
* function render () {
|
||||
*
|
||||
* effect.render( scene, camera );
|
||||
*
|
||||
* }
|
||||
*/
|
||||
this.autoClear = renderer.autoClear;
|
||||
this.domElement = renderer.domElement;
|
||||
this.shadowMap = renderer.shadowMap;
|
||||
|
||||
this.clear = function ( color, depth, stencil ) {
|
||||
|
||||
renderer.clear( color, depth, stencil );
|
||||
|
||||
};
|
||||
|
||||
this.getPixelRatio = function () {
|
||||
|
||||
return renderer.getPixelRatio();
|
||||
|
||||
};
|
||||
|
||||
this.setPixelRatio = function ( value ) {
|
||||
|
||||
renderer.setPixelRatio( value );
|
||||
|
||||
};
|
||||
|
||||
this.getSize = function ( target ) {
|
||||
|
||||
return renderer.getSize( target );
|
||||
|
||||
};
|
||||
|
||||
this.setSize = function ( width, height, updateStyle ) {
|
||||
|
||||
renderer.setSize( width, height, updateStyle );
|
||||
|
||||
};
|
||||
|
||||
this.setViewport = function ( x, y, width, height ) {
|
||||
|
||||
renderer.setViewport( x, y, width, height );
|
||||
|
||||
};
|
||||
|
||||
this.setScissor = function ( x, y, width, height ) {
|
||||
|
||||
renderer.setScissor( x, y, width, height );
|
||||
|
||||
};
|
||||
|
||||
this.setScissorTest = function ( boolean ) {
|
||||
|
||||
renderer.setScissorTest( boolean );
|
||||
|
||||
};
|
||||
|
||||
this.setRenderTarget = function ( renderTarget ) {
|
||||
|
||||
renderer.setRenderTarget( renderTarget );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { OutlineEffect };
|
||||
119
dist/electron/static/sdk/three/jsm/effects/ParallaxBarrierEffect.js
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
import {
|
||||
LinearFilter,
|
||||
Mesh,
|
||||
NearestFilter,
|
||||
OrthographicCamera,
|
||||
PlaneGeometry,
|
||||
RGBAFormat,
|
||||
Scene,
|
||||
ShaderMaterial,
|
||||
StereoCamera,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
|
||||
class ParallaxBarrierEffect {
|
||||
|
||||
constructor( renderer ) {
|
||||
|
||||
const _camera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
|
||||
|
||||
const _scene = new Scene();
|
||||
|
||||
const _stereo = new StereoCamera();
|
||||
|
||||
const _params = { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat };
|
||||
|
||||
const _renderTargetL = new WebGLRenderTarget( 512, 512, _params );
|
||||
const _renderTargetR = new WebGLRenderTarget( 512, 512, _params );
|
||||
|
||||
const _material = new ShaderMaterial( {
|
||||
|
||||
uniforms: {
|
||||
|
||||
'mapLeft': { value: _renderTargetL.texture },
|
||||
'mapRight': { value: _renderTargetR.texture }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
'varying vec2 vUv;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vUv = vec2( uv.x, uv.y );',
|
||||
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
'uniform sampler2D mapLeft;',
|
||||
'uniform sampler2D mapRight;',
|
||||
'varying vec2 vUv;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vec2 uv = vUv;',
|
||||
|
||||
' if ( ( mod( gl_FragCoord.y, 2.0 ) ) > 1.00 ) {',
|
||||
|
||||
' gl_FragColor = texture2D( mapLeft, uv );',
|
||||
|
||||
' } else {',
|
||||
|
||||
' gl_FragColor = texture2D( mapRight, uv );',
|
||||
|
||||
' }',
|
||||
|
||||
' #include <tonemapping_fragment>',
|
||||
' #include <colorspace_fragment>',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' )
|
||||
|
||||
} );
|
||||
|
||||
const mesh = new Mesh( new PlaneGeometry( 2, 2 ), _material );
|
||||
_scene.add( mesh );
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
renderer.setSize( width, height );
|
||||
|
||||
const pixelRatio = renderer.getPixelRatio();
|
||||
|
||||
_renderTargetL.setSize( width * pixelRatio, height * pixelRatio );
|
||||
_renderTargetR.setSize( width * pixelRatio, height * pixelRatio );
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
_stereo.update( camera );
|
||||
|
||||
renderer.setRenderTarget( _renderTargetL );
|
||||
renderer.clear();
|
||||
renderer.render( scene, _stereo.cameraL );
|
||||
|
||||
renderer.setRenderTarget( _renderTargetR );
|
||||
renderer.clear();
|
||||
renderer.render( scene, _stereo.cameraR );
|
||||
|
||||
renderer.setRenderTarget( null );
|
||||
renderer.render( _scene, _camera );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ParallaxBarrierEffect };
|
||||
153
dist/electron/static/sdk/three/jsm/effects/PeppersGhostEffect.js
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
import {
|
||||
PerspectiveCamera,
|
||||
Quaternion,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* peppers ghost effect based on http://www.instructables.com/id/Reflective-Prism/?ALLSTEPS
|
||||
*/
|
||||
|
||||
class PeppersGhostEffect {
|
||||
|
||||
constructor( renderer ) {
|
||||
|
||||
const scope = this;
|
||||
|
||||
scope.cameraDistance = 15;
|
||||
scope.reflectFromAbove = false;
|
||||
|
||||
// Internals
|
||||
let _halfWidth, _width, _height;
|
||||
|
||||
const _cameraF = new PerspectiveCamera(); //front
|
||||
const _cameraB = new PerspectiveCamera(); //back
|
||||
const _cameraL = new PerspectiveCamera(); //left
|
||||
const _cameraR = new PerspectiveCamera(); //right
|
||||
|
||||
const _position = new Vector3();
|
||||
const _quaternion = new Quaternion();
|
||||
const _scale = new Vector3();
|
||||
|
||||
// Initialization
|
||||
renderer.autoClear = false;
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
_halfWidth = width / 2;
|
||||
if ( width < height ) {
|
||||
|
||||
_width = width / 3;
|
||||
_height = width / 3;
|
||||
|
||||
} else {
|
||||
|
||||
_width = height / 3;
|
||||
_height = height / 3;
|
||||
|
||||
}
|
||||
|
||||
renderer.setSize( width, height );
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
camera.matrixWorld.decompose( _position, _quaternion, _scale );
|
||||
|
||||
// front
|
||||
_cameraF.position.copy( _position );
|
||||
_cameraF.quaternion.copy( _quaternion );
|
||||
_cameraF.translateZ( scope.cameraDistance );
|
||||
_cameraF.lookAt( scene.position );
|
||||
|
||||
// back
|
||||
_cameraB.position.copy( _position );
|
||||
_cameraB.quaternion.copy( _quaternion );
|
||||
_cameraB.translateZ( - ( scope.cameraDistance ) );
|
||||
_cameraB.lookAt( scene.position );
|
||||
_cameraB.rotation.z += 180 * ( Math.PI / 180 );
|
||||
|
||||
// left
|
||||
_cameraL.position.copy( _position );
|
||||
_cameraL.quaternion.copy( _quaternion );
|
||||
_cameraL.translateX( - ( scope.cameraDistance ) );
|
||||
_cameraL.lookAt( scene.position );
|
||||
_cameraL.rotation.x += 90 * ( Math.PI / 180 );
|
||||
|
||||
// right
|
||||
_cameraR.position.copy( _position );
|
||||
_cameraR.quaternion.copy( _quaternion );
|
||||
_cameraR.translateX( scope.cameraDistance );
|
||||
_cameraR.lookAt( scene.position );
|
||||
_cameraR.rotation.x += 90 * ( Math.PI / 180 );
|
||||
|
||||
|
||||
renderer.clear();
|
||||
renderer.setScissorTest( true );
|
||||
|
||||
renderer.setScissor( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
|
||||
renderer.setViewport( _halfWidth - ( _width / 2 ), ( _height * 2 ), _width, _height );
|
||||
|
||||
if ( scope.reflectFromAbove ) {
|
||||
|
||||
renderer.render( scene, _cameraB );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.render( scene, _cameraF );
|
||||
|
||||
}
|
||||
|
||||
renderer.setScissor( _halfWidth - ( _width / 2 ), 0, _width, _height );
|
||||
renderer.setViewport( _halfWidth - ( _width / 2 ), 0, _width, _height );
|
||||
|
||||
if ( scope.reflectFromAbove ) {
|
||||
|
||||
renderer.render( scene, _cameraF );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.render( scene, _cameraB );
|
||||
|
||||
}
|
||||
|
||||
renderer.setScissor( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
|
||||
renderer.setViewport( _halfWidth - ( _width / 2 ) - _width, _height, _width, _height );
|
||||
|
||||
if ( scope.reflectFromAbove ) {
|
||||
|
||||
renderer.render( scene, _cameraR );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.render( scene, _cameraL );
|
||||
|
||||
}
|
||||
|
||||
renderer.setScissor( _halfWidth + ( _width / 2 ), _height, _width, _height );
|
||||
renderer.setViewport( _halfWidth + ( _width / 2 ), _height, _width, _height );
|
||||
|
||||
if ( scope.reflectFromAbove ) {
|
||||
|
||||
renderer.render( scene, _cameraL );
|
||||
|
||||
} else {
|
||||
|
||||
renderer.render( scene, _cameraR );
|
||||
|
||||
}
|
||||
|
||||
renderer.setScissorTest( false );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { PeppersGhostEffect };
|
||||
55
dist/electron/static/sdk/three/jsm/effects/StereoEffect.js
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
import {
|
||||
StereoCamera,
|
||||
Vector2
|
||||
} from 'three';
|
||||
|
||||
class StereoEffect {
|
||||
|
||||
constructor( renderer ) {
|
||||
|
||||
const _stereo = new StereoCamera();
|
||||
_stereo.aspect = 0.5;
|
||||
const size = new Vector2();
|
||||
|
||||
this.setEyeSeparation = function ( eyeSep ) {
|
||||
|
||||
_stereo.eyeSep = eyeSep;
|
||||
|
||||
};
|
||||
|
||||
this.setSize = function ( width, height ) {
|
||||
|
||||
renderer.setSize( width, height );
|
||||
|
||||
};
|
||||
|
||||
this.render = function ( scene, camera ) {
|
||||
|
||||
if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
|
||||
|
||||
if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
|
||||
|
||||
_stereo.update( camera );
|
||||
|
||||
renderer.getSize( size );
|
||||
|
||||
if ( renderer.autoClear ) renderer.clear();
|
||||
renderer.setScissorTest( true );
|
||||
|
||||
renderer.setScissor( 0, 0, size.width / 2, size.height );
|
||||
renderer.setViewport( 0, 0, size.width / 2, size.height );
|
||||
renderer.render( scene, _stereo.cameraL );
|
||||
|
||||
renderer.setScissor( size.width / 2, 0, size.width / 2, size.height );
|
||||
renderer.setViewport( size.width / 2, 0, size.width / 2, size.height );
|
||||
renderer.render( scene, _stereo.cameraR );
|
||||
|
||||
renderer.setScissorTest( false );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { StereoEffect };
|
||||
52
dist/electron/static/sdk/three/jsm/environments/DebugEnvironment.js
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
import {
|
||||
BackSide,
|
||||
BoxGeometry,
|
||||
Mesh,
|
||||
MeshLambertMaterial,
|
||||
MeshStandardMaterial,
|
||||
PointLight,
|
||||
Scene,
|
||||
} from 'three';
|
||||
|
||||
class DebugEnvironment extends Scene {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
const geometry = new BoxGeometry();
|
||||
geometry.deleteAttribute( 'uv' );
|
||||
const roomMaterial = new MeshStandardMaterial( { metalness: 0, side: BackSide } );
|
||||
const room = new Mesh( geometry, roomMaterial );
|
||||
room.scale.setScalar( 10 );
|
||||
this.add( room );
|
||||
|
||||
const mainLight = new PointLight( 0xffffff, 50, 0, 2 );
|
||||
this.add( mainLight );
|
||||
|
||||
const material1 = new MeshLambertMaterial( { color: 0xff0000, emissive: 0xffffff, emissiveIntensity: 10 } );
|
||||
|
||||
const light1 = new Mesh( geometry, material1 );
|
||||
light1.position.set( - 5, 2, 0 );
|
||||
light1.scale.set( 0.1, 1, 1 );
|
||||
this.add( light1 );
|
||||
|
||||
const material2 = new MeshLambertMaterial( { color: 0x00ff00, emissive: 0xffffff, emissiveIntensity: 10 } );
|
||||
|
||||
const light2 = new Mesh( geometry, material2 );
|
||||
light2.position.set( 0, 5, 0 );
|
||||
light2.scale.set( 1, 0.1, 1 );
|
||||
this.add( light2 );
|
||||
|
||||
const material3 = new MeshLambertMaterial( { color: 0x0000ff, emissive: 0xffffff, emissiveIntensity: 10 } );
|
||||
|
||||
const light3 = new Mesh( geometry, material3 );
|
||||
light3.position.set( 2, 1, 5 );
|
||||
light3.scale.set( 1.5, 2, 0.1 );
|
||||
this.add( light3 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { DebugEnvironment };
|
||||
148
dist/electron/static/sdk/three/jsm/environments/RoomEnvironment.js
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
/**
|
||||
* https://github.com/google/model-viewer/blob/master/packages/model-viewer/src/three-components/EnvironmentScene.ts
|
||||
*/
|
||||
|
||||
import {
|
||||
BackSide,
|
||||
BoxGeometry,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
MeshStandardMaterial,
|
||||
PointLight,
|
||||
Scene,
|
||||
} from 'three';
|
||||
|
||||
class RoomEnvironment extends Scene {
|
||||
|
||||
constructor( renderer = null ) {
|
||||
|
||||
super();
|
||||
|
||||
const geometry = new BoxGeometry();
|
||||
geometry.deleteAttribute( 'uv' );
|
||||
|
||||
const roomMaterial = new MeshStandardMaterial( { side: BackSide } );
|
||||
const boxMaterial = new MeshStandardMaterial();
|
||||
|
||||
let intensity = 5;
|
||||
|
||||
if ( renderer !== null && renderer._useLegacyLights === false ) intensity = 900;
|
||||
|
||||
const mainLight = new PointLight( 0xffffff, intensity, 28, 2 );
|
||||
mainLight.position.set( 0.418, 16.199, 0.300 );
|
||||
this.add( mainLight );
|
||||
|
||||
const room = new Mesh( geometry, roomMaterial );
|
||||
room.position.set( - 0.757, 13.219, 0.717 );
|
||||
room.scale.set( 31.713, 28.305, 28.591 );
|
||||
this.add( room );
|
||||
|
||||
const box1 = new Mesh( geometry, boxMaterial );
|
||||
box1.position.set( - 10.906, 2.009, 1.846 );
|
||||
box1.rotation.set( 0, - 0.195, 0 );
|
||||
box1.scale.set( 2.328, 7.905, 4.651 );
|
||||
this.add( box1 );
|
||||
|
||||
const box2 = new Mesh( geometry, boxMaterial );
|
||||
box2.position.set( - 5.607, - 0.754, - 0.758 );
|
||||
box2.rotation.set( 0, 0.994, 0 );
|
||||
box2.scale.set( 1.970, 1.534, 3.955 );
|
||||
this.add( box2 );
|
||||
|
||||
const box3 = new Mesh( geometry, boxMaterial );
|
||||
box3.position.set( 6.167, 0.857, 7.803 );
|
||||
box3.rotation.set( 0, 0.561, 0 );
|
||||
box3.scale.set( 3.927, 6.285, 3.687 );
|
||||
this.add( box3 );
|
||||
|
||||
const box4 = new Mesh( geometry, boxMaterial );
|
||||
box4.position.set( - 2.017, 0.018, 6.124 );
|
||||
box4.rotation.set( 0, 0.333, 0 );
|
||||
box4.scale.set( 2.002, 4.566, 2.064 );
|
||||
this.add( box4 );
|
||||
|
||||
const box5 = new Mesh( geometry, boxMaterial );
|
||||
box5.position.set( 2.291, - 0.756, - 2.621 );
|
||||
box5.rotation.set( 0, - 0.286, 0 );
|
||||
box5.scale.set( 1.546, 1.552, 1.496 );
|
||||
this.add( box5 );
|
||||
|
||||
const box6 = new Mesh( geometry, boxMaterial );
|
||||
box6.position.set( - 2.193, - 0.369, - 5.547 );
|
||||
box6.rotation.set( 0, 0.516, 0 );
|
||||
box6.scale.set( 3.875, 3.487, 2.986 );
|
||||
this.add( box6 );
|
||||
|
||||
|
||||
// -x right
|
||||
const light1 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
|
||||
light1.position.set( - 16.116, 14.37, 8.208 );
|
||||
light1.scale.set( 0.1, 2.428, 2.739 );
|
||||
this.add( light1 );
|
||||
|
||||
// -x left
|
||||
const light2 = new Mesh( geometry, createAreaLightMaterial( 50 ) );
|
||||
light2.position.set( - 16.109, 18.021, - 8.207 );
|
||||
light2.scale.set( 0.1, 2.425, 2.751 );
|
||||
this.add( light2 );
|
||||
|
||||
// +x
|
||||
const light3 = new Mesh( geometry, createAreaLightMaterial( 17 ) );
|
||||
light3.position.set( 14.904, 12.198, - 1.832 );
|
||||
light3.scale.set( 0.15, 4.265, 6.331 );
|
||||
this.add( light3 );
|
||||
|
||||
// +z
|
||||
const light4 = new Mesh( geometry, createAreaLightMaterial( 43 ) );
|
||||
light4.position.set( - 0.462, 8.89, 14.520 );
|
||||
light4.scale.set( 4.38, 5.441, 0.088 );
|
||||
this.add( light4 );
|
||||
|
||||
// -z
|
||||
const light5 = new Mesh( geometry, createAreaLightMaterial( 20 ) );
|
||||
light5.position.set( 3.235, 11.486, - 12.541 );
|
||||
light5.scale.set( 2.5, 2.0, 0.1 );
|
||||
this.add( light5 );
|
||||
|
||||
// +y
|
||||
const light6 = new Mesh( geometry, createAreaLightMaterial( 100 ) );
|
||||
light6.position.set( 0.0, 20.0, 0.0 );
|
||||
light6.scale.set( 1.0, 0.1, 1.0 );
|
||||
this.add( light6 );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
const resources = new Set();
|
||||
|
||||
this.traverse( ( object ) => {
|
||||
|
||||
if ( object.isMesh ) {
|
||||
|
||||
resources.add( object.geometry );
|
||||
resources.add( object.material );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
for ( const resource of resources ) {
|
||||
|
||||
resource.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createAreaLightMaterial( intensity ) {
|
||||
|
||||
const material = new MeshBasicMaterial();
|
||||
material.color.setScalar( intensity );
|
||||
return material;
|
||||
|
||||
}
|
||||
|
||||
export { RoomEnvironment };
|
||||
267
dist/electron/static/sdk/three/jsm/exporters/DRACOExporter.js
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
import { Color } from 'three';
|
||||
|
||||
/**
|
||||
* Export draco compressed files from threejs geometry objects.
|
||||
*
|
||||
* Draco files are compressed and usually are smaller than conventional 3D file formats.
|
||||
*
|
||||
* The exporter receives a options object containing
|
||||
* - decodeSpeed, indicates how to tune the encoder regarding decode speed (0 gives better speed but worst quality)
|
||||
* - encodeSpeed, indicates how to tune the encoder parameters (0 gives better speed but worst quality)
|
||||
* - encoderMethod
|
||||
* - quantization, indicates the presision of each type of data stored in the draco file in the order (POSITION, NORMAL, COLOR, TEX_COORD, GENERIC)
|
||||
* - exportUvs
|
||||
* - exportNormals
|
||||
* - exportColor
|
||||
*/
|
||||
|
||||
/* global DracoEncoderModule */
|
||||
|
||||
class DRACOExporter {
|
||||
|
||||
parse( object, options = {} ) {
|
||||
|
||||
options = Object.assign( {
|
||||
decodeSpeed: 5,
|
||||
encodeSpeed: 5,
|
||||
encoderMethod: DRACOExporter.MESH_EDGEBREAKER_ENCODING,
|
||||
quantization: [ 16, 8, 8, 8, 8 ],
|
||||
exportUvs: true,
|
||||
exportNormals: true,
|
||||
exportColor: false,
|
||||
}, options );
|
||||
|
||||
if ( DracoEncoderModule === undefined ) {
|
||||
|
||||
throw new Error( 'THREE.DRACOExporter: required the draco_encoder to work.' );
|
||||
|
||||
}
|
||||
|
||||
const geometry = object.geometry;
|
||||
|
||||
const dracoEncoder = DracoEncoderModule();
|
||||
const encoder = new dracoEncoder.Encoder();
|
||||
let builder;
|
||||
let dracoObject;
|
||||
|
||||
if ( object.isMesh === true ) {
|
||||
|
||||
builder = new dracoEncoder.MeshBuilder();
|
||||
dracoObject = new dracoEncoder.Mesh();
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
|
||||
|
||||
const faces = geometry.getIndex();
|
||||
|
||||
if ( faces !== null ) {
|
||||
|
||||
builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
|
||||
|
||||
} else {
|
||||
|
||||
const faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
|
||||
|
||||
for ( let i = 0; i < faces.length; i ++ ) {
|
||||
|
||||
faces[ i ] = i;
|
||||
|
||||
}
|
||||
|
||||
builder.AddFacesToMesh( dracoObject, vertices.count, faces );
|
||||
|
||||
}
|
||||
|
||||
if ( options.exportNormals === true ) {
|
||||
|
||||
const normals = geometry.getAttribute( 'normal' );
|
||||
|
||||
if ( normals !== undefined ) {
|
||||
|
||||
builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( options.exportUvs === true ) {
|
||||
|
||||
const uvs = geometry.getAttribute( 'uv' );
|
||||
|
||||
if ( uvs !== undefined ) {
|
||||
|
||||
builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( options.exportColor === true ) {
|
||||
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
|
||||
if ( colors !== undefined ) {
|
||||
|
||||
const array = createVertexColorSRGBArray( colors );
|
||||
|
||||
builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, array );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if ( object.isPoints === true ) {
|
||||
|
||||
builder = new dracoEncoder.PointCloudBuilder();
|
||||
dracoObject = new dracoEncoder.PointCloud();
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
|
||||
|
||||
if ( options.exportColor === true ) {
|
||||
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
|
||||
if ( colors !== undefined ) {
|
||||
|
||||
const array = createVertexColorSRGBArray( colors );
|
||||
|
||||
builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, array );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'DRACOExporter: Unsupported object type.' );
|
||||
|
||||
}
|
||||
|
||||
//Compress using draco encoder
|
||||
|
||||
const encodedData = new dracoEncoder.DracoInt8Array();
|
||||
|
||||
//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
|
||||
|
||||
const encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5;
|
||||
const decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5;
|
||||
|
||||
encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
|
||||
|
||||
// Sets the desired encoding method for a given geometry.
|
||||
|
||||
if ( options.encoderMethod !== undefined ) {
|
||||
|
||||
encoder.SetEncodingMethod( options.encoderMethod );
|
||||
|
||||
}
|
||||
|
||||
// Sets the quantization (number of bits used to represent) compression options for a named attribute.
|
||||
// The attribute values will be quantized in a box defined by the maximum extent of the attribute values.
|
||||
if ( options.quantization !== undefined ) {
|
||||
|
||||
for ( let i = 0; i < 5; i ++ ) {
|
||||
|
||||
if ( options.quantization[ i ] !== undefined ) {
|
||||
|
||||
encoder.SetAttributeQuantization( i, options.quantization[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let length;
|
||||
|
||||
if ( object.isMesh === true ) {
|
||||
|
||||
length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
|
||||
|
||||
} else {
|
||||
|
||||
length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
|
||||
|
||||
}
|
||||
|
||||
dracoEncoder.destroy( dracoObject );
|
||||
|
||||
if ( length === 0 ) {
|
||||
|
||||
throw new Error( 'THREE.DRACOExporter: Draco encoding failed.' );
|
||||
|
||||
}
|
||||
|
||||
//Copy encoded data to buffer.
|
||||
const outputData = new Int8Array( new ArrayBuffer( length ) );
|
||||
|
||||
for ( let i = 0; i < length; i ++ ) {
|
||||
|
||||
outputData[ i ] = encodedData.GetValue( i );
|
||||
|
||||
}
|
||||
|
||||
dracoEncoder.destroy( encodedData );
|
||||
dracoEncoder.destroy( encoder );
|
||||
dracoEncoder.destroy( builder );
|
||||
|
||||
return outputData;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createVertexColorSRGBArray( attribute ) {
|
||||
|
||||
// While .drc files do not specify colorspace, the only 'official' tooling
|
||||
// is PLY and OBJ converters, which use sRGB. We'll assume sRGB is expected
|
||||
// for .drc files, but note that Draco buffers embedded in glTF files will
|
||||
// be Linear-sRGB instead.
|
||||
|
||||
const _color = new Color();
|
||||
|
||||
const count = attribute.count;
|
||||
const itemSize = attribute.itemSize;
|
||||
const array = new Float32Array( count * itemSize );
|
||||
|
||||
for ( let i = 0, il = count; i < il; i ++ ) {
|
||||
|
||||
_color.fromBufferAttribute( attribute, i ).convertLinearToSRGB();
|
||||
|
||||
array[ i * itemSize ] = _color.r;
|
||||
array[ i * itemSize + 1 ] = _color.g;
|
||||
array[ i * itemSize + 2 ] = _color.b;
|
||||
|
||||
if ( itemSize === 4 ) {
|
||||
|
||||
array[ i * itemSize + 3 ] = attribute.getW( i );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array;
|
||||
|
||||
}
|
||||
|
||||
// Encoder methods
|
||||
|
||||
DRACOExporter.MESH_EDGEBREAKER_ENCODING = 1;
|
||||
DRACOExporter.MESH_SEQUENTIAL_ENCODING = 0;
|
||||
|
||||
// Geometry type
|
||||
|
||||
DRACOExporter.POINT_CLOUD = 0;
|
||||
DRACOExporter.TRIANGULAR_MESH = 1;
|
||||
|
||||
// Attribute type
|
||||
|
||||
DRACOExporter.INVALID = - 1;
|
||||
DRACOExporter.POSITION = 0;
|
||||
DRACOExporter.NORMAL = 1;
|
||||
DRACOExporter.COLOR = 2;
|
||||
DRACOExporter.TEX_COORD = 3;
|
||||
DRACOExporter.GENERIC = 4;
|
||||
|
||||
export { DRACOExporter };
|
||||
579
dist/electron/static/sdk/three/jsm/exporters/EXRExporter.js
vendored
Normal file
@ -0,0 +1,579 @@
|
||||
/**
|
||||
* @author sciecode / https://github.com/sciecode
|
||||
*
|
||||
* EXR format references:
|
||||
* https://www.openexr.com/documentation/openexrfilelayout.pdf
|
||||
*/
|
||||
|
||||
import {
|
||||
FloatType,
|
||||
HalfFloatType,
|
||||
RGBAFormat,
|
||||
DataUtils,
|
||||
} from 'three';
|
||||
import * as fflate from '../libs/fflate.module.js';
|
||||
|
||||
const textEncoder = new TextEncoder();
|
||||
|
||||
const NO_COMPRESSION = 0;
|
||||
const ZIPS_COMPRESSION = 2;
|
||||
const ZIP_COMPRESSION = 3;
|
||||
|
||||
class EXRExporter {
|
||||
|
||||
parse( arg1, arg2, arg3 ) {
|
||||
|
||||
if ( ! arg1 || ! ( arg1.isWebGLRenderer || arg1.isDataTexture ) ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported first parameter, expected instance of WebGLRenderer or DataTexture.' );
|
||||
|
||||
} else if ( arg1.isWebGLRenderer ) {
|
||||
|
||||
const renderer = arg1, renderTarget = arg2, options = arg3;
|
||||
|
||||
supportedRTT( renderTarget );
|
||||
|
||||
const info = buildInfoRTT( renderTarget, options ),
|
||||
dataBuffer = getPixelData( renderer, renderTarget, info ),
|
||||
rawContentBuffer = reorganizeDataBuffer( dataBuffer, info ),
|
||||
chunks = compressData( rawContentBuffer, info );
|
||||
|
||||
return fillData( chunks, info );
|
||||
|
||||
} else if ( arg1.isDataTexture ) {
|
||||
|
||||
const texture = arg1, options = arg2;
|
||||
|
||||
supportedDT( texture );
|
||||
|
||||
const info = buildInfoDT( texture, options ),
|
||||
dataBuffer = texture.image.data,
|
||||
rawContentBuffer = reorganizeDataBuffer( dataBuffer, info ),
|
||||
chunks = compressData( rawContentBuffer, info );
|
||||
|
||||
return fillData( chunks, info );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function supportedRTT( renderTarget ) {
|
||||
|
||||
if ( ! renderTarget || ! renderTarget.isWebGLRenderTarget ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported second parameter, expected instance of WebGLRenderTarget.' );
|
||||
|
||||
}
|
||||
|
||||
if ( renderTarget.isWebGLCubeRenderTarget || renderTarget.isWebGL3DRenderTarget || renderTarget.isWebGLArrayRenderTarget ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported render target type, expected instance of WebGLRenderTarget.' );
|
||||
|
||||
}
|
||||
|
||||
if ( renderTarget.texture.type !== FloatType && renderTarget.texture.type !== HalfFloatType ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported WebGLRenderTarget texture type.' );
|
||||
|
||||
}
|
||||
|
||||
if ( renderTarget.texture.format !== RGBAFormat ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported WebGLRenderTarget texture format, expected RGBAFormat.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function supportedDT( texture ) {
|
||||
|
||||
if ( texture.type !== FloatType && texture.type !== HalfFloatType ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported DataTexture texture type.' );
|
||||
|
||||
}
|
||||
|
||||
if ( texture.format !== RGBAFormat ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Unsupported DataTexture texture format, expected RGBAFormat.' );
|
||||
|
||||
}
|
||||
|
||||
if ( ! texture.image.data ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: Invalid DataTexture image data.' );
|
||||
|
||||
}
|
||||
|
||||
if ( texture.type === FloatType && texture.image.data.constructor.name !== 'Float32Array' ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: DataTexture image data doesn\'t match type, expected \'Float32Array\'.' );
|
||||
|
||||
}
|
||||
|
||||
if ( texture.type === HalfFloatType && texture.image.data.constructor.name !== 'Uint16Array' ) {
|
||||
|
||||
throw Error( 'EXRExporter.parse: DataTexture image data doesn\'t match type, expected \'Uint16Array\'.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function buildInfoRTT( renderTarget, options = {} ) {
|
||||
|
||||
const compressionSizes = {
|
||||
0: 1,
|
||||
2: 1,
|
||||
3: 16
|
||||
};
|
||||
|
||||
const WIDTH = renderTarget.width,
|
||||
HEIGHT = renderTarget.height,
|
||||
TYPE = renderTarget.texture.type,
|
||||
FORMAT = renderTarget.texture.format,
|
||||
COMPRESSION = ( options.compression !== undefined ) ? options.compression : ZIP_COMPRESSION,
|
||||
EXPORTER_TYPE = ( options.type !== undefined ) ? options.type : HalfFloatType,
|
||||
OUT_TYPE = ( EXPORTER_TYPE === FloatType ) ? 2 : 1,
|
||||
COMPRESSION_SIZE = compressionSizes[ COMPRESSION ],
|
||||
NUM_CHANNELS = 4;
|
||||
|
||||
return {
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
type: TYPE,
|
||||
format: FORMAT,
|
||||
compression: COMPRESSION,
|
||||
blockLines: COMPRESSION_SIZE,
|
||||
dataType: OUT_TYPE,
|
||||
dataSize: 2 * OUT_TYPE,
|
||||
numBlocks: Math.ceil( HEIGHT / COMPRESSION_SIZE ),
|
||||
numInputChannels: 4,
|
||||
numOutputChannels: NUM_CHANNELS,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function buildInfoDT( texture, options = {} ) {
|
||||
|
||||
const compressionSizes = {
|
||||
0: 1,
|
||||
2: 1,
|
||||
3: 16
|
||||
};
|
||||
|
||||
const WIDTH = texture.image.width,
|
||||
HEIGHT = texture.image.height,
|
||||
TYPE = texture.type,
|
||||
FORMAT = texture.format,
|
||||
COMPRESSION = ( options.compression !== undefined ) ? options.compression : ZIP_COMPRESSION,
|
||||
EXPORTER_TYPE = ( options.type !== undefined ) ? options.type : HalfFloatType,
|
||||
OUT_TYPE = ( EXPORTER_TYPE === FloatType ) ? 2 : 1,
|
||||
COMPRESSION_SIZE = compressionSizes[ COMPRESSION ],
|
||||
NUM_CHANNELS = 4;
|
||||
|
||||
return {
|
||||
width: WIDTH,
|
||||
height: HEIGHT,
|
||||
type: TYPE,
|
||||
format: FORMAT,
|
||||
compression: COMPRESSION,
|
||||
blockLines: COMPRESSION_SIZE,
|
||||
dataType: OUT_TYPE,
|
||||
dataSize: 2 * OUT_TYPE,
|
||||
numBlocks: Math.ceil( HEIGHT / COMPRESSION_SIZE ),
|
||||
numInputChannels: 4,
|
||||
numOutputChannels: NUM_CHANNELS,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function getPixelData( renderer, rtt, info ) {
|
||||
|
||||
let dataBuffer;
|
||||
|
||||
if ( info.type === FloatType ) {
|
||||
|
||||
dataBuffer = new Float32Array( info.width * info.height * info.numInputChannels );
|
||||
|
||||
} else {
|
||||
|
||||
dataBuffer = new Uint16Array( info.width * info.height * info.numInputChannels );
|
||||
|
||||
}
|
||||
|
||||
renderer.readRenderTargetPixels( rtt, 0, 0, info.width, info.height, dataBuffer );
|
||||
|
||||
return dataBuffer;
|
||||
|
||||
}
|
||||
|
||||
function reorganizeDataBuffer( inBuffer, info ) {
|
||||
|
||||
const w = info.width,
|
||||
h = info.height,
|
||||
dec = { r: 0, g: 0, b: 0, a: 0 },
|
||||
offset = { value: 0 },
|
||||
cOffset = ( info.numOutputChannels == 4 ) ? 1 : 0,
|
||||
getValue = ( info.type == FloatType ) ? getFloat32 : getFloat16,
|
||||
setValue = ( info.dataType == 1 ) ? setFloat16 : setFloat32,
|
||||
outBuffer = new Uint8Array( info.width * info.height * info.numOutputChannels * info.dataSize ),
|
||||
dv = new DataView( outBuffer.buffer );
|
||||
|
||||
for ( let y = 0; y < h; ++ y ) {
|
||||
|
||||
for ( let x = 0; x < w; ++ x ) {
|
||||
|
||||
const i = y * w * 4 + x * 4;
|
||||
|
||||
const r = getValue( inBuffer, i );
|
||||
const g = getValue( inBuffer, i + 1 );
|
||||
const b = getValue( inBuffer, i + 2 );
|
||||
const a = getValue( inBuffer, i + 3 );
|
||||
|
||||
const line = ( h - y - 1 ) * w * ( 3 + cOffset ) * info.dataSize;
|
||||
|
||||
decodeLinear( dec, r, g, b, a );
|
||||
|
||||
offset.value = line + x * info.dataSize;
|
||||
setValue( dv, dec.a, offset );
|
||||
|
||||
offset.value = line + ( cOffset ) * w * info.dataSize + x * info.dataSize;
|
||||
setValue( dv, dec.b, offset );
|
||||
|
||||
offset.value = line + ( 1 + cOffset ) * w * info.dataSize + x * info.dataSize;
|
||||
setValue( dv, dec.g, offset );
|
||||
|
||||
offset.value = line + ( 2 + cOffset ) * w * info.dataSize + x * info.dataSize;
|
||||
setValue( dv, dec.r, offset );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return outBuffer;
|
||||
|
||||
}
|
||||
|
||||
function compressData( inBuffer, info ) {
|
||||
|
||||
let compress,
|
||||
tmpBuffer,
|
||||
sum = 0;
|
||||
|
||||
const chunks = { data: new Array(), totalSize: 0 },
|
||||
size = info.width * info.numOutputChannels * info.blockLines * info.dataSize;
|
||||
|
||||
switch ( info.compression ) {
|
||||
|
||||
case 0:
|
||||
compress = compressNONE;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
compress = compressZIP;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( info.compression !== 0 ) {
|
||||
|
||||
tmpBuffer = new Uint8Array( size );
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < info.numBlocks; ++ i ) {
|
||||
|
||||
const arr = inBuffer.subarray( size * i, size * ( i + 1 ) );
|
||||
|
||||
const block = compress( arr, tmpBuffer );
|
||||
|
||||
sum += block.length;
|
||||
|
||||
chunks.data.push( { dataChunk: block, size: block.length } );
|
||||
|
||||
}
|
||||
|
||||
chunks.totalSize = sum;
|
||||
|
||||
return chunks;
|
||||
|
||||
}
|
||||
|
||||
function compressNONE( data ) {
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
function compressZIP( data, tmpBuffer ) {
|
||||
|
||||
//
|
||||
// Reorder the pixel data.
|
||||
//
|
||||
|
||||
let t1 = 0,
|
||||
t2 = Math.floor( ( data.length + 1 ) / 2 ),
|
||||
s = 0;
|
||||
|
||||
const stop = data.length - 1;
|
||||
|
||||
while ( true ) {
|
||||
|
||||
if ( s > stop ) break;
|
||||
tmpBuffer[ t1 ++ ] = data[ s ++ ];
|
||||
|
||||
if ( s > stop ) break;
|
||||
tmpBuffer[ t2 ++ ] = data[ s ++ ];
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// Predictor.
|
||||
//
|
||||
|
||||
let p = tmpBuffer[ 0 ];
|
||||
|
||||
for ( let t = 1; t < tmpBuffer.length; t ++ ) {
|
||||
|
||||
const d = tmpBuffer[ t ] - p + ( 128 + 256 );
|
||||
p = tmpBuffer[ t ];
|
||||
tmpBuffer[ t ] = d;
|
||||
|
||||
}
|
||||
|
||||
const deflate = fflate.zlibSync( tmpBuffer );
|
||||
|
||||
return deflate;
|
||||
|
||||
}
|
||||
|
||||
function fillHeader( outBuffer, chunks, info ) {
|
||||
|
||||
const offset = { value: 0 };
|
||||
const dv = new DataView( outBuffer.buffer );
|
||||
|
||||
setUint32( dv, 20000630, offset ); // magic
|
||||
setUint32( dv, 2, offset ); // mask
|
||||
|
||||
// = HEADER =
|
||||
|
||||
setString( dv, 'compression', offset );
|
||||
setString( dv, 'compression', offset );
|
||||
setUint32( dv, 1, offset );
|
||||
setUint8( dv, info.compression, offset );
|
||||
|
||||
setString( dv, 'screenWindowCenter', offset );
|
||||
setString( dv, 'v2f', offset );
|
||||
setUint32( dv, 8, offset );
|
||||
setUint32( dv, 0, offset );
|
||||
setUint32( dv, 0, offset );
|
||||
|
||||
setString( dv, 'screenWindowWidth', offset );
|
||||
setString( dv, 'float', offset );
|
||||
setUint32( dv, 4, offset );
|
||||
setFloat32( dv, 1.0, offset );
|
||||
|
||||
setString( dv, 'pixelAspectRatio', offset );
|
||||
setString( dv, 'float', offset );
|
||||
setUint32( dv, 4, offset );
|
||||
setFloat32( dv, 1.0, offset );
|
||||
|
||||
setString( dv, 'lineOrder', offset );
|
||||
setString( dv, 'lineOrder', offset );
|
||||
setUint32( dv, 1, offset );
|
||||
setUint8( dv, 0, offset );
|
||||
|
||||
setString( dv, 'dataWindow', offset );
|
||||
setString( dv, 'box2i', offset );
|
||||
setUint32( dv, 16, offset );
|
||||
setUint32( dv, 0, offset );
|
||||
setUint32( dv, 0, offset );
|
||||
setUint32( dv, info.width - 1, offset );
|
||||
setUint32( dv, info.height - 1, offset );
|
||||
|
||||
setString( dv, 'displayWindow', offset );
|
||||
setString( dv, 'box2i', offset );
|
||||
setUint32( dv, 16, offset );
|
||||
setUint32( dv, 0, offset );
|
||||
setUint32( dv, 0, offset );
|
||||
setUint32( dv, info.width - 1, offset );
|
||||
setUint32( dv, info.height - 1, offset );
|
||||
|
||||
setString( dv, 'channels', offset );
|
||||
setString( dv, 'chlist', offset );
|
||||
setUint32( dv, info.numOutputChannels * 18 + 1, offset );
|
||||
|
||||
setString( dv, 'A', offset );
|
||||
setUint32( dv, info.dataType, offset );
|
||||
offset.value += 4;
|
||||
setUint32( dv, 1, offset );
|
||||
setUint32( dv, 1, offset );
|
||||
|
||||
setString( dv, 'B', offset );
|
||||
setUint32( dv, info.dataType, offset );
|
||||
offset.value += 4;
|
||||
setUint32( dv, 1, offset );
|
||||
setUint32( dv, 1, offset );
|
||||
|
||||
setString( dv, 'G', offset );
|
||||
setUint32( dv, info.dataType, offset );
|
||||
offset.value += 4;
|
||||
setUint32( dv, 1, offset );
|
||||
setUint32( dv, 1, offset );
|
||||
|
||||
setString( dv, 'R', offset );
|
||||
setUint32( dv, info.dataType, offset );
|
||||
offset.value += 4;
|
||||
setUint32( dv, 1, offset );
|
||||
setUint32( dv, 1, offset );
|
||||
|
||||
setUint8( dv, 0, offset );
|
||||
|
||||
// null-byte
|
||||
setUint8( dv, 0, offset );
|
||||
|
||||
// = OFFSET TABLE =
|
||||
|
||||
let sum = offset.value + info.numBlocks * 8;
|
||||
|
||||
for ( let i = 0; i < chunks.data.length; ++ i ) {
|
||||
|
||||
setUint64( dv, sum, offset );
|
||||
|
||||
sum += chunks.data[ i ].size + 8;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function fillData( chunks, info ) {
|
||||
|
||||
const TableSize = info.numBlocks * 8,
|
||||
HeaderSize = 259 + ( 18 * info.numOutputChannels ), // 259 + 18 * chlist
|
||||
offset = { value: HeaderSize + TableSize },
|
||||
outBuffer = new Uint8Array( HeaderSize + TableSize + chunks.totalSize + info.numBlocks * 8 ),
|
||||
dv = new DataView( outBuffer.buffer );
|
||||
|
||||
fillHeader( outBuffer, chunks, info );
|
||||
|
||||
for ( let i = 0; i < chunks.data.length; ++ i ) {
|
||||
|
||||
const data = chunks.data[ i ].dataChunk;
|
||||
const size = chunks.data[ i ].size;
|
||||
|
||||
setUint32( dv, i * info.blockLines, offset );
|
||||
setUint32( dv, size, offset );
|
||||
|
||||
outBuffer.set( data, offset.value );
|
||||
offset.value += size;
|
||||
|
||||
}
|
||||
|
||||
return outBuffer;
|
||||
|
||||
}
|
||||
|
||||
function decodeLinear( dec, r, g, b, a ) {
|
||||
|
||||
dec.r = r;
|
||||
dec.g = g;
|
||||
dec.b = b;
|
||||
dec.a = a;
|
||||
|
||||
}
|
||||
|
||||
// function decodeSRGB( dec, r, g, b, a ) {
|
||||
|
||||
// dec.r = r > 0.04045 ? Math.pow( r * 0.9478672986 + 0.0521327014, 2.4 ) : r * 0.0773993808;
|
||||
// dec.g = g > 0.04045 ? Math.pow( g * 0.9478672986 + 0.0521327014, 2.4 ) : g * 0.0773993808;
|
||||
// dec.b = b > 0.04045 ? Math.pow( b * 0.9478672986 + 0.0521327014, 2.4 ) : b * 0.0773993808;
|
||||
// dec.a = a;
|
||||
|
||||
// }
|
||||
|
||||
|
||||
function setUint8( dv, value, offset ) {
|
||||
|
||||
dv.setUint8( offset.value, value );
|
||||
|
||||
offset.value += 1;
|
||||
|
||||
}
|
||||
|
||||
function setUint32( dv, value, offset ) {
|
||||
|
||||
dv.setUint32( offset.value, value, true );
|
||||
|
||||
offset.value += 4;
|
||||
|
||||
}
|
||||
|
||||
function setFloat16( dv, value, offset ) {
|
||||
|
||||
dv.setUint16( offset.value, DataUtils.toHalfFloat( value ), true );
|
||||
|
||||
offset.value += 2;
|
||||
|
||||
}
|
||||
|
||||
function setFloat32( dv, value, offset ) {
|
||||
|
||||
dv.setFloat32( offset.value, value, true );
|
||||
|
||||
offset.value += 4;
|
||||
|
||||
}
|
||||
|
||||
function setUint64( dv, value, offset ) {
|
||||
|
||||
dv.setBigUint64( offset.value, BigInt( value ), true );
|
||||
|
||||
offset.value += 8;
|
||||
|
||||
}
|
||||
|
||||
function setString( dv, string, offset ) {
|
||||
|
||||
const tmp = textEncoder.encode( string + '\0' );
|
||||
|
||||
for ( let i = 0; i < tmp.length; ++ i ) {
|
||||
|
||||
setUint8( dv, tmp[ i ], offset );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function decodeFloat16( binary ) {
|
||||
|
||||
const exponent = ( binary & 0x7C00 ) >> 10,
|
||||
fraction = binary & 0x03FF;
|
||||
|
||||
return ( binary >> 15 ? - 1 : 1 ) * (
|
||||
exponent ?
|
||||
(
|
||||
exponent === 0x1F ?
|
||||
fraction ? NaN : Infinity :
|
||||
Math.pow( 2, exponent - 15 ) * ( 1 + fraction / 0x400 )
|
||||
) :
|
||||
6.103515625e-5 * ( fraction / 0x400 )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function getFloat16( arr, i ) {
|
||||
|
||||
return decodeFloat16( arr[ i ] );
|
||||
|
||||
}
|
||||
|
||||
function getFloat32( arr, i ) {
|
||||
|
||||
return arr[ i ];
|
||||
|
||||
}
|
||||
|
||||
export { EXRExporter, NO_COMPRESSION, ZIP_COMPRESSION, ZIPS_COMPRESSION };
|
||||
3380
dist/electron/static/sdk/three/jsm/exporters/GLTFExporter.js
vendored
Normal file
292
dist/electron/static/sdk/three/jsm/exporters/KTX2Exporter.js
vendored
Normal file
@ -0,0 +1,292 @@
|
||||
import {
|
||||
FloatType,
|
||||
HalfFloatType,
|
||||
UnsignedByteType,
|
||||
RGBAFormat,
|
||||
RGFormat,
|
||||
RGIntegerFormat,
|
||||
RedFormat,
|
||||
RedIntegerFormat,
|
||||
NoColorSpace,
|
||||
LinearSRGBColorSpace,
|
||||
SRGBColorSpace,
|
||||
DataTexture,
|
||||
REVISION,
|
||||
} from 'three';
|
||||
|
||||
import {
|
||||
write,
|
||||
KTX2Container,
|
||||
KHR_DF_CHANNEL_RGBSDA_ALPHA,
|
||||
KHR_DF_CHANNEL_RGBSDA_BLUE,
|
||||
KHR_DF_CHANNEL_RGBSDA_GREEN,
|
||||
KHR_DF_CHANNEL_RGBSDA_RED,
|
||||
KHR_DF_MODEL_RGBSDA,
|
||||
KHR_DF_PRIMARIES_BT709,
|
||||
KHR_DF_PRIMARIES_UNSPECIFIED,
|
||||
KHR_DF_SAMPLE_DATATYPE_FLOAT,
|
||||
KHR_DF_SAMPLE_DATATYPE_LINEAR,
|
||||
KHR_DF_SAMPLE_DATATYPE_SIGNED,
|
||||
KHR_DF_TRANSFER_LINEAR,
|
||||
KHR_DF_TRANSFER_SRGB,
|
||||
VK_FORMAT_R16_SFLOAT,
|
||||
VK_FORMAT_R16G16_SFLOAT,
|
||||
VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
VK_FORMAT_R32_SFLOAT,
|
||||
VK_FORMAT_R32G32_SFLOAT,
|
||||
VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
VK_FORMAT_R8_SRGB,
|
||||
VK_FORMAT_R8_UNORM,
|
||||
VK_FORMAT_R8G8_SRGB,
|
||||
VK_FORMAT_R8G8_UNORM,
|
||||
VK_FORMAT_R8G8B8A8_SRGB,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
} from '../libs/ktx-parse.module.js';
|
||||
|
||||
const VK_FORMAT_MAP = {
|
||||
|
||||
[ RGBAFormat ]: {
|
||||
[ FloatType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R32G32B32A32_SFLOAT,
|
||||
},
|
||||
[ HalfFloatType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R16G16B16A16_SFLOAT,
|
||||
},
|
||||
[ UnsignedByteType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R8G8B8A8_UNORM,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R8G8B8A8_UNORM,
|
||||
[ SRGBColorSpace ]: VK_FORMAT_R8G8B8A8_SRGB,
|
||||
},
|
||||
},
|
||||
|
||||
[ RGFormat ]: {
|
||||
[ FloatType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R32G32_SFLOAT,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R32G32_SFLOAT,
|
||||
},
|
||||
[ HalfFloatType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R16G16_SFLOAT,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R16G16_SFLOAT,
|
||||
},
|
||||
[ UnsignedByteType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R8G8_UNORM,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R8G8_UNORM,
|
||||
[ SRGBColorSpace ]: VK_FORMAT_R8G8_SRGB,
|
||||
},
|
||||
},
|
||||
|
||||
[ RedFormat ]: {
|
||||
[ FloatType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R32_SFLOAT,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R32_SFLOAT,
|
||||
},
|
||||
[ HalfFloatType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R16_SFLOAT,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R16_SFLOAT,
|
||||
},
|
||||
[ UnsignedByteType ]: {
|
||||
[ NoColorSpace ]: VK_FORMAT_R8_UNORM,
|
||||
[ LinearSRGBColorSpace ]: VK_FORMAT_R8_UNORM,
|
||||
[ SRGBColorSpace ]: VK_FORMAT_R8_SRGB,
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const KHR_DF_CHANNEL_MAP = {
|
||||
|
||||
0: KHR_DF_CHANNEL_RGBSDA_RED,
|
||||
1: KHR_DF_CHANNEL_RGBSDA_GREEN,
|
||||
2: KHR_DF_CHANNEL_RGBSDA_BLUE,
|
||||
3: KHR_DF_CHANNEL_RGBSDA_ALPHA,
|
||||
|
||||
};
|
||||
|
||||
const ERROR_INPUT = 'THREE.KTX2Exporter: Supported inputs are DataTexture, Data3DTexture, or WebGLRenderer and WebGLRenderTarget.';
|
||||
const ERROR_FORMAT = 'THREE.KTX2Exporter: Supported formats are RGBAFormat, RGFormat, or RedFormat.';
|
||||
const ERROR_TYPE = 'THREE.KTX2Exporter: Supported types are FloatType, HalfFloatType, or UnsignedByteType."';
|
||||
const ERROR_COLOR_SPACE = 'THREE.KTX2Exporter: Supported color spaces are SRGBColorSpace (UnsignedByteType only), LinearSRGBColorSpace, or NoColorSpace.';
|
||||
|
||||
export class KTX2Exporter {
|
||||
|
||||
parse( arg1, arg2 ) {
|
||||
|
||||
let texture;
|
||||
|
||||
if ( arg1.isDataTexture || arg1.isData3DTexture ) {
|
||||
|
||||
texture = arg1;
|
||||
|
||||
} else if ( arg1.isWebGLRenderer && arg2.isWebGLRenderTarget ) {
|
||||
|
||||
texture = toDataTexture( arg1, arg2 );
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( ERROR_INPUT );
|
||||
|
||||
}
|
||||
|
||||
if ( VK_FORMAT_MAP[ texture.format ] === undefined ) {
|
||||
|
||||
throw new Error( ERROR_FORMAT );
|
||||
|
||||
}
|
||||
|
||||
if ( VK_FORMAT_MAP[ texture.format ][ texture.type ] === undefined ) {
|
||||
|
||||
throw new Error( ERROR_TYPE );
|
||||
|
||||
}
|
||||
|
||||
if ( VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ] === undefined ) {
|
||||
|
||||
throw new Error( ERROR_COLOR_SPACE );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const array = texture.image.data;
|
||||
const channelCount = getChannelCount( texture );
|
||||
const container = new KTX2Container();
|
||||
|
||||
container.vkFormat = VK_FORMAT_MAP[ texture.format ][ texture.type ][ texture.colorSpace ];
|
||||
container.typeSize = array.BYTES_PER_ELEMENT;
|
||||
container.pixelWidth = texture.image.width;
|
||||
container.pixelHeight = texture.image.height;
|
||||
|
||||
if ( texture.isData3DTexture ) {
|
||||
|
||||
container.pixelDepth = texture.image.depth;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const basicDesc = container.dataFormatDescriptor[ 0 ];
|
||||
|
||||
basicDesc.colorModel = KHR_DF_MODEL_RGBSDA;
|
||||
basicDesc.colorPrimaries = texture.colorSpace === NoColorSpace
|
||||
? KHR_DF_PRIMARIES_UNSPECIFIED
|
||||
: KHR_DF_PRIMARIES_BT709;
|
||||
basicDesc.transferFunction = texture.colorSpace === SRGBColorSpace
|
||||
? KHR_DF_TRANSFER_SRGB
|
||||
: KHR_DF_TRANSFER_LINEAR;
|
||||
|
||||
basicDesc.texelBlockDimension = [ 0, 0, 0, 0 ];
|
||||
|
||||
basicDesc.bytesPlane = [
|
||||
|
||||
container.typeSize * channelCount, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
];
|
||||
|
||||
for ( let i = 0; i < channelCount; ++ i ) {
|
||||
|
||||
let channelType = KHR_DF_CHANNEL_MAP[ i ];
|
||||
|
||||
if ( texture.colorSpace === LinearSRGBColorSpace || texture.colorSpace === NoColorSpace ) {
|
||||
|
||||
channelType |= KHR_DF_SAMPLE_DATATYPE_LINEAR;
|
||||
|
||||
}
|
||||
|
||||
if ( texture.type === FloatType || texture.type === HalfFloatType ) {
|
||||
|
||||
channelType |= KHR_DF_SAMPLE_DATATYPE_FLOAT;
|
||||
channelType |= KHR_DF_SAMPLE_DATATYPE_SIGNED;
|
||||
|
||||
}
|
||||
|
||||
basicDesc.samples.push( {
|
||||
|
||||
channelType: channelType,
|
||||
bitOffset: i * array.BYTES_PER_ELEMENT,
|
||||
bitLength: array.BYTES_PER_ELEMENT * 8 - 1,
|
||||
samplePosition: [ 0, 0, 0, 0 ],
|
||||
sampleLower: texture.type === UnsignedByteType ? 0 : - 1,
|
||||
sampleUpper: texture.type === UnsignedByteType ? 255 : 1,
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
container.levels = [ {
|
||||
|
||||
levelData: new Uint8Array( array.buffer, array.byteOffset, array.byteLength ),
|
||||
uncompressedByteLength: array.byteLength,
|
||||
|
||||
} ];
|
||||
|
||||
//
|
||||
|
||||
container.keyValue[ 'KTXwriter' ] = `three.js ${ REVISION }`;
|
||||
|
||||
//
|
||||
|
||||
return write( container, { keepWriter: true } );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function toDataTexture( renderer, rtt ) {
|
||||
|
||||
const channelCount = getChannelCount( rtt.texture );
|
||||
|
||||
let view;
|
||||
|
||||
if ( rtt.texture.type === FloatType ) {
|
||||
|
||||
view = new Float32Array( rtt.width * rtt.height * channelCount );
|
||||
|
||||
} else if ( rtt.texture.type === HalfFloatType ) {
|
||||
|
||||
view = new Uint16Array( rtt.width * rtt.height * channelCount );
|
||||
|
||||
} else if ( rtt.texture.type === UnsignedByteType ) {
|
||||
|
||||
view = new Uint8Array( rtt.width * rtt.height * channelCount );
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( ERROR_TYPE );
|
||||
|
||||
}
|
||||
|
||||
renderer.readRenderTargetPixels( rtt, 0, 0, rtt.width, rtt.height, view );
|
||||
|
||||
return new DataTexture( view, rtt.width, rtt.height, rtt.texture.format, rtt.texture.type );
|
||||
|
||||
}
|
||||
|
||||
function getChannelCount( texture ) {
|
||||
|
||||
switch ( texture.format ) {
|
||||
|
||||
case RGBAFormat:
|
||||
|
||||
return 4;
|
||||
|
||||
case RGFormat:
|
||||
case RGIntegerFormat:
|
||||
|
||||
return 2;
|
||||
|
||||
case RedFormat:
|
||||
case RedIntegerFormat:
|
||||
|
||||
return 1;
|
||||
|
||||
default:
|
||||
|
||||
throw new Error( ERROR_FORMAT );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
217
dist/electron/static/sdk/three/jsm/exporters/MMDExporter.js
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
import {
|
||||
Matrix4,
|
||||
Quaternion,
|
||||
Vector3
|
||||
} from 'three';
|
||||
import { MMDParser } from '../libs/mmdparser.module.js';
|
||||
|
||||
/**
|
||||
* Dependencies
|
||||
* - mmd-parser https://github.com/takahirox/mmd-parser
|
||||
*/
|
||||
|
||||
class MMDExporter {
|
||||
|
||||
/* TODO: implement
|
||||
// mesh -> pmd
|
||||
this.parsePmd = function ( object ) {
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
/* TODO: implement
|
||||
// mesh -> pmx
|
||||
this.parsePmx = function ( object ) {
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
/* TODO: implement
|
||||
// animation + skeleton -> vmd
|
||||
this.parseVmd = function ( object ) {
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
* skeleton -> vpd
|
||||
* Returns Shift_JIS encoded Uint8Array. Otherwise return strings.
|
||||
*/
|
||||
parseVpd( skin, outputShiftJis, useOriginalBones ) {
|
||||
|
||||
if ( skin.isSkinnedMesh !== true ) {
|
||||
|
||||
console.warn( 'THREE.MMDExporter: parseVpd() requires SkinnedMesh instance.' );
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
function toStringsFromNumber( num ) {
|
||||
|
||||
if ( Math.abs( num ) < 1e-6 ) num = 0;
|
||||
|
||||
let a = num.toString();
|
||||
|
||||
if ( a.indexOf( '.' ) === - 1 ) {
|
||||
|
||||
a += '.';
|
||||
|
||||
}
|
||||
|
||||
a += '000000';
|
||||
|
||||
const index = a.indexOf( '.' );
|
||||
|
||||
const d = a.slice( 0, index );
|
||||
const p = a.slice( index + 1, index + 7 );
|
||||
|
||||
return d + '.' + p;
|
||||
|
||||
}
|
||||
|
||||
function toStringsFromArray( array ) {
|
||||
|
||||
const a = [];
|
||||
|
||||
for ( let i = 0, il = array.length; i < il; i ++ ) {
|
||||
|
||||
a.push( toStringsFromNumber( array[ i ] ) );
|
||||
|
||||
}
|
||||
|
||||
return a.join( ',' );
|
||||
|
||||
}
|
||||
|
||||
skin.updateMatrixWorld( true );
|
||||
|
||||
const bones = skin.skeleton.bones;
|
||||
const bones2 = getBindBones( skin );
|
||||
|
||||
const position = new Vector3();
|
||||
const quaternion = new Quaternion();
|
||||
const quaternion2 = new Quaternion();
|
||||
const matrix = new Matrix4();
|
||||
|
||||
const array = [];
|
||||
array.push( 'Vocaloid Pose Data file' );
|
||||
array.push( '' );
|
||||
array.push( ( skin.name !== '' ? skin.name.replace( /\s/g, '_' ) : 'skin' ) + '.osm;' );
|
||||
array.push( bones.length + ';' );
|
||||
array.push( '' );
|
||||
|
||||
for ( let i = 0, il = bones.length; i < il; i ++ ) {
|
||||
|
||||
const bone = bones[ i ];
|
||||
const bone2 = bones2[ i ];
|
||||
|
||||
/*
|
||||
* use the bone matrix saved before solving IK.
|
||||
* see CCDIKSolver for the detail.
|
||||
*/
|
||||
if ( useOriginalBones === true &&
|
||||
bone.userData.ik !== undefined &&
|
||||
bone.userData.ik.originalMatrix !== undefined ) {
|
||||
|
||||
matrix.fromArray( bone.userData.ik.originalMatrix );
|
||||
|
||||
} else {
|
||||
|
||||
matrix.copy( bone.matrix );
|
||||
|
||||
}
|
||||
|
||||
position.setFromMatrixPosition( matrix );
|
||||
quaternion.setFromRotationMatrix( matrix );
|
||||
|
||||
const pArray = position.sub( bone2.position ).toArray();
|
||||
const qArray = quaternion2.copy( bone2.quaternion ).conjugate().multiply( quaternion ).toArray();
|
||||
|
||||
// right to left
|
||||
pArray[ 2 ] = - pArray[ 2 ];
|
||||
qArray[ 0 ] = - qArray[ 0 ];
|
||||
qArray[ 1 ] = - qArray[ 1 ];
|
||||
|
||||
array.push( 'Bone' + i + '{' + bone.name );
|
||||
array.push( ' ' + toStringsFromArray( pArray ) + ';' );
|
||||
array.push( ' ' + toStringsFromArray( qArray ) + ';' );
|
||||
array.push( '}' );
|
||||
array.push( '' );
|
||||
|
||||
}
|
||||
|
||||
array.push( '' );
|
||||
|
||||
const lines = array.join( '\n' );
|
||||
|
||||
return ( outputShiftJis === true ) ? unicodeToShiftjis( lines ) : lines;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Unicode to Shift_JIS table
|
||||
let u2sTable;
|
||||
|
||||
function unicodeToShiftjis( str ) {
|
||||
|
||||
if ( u2sTable === undefined ) {
|
||||
|
||||
const encoder = new MMDParser.CharsetEncoder();
|
||||
const table = encoder.s2uTable;
|
||||
u2sTable = {};
|
||||
|
||||
const keys = Object.keys( table );
|
||||
|
||||
for ( let i = 0, il = keys.length; i < il; i ++ ) {
|
||||
|
||||
let key = keys[ i ];
|
||||
|
||||
const value = table[ key ];
|
||||
key = parseInt( key );
|
||||
|
||||
u2sTable[ value ] = key;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const array = [];
|
||||
|
||||
for ( let i = 0, il = str.length; i < il; i ++ ) {
|
||||
|
||||
const code = str.charCodeAt( i );
|
||||
|
||||
const value = u2sTable[ code ];
|
||||
|
||||
if ( value === undefined ) {
|
||||
|
||||
throw new Error( 'cannot convert charcode 0x' + code.toString( 16 ) );
|
||||
|
||||
} else if ( value > 0xff ) {
|
||||
|
||||
array.push( ( value >> 8 ) & 0xff );
|
||||
array.push( value & 0xff );
|
||||
|
||||
} else {
|
||||
|
||||
array.push( value & 0xff );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Uint8Array( array );
|
||||
|
||||
}
|
||||
|
||||
function getBindBones( skin ) {
|
||||
|
||||
// any more efficient ways?
|
||||
const poseSkin = skin.clone();
|
||||
poseSkin.pose();
|
||||
return poseSkin.skeleton.bones;
|
||||
|
||||
}
|
||||
|
||||
export { MMDExporter };
|
||||
284
dist/electron/static/sdk/three/jsm/exporters/OBJExporter.js
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
import {
|
||||
Color,
|
||||
Matrix3,
|
||||
Vector2,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
class OBJExporter {
|
||||
|
||||
parse( object ) {
|
||||
|
||||
let output = '';
|
||||
|
||||
let indexVertex = 0;
|
||||
let indexVertexUvs = 0;
|
||||
let indexNormals = 0;
|
||||
|
||||
const vertex = new Vector3();
|
||||
const color = new Color();
|
||||
const normal = new Vector3();
|
||||
const uv = new Vector2();
|
||||
|
||||
const face = [];
|
||||
|
||||
function parseMesh( mesh ) {
|
||||
|
||||
let nbVertex = 0;
|
||||
let nbNormals = 0;
|
||||
let nbVertexUvs = 0;
|
||||
|
||||
const geometry = mesh.geometry;
|
||||
|
||||
const normalMatrixWorld = new Matrix3();
|
||||
|
||||
// shortcuts
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
const normals = geometry.getAttribute( 'normal' );
|
||||
const uvs = geometry.getAttribute( 'uv' );
|
||||
const indices = geometry.getIndex();
|
||||
|
||||
// name of the mesh object
|
||||
output += 'o ' + mesh.name + '\n';
|
||||
|
||||
// name of the mesh material
|
||||
if ( mesh.material && mesh.material.name ) {
|
||||
|
||||
output += 'usemtl ' + mesh.material.name + '\n';
|
||||
|
||||
}
|
||||
|
||||
// vertices
|
||||
|
||||
if ( vertices !== undefined ) {
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( vertices, i );
|
||||
|
||||
// transform the vertex to world space
|
||||
vertex.applyMatrix4( mesh.matrixWorld );
|
||||
|
||||
// transform the vertex to export format
|
||||
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// uvs
|
||||
|
||||
if ( uvs !== undefined ) {
|
||||
|
||||
for ( let i = 0, l = uvs.count; i < l; i ++, nbVertexUvs ++ ) {
|
||||
|
||||
uv.fromBufferAttribute( uvs, i );
|
||||
|
||||
// transform the uv to export format
|
||||
output += 'vt ' + uv.x + ' ' + uv.y + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// normals
|
||||
|
||||
if ( normals !== undefined ) {
|
||||
|
||||
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
|
||||
|
||||
for ( let i = 0, l = normals.count; i < l; i ++, nbNormals ++ ) {
|
||||
|
||||
normal.fromBufferAttribute( normals, i );
|
||||
|
||||
// transform the normal to world space
|
||||
normal.applyMatrix3( normalMatrixWorld ).normalize();
|
||||
|
||||
// transform the normal to export format
|
||||
output += 'vn ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// faces
|
||||
|
||||
if ( indices !== null ) {
|
||||
|
||||
for ( let i = 0, l = indices.count; i < l; i += 3 ) {
|
||||
|
||||
for ( let m = 0; m < 3; m ++ ) {
|
||||
|
||||
const j = indices.getX( i + m ) + 1;
|
||||
|
||||
face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
|
||||
|
||||
}
|
||||
|
||||
// transform the face to export format
|
||||
output += 'f ' + face.join( ' ' ) + '\n';
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
|
||||
|
||||
for ( let m = 0; m < 3; m ++ ) {
|
||||
|
||||
const j = i + m + 1;
|
||||
|
||||
face[ m ] = ( indexVertex + j ) + ( normals || uvs ? '/' + ( uvs ? ( indexVertexUvs + j ) : '' ) + ( normals ? '/' + ( indexNormals + j ) : '' ) : '' );
|
||||
|
||||
}
|
||||
|
||||
// transform the face to export format
|
||||
output += 'f ' + face.join( ' ' ) + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update index
|
||||
indexVertex += nbVertex;
|
||||
indexVertexUvs += nbVertexUvs;
|
||||
indexNormals += nbNormals;
|
||||
|
||||
}
|
||||
|
||||
function parseLine( line ) {
|
||||
|
||||
let nbVertex = 0;
|
||||
|
||||
const geometry = line.geometry;
|
||||
const type = line.type;
|
||||
|
||||
// shortcuts
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
|
||||
// name of the line object
|
||||
output += 'o ' + line.name + '\n';
|
||||
|
||||
if ( vertices !== undefined ) {
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( vertices, i );
|
||||
|
||||
// transform the vertex to world space
|
||||
vertex.applyMatrix4( line.matrixWorld );
|
||||
|
||||
// transform the vertex to export format
|
||||
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( type === 'Line' ) {
|
||||
|
||||
output += 'l ';
|
||||
|
||||
for ( let j = 1, l = vertices.count; j <= l; j ++ ) {
|
||||
|
||||
output += ( indexVertex + j ) + ' ';
|
||||
|
||||
}
|
||||
|
||||
output += '\n';
|
||||
|
||||
}
|
||||
|
||||
if ( type === 'LineSegments' ) {
|
||||
|
||||
for ( let j = 1, k = j + 1, l = vertices.count; j < l; j += 2, k = j + 1 ) {
|
||||
|
||||
output += 'l ' + ( indexVertex + j ) + ' ' + ( indexVertex + k ) + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// update index
|
||||
indexVertex += nbVertex;
|
||||
|
||||
}
|
||||
|
||||
function parsePoints( points ) {
|
||||
|
||||
let nbVertex = 0;
|
||||
|
||||
const geometry = points.geometry;
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
|
||||
output += 'o ' + points.name + '\n';
|
||||
|
||||
if ( vertices !== undefined ) {
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( vertices, i );
|
||||
vertex.applyMatrix4( points.matrixWorld );
|
||||
|
||||
output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
|
||||
|
||||
if ( colors !== undefined ) {
|
||||
|
||||
color.fromBufferAttribute( colors, i ).convertLinearToSRGB();
|
||||
|
||||
output += ' ' + color.r + ' ' + color.g + ' ' + color.b;
|
||||
|
||||
}
|
||||
|
||||
output += '\n';
|
||||
|
||||
}
|
||||
|
||||
output += 'p ';
|
||||
|
||||
for ( let j = 1, l = vertices.count; j <= l; j ++ ) {
|
||||
|
||||
output += ( indexVertex + j ) + ' ';
|
||||
|
||||
}
|
||||
|
||||
output += '\n';
|
||||
|
||||
}
|
||||
|
||||
// update index
|
||||
indexVertex += nbVertex;
|
||||
|
||||
}
|
||||
|
||||
object.traverse( function ( child ) {
|
||||
|
||||
if ( child.isMesh === true ) {
|
||||
|
||||
parseMesh( child );
|
||||
|
||||
}
|
||||
|
||||
if ( child.isLine === true ) {
|
||||
|
||||
parseLine( child );
|
||||
|
||||
}
|
||||
|
||||
if ( child.isPoints === true ) {
|
||||
|
||||
parsePoints( child );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { OBJExporter };
|
||||
528
dist/electron/static/sdk/three/jsm/exporters/PLYExporter.js
vendored
Normal file
@ -0,0 +1,528 @@
|
||||
import {
|
||||
Matrix3,
|
||||
Vector3,
|
||||
Color
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* https://github.com/gkjohnson/ply-exporter-js
|
||||
*
|
||||
* Usage:
|
||||
* const exporter = new PLYExporter();
|
||||
*
|
||||
* // second argument is a list of options
|
||||
* exporter.parse(mesh, data => console.log(data), { binary: true, excludeAttributes: [ 'color' ], littleEndian: true });
|
||||
*
|
||||
* Format Definition:
|
||||
* http://paulbourke.net/dataformats/ply/
|
||||
*/
|
||||
|
||||
class PLYExporter {
|
||||
|
||||
parse( object, onDone, options = {} ) {
|
||||
|
||||
// Iterate over the valid meshes in the object
|
||||
function traverseMeshes( cb ) {
|
||||
|
||||
object.traverse( function ( child ) {
|
||||
|
||||
if ( child.isMesh === true || child.isPoints ) {
|
||||
|
||||
const mesh = child;
|
||||
const geometry = mesh.geometry;
|
||||
|
||||
if ( geometry.hasAttribute( 'position' ) === true ) {
|
||||
|
||||
cb( mesh, geometry );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
// Default options
|
||||
const defaultOptions = {
|
||||
binary: false,
|
||||
excludeAttributes: [], // normal, uv, color, index
|
||||
littleEndian: false
|
||||
};
|
||||
|
||||
options = Object.assign( defaultOptions, options );
|
||||
|
||||
const excludeAttributes = options.excludeAttributes;
|
||||
let includeIndices = true;
|
||||
let includeNormals = false;
|
||||
let includeColors = false;
|
||||
let includeUVs = false;
|
||||
|
||||
// count the vertices, check which properties are used,
|
||||
// and cache the BufferGeometry
|
||||
let vertexCount = 0;
|
||||
let faceCount = 0;
|
||||
|
||||
object.traverse( function ( child ) {
|
||||
|
||||
if ( child.isMesh === true ) {
|
||||
|
||||
const mesh = child;
|
||||
const geometry = mesh.geometry;
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
const normals = geometry.getAttribute( 'normal' );
|
||||
const uvs = geometry.getAttribute( 'uv' );
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
const indices = geometry.getIndex();
|
||||
|
||||
if ( vertices === undefined ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
vertexCount += vertices.count;
|
||||
faceCount += indices ? indices.count / 3 : vertices.count / 3;
|
||||
|
||||
if ( normals !== undefined ) includeNormals = true;
|
||||
|
||||
if ( uvs !== undefined ) includeUVs = true;
|
||||
|
||||
if ( colors !== undefined ) includeColors = true;
|
||||
|
||||
} else if ( child.isPoints ) {
|
||||
|
||||
const mesh = child;
|
||||
const geometry = mesh.geometry;
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
const normals = geometry.getAttribute( 'normal' );
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
|
||||
vertexCount += vertices.count;
|
||||
|
||||
if ( normals !== undefined ) includeNormals = true;
|
||||
|
||||
if ( colors !== undefined ) includeColors = true;
|
||||
|
||||
includeIndices = false;
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
const tempColor = new Color();
|
||||
includeIndices = includeIndices && excludeAttributes.indexOf( 'index' ) === - 1;
|
||||
includeNormals = includeNormals && excludeAttributes.indexOf( 'normal' ) === - 1;
|
||||
includeColors = includeColors && excludeAttributes.indexOf( 'color' ) === - 1;
|
||||
includeUVs = includeUVs && excludeAttributes.indexOf( 'uv' ) === - 1;
|
||||
|
||||
|
||||
if ( includeIndices && faceCount !== Math.floor( faceCount ) ) {
|
||||
|
||||
// point cloud meshes will not have an index array and may not have a
|
||||
// number of vertices that is divisble by 3 (and therefore representable
|
||||
// as triangles)
|
||||
console.error(
|
||||
|
||||
'PLYExporter: Failed to generate a valid PLY file with triangle indices because the ' +
|
||||
'number of indices is not divisible by 3.'
|
||||
|
||||
);
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
const indexByteCount = 4;
|
||||
|
||||
let header =
|
||||
'ply\n' +
|
||||
`format ${ options.binary ? ( options.littleEndian ? 'binary_little_endian' : 'binary_big_endian' ) : 'ascii' } 1.0\n` +
|
||||
`element vertex ${vertexCount}\n` +
|
||||
|
||||
// position
|
||||
'property float x\n' +
|
||||
'property float y\n' +
|
||||
'property float z\n';
|
||||
|
||||
if ( includeNormals === true ) {
|
||||
|
||||
// normal
|
||||
header +=
|
||||
'property float nx\n' +
|
||||
'property float ny\n' +
|
||||
'property float nz\n';
|
||||
|
||||
}
|
||||
|
||||
if ( includeUVs === true ) {
|
||||
|
||||
// uvs
|
||||
header +=
|
||||
'property float s\n' +
|
||||
'property float t\n';
|
||||
|
||||
}
|
||||
|
||||
if ( includeColors === true ) {
|
||||
|
||||
// colors
|
||||
header +=
|
||||
'property uchar red\n' +
|
||||
'property uchar green\n' +
|
||||
'property uchar blue\n';
|
||||
|
||||
}
|
||||
|
||||
if ( includeIndices === true ) {
|
||||
|
||||
// faces
|
||||
header +=
|
||||
`element face ${faceCount}\n` +
|
||||
'property list uchar int vertex_index\n';
|
||||
|
||||
}
|
||||
|
||||
header += 'end_header\n';
|
||||
|
||||
|
||||
// Generate attribute data
|
||||
const vertex = new Vector3();
|
||||
const normalMatrixWorld = new Matrix3();
|
||||
let result = null;
|
||||
|
||||
if ( options.binary === true ) {
|
||||
|
||||
// Binary File Generation
|
||||
const headerBin = new TextEncoder().encode( header );
|
||||
|
||||
// 3 position values at 4 bytes
|
||||
// 3 normal values at 4 bytes
|
||||
// 3 color channels with 1 byte
|
||||
// 2 uv values at 4 bytes
|
||||
const vertexListLength = vertexCount * ( 4 * 3 + ( includeNormals ? 4 * 3 : 0 ) + ( includeColors ? 3 : 0 ) + ( includeUVs ? 4 * 2 : 0 ) );
|
||||
|
||||
// 1 byte shape desciptor
|
||||
// 3 vertex indices at ${indexByteCount} bytes
|
||||
const faceListLength = includeIndices ? faceCount * ( indexByteCount * 3 + 1 ) : 0;
|
||||
const output = new DataView( new ArrayBuffer( headerBin.length + vertexListLength + faceListLength ) );
|
||||
new Uint8Array( output.buffer ).set( headerBin, 0 );
|
||||
|
||||
|
||||
let vOffset = headerBin.length;
|
||||
let fOffset = headerBin.length + vertexListLength;
|
||||
let writtenVertices = 0;
|
||||
traverseMeshes( function ( mesh, geometry ) {
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
const normals = geometry.getAttribute( 'normal' );
|
||||
const uvs = geometry.getAttribute( 'uv' );
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
const indices = geometry.getIndex();
|
||||
|
||||
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( vertices, i );
|
||||
|
||||
vertex.applyMatrix4( mesh.matrixWorld );
|
||||
|
||||
|
||||
// Position information
|
||||
output.setFloat32( vOffset, vertex.x, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, vertex.y, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, vertex.z, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
// Normal information
|
||||
if ( includeNormals === true ) {
|
||||
|
||||
if ( normals != null ) {
|
||||
|
||||
vertex.fromBufferAttribute( normals, i );
|
||||
|
||||
vertex.applyMatrix3( normalMatrixWorld ).normalize();
|
||||
|
||||
output.setFloat32( vOffset, vertex.x, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, vertex.y, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, vertex.z, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
} else {
|
||||
|
||||
output.setFloat32( vOffset, 0, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, 0, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, 0, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// UV information
|
||||
if ( includeUVs === true ) {
|
||||
|
||||
if ( uvs != null ) {
|
||||
|
||||
output.setFloat32( vOffset, uvs.getX( i ), options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, uvs.getY( i ), options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
} else {
|
||||
|
||||
output.setFloat32( vOffset, 0, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
output.setFloat32( vOffset, 0, options.littleEndian );
|
||||
vOffset += 4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Color information
|
||||
if ( includeColors === true ) {
|
||||
|
||||
if ( colors != null ) {
|
||||
|
||||
tempColor
|
||||
.fromBufferAttribute( colors, i )
|
||||
.convertLinearToSRGB();
|
||||
|
||||
output.setUint8( vOffset, Math.floor( tempColor.r * 255 ) );
|
||||
vOffset += 1;
|
||||
|
||||
output.setUint8( vOffset, Math.floor( tempColor.g * 255 ) );
|
||||
vOffset += 1;
|
||||
|
||||
output.setUint8( vOffset, Math.floor( tempColor.b * 255 ) );
|
||||
vOffset += 1;
|
||||
|
||||
} else {
|
||||
|
||||
output.setUint8( vOffset, 255 );
|
||||
vOffset += 1;
|
||||
|
||||
output.setUint8( vOffset, 255 );
|
||||
vOffset += 1;
|
||||
|
||||
output.setUint8( vOffset, 255 );
|
||||
vOffset += 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( includeIndices === true ) {
|
||||
|
||||
// Create the face list
|
||||
|
||||
if ( indices !== null ) {
|
||||
|
||||
for ( let i = 0, l = indices.count; i < l; i += 3 ) {
|
||||
|
||||
output.setUint8( fOffset, 3 );
|
||||
fOffset += 1;
|
||||
|
||||
output.setUint32( fOffset, indices.getX( i + 0 ) + writtenVertices, options.littleEndian );
|
||||
fOffset += indexByteCount;
|
||||
|
||||
output.setUint32( fOffset, indices.getX( i + 1 ) + writtenVertices, options.littleEndian );
|
||||
fOffset += indexByteCount;
|
||||
|
||||
output.setUint32( fOffset, indices.getX( i + 2 ) + writtenVertices, options.littleEndian );
|
||||
fOffset += indexByteCount;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
|
||||
|
||||
output.setUint8( fOffset, 3 );
|
||||
fOffset += 1;
|
||||
|
||||
output.setUint32( fOffset, writtenVertices + i, options.littleEndian );
|
||||
fOffset += indexByteCount;
|
||||
|
||||
output.setUint32( fOffset, writtenVertices + i + 1, options.littleEndian );
|
||||
fOffset += indexByteCount;
|
||||
|
||||
output.setUint32( fOffset, writtenVertices + i + 2, options.littleEndian );
|
||||
fOffset += indexByteCount;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Save the amount of verts we've already written so we can offset
|
||||
// the face index on the next mesh
|
||||
writtenVertices += vertices.count;
|
||||
|
||||
} );
|
||||
|
||||
result = output.buffer;
|
||||
|
||||
} else {
|
||||
|
||||
// Ascii File Generation
|
||||
// count the number of vertices
|
||||
let writtenVertices = 0;
|
||||
let vertexList = '';
|
||||
let faceList = '';
|
||||
|
||||
traverseMeshes( function ( mesh, geometry ) {
|
||||
|
||||
const vertices = geometry.getAttribute( 'position' );
|
||||
const normals = geometry.getAttribute( 'normal' );
|
||||
const uvs = geometry.getAttribute( 'uv' );
|
||||
const colors = geometry.getAttribute( 'color' );
|
||||
const indices = geometry.getIndex();
|
||||
|
||||
normalMatrixWorld.getNormalMatrix( mesh.matrixWorld );
|
||||
|
||||
// form each line
|
||||
for ( let i = 0, l = vertices.count; i < l; i ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( vertices, i );
|
||||
|
||||
vertex.applyMatrix4( mesh.matrixWorld );
|
||||
|
||||
|
||||
// Position information
|
||||
let line =
|
||||
vertex.x + ' ' +
|
||||
vertex.y + ' ' +
|
||||
vertex.z;
|
||||
|
||||
// Normal information
|
||||
if ( includeNormals === true ) {
|
||||
|
||||
if ( normals != null ) {
|
||||
|
||||
vertex.fromBufferAttribute( normals, i );
|
||||
|
||||
vertex.applyMatrix3( normalMatrixWorld ).normalize();
|
||||
|
||||
line += ' ' +
|
||||
vertex.x + ' ' +
|
||||
vertex.y + ' ' +
|
||||
vertex.z;
|
||||
|
||||
} else {
|
||||
|
||||
line += ' 0 0 0';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// UV information
|
||||
if ( includeUVs === true ) {
|
||||
|
||||
if ( uvs != null ) {
|
||||
|
||||
line += ' ' +
|
||||
uvs.getX( i ) + ' ' +
|
||||
uvs.getY( i );
|
||||
|
||||
} else {
|
||||
|
||||
line += ' 0 0';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Color information
|
||||
if ( includeColors === true ) {
|
||||
|
||||
if ( colors != null ) {
|
||||
|
||||
tempColor
|
||||
.fromBufferAttribute( colors, i )
|
||||
.convertLinearToSRGB();
|
||||
|
||||
line += ' ' +
|
||||
Math.floor( tempColor.r * 255 ) + ' ' +
|
||||
Math.floor( tempColor.g * 255 ) + ' ' +
|
||||
Math.floor( tempColor.b * 255 );
|
||||
|
||||
} else {
|
||||
|
||||
line += ' 255 255 255';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vertexList += line + '\n';
|
||||
|
||||
}
|
||||
|
||||
// Create the face list
|
||||
if ( includeIndices === true ) {
|
||||
|
||||
if ( indices !== null ) {
|
||||
|
||||
for ( let i = 0, l = indices.count; i < l; i += 3 ) {
|
||||
|
||||
faceList += `3 ${ indices.getX( i + 0 ) + writtenVertices }`;
|
||||
faceList += ` ${ indices.getX( i + 1 ) + writtenVertices }`;
|
||||
faceList += ` ${ indices.getX( i + 2 ) + writtenVertices }\n`;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for ( let i = 0, l = vertices.count; i < l; i += 3 ) {
|
||||
|
||||
faceList += `3 ${ writtenVertices + i } ${ writtenVertices + i + 1 } ${ writtenVertices + i + 2 }\n`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
faceCount += indices ? indices.count / 3 : vertices.count / 3;
|
||||
|
||||
}
|
||||
|
||||
writtenVertices += vertices.count;
|
||||
|
||||
} );
|
||||
|
||||
result = `${ header }${vertexList}${ includeIndices ? `${faceList}\n` : '\n' }`;
|
||||
|
||||
}
|
||||
|
||||
if ( typeof onDone === 'function' ) requestAnimationFrame( () => onDone( result ) );
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { PLYExporter };
|
||||
199
dist/electron/static/sdk/three/jsm/exporters/STLExporter.js
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
import { Vector3 } from 'three';
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* const exporter = new STLExporter();
|
||||
*
|
||||
* // second argument is a list of options
|
||||
* const data = exporter.parse( mesh, { binary: true } );
|
||||
*
|
||||
*/
|
||||
|
||||
class STLExporter {
|
||||
|
||||
parse( scene, options = {} ) {
|
||||
|
||||
options = Object.assign( {
|
||||
binary: false
|
||||
}, options );
|
||||
|
||||
const binary = options.binary;
|
||||
|
||||
//
|
||||
|
||||
const objects = [];
|
||||
let triangles = 0;
|
||||
|
||||
scene.traverse( function ( object ) {
|
||||
|
||||
if ( object.isMesh ) {
|
||||
|
||||
const geometry = object.geometry;
|
||||
|
||||
const index = geometry.index;
|
||||
const positionAttribute = geometry.getAttribute( 'position' );
|
||||
|
||||
triangles += ( index !== null ) ? ( index.count / 3 ) : ( positionAttribute.count / 3 );
|
||||
|
||||
objects.push( {
|
||||
object3d: object,
|
||||
geometry: geometry
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
let output;
|
||||
let offset = 80; // skip header
|
||||
|
||||
if ( binary === true ) {
|
||||
|
||||
const bufferLength = triangles * 2 + triangles * 3 * 4 * 4 + 80 + 4;
|
||||
const arrayBuffer = new ArrayBuffer( bufferLength );
|
||||
output = new DataView( arrayBuffer );
|
||||
output.setUint32( offset, triangles, true ); offset += 4;
|
||||
|
||||
} else {
|
||||
|
||||
output = '';
|
||||
output += 'solid exported\n';
|
||||
|
||||
}
|
||||
|
||||
const vA = new Vector3();
|
||||
const vB = new Vector3();
|
||||
const vC = new Vector3();
|
||||
const cb = new Vector3();
|
||||
const ab = new Vector3();
|
||||
const normal = new Vector3();
|
||||
|
||||
for ( let i = 0, il = objects.length; i < il; i ++ ) {
|
||||
|
||||
const object = objects[ i ].object3d;
|
||||
const geometry = objects[ i ].geometry;
|
||||
|
||||
const index = geometry.index;
|
||||
const positionAttribute = geometry.getAttribute( 'position' );
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
// indexed geometry
|
||||
|
||||
for ( let j = 0; j < index.count; j += 3 ) {
|
||||
|
||||
const a = index.getX( j + 0 );
|
||||
const b = index.getX( j + 1 );
|
||||
const c = index.getX( j + 2 );
|
||||
|
||||
writeFace( a, b, c, positionAttribute, object );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// non-indexed geometry
|
||||
|
||||
for ( let j = 0; j < positionAttribute.count; j += 3 ) {
|
||||
|
||||
const a = j + 0;
|
||||
const b = j + 1;
|
||||
const c = j + 2;
|
||||
|
||||
writeFace( a, b, c, positionAttribute, object );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( binary === false ) {
|
||||
|
||||
output += 'endsolid exported\n';
|
||||
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
function writeFace( a, b, c, positionAttribute, object ) {
|
||||
|
||||
vA.fromBufferAttribute( positionAttribute, a );
|
||||
vB.fromBufferAttribute( positionAttribute, b );
|
||||
vC.fromBufferAttribute( positionAttribute, c );
|
||||
|
||||
if ( object.isSkinnedMesh === true ) {
|
||||
|
||||
object.applyBoneTransform( a, vA );
|
||||
object.applyBoneTransform( b, vB );
|
||||
object.applyBoneTransform( c, vC );
|
||||
|
||||
}
|
||||
|
||||
vA.applyMatrix4( object.matrixWorld );
|
||||
vB.applyMatrix4( object.matrixWorld );
|
||||
vC.applyMatrix4( object.matrixWorld );
|
||||
|
||||
writeNormal( vA, vB, vC );
|
||||
|
||||
writeVertex( vA );
|
||||
writeVertex( vB );
|
||||
writeVertex( vC );
|
||||
|
||||
if ( binary === true ) {
|
||||
|
||||
output.setUint16( offset, 0, true ); offset += 2;
|
||||
|
||||
} else {
|
||||
|
||||
output += '\t\tendloop\n';
|
||||
output += '\tendfacet\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function writeNormal( vA, vB, vC ) {
|
||||
|
||||
cb.subVectors( vC, vB );
|
||||
ab.subVectors( vA, vB );
|
||||
cb.cross( ab ).normalize();
|
||||
|
||||
normal.copy( cb ).normalize();
|
||||
|
||||
if ( binary === true ) {
|
||||
|
||||
output.setFloat32( offset, normal.x, true ); offset += 4;
|
||||
output.setFloat32( offset, normal.y, true ); offset += 4;
|
||||
output.setFloat32( offset, normal.z, true ); offset += 4;
|
||||
|
||||
} else {
|
||||
|
||||
output += '\tfacet normal ' + normal.x + ' ' + normal.y + ' ' + normal.z + '\n';
|
||||
output += '\t\touter loop\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function writeVertex( vertex ) {
|
||||
|
||||
if ( binary === true ) {
|
||||
|
||||
output.setFloat32( offset, vertex.x, true ); offset += 4;
|
||||
output.setFloat32( offset, vertex.y, true ); offset += 4;
|
||||
output.setFloat32( offset, vertex.z, true ); offset += 4;
|
||||
|
||||
} else {
|
||||
|
||||
output += '\t\t\tvertex ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z + '\n';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { STLExporter };
|
||||
741
dist/electron/static/sdk/three/jsm/exporters/USDZExporter.js
vendored
Normal file
@ -0,0 +1,741 @@
|
||||
import {
|
||||
NoColorSpace,
|
||||
DoubleSide,
|
||||
} from 'three';
|
||||
|
||||
import {
|
||||
strToU8,
|
||||
zipSync,
|
||||
} from '../libs/fflate.module.js';
|
||||
|
||||
import { decompress } from './../utils/TextureUtils.js';
|
||||
|
||||
class USDZExporter {
|
||||
|
||||
parse( scene, onDone, onError, options ) {
|
||||
|
||||
this.parseAsync( scene, options ).then( onDone ).catch( onError );
|
||||
|
||||
}
|
||||
|
||||
async parseAsync( scene, options = {} ) {
|
||||
|
||||
options = Object.assign( {
|
||||
ar: {
|
||||
anchoring: { type: 'plane' },
|
||||
planeAnchoring: { alignment: 'horizontal' }
|
||||
},
|
||||
quickLookCompatible: false,
|
||||
maxTextureSize: 1024,
|
||||
}, options );
|
||||
|
||||
const files = {};
|
||||
const modelFileName = 'model.usda';
|
||||
|
||||
// model file should be first in USDZ archive so we init it here
|
||||
files[ modelFileName ] = null;
|
||||
|
||||
let output = buildHeader();
|
||||
|
||||
output += buildSceneStart( options );
|
||||
|
||||
const materials = {};
|
||||
const textures = {};
|
||||
|
||||
scene.traverseVisible( ( object ) => {
|
||||
|
||||
if ( object.isMesh ) {
|
||||
|
||||
const geometry = object.geometry;
|
||||
const material = object.material;
|
||||
|
||||
if ( material.isMeshStandardMaterial ) {
|
||||
|
||||
const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usda';
|
||||
|
||||
if ( ! ( geometryFileName in files ) ) {
|
||||
|
||||
const meshObject = buildMeshObject( geometry );
|
||||
files[ geometryFileName ] = buildUSDFileAsString( meshObject );
|
||||
|
||||
}
|
||||
|
||||
if ( ! ( material.uuid in materials ) ) {
|
||||
|
||||
materials[ material.uuid ] = material;
|
||||
|
||||
}
|
||||
|
||||
output += buildXform( object, geometry, material );
|
||||
|
||||
} else {
|
||||
|
||||
console.warn( 'THREE.USDZExporter: Unsupported material type (USDZ only supports MeshStandardMaterial)', object );
|
||||
|
||||
}
|
||||
|
||||
} else if ( object.isCamera ) {
|
||||
|
||||
output += buildCamera( object );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
|
||||
output += buildSceneEnd();
|
||||
|
||||
output += buildMaterials( materials, textures, options.quickLookCompatible );
|
||||
|
||||
files[ modelFileName ] = strToU8( output );
|
||||
output = null;
|
||||
|
||||
for ( const id in textures ) {
|
||||
|
||||
let texture = textures[ id ];
|
||||
|
||||
if ( texture.isCompressedTexture === true ) {
|
||||
|
||||
texture = decompress( texture );
|
||||
|
||||
}
|
||||
|
||||
const canvas = imageToCanvas( texture.image, texture.flipY, options.maxTextureSize );
|
||||
const blob = await new Promise( resolve => canvas.toBlob( resolve, 'image/png', 1 ) );
|
||||
|
||||
files[ `textures/Texture_${ id }.png` ] = new Uint8Array( await blob.arrayBuffer() );
|
||||
|
||||
}
|
||||
|
||||
// 64 byte alignment
|
||||
// https://github.com/101arrowz/fflate/issues/39#issuecomment-777263109
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for ( const filename in files ) {
|
||||
|
||||
const file = files[ filename ];
|
||||
const headerSize = 34 + filename.length;
|
||||
|
||||
offset += headerSize;
|
||||
|
||||
const offsetMod64 = offset & 63;
|
||||
|
||||
if ( offsetMod64 !== 4 ) {
|
||||
|
||||
const padLength = 64 - offsetMod64;
|
||||
const padding = new Uint8Array( padLength );
|
||||
|
||||
files[ filename ] = [ file, { extra: { 12345: padding } } ];
|
||||
|
||||
}
|
||||
|
||||
offset = file.length;
|
||||
|
||||
}
|
||||
|
||||
return zipSync( files, { level: 0 } );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function imageToCanvas( image, flipY, maxTextureSize ) {
|
||||
|
||||
if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
|
||||
( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
|
||||
( typeof OffscreenCanvas !== 'undefined' && image instanceof OffscreenCanvas ) ||
|
||||
( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
|
||||
|
||||
const scale = maxTextureSize / Math.max( image.width, image.height );
|
||||
|
||||
const canvas = document.createElement( 'canvas' );
|
||||
canvas.width = image.width * Math.min( 1, scale );
|
||||
canvas.height = image.height * Math.min( 1, scale );
|
||||
|
||||
const context = canvas.getContext( '2d' );
|
||||
|
||||
// TODO: We should be able to do this in the UsdTransform2d?
|
||||
|
||||
if ( flipY === true ) {
|
||||
|
||||
context.translate( 0, canvas.height );
|
||||
context.scale( 1, - 1 );
|
||||
|
||||
}
|
||||
|
||||
context.drawImage( image, 0, 0, canvas.width, canvas.height );
|
||||
|
||||
return canvas;
|
||||
|
||||
} else {
|
||||
|
||||
throw new Error( 'THREE.USDZExporter: No valid image data found. Unable to process texture.' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const PRECISION = 7;
|
||||
|
||||
function buildHeader() {
|
||||
|
||||
return `#usda 1.0
|
||||
(
|
||||
customLayerData = {
|
||||
string creator = "Three.js USDZExporter"
|
||||
}
|
||||
defaultPrim = "Root"
|
||||
metersPerUnit = 1
|
||||
upAxis = "Y"
|
||||
)
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildSceneStart( options ) {
|
||||
|
||||
return `def Xform "Root"
|
||||
{
|
||||
def Scope "Scenes" (
|
||||
kind = "sceneLibrary"
|
||||
)
|
||||
{
|
||||
def Xform "Scene" (
|
||||
customData = {
|
||||
bool preliminary_collidesWithEnvironment = 0
|
||||
string sceneName = "Scene"
|
||||
}
|
||||
sceneName = "Scene"
|
||||
)
|
||||
{
|
||||
token preliminary:anchoring:type = "${options.ar.anchoring.type}"
|
||||
token preliminary:planeAnchoring:alignment = "${options.ar.planeAnchoring.alignment}"
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildSceneEnd() {
|
||||
|
||||
return `
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildUSDFileAsString( dataToInsert ) {
|
||||
|
||||
let output = buildHeader();
|
||||
output += dataToInsert;
|
||||
return strToU8( output );
|
||||
|
||||
}
|
||||
|
||||
// Xform
|
||||
|
||||
function buildXform( object, geometry, material ) {
|
||||
|
||||
const name = 'Object_' + object.id;
|
||||
const transform = buildMatrix( object.matrixWorld );
|
||||
|
||||
if ( object.matrixWorld.determinant() < 0 ) {
|
||||
|
||||
console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', object );
|
||||
|
||||
}
|
||||
|
||||
return `def Xform "${ name }" (
|
||||
prepend references = @./geometries/Geometry_${ geometry.id }.usda@</Geometry>
|
||||
prepend apiSchemas = ["MaterialBindingAPI"]
|
||||
)
|
||||
{
|
||||
matrix4d xformOp:transform = ${ transform }
|
||||
uniform token[] xformOpOrder = ["xformOp:transform"]
|
||||
|
||||
rel material:binding = </Materials/Material_${ material.id }>
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildMatrix( matrix ) {
|
||||
|
||||
const array = matrix.elements;
|
||||
|
||||
return `( ${ buildMatrixRow( array, 0 ) }, ${ buildMatrixRow( array, 4 ) }, ${ buildMatrixRow( array, 8 ) }, ${ buildMatrixRow( array, 12 ) } )`;
|
||||
|
||||
}
|
||||
|
||||
function buildMatrixRow( array, offset ) {
|
||||
|
||||
return `(${ array[ offset + 0 ] }, ${ array[ offset + 1 ] }, ${ array[ offset + 2 ] }, ${ array[ offset + 3 ] })`;
|
||||
|
||||
}
|
||||
|
||||
// Mesh
|
||||
|
||||
function buildMeshObject( geometry ) {
|
||||
|
||||
const mesh = buildMesh( geometry );
|
||||
return `
|
||||
def "Geometry"
|
||||
{
|
||||
${mesh}
|
||||
}
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildMesh( geometry ) {
|
||||
|
||||
const name = 'Geometry';
|
||||
const attributes = geometry.attributes;
|
||||
const count = attributes.position.count;
|
||||
|
||||
return `
|
||||
def Mesh "${ name }"
|
||||
{
|
||||
int[] faceVertexCounts = [${ buildMeshVertexCount( geometry ) }]
|
||||
int[] faceVertexIndices = [${ buildMeshVertexIndices( geometry ) }]
|
||||
normal3f[] normals = [${ buildVector3Array( attributes.normal, count )}] (
|
||||
interpolation = "vertex"
|
||||
)
|
||||
point3f[] points = [${ buildVector3Array( attributes.position, count )}]
|
||||
${ buildPrimvars( attributes ) }
|
||||
uniform token subdivisionScheme = "none"
|
||||
}
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildMeshVertexCount( geometry ) {
|
||||
|
||||
const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
|
||||
|
||||
return Array( count / 3 ).fill( 3 ).join( ', ' );
|
||||
|
||||
}
|
||||
|
||||
function buildMeshVertexIndices( geometry ) {
|
||||
|
||||
const index = geometry.index;
|
||||
const array = [];
|
||||
|
||||
if ( index !== null ) {
|
||||
|
||||
for ( let i = 0; i < index.count; i ++ ) {
|
||||
|
||||
array.push( index.getX( i ) );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
const length = geometry.attributes.position.count;
|
||||
|
||||
for ( let i = 0; i < length; i ++ ) {
|
||||
|
||||
array.push( i );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return array.join( ', ' );
|
||||
|
||||
}
|
||||
|
||||
function buildVector3Array( attribute, count ) {
|
||||
|
||||
if ( attribute === undefined ) {
|
||||
|
||||
console.warn( 'USDZExporter: Normals missing.' );
|
||||
return Array( count ).fill( '(0, 0, 0)' ).join( ', ' );
|
||||
|
||||
}
|
||||
|
||||
const array = [];
|
||||
|
||||
for ( let i = 0; i < attribute.count; i ++ ) {
|
||||
|
||||
const x = attribute.getX( i );
|
||||
const y = attribute.getY( i );
|
||||
const z = attribute.getZ( i );
|
||||
|
||||
array.push( `(${ x.toPrecision( PRECISION ) }, ${ y.toPrecision( PRECISION ) }, ${ z.toPrecision( PRECISION ) })` );
|
||||
|
||||
}
|
||||
|
||||
return array.join( ', ' );
|
||||
|
||||
}
|
||||
|
||||
function buildVector2Array( attribute ) {
|
||||
|
||||
const array = [];
|
||||
|
||||
for ( let i = 0; i < attribute.count; i ++ ) {
|
||||
|
||||
const x = attribute.getX( i );
|
||||
const y = attribute.getY( i );
|
||||
|
||||
array.push( `(${ x.toPrecision( PRECISION ) }, ${ 1 - y.toPrecision( PRECISION ) })` );
|
||||
|
||||
}
|
||||
|
||||
return array.join( ', ' );
|
||||
|
||||
}
|
||||
|
||||
function buildPrimvars( attributes ) {
|
||||
|
||||
let string = '';
|
||||
|
||||
for ( let i = 0; i < 4; i ++ ) {
|
||||
|
||||
const id = ( i > 0 ? i : '' );
|
||||
const attribute = attributes[ 'uv' + id ];
|
||||
|
||||
if ( attribute !== undefined ) {
|
||||
|
||||
string += `
|
||||
texCoord2f[] primvars:st${ id } = [${ buildVector2Array( attribute )}] (
|
||||
interpolation = "vertex"
|
||||
)`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vertex colors
|
||||
|
||||
const colorAttribute = attributes.color;
|
||||
|
||||
if ( colorAttribute !== undefined ) {
|
||||
|
||||
const count = colorAttribute.count;
|
||||
|
||||
string += `
|
||||
color3f[] primvars:displayColor = [${buildVector3Array( colorAttribute, count )}] (
|
||||
interpolation = "vertex"
|
||||
)`;
|
||||
|
||||
}
|
||||
|
||||
return string;
|
||||
|
||||
}
|
||||
|
||||
// Materials
|
||||
|
||||
function buildMaterials( materials, textures, quickLookCompatible = false ) {
|
||||
|
||||
const array = [];
|
||||
|
||||
for ( const uuid in materials ) {
|
||||
|
||||
const material = materials[ uuid ];
|
||||
|
||||
array.push( buildMaterial( material, textures, quickLookCompatible ) );
|
||||
|
||||
}
|
||||
|
||||
return `def "Materials"
|
||||
{
|
||||
${ array.join( '' ) }
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildMaterial( material, textures, quickLookCompatible = false ) {
|
||||
|
||||
// https://graphics.pixar.com/usd/docs/UsdPreviewSurface-Proposal.html
|
||||
|
||||
const pad = ' ';
|
||||
const inputs = [];
|
||||
const samplers = [];
|
||||
|
||||
function buildTexture( texture, mapType, color ) {
|
||||
|
||||
const id = texture.source.id + '_' + texture.flipY;
|
||||
|
||||
textures[ id ] = texture;
|
||||
|
||||
const uv = texture.channel > 0 ? 'st' + texture.channel : 'st';
|
||||
|
||||
const WRAPPINGS = {
|
||||
1000: 'repeat', // RepeatWrapping
|
||||
1001: 'clamp', // ClampToEdgeWrapping
|
||||
1002: 'mirror' // MirroredRepeatWrapping
|
||||
};
|
||||
|
||||
const repeat = texture.repeat.clone();
|
||||
const offset = texture.offset.clone();
|
||||
const rotation = texture.rotation;
|
||||
|
||||
// rotation is around the wrong point. after rotation we need to shift offset again so that we're rotating around the right spot
|
||||
const xRotationOffset = Math.sin( rotation );
|
||||
const yRotationOffset = Math.cos( rotation );
|
||||
|
||||
// texture coordinates start in the opposite corner, need to correct
|
||||
offset.y = 1 - offset.y - repeat.y;
|
||||
|
||||
// turns out QuickLook is buggy and interprets texture repeat inverted/applies operations in a different order.
|
||||
// Apple Feedback: FB10036297 and FB11442287
|
||||
if ( quickLookCompatible ) {
|
||||
|
||||
// This is NOT correct yet in QuickLook, but comes close for a range of models.
|
||||
// It becomes more incorrect the bigger the offset is
|
||||
|
||||
offset.x = offset.x / repeat.x;
|
||||
offset.y = offset.y / repeat.y;
|
||||
|
||||
offset.x += xRotationOffset / repeat.x;
|
||||
offset.y += yRotationOffset - 1;
|
||||
|
||||
} else {
|
||||
|
||||
// results match glTF results exactly. verified correct in usdview.
|
||||
offset.x += xRotationOffset * repeat.x;
|
||||
offset.y += ( 1 - yRotationOffset ) * repeat.y;
|
||||
|
||||
}
|
||||
|
||||
return `
|
||||
def Shader "PrimvarReader_${ mapType }"
|
||||
{
|
||||
uniform token info:id = "UsdPrimvarReader_float2"
|
||||
float2 inputs:fallback = (0.0, 0.0)
|
||||
token inputs:varname = "${ uv }"
|
||||
float2 outputs:result
|
||||
}
|
||||
|
||||
def Shader "Transform2d_${ mapType }"
|
||||
{
|
||||
uniform token info:id = "UsdTransform2d"
|
||||
token inputs:in.connect = </Materials/Material_${ material.id }/PrimvarReader_${ mapType }.outputs:result>
|
||||
float inputs:rotation = ${ ( rotation * ( 180 / Math.PI ) ).toFixed( PRECISION ) }
|
||||
float2 inputs:scale = ${ buildVector2( repeat ) }
|
||||
float2 inputs:translation = ${ buildVector2( offset ) }
|
||||
float2 outputs:result
|
||||
}
|
||||
|
||||
def Shader "Texture_${ texture.id }_${ mapType }"
|
||||
{
|
||||
uniform token info:id = "UsdUVTexture"
|
||||
asset inputs:file = @textures/Texture_${ id }.png@
|
||||
float2 inputs:st.connect = </Materials/Material_${ material.id }/Transform2d_${ mapType }.outputs:result>
|
||||
${ color !== undefined ? 'float4 inputs:scale = ' + buildColor4( color ) : '' }
|
||||
token inputs:sourceColorSpace = "${ texture.colorSpace === NoColorSpace ? 'raw' : 'sRGB' }"
|
||||
token inputs:wrapS = "${ WRAPPINGS[ texture.wrapS ] }"
|
||||
token inputs:wrapT = "${ WRAPPINGS[ texture.wrapT ] }"
|
||||
float outputs:r
|
||||
float outputs:g
|
||||
float outputs:b
|
||||
float3 outputs:rgb
|
||||
${ material.transparent || material.alphaTest > 0.0 ? 'float outputs:a' : '' }
|
||||
}`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
if ( material.side === DoubleSide ) {
|
||||
|
||||
console.warn( 'THREE.USDZExporter: USDZ does not support double sided materials', material );
|
||||
|
||||
}
|
||||
|
||||
if ( material.map !== null ) {
|
||||
|
||||
inputs.push( `${ pad }color3f inputs:diffuseColor.connect = </Materials/Material_${ material.id }/Texture_${ material.map.id }_diffuse.outputs:rgb>` );
|
||||
|
||||
if ( material.transparent ) {
|
||||
|
||||
inputs.push( `${ pad }float inputs:opacity.connect = </Materials/Material_${ material.id }/Texture_${ material.map.id }_diffuse.outputs:a>` );
|
||||
|
||||
} else if ( material.alphaTest > 0.0 ) {
|
||||
|
||||
inputs.push( `${ pad }float inputs:opacity.connect = </Materials/Material_${ material.id }/Texture_${ material.map.id }_diffuse.outputs:a>` );
|
||||
inputs.push( `${ pad }float inputs:opacityThreshold = ${material.alphaTest}` );
|
||||
|
||||
}
|
||||
|
||||
samplers.push( buildTexture( material.map, 'diffuse', material.color ) );
|
||||
|
||||
} else {
|
||||
|
||||
inputs.push( `${ pad }color3f inputs:diffuseColor = ${ buildColor( material.color ) }` );
|
||||
|
||||
}
|
||||
|
||||
if ( material.emissiveMap !== null ) {
|
||||
|
||||
inputs.push( `${ pad }color3f inputs:emissiveColor.connect = </Materials/Material_${ material.id }/Texture_${ material.emissiveMap.id }_emissive.outputs:rgb>` );
|
||||
|
||||
samplers.push( buildTexture( material.emissiveMap, 'emissive' ) );
|
||||
|
||||
} else if ( material.emissive.getHex() > 0 ) {
|
||||
|
||||
inputs.push( `${ pad }color3f inputs:emissiveColor = ${ buildColor( material.emissive ) }` );
|
||||
|
||||
}
|
||||
|
||||
if ( material.normalMap !== null ) {
|
||||
|
||||
inputs.push( `${ pad }normal3f inputs:normal.connect = </Materials/Material_${ material.id }/Texture_${ material.normalMap.id }_normal.outputs:rgb>` );
|
||||
|
||||
samplers.push( buildTexture( material.normalMap, 'normal' ) );
|
||||
|
||||
}
|
||||
|
||||
if ( material.aoMap !== null ) {
|
||||
|
||||
inputs.push( `${ pad }float inputs:occlusion.connect = </Materials/Material_${ material.id }/Texture_${ material.aoMap.id }_occlusion.outputs:r>` );
|
||||
|
||||
samplers.push( buildTexture( material.aoMap, 'occlusion' ) );
|
||||
|
||||
}
|
||||
|
||||
if ( material.roughnessMap !== null && material.roughness === 1 ) {
|
||||
|
||||
inputs.push( `${ pad }float inputs:roughness.connect = </Materials/Material_${ material.id }/Texture_${ material.roughnessMap.id }_roughness.outputs:g>` );
|
||||
|
||||
samplers.push( buildTexture( material.roughnessMap, 'roughness' ) );
|
||||
|
||||
} else {
|
||||
|
||||
inputs.push( `${ pad }float inputs:roughness = ${ material.roughness }` );
|
||||
|
||||
}
|
||||
|
||||
if ( material.metalnessMap !== null && material.metalness === 1 ) {
|
||||
|
||||
inputs.push( `${ pad }float inputs:metallic.connect = </Materials/Material_${ material.id }/Texture_${ material.metalnessMap.id }_metallic.outputs:b>` );
|
||||
|
||||
samplers.push( buildTexture( material.metalnessMap, 'metallic' ) );
|
||||
|
||||
} else {
|
||||
|
||||
inputs.push( `${ pad }float inputs:metallic = ${ material.metalness }` );
|
||||
|
||||
}
|
||||
|
||||
if ( material.alphaMap !== null ) {
|
||||
|
||||
inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
|
||||
inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
|
||||
|
||||
samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
|
||||
|
||||
} else {
|
||||
|
||||
inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
|
||||
|
||||
}
|
||||
|
||||
if ( material.isMeshPhysicalMaterial ) {
|
||||
|
||||
inputs.push( `${ pad }float inputs:clearcoat = ${ material.clearcoat }` );
|
||||
inputs.push( `${ pad }float inputs:clearcoatRoughness = ${ material.clearcoatRoughness }` );
|
||||
inputs.push( `${ pad }float inputs:ior = ${ material.ior }` );
|
||||
|
||||
}
|
||||
|
||||
return `
|
||||
def Material "Material_${ material.id }"
|
||||
{
|
||||
def Shader "PreviewSurface"
|
||||
{
|
||||
uniform token info:id = "UsdPreviewSurface"
|
||||
${ inputs.join( '\n' ) }
|
||||
int inputs:useSpecularWorkflow = 0
|
||||
token outputs:surface
|
||||
}
|
||||
|
||||
token outputs:surface.connect = </Materials/Material_${ material.id }/PreviewSurface.outputs:surface>
|
||||
|
||||
${ samplers.join( '\n' ) }
|
||||
|
||||
}
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
function buildColor( color ) {
|
||||
|
||||
return `(${ color.r }, ${ color.g }, ${ color.b })`;
|
||||
|
||||
}
|
||||
|
||||
function buildColor4( color ) {
|
||||
|
||||
return `(${ color.r }, ${ color.g }, ${ color.b }, 1.0)`;
|
||||
|
||||
}
|
||||
|
||||
function buildVector2( vector ) {
|
||||
|
||||
return `(${ vector.x }, ${ vector.y })`;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function buildCamera( camera ) {
|
||||
|
||||
const name = camera.name ? camera.name : 'Camera_' + camera.id;
|
||||
|
||||
const transform = buildMatrix( camera.matrixWorld );
|
||||
|
||||
if ( camera.matrixWorld.determinant() < 0 ) {
|
||||
|
||||
console.warn( 'THREE.USDZExporter: USDZ does not support negative scales', camera );
|
||||
|
||||
}
|
||||
|
||||
if ( camera.isOrthographicCamera ) {
|
||||
|
||||
return `def Camera "${name}"
|
||||
{
|
||||
matrix4d xformOp:transform = ${ transform }
|
||||
uniform token[] xformOpOrder = ["xformOp:transform"]
|
||||
|
||||
float2 clippingRange = (${ camera.near.toPrecision( PRECISION ) }, ${ camera.far.toPrecision( PRECISION ) })
|
||||
float horizontalAperture = ${ ( ( Math.abs( camera.left ) + Math.abs( camera.right ) ) * 10 ).toPrecision( PRECISION ) }
|
||||
float verticalAperture = ${ ( ( Math.abs( camera.top ) + Math.abs( camera.bottom ) ) * 10 ).toPrecision( PRECISION ) }
|
||||
token projection = "orthographic"
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
} else {
|
||||
|
||||
return `def Camera "${name}"
|
||||
{
|
||||
matrix4d xformOp:transform = ${ transform }
|
||||
uniform token[] xformOpOrder = ["xformOp:transform"]
|
||||
|
||||
float2 clippingRange = (${ camera.near.toPrecision( PRECISION ) }, ${ camera.far.toPrecision( PRECISION ) })
|
||||
float focalLength = ${ camera.getFocalLength().toPrecision( PRECISION ) }
|
||||
float focusDistance = ${ camera.focus.toPrecision( PRECISION ) }
|
||||
float horizontalAperture = ${ camera.getFilmWidth().toPrecision( PRECISION ) }
|
||||
token projection = "perspective"
|
||||
float verticalAperture = ${ camera.getFilmHeight().toPrecision( PRECISION ) }
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { USDZExporter };
|
||||
69
dist/electron/static/sdk/three/jsm/geometries/BoxLineGeometry.js
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute
|
||||
} from 'three';
|
||||
|
||||
class BoxLineGeometry extends BufferGeometry {
|
||||
|
||||
constructor( width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1 ) {
|
||||
|
||||
super();
|
||||
|
||||
widthSegments = Math.floor( widthSegments );
|
||||
heightSegments = Math.floor( heightSegments );
|
||||
depthSegments = Math.floor( depthSegments );
|
||||
|
||||
const widthHalf = width / 2;
|
||||
const heightHalf = height / 2;
|
||||
const depthHalf = depth / 2;
|
||||
|
||||
const segmentWidth = width / widthSegments;
|
||||
const segmentHeight = height / heightSegments;
|
||||
const segmentDepth = depth / depthSegments;
|
||||
|
||||
const vertices = [];
|
||||
|
||||
let x = - widthHalf;
|
||||
let y = - heightHalf;
|
||||
let z = - depthHalf;
|
||||
|
||||
for ( let i = 0; i <= widthSegments; i ++ ) {
|
||||
|
||||
vertices.push( x, - heightHalf, - depthHalf, x, heightHalf, - depthHalf );
|
||||
vertices.push( x, heightHalf, - depthHalf, x, heightHalf, depthHalf );
|
||||
vertices.push( x, heightHalf, depthHalf, x, - heightHalf, depthHalf );
|
||||
vertices.push( x, - heightHalf, depthHalf, x, - heightHalf, - depthHalf );
|
||||
|
||||
x += segmentWidth;
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i <= heightSegments; i ++ ) {
|
||||
|
||||
vertices.push( - widthHalf, y, - depthHalf, widthHalf, y, - depthHalf );
|
||||
vertices.push( widthHalf, y, - depthHalf, widthHalf, y, depthHalf );
|
||||
vertices.push( widthHalf, y, depthHalf, - widthHalf, y, depthHalf );
|
||||
vertices.push( - widthHalf, y, depthHalf, - widthHalf, y, - depthHalf );
|
||||
|
||||
y += segmentHeight;
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i <= depthSegments; i ++ ) {
|
||||
|
||||
vertices.push( - widthHalf, - heightHalf, z, - widthHalf, heightHalf, z );
|
||||
vertices.push( - widthHalf, heightHalf, z, widthHalf, heightHalf, z );
|
||||
vertices.push( widthHalf, heightHalf, z, widthHalf, - heightHalf, z );
|
||||
vertices.push( widthHalf, - heightHalf, z, - widthHalf, - heightHalf, z );
|
||||
|
||||
z += segmentDepth;
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { BoxLineGeometry };
|
||||
53
dist/electron/static/sdk/three/jsm/geometries/ConvexGeometry.js
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute
|
||||
} from 'three';
|
||||
import { ConvexHull } from '../math/ConvexHull.js';
|
||||
|
||||
class ConvexGeometry extends BufferGeometry {
|
||||
|
||||
constructor( points = [] ) {
|
||||
|
||||
super();
|
||||
|
||||
// buffers
|
||||
|
||||
const vertices = [];
|
||||
const normals = [];
|
||||
|
||||
const convexHull = new ConvexHull().setFromPoints( points );
|
||||
|
||||
// generate vertices and normals
|
||||
|
||||
const faces = convexHull.faces;
|
||||
|
||||
for ( let i = 0; i < faces.length; i ++ ) {
|
||||
|
||||
const face = faces[ i ];
|
||||
let edge = face.edge;
|
||||
|
||||
// we move along a doubly-connected edge list to access all face points (see HalfEdge docs)
|
||||
|
||||
do {
|
||||
|
||||
const point = edge.head().point;
|
||||
|
||||
vertices.push( point.x, point.y, point.z );
|
||||
normals.push( face.normal.x, face.normal.y, face.normal.z );
|
||||
|
||||
edge = edge.next;
|
||||
|
||||
} while ( edge !== face.edge );
|
||||
|
||||
}
|
||||
|
||||
// build geometry
|
||||
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
||||
this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ConvexGeometry };
|
||||
356
dist/electron/static/sdk/three/jsm/geometries/DecalGeometry.js
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
Matrix4,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* You can use this geometry to create a decal mesh, that serves different kinds of purposes.
|
||||
* e.g. adding unique details to models, performing dynamic visual environmental changes or covering seams.
|
||||
*
|
||||
* Constructor parameter:
|
||||
*
|
||||
* mesh — Any mesh object
|
||||
* position — Position of the decal projector
|
||||
* orientation — Orientation of the decal projector
|
||||
* size — Size of the decal projector
|
||||
*
|
||||
* reference: http://blog.wolfire.com/2009/06/how-to-project-decals/
|
||||
*
|
||||
*/
|
||||
|
||||
class DecalGeometry extends BufferGeometry {
|
||||
|
||||
constructor( mesh, position, orientation, size ) {
|
||||
|
||||
super();
|
||||
|
||||
// buffers
|
||||
|
||||
const vertices = [];
|
||||
const normals = [];
|
||||
const uvs = [];
|
||||
|
||||
// helpers
|
||||
|
||||
const plane = new Vector3();
|
||||
|
||||
// this matrix represents the transformation of the decal projector
|
||||
|
||||
const projectorMatrix = new Matrix4();
|
||||
projectorMatrix.makeRotationFromEuler( orientation );
|
||||
projectorMatrix.setPosition( position );
|
||||
|
||||
const projectorMatrixInverse = new Matrix4();
|
||||
projectorMatrixInverse.copy( projectorMatrix ).invert();
|
||||
|
||||
// generate buffers
|
||||
|
||||
generate();
|
||||
|
||||
// build geometry
|
||||
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
||||
this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
|
||||
this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
|
||||
|
||||
function generate() {
|
||||
|
||||
let decalVertices = [];
|
||||
|
||||
const vertex = new Vector3();
|
||||
const normal = new Vector3();
|
||||
|
||||
// handle different geometry types
|
||||
|
||||
const geometry = mesh.geometry;
|
||||
|
||||
const positionAttribute = geometry.attributes.position;
|
||||
const normalAttribute = geometry.attributes.normal;
|
||||
|
||||
// first, create an array of 'DecalVertex' objects
|
||||
// three consecutive 'DecalVertex' objects represent a single face
|
||||
//
|
||||
// this data structure will be later used to perform the clipping
|
||||
|
||||
if ( geometry.index !== null ) {
|
||||
|
||||
// indexed BufferGeometry
|
||||
|
||||
const index = geometry.index;
|
||||
|
||||
for ( let i = 0; i < index.count; i ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( positionAttribute, index.getX( i ) );
|
||||
normal.fromBufferAttribute( normalAttribute, index.getX( i ) );
|
||||
|
||||
pushDecalVertex( decalVertices, vertex, normal );
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// non-indexed BufferGeometry
|
||||
|
||||
for ( let i = 0; i < positionAttribute.count; i ++ ) {
|
||||
|
||||
vertex.fromBufferAttribute( positionAttribute, i );
|
||||
normal.fromBufferAttribute( normalAttribute, i );
|
||||
|
||||
pushDecalVertex( decalVertices, vertex, normal );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// second, clip the geometry so that it doesn't extend out from the projector
|
||||
|
||||
decalVertices = clipGeometry( decalVertices, plane.set( 1, 0, 0 ) );
|
||||
decalVertices = clipGeometry( decalVertices, plane.set( - 1, 0, 0 ) );
|
||||
decalVertices = clipGeometry( decalVertices, plane.set( 0, 1, 0 ) );
|
||||
decalVertices = clipGeometry( decalVertices, plane.set( 0, - 1, 0 ) );
|
||||
decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, 1 ) );
|
||||
decalVertices = clipGeometry( decalVertices, plane.set( 0, 0, - 1 ) );
|
||||
|
||||
// third, generate final vertices, normals and uvs
|
||||
|
||||
for ( let i = 0; i < decalVertices.length; i ++ ) {
|
||||
|
||||
const decalVertex = decalVertices[ i ];
|
||||
|
||||
// create texture coordinates (we are still in projector space)
|
||||
|
||||
uvs.push(
|
||||
0.5 + ( decalVertex.position.x / size.x ),
|
||||
0.5 + ( decalVertex.position.y / size.y )
|
||||
);
|
||||
|
||||
// transform the vertex back to world space
|
||||
|
||||
decalVertex.position.applyMatrix4( projectorMatrix );
|
||||
|
||||
// now create vertex and normal buffer data
|
||||
|
||||
vertices.push( decalVertex.position.x, decalVertex.position.y, decalVertex.position.z );
|
||||
normals.push( decalVertex.normal.x, decalVertex.normal.y, decalVertex.normal.z );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function pushDecalVertex( decalVertices, vertex, normal ) {
|
||||
|
||||
// transform the vertex to world space, then to projector space
|
||||
|
||||
vertex.applyMatrix4( mesh.matrixWorld );
|
||||
vertex.applyMatrix4( projectorMatrixInverse );
|
||||
|
||||
normal.transformDirection( mesh.matrixWorld );
|
||||
|
||||
decalVertices.push( new DecalVertex( vertex.clone(), normal.clone() ) );
|
||||
|
||||
}
|
||||
|
||||
function clipGeometry( inVertices, plane ) {
|
||||
|
||||
const outVertices = [];
|
||||
|
||||
const s = 0.5 * Math.abs( size.dot( plane ) );
|
||||
|
||||
// a single iteration clips one face,
|
||||
// which consists of three consecutive 'DecalVertex' objects
|
||||
|
||||
for ( let i = 0; i < inVertices.length; i += 3 ) {
|
||||
|
||||
let total = 0;
|
||||
let nV1;
|
||||
let nV2;
|
||||
let nV3;
|
||||
let nV4;
|
||||
|
||||
const d1 = inVertices[ i + 0 ].position.dot( plane ) - s;
|
||||
const d2 = inVertices[ i + 1 ].position.dot( plane ) - s;
|
||||
const d3 = inVertices[ i + 2 ].position.dot( plane ) - s;
|
||||
|
||||
const v1Out = d1 > 0;
|
||||
const v2Out = d2 > 0;
|
||||
const v3Out = d3 > 0;
|
||||
|
||||
// calculate, how many vertices of the face lie outside of the clipping plane
|
||||
|
||||
total = ( v1Out ? 1 : 0 ) + ( v2Out ? 1 : 0 ) + ( v3Out ? 1 : 0 );
|
||||
|
||||
switch ( total ) {
|
||||
|
||||
case 0: {
|
||||
|
||||
// the entire face lies inside of the plane, no clipping needed
|
||||
|
||||
outVertices.push( inVertices[ i ] );
|
||||
outVertices.push( inVertices[ i + 1 ] );
|
||||
outVertices.push( inVertices[ i + 2 ] );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 1: {
|
||||
|
||||
// one vertex lies outside of the plane, perform clipping
|
||||
|
||||
if ( v1Out ) {
|
||||
|
||||
nV1 = inVertices[ i + 1 ];
|
||||
nV2 = inVertices[ i + 2 ];
|
||||
nV3 = clip( inVertices[ i ], nV1, plane, s );
|
||||
nV4 = clip( inVertices[ i ], nV2, plane, s );
|
||||
|
||||
}
|
||||
|
||||
if ( v2Out ) {
|
||||
|
||||
nV1 = inVertices[ i ];
|
||||
nV2 = inVertices[ i + 2 ];
|
||||
nV3 = clip( inVertices[ i + 1 ], nV1, plane, s );
|
||||
nV4 = clip( inVertices[ i + 1 ], nV2, plane, s );
|
||||
|
||||
outVertices.push( nV3 );
|
||||
outVertices.push( nV2.clone() );
|
||||
outVertices.push( nV1.clone() );
|
||||
|
||||
outVertices.push( nV2.clone() );
|
||||
outVertices.push( nV3.clone() );
|
||||
outVertices.push( nV4 );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if ( v3Out ) {
|
||||
|
||||
nV1 = inVertices[ i ];
|
||||
nV2 = inVertices[ i + 1 ];
|
||||
nV3 = clip( inVertices[ i + 2 ], nV1, plane, s );
|
||||
nV4 = clip( inVertices[ i + 2 ], nV2, plane, s );
|
||||
|
||||
}
|
||||
|
||||
outVertices.push( nV1.clone() );
|
||||
outVertices.push( nV2.clone() );
|
||||
outVertices.push( nV3 );
|
||||
|
||||
outVertices.push( nV4 );
|
||||
outVertices.push( nV3.clone() );
|
||||
outVertices.push( nV2.clone() );
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 2: {
|
||||
|
||||
// two vertices lies outside of the plane, perform clipping
|
||||
|
||||
if ( ! v1Out ) {
|
||||
|
||||
nV1 = inVertices[ i ].clone();
|
||||
nV2 = clip( nV1, inVertices[ i + 1 ], plane, s );
|
||||
nV3 = clip( nV1, inVertices[ i + 2 ], plane, s );
|
||||
outVertices.push( nV1 );
|
||||
outVertices.push( nV2 );
|
||||
outVertices.push( nV3 );
|
||||
|
||||
}
|
||||
|
||||
if ( ! v2Out ) {
|
||||
|
||||
nV1 = inVertices[ i + 1 ].clone();
|
||||
nV2 = clip( nV1, inVertices[ i + 2 ], plane, s );
|
||||
nV3 = clip( nV1, inVertices[ i ], plane, s );
|
||||
outVertices.push( nV1 );
|
||||
outVertices.push( nV2 );
|
||||
outVertices.push( nV3 );
|
||||
|
||||
}
|
||||
|
||||
if ( ! v3Out ) {
|
||||
|
||||
nV1 = inVertices[ i + 2 ].clone();
|
||||
nV2 = clip( nV1, inVertices[ i ], plane, s );
|
||||
nV3 = clip( nV1, inVertices[ i + 1 ], plane, s );
|
||||
outVertices.push( nV1 );
|
||||
outVertices.push( nV2 );
|
||||
outVertices.push( nV3 );
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
case 3: {
|
||||
|
||||
// the entire face lies outside of the plane, so let's discard the corresponding vertices
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return outVertices;
|
||||
|
||||
}
|
||||
|
||||
function clip( v0, v1, p, s ) {
|
||||
|
||||
const d0 = v0.position.dot( p ) - s;
|
||||
const d1 = v1.position.dot( p ) - s;
|
||||
|
||||
const s0 = d0 / ( d0 - d1 );
|
||||
|
||||
const v = new DecalVertex(
|
||||
new Vector3(
|
||||
v0.position.x + s0 * ( v1.position.x - v0.position.x ),
|
||||
v0.position.y + s0 * ( v1.position.y - v0.position.y ),
|
||||
v0.position.z + s0 * ( v1.position.z - v0.position.z )
|
||||
),
|
||||
new Vector3(
|
||||
v0.normal.x + s0 * ( v1.normal.x - v0.normal.x ),
|
||||
v0.normal.y + s0 * ( v1.normal.y - v0.normal.y ),
|
||||
v0.normal.z + s0 * ( v1.normal.z - v0.normal.z )
|
||||
)
|
||||
);
|
||||
|
||||
// need to clip more values (texture coordinates)? do it this way:
|
||||
// intersectpoint.value = a.value + s * ( b.value - a.value );
|
||||
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// helper
|
||||
|
||||
class DecalVertex {
|
||||
|
||||
constructor( position, normal ) {
|
||||
|
||||
this.position = position;
|
||||
this.normal = normal;
|
||||
|
||||
}
|
||||
|
||||
clone() {
|
||||
|
||||
return new this.constructor( this.position.clone(), this.normal.clone() );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { DecalGeometry, DecalVertex };
|
||||
174
dist/electron/static/sdk/three/jsm/geometries/InstancedPointsGeometry.js
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
import {
|
||||
Box3,
|
||||
Float32BufferAttribute,
|
||||
InstancedBufferGeometry,
|
||||
InstancedBufferAttribute,
|
||||
Sphere,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _vector = new Vector3();
|
||||
|
||||
class InstancedPointsGeometry extends InstancedBufferGeometry {
|
||||
|
||||
constructor() {
|
||||
|
||||
super();
|
||||
|
||||
this.isInstancedPointsGeometry = true;
|
||||
|
||||
this.type = 'InstancedPointsGeometry';
|
||||
|
||||
const positions = [ - 1, 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];
|
||||
const uvs = [ - 1, 1, 1, 1, - 1, - 1, 1, - 1 ];
|
||||
const index = [ 0, 2, 1, 2, 3, 1 ];
|
||||
|
||||
this.setIndex( index );
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
|
||||
this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
|
||||
|
||||
}
|
||||
|
||||
applyMatrix4( matrix ) {
|
||||
|
||||
const pos = this.attributes.instancePosition;
|
||||
|
||||
if ( pos !== undefined ) {
|
||||
|
||||
pos.applyMatrix4( matrix );
|
||||
|
||||
pos.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
if ( this.boundingBox !== null ) {
|
||||
|
||||
this.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
if ( this.boundingSphere !== null ) {
|
||||
|
||||
this.computeBoundingSphere();
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
setPositions( array ) {
|
||||
|
||||
let points;
|
||||
|
||||
if ( array instanceof Float32Array ) {
|
||||
|
||||
points = array;
|
||||
|
||||
} else if ( Array.isArray( array ) ) {
|
||||
|
||||
points = new Float32Array( array );
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'instancePosition', new InstancedBufferAttribute( points, 3 ) ); // xyz
|
||||
|
||||
//
|
||||
|
||||
this.computeBoundingBox();
|
||||
this.computeBoundingSphere();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
setColors( array ) {
|
||||
|
||||
let colors;
|
||||
|
||||
if ( array instanceof Float32Array ) {
|
||||
|
||||
colors = array;
|
||||
|
||||
} else if ( Array.isArray( array ) ) {
|
||||
|
||||
colors = new Float32Array( array );
|
||||
|
||||
}
|
||||
|
||||
this.setAttribute( 'instanceColor', new InstancedBufferAttribute( colors, 3 ) ); // rgb
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
computeBoundingBox() {
|
||||
|
||||
if ( this.boundingBox === null ) {
|
||||
|
||||
this.boundingBox = new Box3();
|
||||
|
||||
}
|
||||
|
||||
const pos = this.attributes.instancePosition;
|
||||
|
||||
if ( pos !== undefined ) {
|
||||
|
||||
this.boundingBox.setFromBufferAttribute( pos );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
computeBoundingSphere() {
|
||||
|
||||
if ( this.boundingSphere === null ) {
|
||||
|
||||
this.boundingSphere = new Sphere();
|
||||
|
||||
}
|
||||
|
||||
if ( this.boundingBox === null ) {
|
||||
|
||||
this.computeBoundingBox();
|
||||
|
||||
}
|
||||
|
||||
const pos = this.attributes.instancePosition;
|
||||
|
||||
if ( pos !== undefined ) {
|
||||
|
||||
const center = this.boundingSphere.center;
|
||||
|
||||
this.boundingBox.getCenter( center );
|
||||
|
||||
let maxRadiusSq = 0;
|
||||
|
||||
for ( let i = 0, il = pos.count; i < il; i ++ ) {
|
||||
|
||||
_vector.fromBufferAttribute( pos, i );
|
||||
maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( _vector ) );
|
||||
|
||||
}
|
||||
|
||||
this.boundingSphere.radius = Math.sqrt( maxRadiusSq );
|
||||
|
||||
if ( isNaN( this.boundingSphere.radius ) ) {
|
||||
|
||||
console.error( 'THREE.InstancedPointsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
|
||||
// todo
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default InstancedPointsGeometry;
|
||||
254
dist/electron/static/sdk/three/jsm/geometries/ParametricGeometries.js
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
import {
|
||||
Curve,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
import { ParametricGeometry } from './ParametricGeometry.js';
|
||||
|
||||
/**
|
||||
* Experimenting of primitive geometry creation using Surface Parametric equations
|
||||
*/
|
||||
|
||||
const ParametricGeometries = {
|
||||
|
||||
klein: function ( v, u, target ) {
|
||||
|
||||
u *= Math.PI;
|
||||
v *= 2 * Math.PI;
|
||||
|
||||
u = u * 2;
|
||||
let x, z;
|
||||
if ( u < Math.PI ) {
|
||||
|
||||
x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( u ) * Math.cos( v );
|
||||
z = - 8 * Math.sin( u ) - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( u ) * Math.cos( v );
|
||||
|
||||
} else {
|
||||
|
||||
x = 3 * Math.cos( u ) * ( 1 + Math.sin( u ) ) + ( 2 * ( 1 - Math.cos( u ) / 2 ) ) * Math.cos( v + Math.PI );
|
||||
z = - 8 * Math.sin( u );
|
||||
|
||||
}
|
||||
|
||||
const y = - 2 * ( 1 - Math.cos( u ) / 2 ) * Math.sin( v );
|
||||
|
||||
target.set( x, y, z );
|
||||
|
||||
},
|
||||
|
||||
plane: function ( width, height ) {
|
||||
|
||||
return function ( u, v, target ) {
|
||||
|
||||
const x = u * width;
|
||||
const y = 0;
|
||||
const z = v * height;
|
||||
|
||||
target.set( x, y, z );
|
||||
|
||||
};
|
||||
|
||||
},
|
||||
|
||||
mobius: function ( u, t, target ) {
|
||||
|
||||
// flat mobius strip
|
||||
// http://www.wolframalpha.com/input/?i=M%C3%B6bius+strip+parametric+equations&lk=1&a=ClashPrefs_*Surface.MoebiusStrip.SurfaceProperty.ParametricEquations-
|
||||
u = u - 0.5;
|
||||
const v = 2 * Math.PI * t;
|
||||
|
||||
const a = 2;
|
||||
|
||||
const x = Math.cos( v ) * ( a + u * Math.cos( v / 2 ) );
|
||||
const y = Math.sin( v ) * ( a + u * Math.cos( v / 2 ) );
|
||||
const z = u * Math.sin( v / 2 );
|
||||
|
||||
target.set( x, y, z );
|
||||
|
||||
},
|
||||
|
||||
mobius3d: function ( u, t, target ) {
|
||||
|
||||
// volumetric mobius strip
|
||||
|
||||
u *= Math.PI;
|
||||
t *= 2 * Math.PI;
|
||||
|
||||
u = u * 2;
|
||||
const phi = u / 2;
|
||||
const major = 2.25, a = 0.125, b = 0.65;
|
||||
|
||||
let x = a * Math.cos( t ) * Math.cos( phi ) - b * Math.sin( t ) * Math.sin( phi );
|
||||
const z = a * Math.cos( t ) * Math.sin( phi ) + b * Math.sin( t ) * Math.cos( phi );
|
||||
const y = ( major + x ) * Math.sin( u );
|
||||
x = ( major + x ) * Math.cos( u );
|
||||
|
||||
target.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Parametric Replacement for TubeGeometry
|
||||
*
|
||||
*********************************************/
|
||||
|
||||
ParametricGeometries.TubeGeometry = class TubeGeometry extends ParametricGeometry {
|
||||
|
||||
constructor( path, segments = 64, radius = 1, segmentsRadius = 8, closed = false ) {
|
||||
|
||||
const numpoints = segments + 1;
|
||||
|
||||
const frames = path.computeFrenetFrames( segments, closed ),
|
||||
tangents = frames.tangents,
|
||||
normals = frames.normals,
|
||||
binormals = frames.binormals;
|
||||
|
||||
const position = new Vector3();
|
||||
|
||||
function ParametricTube( u, v, target ) {
|
||||
|
||||
v *= 2 * Math.PI;
|
||||
|
||||
const i = Math.floor( u * ( numpoints - 1 ) );
|
||||
|
||||
path.getPointAt( u, position );
|
||||
|
||||
const normal = normals[ i ];
|
||||
const binormal = binormals[ i ];
|
||||
|
||||
const cx = - radius * Math.cos( v ); // TODO: Hack: Negating it so it faces outside.
|
||||
const cy = radius * Math.sin( v );
|
||||
|
||||
position.x += cx * normal.x + cy * binormal.x;
|
||||
position.y += cx * normal.y + cy * binormal.y;
|
||||
position.z += cx * normal.z + cy * binormal.z;
|
||||
|
||||
target.copy( position );
|
||||
|
||||
}
|
||||
|
||||
super( ParametricTube, segments, segmentsRadius );
|
||||
|
||||
// proxy internals
|
||||
|
||||
this.tangents = tangents;
|
||||
this.normals = normals;
|
||||
this.binormals = binormals;
|
||||
|
||||
this.path = path;
|
||||
this.segments = segments;
|
||||
this.radius = radius;
|
||||
this.segmentsRadius = segmentsRadius;
|
||||
this.closed = closed;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Parametric Replacement for TorusKnotGeometry
|
||||
*
|
||||
*********************************************/
|
||||
ParametricGeometries.TorusKnotGeometry = class TorusKnotGeometry extends ParametricGeometries.TubeGeometry {
|
||||
|
||||
constructor( radius = 200, tube = 40, segmentsT = 64, segmentsR = 8, p = 2, q = 3 ) {
|
||||
|
||||
class TorusKnotCurve extends Curve {
|
||||
|
||||
getPoint( t, optionalTarget = new Vector3() ) {
|
||||
|
||||
const point = optionalTarget;
|
||||
|
||||
t *= Math.PI * 2;
|
||||
|
||||
const r = 0.5;
|
||||
|
||||
const x = ( 1 + r * Math.cos( q * t ) ) * Math.cos( p * t );
|
||||
const y = ( 1 + r * Math.cos( q * t ) ) * Math.sin( p * t );
|
||||
const z = r * Math.sin( q * t );
|
||||
|
||||
return point.set( x, y, z ).multiplyScalar( radius );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const segments = segmentsT;
|
||||
const radiusSegments = segmentsR;
|
||||
const extrudePath = new TorusKnotCurve();
|
||||
|
||||
super( extrudePath, segments, tube, radiusSegments, true, false );
|
||||
|
||||
this.radius = radius;
|
||||
this.tube = tube;
|
||||
this.segmentsT = segmentsT;
|
||||
this.segmentsR = segmentsR;
|
||||
this.p = p;
|
||||
this.q = q;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Parametric Replacement for SphereGeometry
|
||||
*
|
||||
*********************************************/
|
||||
ParametricGeometries.SphereGeometry = class SphereGeometry extends ParametricGeometry {
|
||||
|
||||
constructor( size, u, v ) {
|
||||
|
||||
function sphere( u, v, target ) {
|
||||
|
||||
u *= Math.PI;
|
||||
v *= 2 * Math.PI;
|
||||
|
||||
const x = size * Math.sin( u ) * Math.cos( v );
|
||||
const y = size * Math.sin( u ) * Math.sin( v );
|
||||
const z = size * Math.cos( u );
|
||||
|
||||
target.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
super( sphere, u, v );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/*********************************************
|
||||
*
|
||||
* Parametric Replacement for PlaneGeometry
|
||||
*
|
||||
*********************************************/
|
||||
|
||||
ParametricGeometries.PlaneGeometry = class PlaneGeometry extends ParametricGeometry {
|
||||
|
||||
constructor( width, depth, segmentsWidth, segmentsDepth ) {
|
||||
|
||||
function plane( u, v, target ) {
|
||||
|
||||
const x = u * width;
|
||||
const y = 0;
|
||||
const z = v * depth;
|
||||
|
||||
target.set( x, y, z );
|
||||
|
||||
}
|
||||
|
||||
super( plane, segmentsWidth, segmentsDepth );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export { ParametricGeometries };
|
||||
139
dist/electron/static/sdk/three/jsm/geometries/ParametricGeometry.js
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
/**
|
||||
* Parametric Surfaces Geometry
|
||||
* based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
|
||||
*/
|
||||
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
class ParametricGeometry extends BufferGeometry {
|
||||
|
||||
constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
|
||||
|
||||
super();
|
||||
|
||||
this.type = 'ParametricGeometry';
|
||||
|
||||
this.parameters = {
|
||||
func: func,
|
||||
slices: slices,
|
||||
stacks: stacks
|
||||
};
|
||||
|
||||
// buffers
|
||||
|
||||
const indices = [];
|
||||
const vertices = [];
|
||||
const normals = [];
|
||||
const uvs = [];
|
||||
|
||||
const EPS = 0.00001;
|
||||
|
||||
const normal = new Vector3();
|
||||
|
||||
const p0 = new Vector3(), p1 = new Vector3();
|
||||
const pu = new Vector3(), pv = new Vector3();
|
||||
|
||||
// generate vertices, normals and uvs
|
||||
|
||||
const sliceCount = slices + 1;
|
||||
|
||||
for ( let i = 0; i <= stacks; i ++ ) {
|
||||
|
||||
const v = i / stacks;
|
||||
|
||||
for ( let j = 0; j <= slices; j ++ ) {
|
||||
|
||||
const u = j / slices;
|
||||
|
||||
// vertex
|
||||
|
||||
func( u, v, p0 );
|
||||
vertices.push( p0.x, p0.y, p0.z );
|
||||
|
||||
// normal
|
||||
|
||||
// approximate tangent vectors via finite differences
|
||||
|
||||
if ( u - EPS >= 0 ) {
|
||||
|
||||
func( u - EPS, v, p1 );
|
||||
pu.subVectors( p0, p1 );
|
||||
|
||||
} else {
|
||||
|
||||
func( u + EPS, v, p1 );
|
||||
pu.subVectors( p1, p0 );
|
||||
|
||||
}
|
||||
|
||||
if ( v - EPS >= 0 ) {
|
||||
|
||||
func( u, v - EPS, p1 );
|
||||
pv.subVectors( p0, p1 );
|
||||
|
||||
} else {
|
||||
|
||||
func( u, v + EPS, p1 );
|
||||
pv.subVectors( p1, p0 );
|
||||
|
||||
}
|
||||
|
||||
// cross product of tangent vectors returns surface normal
|
||||
|
||||
normal.crossVectors( pu, pv ).normalize();
|
||||
normals.push( normal.x, normal.y, normal.z );
|
||||
|
||||
// uv
|
||||
|
||||
uvs.push( u, v );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// generate indices
|
||||
|
||||
for ( let i = 0; i < stacks; i ++ ) {
|
||||
|
||||
for ( let j = 0; j < slices; j ++ ) {
|
||||
|
||||
const a = i * sliceCount + j;
|
||||
const b = i * sliceCount + j + 1;
|
||||
const c = ( i + 1 ) * sliceCount + j + 1;
|
||||
const d = ( i + 1 ) * sliceCount + j;
|
||||
|
||||
// faces one and two
|
||||
|
||||
indices.push( a, b, d );
|
||||
indices.push( b, c, d );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// build geometry
|
||||
|
||||
this.setIndex( indices );
|
||||
this.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
||||
this.setAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
|
||||
this.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
|
||||
|
||||
}
|
||||
|
||||
copy( source ) {
|
||||
|
||||
super.copy( source );
|
||||
|
||||
this.parameters = Object.assign( {}, source.parameters );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ParametricGeometry };
|
||||
155
dist/electron/static/sdk/three/jsm/geometries/RoundedBoxGeometry.js
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
import {
|
||||
BoxGeometry,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _tempNormal = new Vector3();
|
||||
|
||||
function getUv( faceDirVector, normal, uvAxis, projectionAxis, radius, sideLength ) {
|
||||
|
||||
const totArcLength = 2 * Math.PI * radius / 4;
|
||||
|
||||
// length of the planes between the arcs on each axis
|
||||
const centerLength = Math.max( sideLength - 2 * radius, 0 );
|
||||
const halfArc = Math.PI / 4;
|
||||
|
||||
// Get the vector projected onto the Y plane
|
||||
_tempNormal.copy( normal );
|
||||
_tempNormal[ projectionAxis ] = 0;
|
||||
_tempNormal.normalize();
|
||||
|
||||
// total amount of UV space alloted to a single arc
|
||||
const arcUvRatio = 0.5 * totArcLength / ( totArcLength + centerLength );
|
||||
|
||||
// the distance along one arc the point is at
|
||||
const arcAngleRatio = 1.0 - ( _tempNormal.angleTo( faceDirVector ) / halfArc );
|
||||
|
||||
if ( Math.sign( _tempNormal[ uvAxis ] ) === 1 ) {
|
||||
|
||||
return arcAngleRatio * arcUvRatio;
|
||||
|
||||
} else {
|
||||
|
||||
// total amount of UV space alloted to the plane between the arcs
|
||||
const lenUv = centerLength / ( totArcLength + centerLength );
|
||||
return lenUv + arcUvRatio + arcUvRatio * ( 1.0 - arcAngleRatio );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RoundedBoxGeometry extends BoxGeometry {
|
||||
|
||||
constructor( width = 1, height = 1, depth = 1, segments = 2, radius = 0.1 ) {
|
||||
|
||||
// ensure segments is odd so we have a plane connecting the rounded corners
|
||||
segments = segments * 2 + 1;
|
||||
|
||||
// ensure radius isn't bigger than shortest side
|
||||
radius = Math.min( width / 2, height / 2, depth / 2, radius );
|
||||
|
||||
super( 1, 1, 1, segments, segments, segments );
|
||||
|
||||
// if we just have one segment we're the same as a regular box
|
||||
if ( segments === 1 ) return;
|
||||
|
||||
const geometry2 = this.toNonIndexed();
|
||||
|
||||
this.index = null;
|
||||
this.attributes.position = geometry2.attributes.position;
|
||||
this.attributes.normal = geometry2.attributes.normal;
|
||||
this.attributes.uv = geometry2.attributes.uv;
|
||||
|
||||
//
|
||||
|
||||
const position = new Vector3();
|
||||
const normal = new Vector3();
|
||||
|
||||
const box = new Vector3( width, height, depth ).divideScalar( 2 ).subScalar( radius );
|
||||
|
||||
const positions = this.attributes.position.array;
|
||||
const normals = this.attributes.normal.array;
|
||||
const uvs = this.attributes.uv.array;
|
||||
|
||||
const faceTris = positions.length / 6;
|
||||
const faceDirVector = new Vector3();
|
||||
const halfSegmentSize = 0.5 / segments;
|
||||
|
||||
for ( let i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {
|
||||
|
||||
position.fromArray( positions, i );
|
||||
normal.copy( position );
|
||||
normal.x -= Math.sign( normal.x ) * halfSegmentSize;
|
||||
normal.y -= Math.sign( normal.y ) * halfSegmentSize;
|
||||
normal.z -= Math.sign( normal.z ) * halfSegmentSize;
|
||||
normal.normalize();
|
||||
|
||||
positions[ i + 0 ] = box.x * Math.sign( position.x ) + normal.x * radius;
|
||||
positions[ i + 1 ] = box.y * Math.sign( position.y ) + normal.y * radius;
|
||||
positions[ i + 2 ] = box.z * Math.sign( position.z ) + normal.z * radius;
|
||||
|
||||
normals[ i + 0 ] = normal.x;
|
||||
normals[ i + 1 ] = normal.y;
|
||||
normals[ i + 2 ] = normal.z;
|
||||
|
||||
const side = Math.floor( i / faceTris );
|
||||
|
||||
switch ( side ) {
|
||||
|
||||
case 0: // right
|
||||
|
||||
// generate UVs along Z then Y
|
||||
faceDirVector.set( 1, 0, 0 );
|
||||
uvs[ j + 0 ] = getUv( faceDirVector, normal, 'z', 'y', radius, depth );
|
||||
uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
|
||||
break;
|
||||
|
||||
case 1: // left
|
||||
|
||||
// generate UVs along Z then Y
|
||||
faceDirVector.set( - 1, 0, 0 );
|
||||
uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'y', radius, depth );
|
||||
uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'z', radius, height );
|
||||
break;
|
||||
|
||||
case 2: // top
|
||||
|
||||
// generate UVs along X then Z
|
||||
faceDirVector.set( 0, 1, 0 );
|
||||
uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
|
||||
uvs[ j + 1 ] = getUv( faceDirVector, normal, 'z', 'x', radius, depth );
|
||||
break;
|
||||
|
||||
case 3: // bottom
|
||||
|
||||
// generate UVs along X then Z
|
||||
faceDirVector.set( 0, - 1, 0 );
|
||||
uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'z', radius, width );
|
||||
uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'z', 'x', radius, depth );
|
||||
break;
|
||||
|
||||
case 4: // front
|
||||
|
||||
// generate UVs along X then Y
|
||||
faceDirVector.set( 0, 0, 1 );
|
||||
uvs[ j + 0 ] = 1.0 - getUv( faceDirVector, normal, 'x', 'y', radius, width );
|
||||
uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
|
||||
break;
|
||||
|
||||
case 5: // back
|
||||
|
||||
// generate UVs along X then Y
|
||||
faceDirVector.set( 0, 0, - 1 );
|
||||
uvs[ j + 0 ] = getUv( faceDirVector, normal, 'x', 'y', radius, width );
|
||||
uvs[ j + 1 ] = 1.0 - getUv( faceDirVector, normal, 'y', 'x', radius, height );
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { RoundedBoxGeometry };
|
||||
144
dist/electron/static/sdk/three/jsm/geometries/SDFGeometryGenerator.js
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
/**
|
||||
* @author santiago / @glitch_life
|
||||
* wrapper of https://www.npmjs.com/package/isosurface by https://github.com/mikolalysenko
|
||||
*
|
||||
* Returns BufferGeometry from SDF
|
||||
*/
|
||||
|
||||
import {
|
||||
BufferAttribute,
|
||||
BufferGeometry,
|
||||
FloatType,
|
||||
Mesh,
|
||||
OrthographicCamera,
|
||||
PlaneGeometry,
|
||||
Scene,
|
||||
ShaderMaterial,
|
||||
Vector2,
|
||||
WebGLRenderTarget
|
||||
} from 'three';
|
||||
|
||||
import { surfaceNet } from './../libs/surfaceNet.js';
|
||||
|
||||
class SDFGeometryGenerator {
|
||||
|
||||
constructor( renderer ) {
|
||||
|
||||
this.renderer = renderer;
|
||||
|
||||
}
|
||||
|
||||
generate( res = 64, distFunc = 'float dist( vec3 p ){ return length(p) - 0.5; }', bounds = 1 ) {
|
||||
|
||||
let w, h;
|
||||
if ( res == 8 ) [ w, h ] = [ 32, 16 ];
|
||||
else if ( res == 16 ) [ w, h ] = [ 64, 64 ];
|
||||
else if ( res == 32 ) [ w, h ] = [ 256, 128 ];
|
||||
else if ( res == 64 ) [ w, h ] = [ 512, 512 ];
|
||||
else if ( res == 128 ) [ w, h ] = [ 2048, 1024 ];
|
||||
else if ( res == 256 ) [ w, h ] = [ 4096, 4096 ];
|
||||
else if ( res == 512 ) [ w, h ] = [ 16384, 8096 ];
|
||||
else if ( res == 1024 ) [ w, h ] = [ 32768, 32768 ];
|
||||
else throw new Error( 'THREE.SDFGeometryGenerator: Resolution must be in range 8 < res < 1024 and must be ^2' );
|
||||
|
||||
const maxTexSize = this.renderer.capabilities.maxTextureSize;
|
||||
|
||||
if ( w > maxTexSize || h > maxTexSize ) throw new Error( 'THREE.SDFGeometryGenerator: Your device does not support this resolution ( ' + res + ' ), decrease [res] param.' );
|
||||
|
||||
const [ tilesX, tilesY ] = [ ( w / res ), ( h / res ) ];
|
||||
|
||||
const sdfCompute = `
|
||||
varying vec2 vUv;
|
||||
uniform float tileNum;
|
||||
uniform float bounds;
|
||||
[#dist#]
|
||||
void main() { gl_FragColor=vec4( ( dist( vec3( vUv, tileNum ) * 2.0 * bounds - vec3( bounds ) ) < 0.00001 ) ? 1.0 : 0.0 ); }
|
||||
`;
|
||||
|
||||
const sdfRT = this.computeSDF( w, h, tilesX, tilesY, bounds, sdfCompute.replace( '[#dist#]', distFunc ) );
|
||||
|
||||
const read = new Float32Array( w * h * 4 );
|
||||
this.renderer.readRenderTargetPixels( sdfRT, 0, 0, w, h, read );
|
||||
sdfRT.dispose();
|
||||
|
||||
//
|
||||
|
||||
const mesh = surfaceNet( [ res, res, res ], ( x, y, z ) => {
|
||||
|
||||
x = ( x + bounds ) * ( res / ( bounds * 2 ) );
|
||||
y = ( y + bounds ) * ( res / ( bounds * 2 ) );
|
||||
z = ( z + bounds ) * ( res / ( bounds * 2 ) );
|
||||
let p = ( x + ( z % tilesX ) * res ) + y * w + ( Math.floor( z / tilesX ) * res * w );
|
||||
p *= 4;
|
||||
return ( read[ p + 3 ] > 0 ) ? - 0.000000001 : 1;
|
||||
|
||||
}, [[ - bounds, - bounds, - bounds ], [ bounds, bounds, bounds ]] );
|
||||
|
||||
const ps = [], ids = [];
|
||||
const geometry = new BufferGeometry();
|
||||
mesh.positions.forEach( p => {
|
||||
|
||||
ps.push( p[ 0 ], p[ 1 ], p[ 2 ] );
|
||||
|
||||
} );
|
||||
mesh.cells.forEach( p => ids.push( p[ 0 ], p[ 1 ], p[ 2 ] ) );
|
||||
geometry.setAttribute( 'position', new BufferAttribute( new Float32Array( ps ), 3 ) );
|
||||
geometry.setIndex( ids );
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
computeSDF( width, height, tilesX, tilesY, bounds, shader ) {
|
||||
|
||||
const rt = new WebGLRenderTarget( width, height, { type: FloatType } );
|
||||
const scn = new Scene();
|
||||
const cam = new OrthographicCamera();
|
||||
const tiles = tilesX * tilesY;
|
||||
let currentTile = 0;
|
||||
|
||||
Object.assign( cam, { left: width / - 2, right: width / 2, top: height / 2, bottom: height / - 2 } ).updateProjectionMatrix();
|
||||
cam.position.z = 2;
|
||||
|
||||
const tileSize = width / tilesX;
|
||||
const geometry = new PlaneGeometry( tileSize, tileSize );
|
||||
|
||||
while ( currentTile ++ < tiles ) {
|
||||
|
||||
const c = currentTile - 1;
|
||||
const [ px, py ] = [ ( tileSize ) / 2 + ( c % tilesX ) * ( tileSize ) - width / 2, ( tileSize ) / 2 + Math.floor( c / tilesX ) * ( tileSize ) - height / 2 ];
|
||||
const compPlane = new Mesh( geometry, new ShaderMaterial( {
|
||||
uniforms: {
|
||||
res: { value: new Vector2( width, height ) },
|
||||
tileNum: { value: c / ( tilesX * tilesY - 1 ) },
|
||||
bounds: { value: bounds }
|
||||
},
|
||||
vertexShader: 'varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.0);}',
|
||||
fragmentShader: shader
|
||||
} ) );
|
||||
compPlane.position.set( px, py, 0 );
|
||||
scn.add( compPlane );
|
||||
|
||||
}
|
||||
|
||||
this.renderer.setRenderTarget( rt );
|
||||
this.renderer.render( scn, cam );
|
||||
this.renderer.setRenderTarget( null );
|
||||
|
||||
//
|
||||
|
||||
geometry.dispose();
|
||||
|
||||
scn.traverse( function ( object ) {
|
||||
|
||||
if ( object.material !== undefined ) object.material.dispose();
|
||||
|
||||
} );
|
||||
|
||||
return rt;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { SDFGeometryGenerator };
|
||||
704
dist/electron/static/sdk/three/jsm/geometries/TeapotGeometry.js
vendored
Normal file
@ -0,0 +1,704 @@
|
||||
import {
|
||||
BufferAttribute,
|
||||
BufferGeometry,
|
||||
Matrix4,
|
||||
Vector3,
|
||||
Vector4
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* Tessellates the famous Utah teapot database by Martin Newell into triangles.
|
||||
*
|
||||
* Parameters: size = 50, segments = 10, bottom = true, lid = true, body = true,
|
||||
* fitLid = false, blinn = true
|
||||
*
|
||||
* size is a relative scale: I've scaled the teapot to fit vertically between -1 and 1.
|
||||
* Think of it as a "radius".
|
||||
* segments - number of line segments to subdivide each patch edge;
|
||||
* 1 is possible but gives degenerates, so two is the real minimum.
|
||||
* bottom - boolean, if true (default) then the bottom patches are added. Some consider
|
||||
* adding the bottom heresy, so set this to "false" to adhere to the One True Way.
|
||||
* lid - to remove the lid and look inside, set to true.
|
||||
* body - to remove the body and leave the lid, set this and "bottom" to false.
|
||||
* fitLid - the lid is a tad small in the original. This stretches it a bit so you can't
|
||||
* see the teapot's insides through the gap.
|
||||
* blinn - Jim Blinn scaled the original data vertically by dividing by about 1.3 to look
|
||||
* nicer. If you want to see the original teapot, similar to the real-world model, set
|
||||
* this to false. True by default.
|
||||
* See http://en.wikipedia.org/wiki/File:Original_Utah_Teapot.jpg for the original
|
||||
* real-world teapot (from http://en.wikipedia.org/wiki/Utah_teapot).
|
||||
*
|
||||
* Note that the bottom (the last four patches) is not flat - blame Frank Crow, not me.
|
||||
*
|
||||
* The teapot should normally be rendered as a double sided object, since for some
|
||||
* patches both sides can be seen, e.g., the gap around the lid and inside the spout.
|
||||
*
|
||||
* Segments 'n' determines the number of triangles output.
|
||||
* Total triangles = 32*2*n*n - 8*n [degenerates at the top and bottom cusps are deleted]
|
||||
*
|
||||
* size_factor # triangles
|
||||
* 1 56
|
||||
* 2 240
|
||||
* 3 552
|
||||
* 4 992
|
||||
*
|
||||
* 10 6320
|
||||
* 20 25440
|
||||
* 30 57360
|
||||
*
|
||||
* Code converted from my ancient SPD software, http://tog.acm.org/resources/SPD/
|
||||
* Created for the Udacity course "Interactive Rendering", http://bit.ly/ericity
|
||||
* YouTube video on teapot history: https://www.youtube.com/watch?v=DxMfblPzFNc
|
||||
*
|
||||
* See https://en.wikipedia.org/wiki/Utah_teapot for the history of the teapot
|
||||
*
|
||||
*/
|
||||
|
||||
class TeapotGeometry extends BufferGeometry {
|
||||
|
||||
constructor( size = 50, segments = 10, bottom = true, lid = true, body = true, fitLid = true, blinn = true ) {
|
||||
|
||||
// 32 * 4 * 4 Bezier spline patches
|
||||
const teapotPatches = [
|
||||
/*rim*/
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
3, 16, 17, 18, 7, 19, 20, 21, 11, 22, 23, 24, 15, 25, 26, 27,
|
||||
18, 28, 29, 30, 21, 31, 32, 33, 24, 34, 35, 36, 27, 37, 38, 39,
|
||||
30, 40, 41, 0, 33, 42, 43, 4, 36, 44, 45, 8, 39, 46, 47, 12,
|
||||
/*body*/
|
||||
12, 13, 14, 15, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
|
||||
15, 25, 26, 27, 51, 60, 61, 62, 55, 63, 64, 65, 59, 66, 67, 68,
|
||||
27, 37, 38, 39, 62, 69, 70, 71, 65, 72, 73, 74, 68, 75, 76, 77,
|
||||
39, 46, 47, 12, 71, 78, 79, 48, 74, 80, 81, 52, 77, 82, 83, 56,
|
||||
56, 57, 58, 59, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
|
||||
59, 66, 67, 68, 87, 96, 97, 98, 91, 99, 100, 101, 95, 102, 103, 104,
|
||||
68, 75, 76, 77, 98, 105, 106, 107, 101, 108, 109, 110, 104, 111, 112, 113,
|
||||
77, 82, 83, 56, 107, 114, 115, 84, 110, 116, 117, 88, 113, 118, 119, 92,
|
||||
/*handle*/
|
||||
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
|
||||
123, 136, 137, 120, 127, 138, 139, 124, 131, 140, 141, 128, 135, 142, 143, 132,
|
||||
132, 133, 134, 135, 144, 145, 146, 147, 148, 149, 150, 151, 68, 152, 153, 154,
|
||||
135, 142, 143, 132, 147, 155, 156, 144, 151, 157, 158, 148, 154, 159, 160, 68,
|
||||
/*spout*/
|
||||
161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
|
||||
164, 177, 178, 161, 168, 179, 180, 165, 172, 181, 182, 169, 176, 183, 184, 173,
|
||||
173, 174, 175, 176, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
|
||||
176, 183, 184, 173, 188, 197, 198, 185, 192, 199, 200, 189, 196, 201, 202, 193,
|
||||
/*lid*/
|
||||
203, 203, 203, 203, 204, 205, 206, 207, 208, 208, 208, 208, 209, 210, 211, 212,
|
||||
203, 203, 203, 203, 207, 213, 214, 215, 208, 208, 208, 208, 212, 216, 217, 218,
|
||||
203, 203, 203, 203, 215, 219, 220, 221, 208, 208, 208, 208, 218, 222, 223, 224,
|
||||
203, 203, 203, 203, 221, 225, 226, 204, 208, 208, 208, 208, 224, 227, 228, 209,
|
||||
209, 210, 211, 212, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
|
||||
212, 216, 217, 218, 232, 241, 242, 243, 236, 244, 245, 246, 240, 247, 248, 249,
|
||||
218, 222, 223, 224, 243, 250, 251, 252, 246, 253, 254, 255, 249, 256, 257, 258,
|
||||
224, 227, 228, 209, 252, 259, 260, 229, 255, 261, 262, 233, 258, 263, 264, 237,
|
||||
/*bottom*/
|
||||
265, 265, 265, 265, 266, 267, 268, 269, 270, 271, 272, 273, 92, 119, 118, 113,
|
||||
265, 265, 265, 265, 269, 274, 275, 276, 273, 277, 278, 279, 113, 112, 111, 104,
|
||||
265, 265, 265, 265, 276, 280, 281, 282, 279, 283, 284, 285, 104, 103, 102, 95,
|
||||
265, 265, 265, 265, 282, 286, 287, 266, 285, 288, 289, 270, 95, 94, 93, 92
|
||||
];
|
||||
|
||||
const teapotVertices = [
|
||||
1.4, 0, 2.4,
|
||||
1.4, - 0.784, 2.4,
|
||||
0.784, - 1.4, 2.4,
|
||||
0, - 1.4, 2.4,
|
||||
1.3375, 0, 2.53125,
|
||||
1.3375, - 0.749, 2.53125,
|
||||
0.749, - 1.3375, 2.53125,
|
||||
0, - 1.3375, 2.53125,
|
||||
1.4375, 0, 2.53125,
|
||||
1.4375, - 0.805, 2.53125,
|
||||
0.805, - 1.4375, 2.53125,
|
||||
0, - 1.4375, 2.53125,
|
||||
1.5, 0, 2.4,
|
||||
1.5, - 0.84, 2.4,
|
||||
0.84, - 1.5, 2.4,
|
||||
0, - 1.5, 2.4,
|
||||
- 0.784, - 1.4, 2.4,
|
||||
- 1.4, - 0.784, 2.4,
|
||||
- 1.4, 0, 2.4,
|
||||
- 0.749, - 1.3375, 2.53125,
|
||||
- 1.3375, - 0.749, 2.53125,
|
||||
- 1.3375, 0, 2.53125,
|
||||
- 0.805, - 1.4375, 2.53125,
|
||||
- 1.4375, - 0.805, 2.53125,
|
||||
- 1.4375, 0, 2.53125,
|
||||
- 0.84, - 1.5, 2.4,
|
||||
- 1.5, - 0.84, 2.4,
|
||||
- 1.5, 0, 2.4,
|
||||
- 1.4, 0.784, 2.4,
|
||||
- 0.784, 1.4, 2.4,
|
||||
0, 1.4, 2.4,
|
||||
- 1.3375, 0.749, 2.53125,
|
||||
- 0.749, 1.3375, 2.53125,
|
||||
0, 1.3375, 2.53125,
|
||||
- 1.4375, 0.805, 2.53125,
|
||||
- 0.805, 1.4375, 2.53125,
|
||||
0, 1.4375, 2.53125,
|
||||
- 1.5, 0.84, 2.4,
|
||||
- 0.84, 1.5, 2.4,
|
||||
0, 1.5, 2.4,
|
||||
0.784, 1.4, 2.4,
|
||||
1.4, 0.784, 2.4,
|
||||
0.749, 1.3375, 2.53125,
|
||||
1.3375, 0.749, 2.53125,
|
||||
0.805, 1.4375, 2.53125,
|
||||
1.4375, 0.805, 2.53125,
|
||||
0.84, 1.5, 2.4,
|
||||
1.5, 0.84, 2.4,
|
||||
1.75, 0, 1.875,
|
||||
1.75, - 0.98, 1.875,
|
||||
0.98, - 1.75, 1.875,
|
||||
0, - 1.75, 1.875,
|
||||
2, 0, 1.35,
|
||||
2, - 1.12, 1.35,
|
||||
1.12, - 2, 1.35,
|
||||
0, - 2, 1.35,
|
||||
2, 0, 0.9,
|
||||
2, - 1.12, 0.9,
|
||||
1.12, - 2, 0.9,
|
||||
0, - 2, 0.9,
|
||||
- 0.98, - 1.75, 1.875,
|
||||
- 1.75, - 0.98, 1.875,
|
||||
- 1.75, 0, 1.875,
|
||||
- 1.12, - 2, 1.35,
|
||||
- 2, - 1.12, 1.35,
|
||||
- 2, 0, 1.35,
|
||||
- 1.12, - 2, 0.9,
|
||||
- 2, - 1.12, 0.9,
|
||||
- 2, 0, 0.9,
|
||||
- 1.75, 0.98, 1.875,
|
||||
- 0.98, 1.75, 1.875,
|
||||
0, 1.75, 1.875,
|
||||
- 2, 1.12, 1.35,
|
||||
- 1.12, 2, 1.35,
|
||||
0, 2, 1.35,
|
||||
- 2, 1.12, 0.9,
|
||||
- 1.12, 2, 0.9,
|
||||
0, 2, 0.9,
|
||||
0.98, 1.75, 1.875,
|
||||
1.75, 0.98, 1.875,
|
||||
1.12, 2, 1.35,
|
||||
2, 1.12, 1.35,
|
||||
1.12, 2, 0.9,
|
||||
2, 1.12, 0.9,
|
||||
2, 0, 0.45,
|
||||
2, - 1.12, 0.45,
|
||||
1.12, - 2, 0.45,
|
||||
0, - 2, 0.45,
|
||||
1.5, 0, 0.225,
|
||||
1.5, - 0.84, 0.225,
|
||||
0.84, - 1.5, 0.225,
|
||||
0, - 1.5, 0.225,
|
||||
1.5, 0, 0.15,
|
||||
1.5, - 0.84, 0.15,
|
||||
0.84, - 1.5, 0.15,
|
||||
0, - 1.5, 0.15,
|
||||
- 1.12, - 2, 0.45,
|
||||
- 2, - 1.12, 0.45,
|
||||
- 2, 0, 0.45,
|
||||
- 0.84, - 1.5, 0.225,
|
||||
- 1.5, - 0.84, 0.225,
|
||||
- 1.5, 0, 0.225,
|
||||
- 0.84, - 1.5, 0.15,
|
||||
- 1.5, - 0.84, 0.15,
|
||||
- 1.5, 0, 0.15,
|
||||
- 2, 1.12, 0.45,
|
||||
- 1.12, 2, 0.45,
|
||||
0, 2, 0.45,
|
||||
- 1.5, 0.84, 0.225,
|
||||
- 0.84, 1.5, 0.225,
|
||||
0, 1.5, 0.225,
|
||||
- 1.5, 0.84, 0.15,
|
||||
- 0.84, 1.5, 0.15,
|
||||
0, 1.5, 0.15,
|
||||
1.12, 2, 0.45,
|
||||
2, 1.12, 0.45,
|
||||
0.84, 1.5, 0.225,
|
||||
1.5, 0.84, 0.225,
|
||||
0.84, 1.5, 0.15,
|
||||
1.5, 0.84, 0.15,
|
||||
- 1.6, 0, 2.025,
|
||||
- 1.6, - 0.3, 2.025,
|
||||
- 1.5, - 0.3, 2.25,
|
||||
- 1.5, 0, 2.25,
|
||||
- 2.3, 0, 2.025,
|
||||
- 2.3, - 0.3, 2.025,
|
||||
- 2.5, - 0.3, 2.25,
|
||||
- 2.5, 0, 2.25,
|
||||
- 2.7, 0, 2.025,
|
||||
- 2.7, - 0.3, 2.025,
|
||||
- 3, - 0.3, 2.25,
|
||||
- 3, 0, 2.25,
|
||||
- 2.7, 0, 1.8,
|
||||
- 2.7, - 0.3, 1.8,
|
||||
- 3, - 0.3, 1.8,
|
||||
- 3, 0, 1.8,
|
||||
- 1.5, 0.3, 2.25,
|
||||
- 1.6, 0.3, 2.025,
|
||||
- 2.5, 0.3, 2.25,
|
||||
- 2.3, 0.3, 2.025,
|
||||
- 3, 0.3, 2.25,
|
||||
- 2.7, 0.3, 2.025,
|
||||
- 3, 0.3, 1.8,
|
||||
- 2.7, 0.3, 1.8,
|
||||
- 2.7, 0, 1.575,
|
||||
- 2.7, - 0.3, 1.575,
|
||||
- 3, - 0.3, 1.35,
|
||||
- 3, 0, 1.35,
|
||||
- 2.5, 0, 1.125,
|
||||
- 2.5, - 0.3, 1.125,
|
||||
- 2.65, - 0.3, 0.9375,
|
||||
- 2.65, 0, 0.9375,
|
||||
- 2, - 0.3, 0.9,
|
||||
- 1.9, - 0.3, 0.6,
|
||||
- 1.9, 0, 0.6,
|
||||
- 3, 0.3, 1.35,
|
||||
- 2.7, 0.3, 1.575,
|
||||
- 2.65, 0.3, 0.9375,
|
||||
- 2.5, 0.3, 1.125,
|
||||
- 1.9, 0.3, 0.6,
|
||||
- 2, 0.3, 0.9,
|
||||
1.7, 0, 1.425,
|
||||
1.7, - 0.66, 1.425,
|
||||
1.7, - 0.66, 0.6,
|
||||
1.7, 0, 0.6,
|
||||
2.6, 0, 1.425,
|
||||
2.6, - 0.66, 1.425,
|
||||
3.1, - 0.66, 0.825,
|
||||
3.1, 0, 0.825,
|
||||
2.3, 0, 2.1,
|
||||
2.3, - 0.25, 2.1,
|
||||
2.4, - 0.25, 2.025,
|
||||
2.4, 0, 2.025,
|
||||
2.7, 0, 2.4,
|
||||
2.7, - 0.25, 2.4,
|
||||
3.3, - 0.25, 2.4,
|
||||
3.3, 0, 2.4,
|
||||
1.7, 0.66, 0.6,
|
||||
1.7, 0.66, 1.425,
|
||||
3.1, 0.66, 0.825,
|
||||
2.6, 0.66, 1.425,
|
||||
2.4, 0.25, 2.025,
|
||||
2.3, 0.25, 2.1,
|
||||
3.3, 0.25, 2.4,
|
||||
2.7, 0.25, 2.4,
|
||||
2.8, 0, 2.475,
|
||||
2.8, - 0.25, 2.475,
|
||||
3.525, - 0.25, 2.49375,
|
||||
3.525, 0, 2.49375,
|
||||
2.9, 0, 2.475,
|
||||
2.9, - 0.15, 2.475,
|
||||
3.45, - 0.15, 2.5125,
|
||||
3.45, 0, 2.5125,
|
||||
2.8, 0, 2.4,
|
||||
2.8, - 0.15, 2.4,
|
||||
3.2, - 0.15, 2.4,
|
||||
3.2, 0, 2.4,
|
||||
3.525, 0.25, 2.49375,
|
||||
2.8, 0.25, 2.475,
|
||||
3.45, 0.15, 2.5125,
|
||||
2.9, 0.15, 2.475,
|
||||
3.2, 0.15, 2.4,
|
||||
2.8, 0.15, 2.4,
|
||||
0, 0, 3.15,
|
||||
0.8, 0, 3.15,
|
||||
0.8, - 0.45, 3.15,
|
||||
0.45, - 0.8, 3.15,
|
||||
0, - 0.8, 3.15,
|
||||
0, 0, 2.85,
|
||||
0.2, 0, 2.7,
|
||||
0.2, - 0.112, 2.7,
|
||||
0.112, - 0.2, 2.7,
|
||||
0, - 0.2, 2.7,
|
||||
- 0.45, - 0.8, 3.15,
|
||||
- 0.8, - 0.45, 3.15,
|
||||
- 0.8, 0, 3.15,
|
||||
- 0.112, - 0.2, 2.7,
|
||||
- 0.2, - 0.112, 2.7,
|
||||
- 0.2, 0, 2.7,
|
||||
- 0.8, 0.45, 3.15,
|
||||
- 0.45, 0.8, 3.15,
|
||||
0, 0.8, 3.15,
|
||||
- 0.2, 0.112, 2.7,
|
||||
- 0.112, 0.2, 2.7,
|
||||
0, 0.2, 2.7,
|
||||
0.45, 0.8, 3.15,
|
||||
0.8, 0.45, 3.15,
|
||||
0.112, 0.2, 2.7,
|
||||
0.2, 0.112, 2.7,
|
||||
0.4, 0, 2.55,
|
||||
0.4, - 0.224, 2.55,
|
||||
0.224, - 0.4, 2.55,
|
||||
0, - 0.4, 2.55,
|
||||
1.3, 0, 2.55,
|
||||
1.3, - 0.728, 2.55,
|
||||
0.728, - 1.3, 2.55,
|
||||
0, - 1.3, 2.55,
|
||||
1.3, 0, 2.4,
|
||||
1.3, - 0.728, 2.4,
|
||||
0.728, - 1.3, 2.4,
|
||||
0, - 1.3, 2.4,
|
||||
- 0.224, - 0.4, 2.55,
|
||||
- 0.4, - 0.224, 2.55,
|
||||
- 0.4, 0, 2.55,
|
||||
- 0.728, - 1.3, 2.55,
|
||||
- 1.3, - 0.728, 2.55,
|
||||
- 1.3, 0, 2.55,
|
||||
- 0.728, - 1.3, 2.4,
|
||||
- 1.3, - 0.728, 2.4,
|
||||
- 1.3, 0, 2.4,
|
||||
- 0.4, 0.224, 2.55,
|
||||
- 0.224, 0.4, 2.55,
|
||||
0, 0.4, 2.55,
|
||||
- 1.3, 0.728, 2.55,
|
||||
- 0.728, 1.3, 2.55,
|
||||
0, 1.3, 2.55,
|
||||
- 1.3, 0.728, 2.4,
|
||||
- 0.728, 1.3, 2.4,
|
||||
0, 1.3, 2.4,
|
||||
0.224, 0.4, 2.55,
|
||||
0.4, 0.224, 2.55,
|
||||
0.728, 1.3, 2.55,
|
||||
1.3, 0.728, 2.55,
|
||||
0.728, 1.3, 2.4,
|
||||
1.3, 0.728, 2.4,
|
||||
0, 0, 0,
|
||||
1.425, 0, 0,
|
||||
1.425, 0.798, 0,
|
||||
0.798, 1.425, 0,
|
||||
0, 1.425, 0,
|
||||
1.5, 0, 0.075,
|
||||
1.5, 0.84, 0.075,
|
||||
0.84, 1.5, 0.075,
|
||||
0, 1.5, 0.075,
|
||||
- 0.798, 1.425, 0,
|
||||
- 1.425, 0.798, 0,
|
||||
- 1.425, 0, 0,
|
||||
- 0.84, 1.5, 0.075,
|
||||
- 1.5, 0.84, 0.075,
|
||||
- 1.5, 0, 0.075,
|
||||
- 1.425, - 0.798, 0,
|
||||
- 0.798, - 1.425, 0,
|
||||
0, - 1.425, 0,
|
||||
- 1.5, - 0.84, 0.075,
|
||||
- 0.84, - 1.5, 0.075,
|
||||
0, - 1.5, 0.075,
|
||||
0.798, - 1.425, 0,
|
||||
1.425, - 0.798, 0,
|
||||
0.84, - 1.5, 0.075,
|
||||
1.5, - 0.84, 0.075
|
||||
];
|
||||
|
||||
super();
|
||||
|
||||
// number of segments per patch
|
||||
segments = Math.max( 2, Math.floor( segments ) );
|
||||
|
||||
// Jim Blinn scaled the teapot down in size by about 1.3 for
|
||||
// some rendering tests. He liked the new proportions that he kept
|
||||
// the data in this form. The model was distributed with these new
|
||||
// proportions and became the norm. Trivia: comparing images of the
|
||||
// real teapot and the computer model, the ratio for the bowl of the
|
||||
// real teapot is more like 1.25, but since 1.3 is the traditional
|
||||
// value given, we use it here.
|
||||
const blinnScale = 1.3;
|
||||
|
||||
// scale the size to be the real scaling factor
|
||||
const maxHeight = 3.15 * ( blinn ? 1 : blinnScale );
|
||||
|
||||
const maxHeight2 = maxHeight / 2;
|
||||
const trueSize = size / maxHeight2;
|
||||
|
||||
// Number of elements depends on what is needed. Subtract degenerate
|
||||
// triangles at tip of bottom and lid out in advance.
|
||||
let numTriangles = bottom ? ( 8 * segments - 4 ) * segments : 0;
|
||||
numTriangles += lid ? ( 16 * segments - 4 ) * segments : 0;
|
||||
numTriangles += body ? 40 * segments * segments : 0;
|
||||
|
||||
const indices = new Uint32Array( numTriangles * 3 );
|
||||
|
||||
let numVertices = bottom ? 4 : 0;
|
||||
numVertices += lid ? 8 : 0;
|
||||
numVertices += body ? 20 : 0;
|
||||
numVertices *= ( segments + 1 ) * ( segments + 1 );
|
||||
|
||||
const vertices = new Float32Array( numVertices * 3 );
|
||||
const normals = new Float32Array( numVertices * 3 );
|
||||
const uvs = new Float32Array( numVertices * 2 );
|
||||
|
||||
// Bezier form
|
||||
const ms = new Matrix4();
|
||||
ms.set(
|
||||
- 1.0, 3.0, - 3.0, 1.0,
|
||||
3.0, - 6.0, 3.0, 0.0,
|
||||
- 3.0, 3.0, 0.0, 0.0,
|
||||
1.0, 0.0, 0.0, 0.0 );
|
||||
|
||||
const g = [];
|
||||
|
||||
const sp = [];
|
||||
const tp = [];
|
||||
const dsp = [];
|
||||
const dtp = [];
|
||||
|
||||
// M * G * M matrix, sort of see
|
||||
// http://www.cs.helsinki.fi/group/goa/mallinnus/curves/surfaces.html
|
||||
const mgm = [];
|
||||
|
||||
const vert = [];
|
||||
const sdir = [];
|
||||
const tdir = [];
|
||||
|
||||
const norm = new Vector3();
|
||||
|
||||
let tcoord;
|
||||
|
||||
let sval;
|
||||
let tval;
|
||||
let p;
|
||||
let dsval = 0;
|
||||
let dtval = 0;
|
||||
|
||||
const normOut = new Vector3();
|
||||
|
||||
const gmx = new Matrix4();
|
||||
const tmtx = new Matrix4();
|
||||
|
||||
const vsp = new Vector4();
|
||||
const vtp = new Vector4();
|
||||
const vdsp = new Vector4();
|
||||
const vdtp = new Vector4();
|
||||
|
||||
const vsdir = new Vector3();
|
||||
const vtdir = new Vector3();
|
||||
|
||||
const mst = ms.clone();
|
||||
mst.transpose();
|
||||
|
||||
// internal function: test if triangle has any matching vertices;
|
||||
// if so, don't save triangle, since it won't display anything.
|
||||
const notDegenerate = ( vtx1, vtx2, vtx3 ) => // if any vertex matches, return false
|
||||
! ( ( ( vertices[ vtx1 * 3 ] === vertices[ vtx2 * 3 ] ) &&
|
||||
( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx2 * 3 + 1 ] ) &&
|
||||
( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx2 * 3 + 2 ] ) ) ||
|
||||
( ( vertices[ vtx1 * 3 ] === vertices[ vtx3 * 3 ] ) &&
|
||||
( vertices[ vtx1 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) &&
|
||||
( vertices[ vtx1 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) ) || ( vertices[ vtx2 * 3 ] === vertices[ vtx3 * 3 ] ) &&
|
||||
( vertices[ vtx2 * 3 + 1 ] === vertices[ vtx3 * 3 + 1 ] ) &&
|
||||
( vertices[ vtx2 * 3 + 2 ] === vertices[ vtx3 * 3 + 2 ] ) );
|
||||
|
||||
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
mgm[ i ] = new Matrix4();
|
||||
|
||||
}
|
||||
|
||||
const minPatches = body ? 0 : 20;
|
||||
const maxPatches = bottom ? 32 : 28;
|
||||
|
||||
const vertPerRow = segments + 1;
|
||||
|
||||
let surfCount = 0;
|
||||
|
||||
let vertCount = 0;
|
||||
let normCount = 0;
|
||||
let uvCount = 0;
|
||||
|
||||
let indexCount = 0;
|
||||
|
||||
for ( let surf = minPatches; surf < maxPatches; surf ++ ) {
|
||||
|
||||
// lid is in the middle of the data, patches 20-27,
|
||||
// so ignore it for this part of the loop if the lid is not desired
|
||||
if ( lid || ( surf < 20 || surf >= 28 ) ) {
|
||||
|
||||
// get M * G * M matrix for x,y,z
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
// get control patches
|
||||
for ( let r = 0; r < 4; r ++ ) {
|
||||
|
||||
for ( let c = 0; c < 4; c ++ ) {
|
||||
|
||||
// transposed
|
||||
g[ c * 4 + r ] = teapotVertices[ teapotPatches[ surf * 16 + r * 4 + c ] * 3 + i ];
|
||||
|
||||
// is the lid to be made larger, and is this a point on the lid
|
||||
// that is X or Y?
|
||||
if ( fitLid && ( surf >= 20 && surf < 28 ) && ( i !== 2 ) ) {
|
||||
|
||||
// increase XY size by 7.7%, found empirically. I don't
|
||||
// increase Z so that the teapot will continue to fit in the
|
||||
// space -1 to 1 for Y (Y is up for the final model).
|
||||
g[ c * 4 + r ] *= 1.077;
|
||||
|
||||
}
|
||||
|
||||
// Blinn "fixed" the teapot by dividing Z by blinnScale, and that's the
|
||||
// data we now use. The original teapot is taller. Fix it:
|
||||
if ( ! blinn && ( i === 2 ) ) {
|
||||
|
||||
g[ c * 4 + r ] *= blinnScale;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
gmx.set( g[ 0 ], g[ 1 ], g[ 2 ], g[ 3 ], g[ 4 ], g[ 5 ], g[ 6 ], g[ 7 ], g[ 8 ], g[ 9 ], g[ 10 ], g[ 11 ], g[ 12 ], g[ 13 ], g[ 14 ], g[ 15 ] );
|
||||
|
||||
tmtx.multiplyMatrices( gmx, ms );
|
||||
mgm[ i ].multiplyMatrices( mst, tmtx );
|
||||
|
||||
}
|
||||
|
||||
// step along, get points, and output
|
||||
for ( let sstep = 0; sstep <= segments; sstep ++ ) {
|
||||
|
||||
const s = sstep / segments;
|
||||
|
||||
for ( let tstep = 0; tstep <= segments; tstep ++ ) {
|
||||
|
||||
const t = tstep / segments;
|
||||
|
||||
// point from basis
|
||||
// get power vectors and their derivatives
|
||||
for ( p = 4, sval = tval = 1.0; p --; ) {
|
||||
|
||||
sp[ p ] = sval;
|
||||
tp[ p ] = tval;
|
||||
sval *= s;
|
||||
tval *= t;
|
||||
|
||||
if ( p === 3 ) {
|
||||
|
||||
dsp[ p ] = dtp[ p ] = 0.0;
|
||||
dsval = dtval = 1.0;
|
||||
|
||||
} else {
|
||||
|
||||
dsp[ p ] = dsval * ( 3 - p );
|
||||
dtp[ p ] = dtval * ( 3 - p );
|
||||
dsval *= s;
|
||||
dtval *= t;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vsp.fromArray( sp );
|
||||
vtp.fromArray( tp );
|
||||
vdsp.fromArray( dsp );
|
||||
vdtp.fromArray( dtp );
|
||||
|
||||
// do for x,y,z
|
||||
for ( let i = 0; i < 3; i ++ ) {
|
||||
|
||||
// multiply power vectors times matrix to get value
|
||||
tcoord = vsp.clone();
|
||||
tcoord.applyMatrix4( mgm[ i ] );
|
||||
vert[ i ] = tcoord.dot( vtp );
|
||||
|
||||
// get s and t tangent vectors
|
||||
tcoord = vdsp.clone();
|
||||
tcoord.applyMatrix4( mgm[ i ] );
|
||||
sdir[ i ] = tcoord.dot( vtp );
|
||||
|
||||
tcoord = vsp.clone();
|
||||
tcoord.applyMatrix4( mgm[ i ] );
|
||||
tdir[ i ] = tcoord.dot( vdtp );
|
||||
|
||||
}
|
||||
|
||||
// find normal
|
||||
vsdir.fromArray( sdir );
|
||||
vtdir.fromArray( tdir );
|
||||
norm.crossVectors( vtdir, vsdir );
|
||||
norm.normalize();
|
||||
|
||||
// if X and Z length is 0, at the cusp, so point the normal up or down, depending on patch number
|
||||
if ( vert[ 0 ] === 0 && vert[ 1 ] === 0 ) {
|
||||
|
||||
// if above the middle of the teapot, normal points up, else down
|
||||
normOut.set( 0, vert[ 2 ] > maxHeight2 ? 1 : - 1, 0 );
|
||||
|
||||
} else {
|
||||
|
||||
// standard output: rotate on X axis
|
||||
normOut.set( norm.x, norm.z, - norm.y );
|
||||
|
||||
}
|
||||
|
||||
// store it all
|
||||
vertices[ vertCount ++ ] = trueSize * vert[ 0 ];
|
||||
vertices[ vertCount ++ ] = trueSize * ( vert[ 2 ] - maxHeight2 );
|
||||
vertices[ vertCount ++ ] = - trueSize * vert[ 1 ];
|
||||
|
||||
normals[ normCount ++ ] = normOut.x;
|
||||
normals[ normCount ++ ] = normOut.y;
|
||||
normals[ normCount ++ ] = normOut.z;
|
||||
|
||||
uvs[ uvCount ++ ] = 1 - t;
|
||||
uvs[ uvCount ++ ] = 1 - s;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// save the faces
|
||||
for ( let sstep = 0; sstep < segments; sstep ++ ) {
|
||||
|
||||
for ( let tstep = 0; tstep < segments; tstep ++ ) {
|
||||
|
||||
const v1 = surfCount * vertPerRow * vertPerRow + sstep * vertPerRow + tstep;
|
||||
const v2 = v1 + 1;
|
||||
const v3 = v2 + vertPerRow;
|
||||
const v4 = v1 + vertPerRow;
|
||||
|
||||
// Normals and UVs cannot be shared. Without clone(), you can see the consequences
|
||||
// of sharing if you call geometry.applyMatrix4( matrix ).
|
||||
if ( notDegenerate( v1, v2, v3 ) ) {
|
||||
|
||||
indices[ indexCount ++ ] = v1;
|
||||
indices[ indexCount ++ ] = v2;
|
||||
indices[ indexCount ++ ] = v3;
|
||||
|
||||
}
|
||||
|
||||
if ( notDegenerate( v1, v3, v4 ) ) {
|
||||
|
||||
indices[ indexCount ++ ] = v1;
|
||||
indices[ indexCount ++ ] = v3;
|
||||
indices[ indexCount ++ ] = v4;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// increment only if a surface was used
|
||||
surfCount ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.setIndex( new BufferAttribute( indices, 1 ) );
|
||||
this.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
|
||||
this.setAttribute( 'normal', new BufferAttribute( normals, 3 ) );
|
||||
this.setAttribute( 'uv', new BufferAttribute( uvs, 2 ) );
|
||||
|
||||
this.computeBoundingSphere();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { TeapotGeometry };
|
||||
65
dist/electron/static/sdk/three/jsm/geometries/TextGeometry.js
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Text = 3D Text
|
||||
*
|
||||
* parameters = {
|
||||
* font: <THREE.Font>, // font
|
||||
*
|
||||
* size: <float>, // size of the text
|
||||
* depth: <float>, // thickness to extrude text
|
||||
* curveSegments: <int>, // number of points on the curves
|
||||
*
|
||||
* bevelEnabled: <bool>, // turn on bevel
|
||||
* bevelThickness: <float>, // how deep into text bevel goes
|
||||
* bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
|
||||
* bevelOffset: <float> // how far from text outline does bevel start
|
||||
* }
|
||||
*/
|
||||
|
||||
import {
|
||||
ExtrudeGeometry
|
||||
} from '../../three.module.min.js';
|
||||
|
||||
class TextGeometry extends ExtrudeGeometry {
|
||||
|
||||
constructor( text, parameters = {} ) {
|
||||
|
||||
const font = parameters.font;
|
||||
|
||||
if ( font === undefined ) {
|
||||
|
||||
super(); // generate default extrude geometry
|
||||
|
||||
} else {
|
||||
|
||||
const shapes = font.generateShapes( text, parameters.size );
|
||||
|
||||
// translate parameters to ExtrudeGeometry API
|
||||
|
||||
if ( parameters.depth === undefined && parameters.height !== undefined ) {
|
||||
|
||||
console.warn( 'THREE.TextGeometry: .height is now depreciated. Please use .depth instead' ); // @deprecated, r163
|
||||
|
||||
}
|
||||
|
||||
parameters.depth = parameters.depth !== undefined ?
|
||||
parameters.depth : parameters.height !== undefined ?
|
||||
parameters.height : 50;
|
||||
|
||||
// defaults
|
||||
|
||||
if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
|
||||
if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
|
||||
if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
|
||||
|
||||
super( shapes, parameters );
|
||||
|
||||
}
|
||||
|
||||
this.type = 'TextGeometry';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export { TextGeometry };
|
||||
130
dist/electron/static/sdk/three/jsm/helpers/LightProbeHelper.js
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
import {
|
||||
Mesh,
|
||||
ShaderMaterial,
|
||||
SphereGeometry
|
||||
} from 'three';
|
||||
|
||||
class LightProbeHelper extends Mesh {
|
||||
|
||||
constructor( lightProbe, size ) {
|
||||
|
||||
const material = new ShaderMaterial( {
|
||||
|
||||
type: 'LightProbeHelperMaterial',
|
||||
|
||||
uniforms: {
|
||||
|
||||
sh: { value: lightProbe.sh.coefficients }, // by reference
|
||||
|
||||
intensity: { value: lightProbe.intensity }
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
'varying vec3 vNormal;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vNormal = normalize( normalMatrix * normal );',
|
||||
|
||||
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||||
|
||||
'}',
|
||||
|
||||
].join( '\n' ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
'#define RECIPROCAL_PI 0.318309886',
|
||||
|
||||
'vec3 inverseTransformDirection( in vec3 normal, in mat4 matrix ) {',
|
||||
|
||||
' // matrix is assumed to be orthogonal',
|
||||
|
||||
' return normalize( ( vec4( normal, 0.0 ) * matrix ).xyz );',
|
||||
|
||||
'}',
|
||||
|
||||
'// source: https://graphics.stanford.edu/papers/envmap/envmap.pdf',
|
||||
'vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) {',
|
||||
|
||||
' // normal is assumed to have unit length',
|
||||
|
||||
' float x = normal.x, y = normal.y, z = normal.z;',
|
||||
|
||||
' // band 0',
|
||||
' vec3 result = shCoefficients[ 0 ] * 0.886227;',
|
||||
|
||||
' // band 1',
|
||||
' result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y;',
|
||||
' result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z;',
|
||||
' result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x;',
|
||||
|
||||
' // band 2',
|
||||
' result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y;',
|
||||
' result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z;',
|
||||
' result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 );',
|
||||
' result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z;',
|
||||
' result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y );',
|
||||
|
||||
' return result;',
|
||||
|
||||
'}',
|
||||
|
||||
'uniform vec3 sh[ 9 ]; // sh coefficients',
|
||||
|
||||
'uniform float intensity; // light probe intensity',
|
||||
|
||||
'varying vec3 vNormal;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vec3 normal = normalize( vNormal );',
|
||||
|
||||
' vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );',
|
||||
|
||||
' vec3 irradiance = shGetIrradianceAt( worldNormal, sh );',
|
||||
|
||||
' vec3 outgoingLight = RECIPROCAL_PI * irradiance * intensity;',
|
||||
|
||||
' gl_FragColor = linearToOutputTexel( vec4( outgoingLight, 1.0 ) );',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' )
|
||||
|
||||
} );
|
||||
|
||||
const geometry = new SphereGeometry( 1, 32, 16 );
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
this.lightProbe = lightProbe;
|
||||
this.size = size;
|
||||
this.type = 'LightProbeHelper';
|
||||
|
||||
this.onBeforeRender();
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
onBeforeRender() {
|
||||
|
||||
this.position.copy( this.lightProbe.position );
|
||||
|
||||
this.scale.set( 1, 1, 1 ).multiplyScalar( this.size );
|
||||
|
||||
this.material.uniforms.intensity.value = this.lightProbe.intensity;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { LightProbeHelper };
|
||||
73
dist/electron/static/sdk/three/jsm/helpers/OctreeHelper.js
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
import {
|
||||
LineSegments,
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
LineBasicMaterial
|
||||
} from 'three';
|
||||
|
||||
class OctreeHelper extends LineSegments {
|
||||
|
||||
constructor( octree, color = 0xffff00 ) {
|
||||
|
||||
super( new BufferGeometry(), new LineBasicMaterial( { color: color, toneMapped: false } ) );
|
||||
|
||||
this.octree = octree;
|
||||
this.color = color;
|
||||
|
||||
this.type = 'OctreeHelper';
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const vertices = [];
|
||||
|
||||
function traverse( tree ) {
|
||||
|
||||
for ( let i = 0; i < tree.length; i ++ ) {
|
||||
|
||||
const min = tree[ i ].box.min;
|
||||
const max = tree[ i ].box.max;
|
||||
|
||||
vertices.push( max.x, max.y, max.z ); vertices.push( min.x, max.y, max.z ); // 0, 1
|
||||
vertices.push( min.x, max.y, max.z ); vertices.push( min.x, min.y, max.z ); // 1, 2
|
||||
vertices.push( min.x, min.y, max.z ); vertices.push( max.x, min.y, max.z ); // 2, 3
|
||||
vertices.push( max.x, min.y, max.z ); vertices.push( max.x, max.y, max.z ); // 3, 0
|
||||
|
||||
vertices.push( max.x, max.y, min.z ); vertices.push( min.x, max.y, min.z ); // 4, 5
|
||||
vertices.push( min.x, max.y, min.z ); vertices.push( min.x, min.y, min.z ); // 5, 6
|
||||
vertices.push( min.x, min.y, min.z ); vertices.push( max.x, min.y, min.z ); // 6, 7
|
||||
vertices.push( max.x, min.y, min.z ); vertices.push( max.x, max.y, min.z ); // 7, 4
|
||||
|
||||
vertices.push( max.x, max.y, max.z ); vertices.push( max.x, max.y, min.z ); // 0, 4
|
||||
vertices.push( min.x, max.y, max.z ); vertices.push( min.x, max.y, min.z ); // 1, 5
|
||||
vertices.push( min.x, min.y, max.z ); vertices.push( min.x, min.y, min.z ); // 2, 6
|
||||
vertices.push( max.x, min.y, max.z ); vertices.push( max.x, min.y, min.z ); // 3, 7
|
||||
|
||||
traverse( tree[ i ].subTrees );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
traverse( this.octree.subTrees );
|
||||
|
||||
this.geometry.dispose();
|
||||
|
||||
this.geometry = new BufferGeometry();
|
||||
this.geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { OctreeHelper };
|
||||
109
dist/electron/static/sdk/three/jsm/helpers/PositionalAudioHelper.js
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
BufferAttribute,
|
||||
LineBasicMaterial,
|
||||
Line,
|
||||
MathUtils
|
||||
} from 'three';
|
||||
|
||||
class PositionalAudioHelper extends Line {
|
||||
|
||||
constructor( audio, range = 1, divisionsInnerAngle = 16, divisionsOuterAngle = 2 ) {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
const divisions = divisionsInnerAngle + divisionsOuterAngle * 2;
|
||||
const positions = new Float32Array( ( divisions * 3 + 3 ) * 3 );
|
||||
geometry.setAttribute( 'position', new BufferAttribute( positions, 3 ) );
|
||||
|
||||
const materialInnerAngle = new LineBasicMaterial( { color: 0x00ff00 } );
|
||||
const materialOuterAngle = new LineBasicMaterial( { color: 0xffff00 } );
|
||||
|
||||
super( geometry, [ materialOuterAngle, materialInnerAngle ] );
|
||||
|
||||
this.audio = audio;
|
||||
this.range = range;
|
||||
this.divisionsInnerAngle = divisionsInnerAngle;
|
||||
this.divisionsOuterAngle = divisionsOuterAngle;
|
||||
this.type = 'PositionalAudioHelper';
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const audio = this.audio;
|
||||
const range = this.range;
|
||||
const divisionsInnerAngle = this.divisionsInnerAngle;
|
||||
const divisionsOuterAngle = this.divisionsOuterAngle;
|
||||
|
||||
const coneInnerAngle = MathUtils.degToRad( audio.panner.coneInnerAngle );
|
||||
const coneOuterAngle = MathUtils.degToRad( audio.panner.coneOuterAngle );
|
||||
|
||||
const halfConeInnerAngle = coneInnerAngle / 2;
|
||||
const halfConeOuterAngle = coneOuterAngle / 2;
|
||||
|
||||
let start = 0;
|
||||
let count = 0;
|
||||
let i;
|
||||
let stride;
|
||||
|
||||
const geometry = this.geometry;
|
||||
const positionAttribute = geometry.attributes.position;
|
||||
|
||||
geometry.clearGroups();
|
||||
|
||||
//
|
||||
|
||||
function generateSegment( from, to, divisions, materialIndex ) {
|
||||
|
||||
const step = ( to - from ) / divisions;
|
||||
|
||||
positionAttribute.setXYZ( start, 0, 0, 0 );
|
||||
count ++;
|
||||
|
||||
for ( i = from; i < to; i += step ) {
|
||||
|
||||
stride = start + count;
|
||||
|
||||
positionAttribute.setXYZ( stride, Math.sin( i ) * range, 0, Math.cos( i ) * range );
|
||||
positionAttribute.setXYZ( stride + 1, Math.sin( Math.min( i + step, to ) ) * range, 0, Math.cos( Math.min( i + step, to ) ) * range );
|
||||
positionAttribute.setXYZ( stride + 2, 0, 0, 0 );
|
||||
|
||||
count += 3;
|
||||
|
||||
}
|
||||
|
||||
geometry.addGroup( start, count, materialIndex );
|
||||
|
||||
start += count;
|
||||
count = 0;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
generateSegment( - halfConeOuterAngle, - halfConeInnerAngle, divisionsOuterAngle, 0 );
|
||||
generateSegment( - halfConeInnerAngle, halfConeInnerAngle, divisionsInnerAngle, 1 );
|
||||
generateSegment( halfConeInnerAngle, halfConeOuterAngle, divisionsOuterAngle, 0 );
|
||||
|
||||
//
|
||||
|
||||
positionAttribute.needsUpdate = true;
|
||||
|
||||
if ( coneInnerAngle === coneOuterAngle ) this.material[ 0 ].visible = false;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material[ 0 ].dispose();
|
||||
this.material[ 1 ].dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export { PositionalAudioHelper };
|
||||
85
dist/electron/static/sdk/three/jsm/helpers/RectAreaLightHelper.js
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
import {
|
||||
BackSide,
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
Line,
|
||||
LineBasicMaterial,
|
||||
Mesh,
|
||||
MeshBasicMaterial
|
||||
} from 'three';
|
||||
|
||||
/**
|
||||
* This helper must be added as a child of the light
|
||||
*/
|
||||
|
||||
class RectAreaLightHelper extends Line {
|
||||
|
||||
constructor( light, color ) {
|
||||
|
||||
const positions = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, - 1, 0, 1, 1, 0 ];
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
geometry.setAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );
|
||||
geometry.computeBoundingSphere();
|
||||
|
||||
const material = new LineBasicMaterial( { fog: false } );
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
this.light = light;
|
||||
this.color = color; // optional hardwired color for the helper
|
||||
this.type = 'RectAreaLightHelper';
|
||||
|
||||
//
|
||||
|
||||
const positions2 = [ 1, 1, 0, - 1, 1, 0, - 1, - 1, 0, 1, 1, 0, - 1, - 1, 0, 1, - 1, 0 ];
|
||||
|
||||
const geometry2 = new BufferGeometry();
|
||||
geometry2.setAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );
|
||||
geometry2.computeBoundingSphere();
|
||||
|
||||
this.add( new Mesh( geometry2, new MeshBasicMaterial( { side: BackSide, fog: false } ) ) );
|
||||
|
||||
}
|
||||
|
||||
updateMatrixWorld() {
|
||||
|
||||
this.scale.set( 0.5 * this.light.width, 0.5 * this.light.height, 1 );
|
||||
|
||||
if ( this.color !== undefined ) {
|
||||
|
||||
this.material.color.set( this.color );
|
||||
this.children[ 0 ].material.color.set( this.color );
|
||||
|
||||
} else {
|
||||
|
||||
this.material.color.copy( this.light.color ).multiplyScalar( this.light.intensity );
|
||||
|
||||
// prevent hue shift
|
||||
const c = this.material.color;
|
||||
const max = Math.max( c.r, c.g, c.b );
|
||||
if ( max > 1 ) c.multiplyScalar( 1 / max );
|
||||
|
||||
this.children[ 0 ].material.color.copy( this.material.color );
|
||||
|
||||
}
|
||||
|
||||
// ignore world scale on light
|
||||
this.matrixWorld.extractRotation( this.light.matrixWorld ).scale( this.scale ).copyPosition( this.light.matrixWorld );
|
||||
|
||||
this.children[ 0 ].matrixWorld.copy( this.matrixWorld );
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
this.children[ 0 ].geometry.dispose();
|
||||
this.children[ 0 ].material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { RectAreaLightHelper };
|
||||
237
dist/electron/static/sdk/three/jsm/helpers/TextureHelper.js
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
import {
|
||||
BoxGeometry,
|
||||
BufferAttribute,
|
||||
DoubleSide,
|
||||
Mesh,
|
||||
PlaneGeometry,
|
||||
ShaderMaterial,
|
||||
Vector3,
|
||||
} from 'three';
|
||||
import { mergeGeometries } from '../utils/BufferGeometryUtils.js';
|
||||
|
||||
class TextureHelper extends Mesh {
|
||||
|
||||
constructor( texture, width = 1, height = 1, depth = 1 ) {
|
||||
|
||||
const material = new ShaderMaterial( {
|
||||
|
||||
type: 'TextureHelperMaterial',
|
||||
|
||||
side: DoubleSide,
|
||||
transparent: true,
|
||||
|
||||
uniforms: {
|
||||
|
||||
map: { value: texture },
|
||||
alpha: { value: getAlpha( texture ) },
|
||||
|
||||
},
|
||||
|
||||
vertexShader: [
|
||||
|
||||
'attribute vec3 uvw;',
|
||||
|
||||
'varying vec3 vUvw;',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' vUvw = uvw;',
|
||||
|
||||
' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );',
|
||||
|
||||
'}',
|
||||
|
||||
].join( '\n' ),
|
||||
|
||||
fragmentShader: [
|
||||
|
||||
'precision highp float;',
|
||||
|
||||
'precision highp sampler2DArray;',
|
||||
|
||||
'precision highp sampler3D;',
|
||||
|
||||
'uniform {samplerType} map;',
|
||||
|
||||
'uniform float alpha;',
|
||||
|
||||
'varying vec3 vUvw;',
|
||||
|
||||
'vec4 textureHelper( in sampler2D map ) { return texture( map, vUvw.xy ); }',
|
||||
|
||||
'vec4 textureHelper( in sampler2DArray map ) { return texture( map, vUvw ); }',
|
||||
|
||||
'vec4 textureHelper( in sampler3D map ) { return texture( map, vUvw ); }',
|
||||
|
||||
'vec4 textureHelper( in samplerCube map ) { return texture( map, vUvw ); }',
|
||||
|
||||
'void main() {',
|
||||
|
||||
' gl_FragColor = linearToOutputTexel( vec4( textureHelper( map ).xyz, alpha ) );',
|
||||
|
||||
'}'
|
||||
|
||||
].join( '\n' ).replace( '{samplerType}', getSamplerType( texture ) )
|
||||
|
||||
} );
|
||||
|
||||
const geometry = texture.isCubeTexture
|
||||
? createCubeGeometry( width, height, depth )
|
||||
: createSliceGeometry( texture, width, height, depth );
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
this.texture = texture;
|
||||
this.type = 'TextureHelper';
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getSamplerType( texture ) {
|
||||
|
||||
if ( texture.isCubeTexture ) {
|
||||
|
||||
return 'samplerCube';
|
||||
|
||||
} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
|
||||
|
||||
return 'sampler2DArray';
|
||||
|
||||
} else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
|
||||
|
||||
return 'sampler3D';
|
||||
|
||||
} else {
|
||||
|
||||
return 'sampler2D';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getImageCount( texture ) {
|
||||
|
||||
if ( texture.isCubeTexture ) {
|
||||
|
||||
return 6;
|
||||
|
||||
} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
|
||||
|
||||
return texture.image.depth;
|
||||
|
||||
} else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
|
||||
|
||||
return texture.image.depth;
|
||||
|
||||
} else {
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getAlpha( texture ) {
|
||||
|
||||
if ( texture.isCubeTexture ) {
|
||||
|
||||
return 1;
|
||||
|
||||
} else if ( texture.isDataArrayTexture || texture.isCompressedArrayTexture ) {
|
||||
|
||||
return Math.max( 1 / texture.image.depth, 0.25 );
|
||||
|
||||
} else if ( texture.isData3DTexture || texture.isCompressed3DTexture ) {
|
||||
|
||||
return Math.max( 1 / texture.image.depth, 0.25 );
|
||||
|
||||
} else {
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function createCubeGeometry( width, height, depth ) {
|
||||
|
||||
const geometry = new BoxGeometry( width, height, depth );
|
||||
|
||||
const position = geometry.attributes.position;
|
||||
const uv = geometry.attributes.uv;
|
||||
const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 );
|
||||
|
||||
const _direction = new Vector3();
|
||||
|
||||
for ( let j = 0, jl = uv.count; j < jl; ++ j ) {
|
||||
|
||||
_direction.fromBufferAttribute( position, j ).normalize();
|
||||
|
||||
const u = _direction.x;
|
||||
const v = _direction.y;
|
||||
const w = _direction.z;
|
||||
|
||||
uvw.setXYZ( j, u, v, w );
|
||||
|
||||
}
|
||||
|
||||
geometry.deleteAttribute( 'uv' );
|
||||
geometry.setAttribute( 'uvw', uvw );
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
function createSliceGeometry( texture, width, height, depth ) {
|
||||
|
||||
const sliceCount = getImageCount( texture );
|
||||
|
||||
const geometries = [];
|
||||
|
||||
for ( let i = 0; i < sliceCount; ++ i ) {
|
||||
|
||||
const geometry = new PlaneGeometry( width, height );
|
||||
|
||||
if ( sliceCount > 1 ) {
|
||||
|
||||
geometry.translate( 0, 0, depth * ( i / ( sliceCount - 1 ) - 0.5 ) );
|
||||
|
||||
}
|
||||
|
||||
const uv = geometry.attributes.uv;
|
||||
const uvw = new BufferAttribute( new Float32Array( uv.count * 3 ), 3 );
|
||||
|
||||
for ( let j = 0, jl = uv.count; j < jl; ++ j ) {
|
||||
|
||||
const u = uv.getX( j );
|
||||
const v = texture.flipY ? uv.getY( j ) : 1 - uv.getY( j );
|
||||
const w = sliceCount === 1
|
||||
? 1
|
||||
: texture.isDataArrayTexture || texture.isCompressedArrayTexture
|
||||
? i
|
||||
: i / ( sliceCount - 1 );
|
||||
|
||||
uvw.setXYZ( j, u, v, w );
|
||||
|
||||
}
|
||||
|
||||
geometry.deleteAttribute( 'uv' );
|
||||
geometry.setAttribute( 'uvw', uvw );
|
||||
|
||||
geometries.push( geometry );
|
||||
|
||||
}
|
||||
|
||||
return mergeGeometries( geometries );
|
||||
|
||||
}
|
||||
|
||||
export { TextureHelper };
|
||||
96
dist/electron/static/sdk/three/jsm/helpers/VertexNormalsHelper.js
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
LineSegments,
|
||||
LineBasicMaterial,
|
||||
Matrix3,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _v1 = new Vector3();
|
||||
const _v2 = new Vector3();
|
||||
const _normalMatrix = new Matrix3();
|
||||
|
||||
class VertexNormalsHelper extends LineSegments {
|
||||
|
||||
constructor( object, size = 1, color = 0xff0000 ) {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
|
||||
const nNormals = object.geometry.attributes.normal.count;
|
||||
const positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );
|
||||
|
||||
geometry.setAttribute( 'position', positions );
|
||||
|
||||
super( geometry, new LineBasicMaterial( { color, toneMapped: false } ) );
|
||||
|
||||
this.object = object;
|
||||
this.size = size;
|
||||
this.type = 'VertexNormalsHelper';
|
||||
|
||||
//
|
||||
|
||||
this.matrixAutoUpdate = false;
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
this.object.updateMatrixWorld( true );
|
||||
|
||||
_normalMatrix.getNormalMatrix( this.object.matrixWorld );
|
||||
|
||||
const matrixWorld = this.object.matrixWorld;
|
||||
|
||||
const position = this.geometry.attributes.position;
|
||||
|
||||
//
|
||||
|
||||
const objGeometry = this.object.geometry;
|
||||
|
||||
if ( objGeometry ) {
|
||||
|
||||
const objPos = objGeometry.attributes.position;
|
||||
|
||||
const objNorm = objGeometry.attributes.normal;
|
||||
|
||||
let idx = 0;
|
||||
|
||||
// for simplicity, ignore index and drawcalls, and render every normal
|
||||
|
||||
for ( let j = 0, jl = objPos.count; j < jl; j ++ ) {
|
||||
|
||||
_v1.fromBufferAttribute( objPos, j ).applyMatrix4( matrixWorld );
|
||||
|
||||
_v2.fromBufferAttribute( objNorm, j );
|
||||
|
||||
_v2.applyMatrix3( _normalMatrix ).normalize().multiplyScalar( this.size ).add( _v1 );
|
||||
|
||||
position.setXYZ( idx, _v1.x, _v1.y, _v1.z );
|
||||
|
||||
idx = idx + 1;
|
||||
|
||||
position.setXYZ( idx, _v2.x, _v2.y, _v2.z );
|
||||
|
||||
idx = idx + 1;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
position.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { VertexNormalsHelper };
|
||||
88
dist/electron/static/sdk/three/jsm/helpers/VertexTangentsHelper.js
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
import {
|
||||
BufferGeometry,
|
||||
Float32BufferAttribute,
|
||||
LineSegments,
|
||||
LineBasicMaterial,
|
||||
Vector3
|
||||
} from 'three';
|
||||
|
||||
const _v1 = new Vector3();
|
||||
const _v2 = new Vector3();
|
||||
|
||||
class VertexTangentsHelper extends LineSegments {
|
||||
|
||||
constructor( object, size = 1, color = 0x00ffff ) {
|
||||
|
||||
const geometry = new BufferGeometry();
|
||||
|
||||
const nTangents = object.geometry.attributes.tangent.count;
|
||||
const positions = new Float32BufferAttribute( nTangents * 2 * 3, 3 );
|
||||
|
||||
geometry.setAttribute( 'position', positions );
|
||||
|
||||
super( geometry, new LineBasicMaterial( { color, toneMapped: false } ) );
|
||||
|
||||
this.object = object;
|
||||
this.size = size;
|
||||
this.type = 'VertexTangentsHelper';
|
||||
|
||||
//
|
||||
|
||||
this.matrixAutoUpdate = false;
|
||||
|
||||
this.update();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
this.object.updateMatrixWorld( true );
|
||||
|
||||
const matrixWorld = this.object.matrixWorld;
|
||||
|
||||
const position = this.geometry.attributes.position;
|
||||
|
||||
//
|
||||
|
||||
const objGeometry = this.object.geometry;
|
||||
|
||||
const objPos = objGeometry.attributes.position;
|
||||
|
||||
const objTan = objGeometry.attributes.tangent;
|
||||
|
||||
let idx = 0;
|
||||
|
||||
// for simplicity, ignore index and drawcalls, and render every tangent
|
||||
|
||||
for ( let j = 0, jl = objPos.count; j < jl; j ++ ) {
|
||||
|
||||
_v1.fromBufferAttribute( objPos, j ).applyMatrix4( matrixWorld );
|
||||
|
||||
_v2.fromBufferAttribute( objTan, j );
|
||||
|
||||
_v2.transformDirection( matrixWorld ).multiplyScalar( this.size ).add( _v1 );
|
||||
|
||||
position.setXYZ( idx, _v1.x, _v1.y, _v1.z );
|
||||
|
||||
idx = idx + 1;
|
||||
|
||||
position.setXYZ( idx, _v2.x, _v2.y, _v2.z );
|
||||
|
||||
idx = idx + 1;
|
||||
|
||||
}
|
||||
|
||||
position.needsUpdate = true;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
this.geometry.dispose();
|
||||
this.material.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { VertexTangentsHelper };
|
||||
333
dist/electron/static/sdk/three/jsm/helpers/ViewHelper.js
vendored
Normal file
@ -0,0 +1,333 @@
|
||||
import {
|
||||
BoxGeometry,
|
||||
CanvasTexture,
|
||||
Color,
|
||||
Euler,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
Object3D,
|
||||
OrthographicCamera,
|
||||
Quaternion,
|
||||
Raycaster,
|
||||
Sprite,
|
||||
SpriteMaterial,
|
||||
Vector2,
|
||||
Vector3,
|
||||
Vector4
|
||||
} from 'three';
|
||||
|
||||
class ViewHelper extends Object3D {
|
||||
|
||||
constructor( camera, domElement ) {
|
||||
|
||||
super();
|
||||
|
||||
this.isViewHelper = true;
|
||||
|
||||
this.animating = false;
|
||||
this.center = new Vector3();
|
||||
|
||||
const color1 = new Color( '#ff3653' );
|
||||
const color2 = new Color( '#8adb00' );
|
||||
const color3 = new Color( '#2c8fff' );
|
||||
|
||||
const interactiveObjects = [];
|
||||
const raycaster = new Raycaster();
|
||||
const mouse = new Vector2();
|
||||
const dummy = new Object3D();
|
||||
|
||||
const orthoCamera = new OrthographicCamera( - 2, 2, 2, - 2, 0, 4 );
|
||||
orthoCamera.position.set( 0, 0, 2 );
|
||||
|
||||
const geometry = new BoxGeometry( 0.8, 0.05, 0.05 ).translate( 0.4, 0, 0 );
|
||||
|
||||
const xAxis = new Mesh( geometry, getAxisMaterial( color1 ) );
|
||||
const yAxis = new Mesh( geometry, getAxisMaterial( color2 ) );
|
||||
const zAxis = new Mesh( geometry, getAxisMaterial( color3 ) );
|
||||
|
||||
yAxis.rotation.z = Math.PI / 2;
|
||||
zAxis.rotation.y = - Math.PI / 2;
|
||||
|
||||
this.add( xAxis );
|
||||
this.add( zAxis );
|
||||
this.add( yAxis );
|
||||
|
||||
const posXAxisHelper = new Sprite( getSpriteMaterial( color1, 'X' ) );
|
||||
posXAxisHelper.userData.type = 'posX';
|
||||
const posYAxisHelper = new Sprite( getSpriteMaterial( color2, 'Y' ) );
|
||||
posYAxisHelper.userData.type = 'posY';
|
||||
const posZAxisHelper = new Sprite( getSpriteMaterial( color3, 'Z' ) );
|
||||
posZAxisHelper.userData.type = 'posZ';
|
||||
const negXAxisHelper = new Sprite( getSpriteMaterial( color1 ) );
|
||||
negXAxisHelper.userData.type = 'negX';
|
||||
const negYAxisHelper = new Sprite( getSpriteMaterial( color2 ) );
|
||||
negYAxisHelper.userData.type = 'negY';
|
||||
const negZAxisHelper = new Sprite( getSpriteMaterial( color3 ) );
|
||||
negZAxisHelper.userData.type = 'negZ';
|
||||
|
||||
posXAxisHelper.position.x = 1;
|
||||
posYAxisHelper.position.y = 1;
|
||||
posZAxisHelper.position.z = 1;
|
||||
negXAxisHelper.position.x = - 1;
|
||||
negXAxisHelper.scale.setScalar( 0.8 );
|
||||
negYAxisHelper.position.y = - 1;
|
||||
negYAxisHelper.scale.setScalar( 0.8 );
|
||||
negZAxisHelper.position.z = - 1;
|
||||
negZAxisHelper.scale.setScalar( 0.8 );
|
||||
|
||||
this.add( posXAxisHelper );
|
||||
this.add( posYAxisHelper );
|
||||
this.add( posZAxisHelper );
|
||||
this.add( negXAxisHelper );
|
||||
this.add( negYAxisHelper );
|
||||
this.add( negZAxisHelper );
|
||||
|
||||
interactiveObjects.push( posXAxisHelper );
|
||||
interactiveObjects.push( posYAxisHelper );
|
||||
interactiveObjects.push( posZAxisHelper );
|
||||
interactiveObjects.push( negXAxisHelper );
|
||||
interactiveObjects.push( negYAxisHelper );
|
||||
interactiveObjects.push( negZAxisHelper );
|
||||
|
||||
const point = new Vector3();
|
||||
const dim = 128;
|
||||
const turnRate = 2 * Math.PI; // turn rate in angles per second
|
||||
|
||||
this.render = function ( renderer ) {
|
||||
|
||||
this.quaternion.copy( camera.quaternion ).invert();
|
||||
this.updateMatrixWorld();
|
||||
|
||||
point.set( 0, 0, 1 );
|
||||
point.applyQuaternion( camera.quaternion );
|
||||
|
||||
if ( point.x >= 0 ) {
|
||||
|
||||
posXAxisHelper.material.opacity = 1;
|
||||
negXAxisHelper.material.opacity = 0.5;
|
||||
|
||||
} else {
|
||||
|
||||
posXAxisHelper.material.opacity = 0.5;
|
||||
negXAxisHelper.material.opacity = 1;
|
||||
|
||||
}
|
||||
|
||||
if ( point.y >= 0 ) {
|
||||
|
||||
posYAxisHelper.material.opacity = 1;
|
||||
negYAxisHelper.material.opacity = 0.5;
|
||||
|
||||
} else {
|
||||
|
||||
posYAxisHelper.material.opacity = 0.5;
|
||||
negYAxisHelper.material.opacity = 1;
|
||||
|
||||
}
|
||||
|
||||
if ( point.z >= 0 ) {
|
||||
|
||||
posZAxisHelper.material.opacity = 1;
|
||||
negZAxisHelper.material.opacity = 0.5;
|
||||
|
||||
} else {
|
||||
|
||||
posZAxisHelper.material.opacity = 0.5;
|
||||
negZAxisHelper.material.opacity = 1;
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
const x = domElement.offsetWidth - dim;
|
||||
|
||||
renderer.clearDepth();
|
||||
|
||||
renderer.getViewport( viewport );
|
||||
renderer.setViewport( x, 0, dim, dim );
|
||||
|
||||
renderer.render( this, orthoCamera );
|
||||
|
||||
renderer.setViewport( viewport.x, viewport.y, viewport.z, viewport.w );
|
||||
|
||||
};
|
||||
|
||||
const targetPosition = new Vector3();
|
||||
const targetQuaternion = new Quaternion();
|
||||
|
||||
const q1 = new Quaternion();
|
||||
const q2 = new Quaternion();
|
||||
const viewport = new Vector4();
|
||||
let radius = 0;
|
||||
|
||||
this.handleClick = function ( event ) {
|
||||
|
||||
if ( this.animating === true ) return false;
|
||||
|
||||
const rect = domElement.getBoundingClientRect();
|
||||
const offsetX = rect.left + ( domElement.offsetWidth - dim );
|
||||
const offsetY = rect.top + ( domElement.offsetHeight - dim );
|
||||
mouse.x = ( ( event.clientX - offsetX ) / ( rect.right - offsetX ) ) * 2 - 1;
|
||||
mouse.y = - ( ( event.clientY - offsetY ) / ( rect.bottom - offsetY ) ) * 2 + 1;
|
||||
|
||||
raycaster.setFromCamera( mouse, orthoCamera );
|
||||
|
||||
const intersects = raycaster.intersectObjects( interactiveObjects );
|
||||
|
||||
if ( intersects.length > 0 ) {
|
||||
|
||||
const intersection = intersects[ 0 ];
|
||||
const object = intersection.object;
|
||||
|
||||
prepareAnimationData( object, this.center );
|
||||
|
||||
this.animating = true;
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.update = function ( delta ) {
|
||||
|
||||
const step = delta * turnRate;
|
||||
|
||||
// animate position by doing a slerp and then scaling the position on the unit sphere
|
||||
|
||||
q1.rotateTowards( q2, step );
|
||||
camera.position.set( 0, 0, 1 ).applyQuaternion( q1 ).multiplyScalar( radius ).add( this.center );
|
||||
|
||||
// animate orientation
|
||||
|
||||
camera.quaternion.rotateTowards( targetQuaternion, step );
|
||||
|
||||
if ( q1.angleTo( q2 ) === 0 ) {
|
||||
|
||||
this.animating = false;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
geometry.dispose();
|
||||
|
||||
xAxis.material.dispose();
|
||||
yAxis.material.dispose();
|
||||
zAxis.material.dispose();
|
||||
|
||||
posXAxisHelper.material.map.dispose();
|
||||
posYAxisHelper.material.map.dispose();
|
||||
posZAxisHelper.material.map.dispose();
|
||||
negXAxisHelper.material.map.dispose();
|
||||
negYAxisHelper.material.map.dispose();
|
||||
negZAxisHelper.material.map.dispose();
|
||||
|
||||
posXAxisHelper.material.dispose();
|
||||
posYAxisHelper.material.dispose();
|
||||
posZAxisHelper.material.dispose();
|
||||
negXAxisHelper.material.dispose();
|
||||
negYAxisHelper.material.dispose();
|
||||
negZAxisHelper.material.dispose();
|
||||
|
||||
};
|
||||
|
||||
function prepareAnimationData( object, focusPoint ) {
|
||||
|
||||
switch ( object.userData.type ) {
|
||||
|
||||
case 'posX':
|
||||
targetPosition.set( 1, 0, 0 );
|
||||
targetQuaternion.setFromEuler( new Euler( 0, Math.PI * 0.5, 0 ) );
|
||||
break;
|
||||
|
||||
case 'posY':
|
||||
targetPosition.set( 0, 1, 0 );
|
||||
targetQuaternion.setFromEuler( new Euler( - Math.PI * 0.5, 0, 0 ) );
|
||||
break;
|
||||
|
||||
case 'posZ':
|
||||
targetPosition.set( 0, 0, 1 );
|
||||
targetQuaternion.setFromEuler( new Euler() );
|
||||
break;
|
||||
|
||||
case 'negX':
|
||||
targetPosition.set( - 1, 0, 0 );
|
||||
targetQuaternion.setFromEuler( new Euler( 0, - Math.PI * 0.5, 0 ) );
|
||||
break;
|
||||
|
||||
case 'negY':
|
||||
targetPosition.set( 0, - 1, 0 );
|
||||
targetQuaternion.setFromEuler( new Euler( Math.PI * 0.5, 0, 0 ) );
|
||||
break;
|
||||
|
||||
case 'negZ':
|
||||
targetPosition.set( 0, 0, - 1 );
|
||||
targetQuaternion.setFromEuler( new Euler( 0, Math.PI, 0 ) );
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error( 'ViewHelper: Invalid axis.' );
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
radius = camera.position.distanceTo( focusPoint );
|
||||
targetPosition.multiplyScalar( radius ).add( focusPoint );
|
||||
|
||||
dummy.position.copy( focusPoint );
|
||||
|
||||
dummy.lookAt( camera.position );
|
||||
q1.copy( dummy.quaternion );
|
||||
|
||||
dummy.lookAt( targetPosition );
|
||||
q2.copy( dummy.quaternion );
|
||||
|
||||
}
|
||||
|
||||
function getAxisMaterial( color ) {
|
||||
|
||||
return new MeshBasicMaterial( { color: color, toneMapped: false } );
|
||||
|
||||
}
|
||||
|
||||
function getSpriteMaterial( color, text = null ) {
|
||||
|
||||
const canvas = document.createElement( 'canvas' );
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
|
||||
const context = canvas.getContext( '2d' );
|
||||
context.beginPath();
|
||||
context.arc( 32, 32, 16, 0, 2 * Math.PI );
|
||||
context.closePath();
|
||||
context.fillStyle = color.getStyle();
|
||||
context.fill();
|
||||
|
||||
if ( text !== null ) {
|
||||
|
||||
context.font = '24px Arial';
|
||||
context.textAlign = 'center';
|
||||
context.fillStyle = '#000000';
|
||||
context.fillText( text, 32, 41 );
|
||||
|
||||
}
|
||||
|
||||
const texture = new CanvasTexture( canvas );
|
||||
|
||||
return new SpriteMaterial( { map: texture, toneMapped: false } );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ViewHelper };
|
||||
570
dist/electron/static/sdk/three/jsm/interactive/HTMLMesh.js
vendored
Normal file
@ -0,0 +1,570 @@
|
||||
import {
|
||||
CanvasTexture,
|
||||
LinearFilter,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
PlaneGeometry,
|
||||
SRGBColorSpace,
|
||||
Color
|
||||
} from 'three';
|
||||
|
||||
class HTMLMesh extends Mesh {
|
||||
|
||||
constructor( dom ) {
|
||||
|
||||
const texture = new HTMLTexture( dom );
|
||||
|
||||
const geometry = new PlaneGeometry( texture.image.width * 0.001, texture.image.height * 0.001 );
|
||||
const material = new MeshBasicMaterial( { map: texture, toneMapped: false, transparent: true } );
|
||||
|
||||
super( geometry, material );
|
||||
|
||||
function onEvent( event ) {
|
||||
|
||||
material.map.dispatchDOMEvent( event );
|
||||
|
||||
}
|
||||
|
||||
this.addEventListener( 'mousedown', onEvent );
|
||||
this.addEventListener( 'mousemove', onEvent );
|
||||
this.addEventListener( 'mouseup', onEvent );
|
||||
this.addEventListener( 'click', onEvent );
|
||||
|
||||
this.dispose = function () {
|
||||
|
||||
geometry.dispose();
|
||||
material.dispose();
|
||||
|
||||
material.map.dispose();
|
||||
|
||||
canvases.delete( dom );
|
||||
|
||||
this.removeEventListener( 'mousedown', onEvent );
|
||||
this.removeEventListener( 'mousemove', onEvent );
|
||||
this.removeEventListener( 'mouseup', onEvent );
|
||||
this.removeEventListener( 'click', onEvent );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HTMLTexture extends CanvasTexture {
|
||||
|
||||
constructor( dom ) {
|
||||
|
||||
super( html2canvas( dom ) );
|
||||
|
||||
this.dom = dom;
|
||||
|
||||
this.anisotropy = 16;
|
||||
this.colorSpace = SRGBColorSpace;
|
||||
this.minFilter = LinearFilter;
|
||||
this.magFilter = LinearFilter;
|
||||
|
||||
// Create an observer on the DOM, and run html2canvas update in the next loop
|
||||
const observer = new MutationObserver( () => {
|
||||
|
||||
if ( ! this.scheduleUpdate ) {
|
||||
|
||||
// ideally should use xr.requestAnimationFrame, here setTimeout to avoid passing the renderer
|
||||
this.scheduleUpdate = setTimeout( () => this.update(), 16 );
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
const config = { attributes: true, childList: true, subtree: true, characterData: true };
|
||||
observer.observe( dom, config );
|
||||
|
||||
this.observer = observer;
|
||||
|
||||
}
|
||||
|
||||
dispatchDOMEvent( event ) {
|
||||
|
||||
if ( event.data ) {
|
||||
|
||||
htmlevent( this.dom, event.type, event.data.x, event.data.y );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
this.image = html2canvas( this.dom );
|
||||
this.needsUpdate = true;
|
||||
|
||||
this.scheduleUpdate = null;
|
||||
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
||||
if ( this.observer ) {
|
||||
|
||||
this.observer.disconnect();
|
||||
|
||||
}
|
||||
|
||||
this.scheduleUpdate = clearTimeout( this.scheduleUpdate );
|
||||
|
||||
super.dispose();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
||||
const canvases = new WeakMap();
|
||||
|
||||
function html2canvas( element ) {
|
||||
|
||||
const range = document.createRange();
|
||||
const color = new Color();
|
||||
|
||||
function Clipper( context ) {
|
||||
|
||||
const clips = [];
|
||||
let isClipping = false;
|
||||
|
||||
function doClip() {
|
||||
|
||||
if ( isClipping ) {
|
||||
|
||||
isClipping = false;
|
||||
context.restore();
|
||||
|
||||
}
|
||||
|
||||
if ( clips.length === 0 ) return;
|
||||
|
||||
let minX = - Infinity, minY = - Infinity;
|
||||
let maxX = Infinity, maxY = Infinity;
|
||||
|
||||
for ( let i = 0; i < clips.length; i ++ ) {
|
||||
|
||||
const clip = clips[ i ];
|
||||
|
||||
minX = Math.max( minX, clip.x );
|
||||
minY = Math.max( minY, clip.y );
|
||||
maxX = Math.min( maxX, clip.x + clip.width );
|
||||
maxY = Math.min( maxY, clip.y + clip.height );
|
||||
|
||||
}
|
||||
|
||||
context.save();
|
||||
context.beginPath();
|
||||
context.rect( minX, minY, maxX - minX, maxY - minY );
|
||||
context.clip();
|
||||
|
||||
isClipping = true;
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
add: function ( clip ) {
|
||||
|
||||
clips.push( clip );
|
||||
doClip();
|
||||
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
|
||||
clips.pop();
|
||||
doClip();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
function drawText( style, x, y, string ) {
|
||||
|
||||
if ( string !== '' ) {
|
||||
|
||||
if ( style.textTransform === 'uppercase' ) {
|
||||
|
||||
string = string.toUpperCase();
|
||||
|
||||
}
|
||||
|
||||
context.font = style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily;
|
||||
context.textBaseline = 'top';
|
||||
context.fillStyle = style.color;
|
||||
context.fillText( string, x, y + parseFloat( style.fontSize ) * 0.1 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function buildRectPath( x, y, w, h, r ) {
|
||||
|
||||
if ( w < 2 * r ) r = w / 2;
|
||||
if ( h < 2 * r ) r = h / 2;
|
||||
|
||||
context.beginPath();
|
||||
context.moveTo( x + r, y );
|
||||
context.arcTo( x + w, y, x + w, y + h, r );
|
||||
context.arcTo( x + w, y + h, x, y + h, r );
|
||||
context.arcTo( x, y + h, x, y, r );
|
||||
context.arcTo( x, y, x + w, y, r );
|
||||
context.closePath();
|
||||
|
||||
}
|
||||
|
||||
function drawBorder( style, which, x, y, width, height ) {
|
||||
|
||||
const borderWidth = style[ which + 'Width' ];
|
||||
const borderStyle = style[ which + 'Style' ];
|
||||
const borderColor = style[ which + 'Color' ];
|
||||
|
||||
if ( borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)' ) {
|
||||
|
||||
context.strokeStyle = borderColor;
|
||||
context.lineWidth = parseFloat( borderWidth );
|
||||
context.beginPath();
|
||||
context.moveTo( x, y );
|
||||
context.lineTo( x + width, y + height );
|
||||
context.stroke();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function drawElement( element, style ) {
|
||||
|
||||
// Do not render invisible elements, comments and scripts.
|
||||
if ( element.nodeType === Node.COMMENT_NODE || element.nodeName === 'SCRIPT' || ( element.style && element.style.display === 'none' ) ) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
let x = 0, y = 0, width = 0, height = 0;
|
||||
|
||||
if ( element.nodeType === Node.TEXT_NODE ) {
|
||||
|
||||
// text
|
||||
|
||||
range.selectNode( element );
|
||||
|
||||
const rect = range.getBoundingClientRect();
|
||||
|
||||
x = rect.left - offset.left - 0.5;
|
||||
y = rect.top - offset.top - 0.5;
|
||||
width = rect.width;
|
||||
height = rect.height;
|
||||
|
||||
drawText( style, x, y, element.nodeValue.trim() );
|
||||
|
||||
} else if ( element instanceof HTMLCanvasElement ) {
|
||||
|
||||
// Canvas element
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
x = rect.left - offset.left - 0.5;
|
||||
y = rect.top - offset.top - 0.5;
|
||||
|
||||
context.save();
|
||||
const dpr = window.devicePixelRatio;
|
||||
context.scale( 1 / dpr, 1 / dpr );
|
||||
context.drawImage( element, x, y );
|
||||
context.restore();
|
||||
|
||||
} else if ( element instanceof HTMLImageElement ) {
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
x = rect.left - offset.left - 0.5;
|
||||
y = rect.top - offset.top - 0.5;
|
||||
width = rect.width;
|
||||
height = rect.height;
|
||||
|
||||
context.drawImage( element, x, y, width, height );
|
||||
|
||||
} else {
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
x = rect.left - offset.left - 0.5;
|
||||
y = rect.top - offset.top - 0.5;
|
||||
width = rect.width;
|
||||
height = rect.height;
|
||||
|
||||
style = window.getComputedStyle( element );
|
||||
|
||||
// Get the border of the element used for fill and border
|
||||
|
||||
buildRectPath( x, y, width, height, parseFloat( style.borderRadius ) );
|
||||
|
||||
const backgroundColor = style.backgroundColor;
|
||||
|
||||
if ( backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)' ) {
|
||||
|
||||
context.fillStyle = backgroundColor;
|
||||
context.fill();
|
||||
|
||||
}
|
||||
|
||||
// If all the borders match then stroke the round rectangle
|
||||
|
||||
const borders = [ 'borderTop', 'borderLeft', 'borderBottom', 'borderRight' ];
|
||||
|
||||
let match = true;
|
||||
let prevBorder = null;
|
||||
|
||||
for ( const border of borders ) {
|
||||
|
||||
if ( prevBorder !== null ) {
|
||||
|
||||
match = ( style[ border + 'Width' ] === style[ prevBorder + 'Width' ] ) &&
|
||||
( style[ border + 'Color' ] === style[ prevBorder + 'Color' ] ) &&
|
||||
( style[ border + 'Style' ] === style[ prevBorder + 'Style' ] );
|
||||
|
||||
}
|
||||
|
||||
if ( match === false ) break;
|
||||
|
||||
prevBorder = border;
|
||||
|
||||
}
|
||||
|
||||
if ( match === true ) {
|
||||
|
||||
// They all match so stroke the rectangle from before allows for border-radius
|
||||
|
||||
const width = parseFloat( style.borderTopWidth );
|
||||
|
||||
if ( style.borderTopWidth !== '0px' && style.borderTopStyle !== 'none' && style.borderTopColor !== 'transparent' && style.borderTopColor !== 'rgba(0, 0, 0, 0)' ) {
|
||||
|
||||
context.strokeStyle = style.borderTopColor;
|
||||
context.lineWidth = width;
|
||||
context.stroke();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Otherwise draw individual borders
|
||||
|
||||
drawBorder( style, 'borderTop', x, y, width, 0 );
|
||||
drawBorder( style, 'borderLeft', x, y, 0, height );
|
||||
drawBorder( style, 'borderBottom', x, y + height, width, 0 );
|
||||
drawBorder( style, 'borderRight', x + width, y, 0, height );
|
||||
|
||||
}
|
||||
|
||||
if ( element instanceof HTMLInputElement ) {
|
||||
|
||||
let accentColor = style.accentColor;
|
||||
|
||||
if ( accentColor === undefined || accentColor === 'auto' ) accentColor = style.color;
|
||||
|
||||
color.set( accentColor );
|
||||
|
||||
const luminance = Math.sqrt( 0.299 * ( color.r ** 2 ) + 0.587 * ( color.g ** 2 ) + 0.114 * ( color.b ** 2 ) );
|
||||
const accentTextColor = luminance < 0.5 ? 'white' : '#111111';
|
||||
|
||||
if ( element.type === 'radio' ) {
|
||||
|
||||
buildRectPath( x, y, width, height, height );
|
||||
|
||||
context.fillStyle = 'white';
|
||||
context.strokeStyle = accentColor;
|
||||
context.lineWidth = 1;
|
||||
context.fill();
|
||||
context.stroke();
|
||||
|
||||
if ( element.checked ) {
|
||||
|
||||
buildRectPath( x + 2, y + 2, width - 4, height - 4, height );
|
||||
|
||||
context.fillStyle = accentColor;
|
||||
context.strokeStyle = accentTextColor;
|
||||
context.lineWidth = 2;
|
||||
context.fill();
|
||||
context.stroke();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( element.type === 'checkbox' ) {
|
||||
|
||||
buildRectPath( x, y, width, height, 2 );
|
||||
|
||||
context.fillStyle = element.checked ? accentColor : 'white';
|
||||
context.strokeStyle = element.checked ? accentTextColor : accentColor;
|
||||
context.lineWidth = 1;
|
||||
context.stroke();
|
||||
context.fill();
|
||||
|
||||
if ( element.checked ) {
|
||||
|
||||
const currentTextAlign = context.textAlign;
|
||||
|
||||
context.textAlign = 'center';
|
||||
|
||||
const properties = {
|
||||
color: accentTextColor,
|
||||
fontFamily: style.fontFamily,
|
||||
fontSize: height + 'px',
|
||||
fontWeight: 'bold'
|
||||
};
|
||||
|
||||
drawText( properties, x + ( width / 2 ), y, '✔' );
|
||||
|
||||
context.textAlign = currentTextAlign;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( element.type === 'range' ) {
|
||||
|
||||
const [ min, max, value ] = [ 'min', 'max', 'value' ].map( property => parseFloat( element[ property ] ) );
|
||||
const position = ( ( value - min ) / ( max - min ) ) * ( width - height );
|
||||
|
||||
buildRectPath( x, y + ( height / 4 ), width, height / 2, height / 4 );
|
||||
context.fillStyle = accentTextColor;
|
||||
context.strokeStyle = accentColor;
|
||||
context.lineWidth = 1;
|
||||
context.fill();
|
||||
context.stroke();
|
||||
|
||||
buildRectPath( x, y + ( height / 4 ), position + ( height / 2 ), height / 2, height / 4 );
|
||||
context.fillStyle = accentColor;
|
||||
context.fill();
|
||||
|
||||
buildRectPath( x + position, y, height, height, height / 2 );
|
||||
context.fillStyle = accentColor;
|
||||
context.fill();
|
||||
|
||||
}
|
||||
|
||||
if ( element.type === 'color' || element.type === 'text' || element.type === 'number' ) {
|
||||
|
||||
clipper.add( { x: x, y: y, width: width, height: height } );
|
||||
|
||||
drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
|
||||
|
||||
clipper.remove();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// debug
|
||||
context.strokeStyle = '#' + Math.random().toString( 16 ).slice( - 3 );
|
||||
context.strokeRect( x - 0.5, y - 0.5, width + 1, height + 1 );
|
||||
*/
|
||||
|
||||
const isClipping = style.overflow === 'auto' || style.overflow === 'hidden';
|
||||
|
||||
if ( isClipping ) clipper.add( { x: x, y: y, width: width, height: height } );
|
||||
|
||||
for ( let i = 0; i < element.childNodes.length; i ++ ) {
|
||||
|
||||
drawElement( element.childNodes[ i ], style );
|
||||
|
||||
}
|
||||
|
||||
if ( isClipping ) clipper.remove();
|
||||
|
||||
}
|
||||
|
||||
const offset = element.getBoundingClientRect();
|
||||
|
||||
let canvas = canvases.get( element );
|
||||
|
||||
if ( canvas === undefined ) {
|
||||
|
||||
canvas = document.createElement( 'canvas' );
|
||||
canvas.width = offset.width;
|
||||
canvas.height = offset.height;
|
||||
canvases.set( element, canvas );
|
||||
|
||||
}
|
||||
|
||||
const context = canvas.getContext( '2d'/*, { alpha: false }*/ );
|
||||
|
||||
const clipper = new Clipper( context );
|
||||
|
||||
// console.time( 'drawElement' );
|
||||
|
||||
context.clearRect( 0, 0, canvas.width, canvas.height );
|
||||
|
||||
drawElement( element );
|
||||
|
||||
// console.timeEnd( 'drawElement' );
|
||||
|
||||
return canvas;
|
||||
|
||||
}
|
||||
|
||||
function htmlevent( element, event, x, y ) {
|
||||
|
||||
const mouseEventInit = {
|
||||
clientX: ( x * element.offsetWidth ) + element.offsetLeft,
|
||||
clientY: ( y * element.offsetHeight ) + element.offsetTop,
|
||||
view: element.ownerDocument.defaultView
|
||||
};
|
||||
|
||||
window.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
x = x * rect.width + rect.left;
|
||||
y = y * rect.height + rect.top;
|
||||
|
||||
function traverse( element ) {
|
||||
|
||||
if ( element.nodeType !== Node.TEXT_NODE && element.nodeType !== Node.COMMENT_NODE ) {
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
if ( x > rect.left && x < rect.right && y > rect.top && y < rect.bottom ) {
|
||||
|
||||
element.dispatchEvent( new MouseEvent( event, mouseEventInit ) );
|
||||
|
||||
if ( element instanceof HTMLInputElement && element.type === 'range' && ( event === 'mousedown' || event === 'click' ) ) {
|
||||
|
||||
const [ min, max ] = [ 'min', 'max' ].map( property => parseFloat( element[ property ] ) );
|
||||
|
||||
const width = rect.width;
|
||||
const offsetX = x - rect.x;
|
||||
const proportion = offsetX / width;
|
||||
element.value = min + ( max - min ) * proportion;
|
||||
element.dispatchEvent( new InputEvent( 'input', { bubbles: true } ) );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( let i = 0; i < element.childNodes.length; i ++ ) {
|
||||
|
||||
traverse( element.childNodes[ i ] );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
traverse( element );
|
||||
|
||||
}
|
||||
|
||||
export { HTMLMesh };
|
||||
106
dist/electron/static/sdk/three/jsm/interactive/InteractiveGroup.js
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
import {
|
||||
Group,
|
||||
Raycaster,
|
||||
Vector2
|
||||
} from 'three';
|
||||
|
||||
const _pointer = new Vector2();
|
||||
const _event = { type: '', data: _pointer };
|
||||
|
||||
const _raycaster = new Raycaster();
|
||||
|
||||
class InteractiveGroup extends Group {
|
||||
|
||||
listenToPointerEvents( renderer, camera ) {
|
||||
|
||||
const scope = this;
|
||||
const raycaster = new Raycaster();
|
||||
|
||||
const element = renderer.domElement;
|
||||
|
||||
function onPointerEvent( event ) {
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
const rect = renderer.domElement.getBoundingClientRect();
|
||||
|
||||
_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
|
||||
_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
|
||||
|
||||
raycaster.setFromCamera( _pointer, camera );
|
||||
|
||||
const intersects = raycaster.intersectObjects( scope.children, false );
|
||||
|
||||
if ( intersects.length > 0 ) {
|
||||
|
||||
const intersection = intersects[ 0 ];
|
||||
|
||||
const object = intersection.object;
|
||||
const uv = intersection.uv;
|
||||
|
||||
_event.type = event.type;
|
||||
_event.data.set( uv.x, 1 - uv.y );
|
||||
|
||||
object.dispatchEvent( _event );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
element.addEventListener( 'pointerdown', onPointerEvent );
|
||||
element.addEventListener( 'pointerup', onPointerEvent );
|
||||
element.addEventListener( 'pointermove', onPointerEvent );
|
||||
element.addEventListener( 'mousedown', onPointerEvent );
|
||||
element.addEventListener( 'mouseup', onPointerEvent );
|
||||
element.addEventListener( 'mousemove', onPointerEvent );
|
||||
element.addEventListener( 'click', onPointerEvent );
|
||||
|
||||
}
|
||||
|
||||
listenToXRControllerEvents( controller ) {
|
||||
|
||||
const scope = this;
|
||||
|
||||
// TODO: Dispatch pointerevents too
|
||||
|
||||
const events = {
|
||||
'move': 'mousemove',
|
||||
'select': 'click',
|
||||
'selectstart': 'mousedown',
|
||||
'selectend': 'mouseup'
|
||||
};
|
||||
|
||||
function onXRControllerEvent( event ) {
|
||||
|
||||
const controller = event.target;
|
||||
|
||||
_raycaster.setFromXRController( controller );
|
||||
|
||||
const intersections = _raycaster.intersectObjects( scope.children, false );
|
||||
|
||||
if ( intersections.length > 0 ) {
|
||||
|
||||
const intersection = intersections[ 0 ];
|
||||
|
||||
const object = intersection.object;
|
||||
const uv = intersection.uv;
|
||||
|
||||
_event.type = events[ event.type ];
|
||||
_event.data.set( uv.x, 1 - uv.y );
|
||||
|
||||
object.dispatchEvent( _event );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
controller.addEventListener( 'move', onXRControllerEvent );
|
||||
controller.addEventListener( 'select', onXRControllerEvent );
|
||||
controller.addEventListener( 'selectstart', onXRControllerEvent );
|
||||
controller.addEventListener( 'selectend', onXRControllerEvent );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { InteractiveGroup };
|
||||