Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
fktz008
three.js
提交
c12c9a16
T
three.js
项目概览
fktz008
/
three.js
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
T
three.js
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
c12c9a16
编写于
2月 28, 2022
作者:
M
Mr.doob
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
r138 (bis) (bis)
上级
473c7e67
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
888 addition
and
74 deletion
+888
-74
examples/js/controls/PointerLockControls.js
examples/js/controls/PointerLockControls.js
+0
-1
examples/js/exporters/EXRExporter.js
examples/js/exporters/EXRExporter.js
+458
-0
examples/js/exporters/GLTFExporter.js
examples/js/exporters/GLTFExporter.js
+41
-52
examples/js/helpers/OctreeHelper.js
examples/js/helpers/OctreeHelper.js
+74
-0
examples/js/helpers/ViewHelper.js
examples/js/helpers/ViewHelper.js
+267
-0
examples/js/loaders/AMFLoader.js
examples/js/loaders/AMFLoader.js
+1
-1
examples/js/loaders/GLTFLoader.js
examples/js/loaders/GLTFLoader.js
+24
-3
examples/js/loaders/SVGLoader.js
examples/js/loaders/SVGLoader.js
+13
-6
examples/js/renderers/CSS2DRenderer.js
examples/js/renderers/CSS2DRenderer.js
+6
-7
examples/js/renderers/CSS3DRenderer.js
examples/js/renderers/CSS3DRenderer.js
+3
-3
package.json
package.json
+1
-1
未找到文件。
examples/js/controls/PointerLockControls.js
浏览文件 @
c12c9a16
...
...
@@ -38,7 +38,6 @@
this
.
maxPolarAngle
=
Math
.
PI
;
// radians
this
.
pointerSpeed
=
1.0
;
const
scope
=
this
;
function
onMouseMove
(
event
)
{
...
...
examples/js/exporters/EXRExporter.js
0 → 100644
浏览文件 @
c12c9a16
(
function
()
{
/**
* @author sciecode / https://github.com/sciecode
*
* EXR format references:
* https://www.openexr.com/documentation/openexrfilelayout.pdf
*/
const
textEncoder
=
new
TextEncoder
();
const
NO_COMPRESSION
=
0
;
const
ZIPS_COMPRESSION
=
2
;
const
ZIP_COMPRESSION
=
3
;
class
EXRExporter
{
parse
(
renderer
,
renderTarget
,
options
)
{
if
(
!
supported
(
renderer
,
renderTarget
)
)
return
undefined
;
const
info
=
buildInfo
(
renderTarget
,
options
),
dataBuffer
=
getPixelData
(
renderer
,
renderTarget
,
info
),
rawContentBuffer
=
reorganizeDataBuffer
(
dataBuffer
,
info
),
chunks
=
compressData
(
rawContentBuffer
,
info
);
return
fillData
(
chunks
,
info
);
}
}
function
supported
(
renderer
,
renderTarget
)
{
if
(
!
renderer
||
!
renderer
.
isWebGLRenderer
)
{
console
.
error
(
'
EXRExporter.parse: Unsupported first parameter, expected instance of WebGLRenderer.
'
);
return
false
;
}
if
(
!
renderTarget
||
!
renderTarget
.
isWebGLRenderTarget
)
{
console
.
error
(
'
EXRExporter.parse: Unsupported second parameter, expected instance of WebGLRenderTarget.
'
);
return
false
;
}
if
(
renderTarget
.
texture
.
type
!==
THREE
.
FloatType
&&
renderTarget
.
texture
.
type
!==
THREE
.
HalfFloatType
)
{
console
.
error
(
'
EXRExporter.parse: Unsupported WebGLRenderTarget texture type.
'
);
return
false
;
}
if
(
renderTarget
.
texture
.
format
!==
THREE
.
RGBAFormat
)
{
console
.
error
(
'
EXRExporter.parse: Unsupported WebGLRenderTarget texture format, expected THREE.RGBAFormat.
'
);
return
false
;
}
return
true
;
}
function
buildInfo
(
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
,
ENCODING
=
renderTarget
.
texture
.
encoding
,
COMPRESSION
=
options
.
compression
!==
undefined
?
options
.
compression
:
ZIP_COMPRESSION
,
EXPORTER_TYPE
=
options
.
type
!==
undefined
?
options
.
type
:
THREE
.
HalfFloatType
,
OUT_TYPE
=
EXPORTER_TYPE
===
THREE
.
FloatType
?
2
:
1
,
COMPRESSION_SIZE
=
compressionSizes
[
COMPRESSION
],
NUM_CHANNELS
=
4
;
return
{
width
:
WIDTH
,
height
:
HEIGHT
,
type
:
TYPE
,
format
:
FORMAT
,
encoding
:
ENCODING
,
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
===
THREE
.
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
==
THREE
.
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
;
}
if
(
typeof
fflate
===
'
undefined
'
)
{
console
.
error
(
'
THREE.EXRLoader: External
\
`fflate.module.js
\
` required
'
);
}
const
deflate
=
fflate
.
zlibSync
(
tmpBuffer
);
// eslint-disable-line no-undef
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
,
THREE
.
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.103515625
e
-
5
*
(
fraction
/
0x400
)
);
}
function
getFloat16
(
arr
,
i
)
{
return
decodeFloat16
(
arr
[
i
]
);
}
function
getFloat32
(
arr
,
i
)
{
return
arr
[
i
];
}
THREE
.
EXRExporter
=
EXRExporter
;
THREE
.
NO_COMPRESSION
=
NO_COMPRESSION
;
THREE
.
ZIPS_COMPRESSION
=
ZIPS_COMPRESSION
;
THREE
.
ZIP_COMPRESSION
=
ZIP_COMPRESSION
;
}
)();
examples/js/exporters/GLTFExporter.js
浏览文件 @
c12c9a16
...
...
@@ -626,68 +626,55 @@
}
build
ORMTexture
(
material
)
{
build
MetalRoughTexture
(
metalnessMap
,
roughnessMap
)
{
const
occlusion
=
material
.
aoMap
?.
image
;
const
roughness
=
material
.
roughnessMap
?.
image
;
const
metalness
=
material
.
metalnessMap
?.
image
;
if
(
occlusion
===
roughness
&&
roughness
===
metalness
)
return
material
.
aoMap
;
if
(
metalnessMap
===
roughnessMap
)
return
metalnessMap
;
console
.
warn
(
'
THREE.GLTFExporter: Merged metalnessMap and roughnessMap textures.
'
);
const
metalness
=
metalnessMap
?.
image
;
const
roughness
=
roughnessMap
?.
image
;
const
width
=
Math
.
max
(
metalness
?.
width
||
0
,
roughness
?.
width
||
0
);
const
height
=
Math
.
max
(
metalness
?.
height
||
0
,
roughness
?.
height
||
0
);
const
canvas
=
document
.
createElement
(
'
canvas
'
);
canvas
.
width
=
width
;
canvas
.
height
=
height
;
const
context
=
canvas
.
getContext
(
'
2d
'
);
context
.
fillStyle
=
'
#00ffff
'
;
context
.
fillRect
(
0
,
0
,
width
,
height
);
const
composite
=
context
.
getImageData
(
0
,
0
,
width
,
height
);
if
(
occlusion
||
roughness
||
metalness
)
{
if
(
metalness
)
{
const
width
=
Math
.
max
(
occlusion
?.
width
||
0
,
roughness
?.
width
||
0
,
metalness
?.
width
||
0
);
const
height
=
Math
.
max
(
occlusion
?.
height
||
0
,
roughness
?.
height
||
0
,
metalness
?.
height
||
0
);
const
canvas
=
document
.
createElement
(
'
canvas
'
);
canvas
.
width
=
width
;
canvas
.
height
=
height
;
const
context
=
canvas
.
getContext
(
'
2d
'
);
context
.
fillStyle
=
'
#ffffff
'
;
context
.
fillRect
(
0
,
0
,
width
,
height
);
const
composite
=
context
.
getImageData
(
0
,
0
,
width
,
height
);
context
.
drawImage
(
metalness
,
0
,
0
,
width
,
height
);
const
data
=
context
.
getImageData
(
0
,
0
,
width
,
height
).
data
;
if
(
occlusion
)
{
for
(
let
i
=
2
;
i
<
data
.
length
;
i
+=
4
)
{
context
.
drawImage
(
occlusion
,
0
,
0
,
width
,
height
);
const
data
=
context
.
getImageData
(
0
,
0
,
width
,
height
).
data
;
for
(
let
i
=
0
;
i
<
data
.
length
;
i
+=
4
)
{
composite
.
data
[
i
]
=
data
[
i
];
}
composite
.
data
[
i
]
=
data
[
i
];
}
if
(
roughness
)
{
}
context
.
drawImage
(
roughness
,
0
,
0
,
width
,
height
);
const
data
=
context
.
getImageData
(
0
,
0
,
width
,
height
).
data
;
if
(
roughness
)
{
for
(
let
i
=
1
;
i
<
data
.
length
;
i
+=
4
)
{
context
.
drawImage
(
roughness
,
0
,
0
,
width
,
height
);
const
data
=
context
.
getImageData
(
0
,
0
,
width
,
height
).
data
;
composite
.
data
[
i
]
=
data
[
i
];
for
(
let
i
=
1
;
i
<
data
.
length
;
i
+=
4
)
{
}
composite
.
data
[
i
]
=
data
[
i
];
}
if
(
metalness
)
{
context
.
drawImage
(
metalness
,
0
,
0
,
width
,
height
);
const
data
=
context
.
getImageData
(
0
,
0
,
width
,
height
).
data
;
for
(
let
i
=
2
;
i
<
data
.
length
;
i
+=
4
)
{
composite
.
data
[
i
]
=
data
[
i
];
}
}
}
context
.
putImageData
(
composite
,
0
,
0
);
//
context
.
putImageData
(
composite
,
0
,
0
)
;
return
new
THREE
.
Texture
(
canvas
);
const
reference
=
metalnessMap
||
roughnessMap
;
const
texture
=
reference
.
clone
();
// TODO Use new Source() instead?
}
texture
.
source
=
new
THREE
.
Texture
(
canvas
).
source
;
return
texture
;
}
/**
...
...
@@ -933,11 +920,12 @@
* @param {Image} image to process
* @param {Integer} format of the image (THREE.RGBAFormat)
* @param {Boolean} flipY before writing out the image
* @param {String} mimeType export format
* @return {Integer} Index of the processed texture in the "images" array
*/
processImage
(
image
,
format
,
flipY
)
{
processImage
(
image
,
format
,
flipY
,
mimeType
=
'
image/png
'
)
{
const
writer
=
this
;
const
cache
=
writer
.
cache
;
...
...
@@ -946,7 +934,6 @@
const
pending
=
writer
.
pending
;
if
(
!
cache
.
images
.
has
(
image
)
)
cache
.
images
.
set
(
image
,
{}
);
const
cachedImages
=
cache
.
images
.
get
(
image
);
const
mimeType
=
'
image/png
'
;
const
key
=
mimeType
+
'
:flipY/
'
+
flipY
.
toString
();
if
(
cachedImages
[
key
]
!==
undefined
)
return
cachedImages
[
key
];
if
(
!
json
.
images
)
json
.
images
=
[];
...
...
@@ -1068,9 +1055,11 @@
const
json
=
this
.
json
;
if
(
cache
.
textures
.
has
(
map
)
)
return
cache
.
textures
.
get
(
map
);
if
(
!
json
.
textures
)
json
.
textures
=
[];
let
mimeType
=
map
.
userData
.
mimeType
;
if
(
mimeType
===
'
image/webp
'
)
mimeType
=
'
image/png
'
;
const
textureDef
=
{
sampler
:
this
.
processSampler
(
map
),
source
:
this
.
processImage
(
map
.
image
,
map
.
format
,
map
.
flipY
)
source
:
this
.
processImage
(
map
.
image
,
map
.
format
,
map
.
flipY
,
mimeType
)
};
if
(
map
.
name
)
textureDef
.
name
=
map
.
name
;
...
...
@@ -1136,16 +1125,16 @@
materialDef
.
pbrMetallicRoughness
.
metallicFactor
=
0.5
;
materialDef
.
pbrMetallicRoughness
.
roughnessFactor
=
0.5
;
}
}
// pbrMetallicRoughness.metallicRoughnessTexture
const
ormTexture
=
this
.
buildORMTexture
(
material
);
// pbrMetallicRoughness.metallicRoughnessTexture
if
(
material
.
metalnessMap
||
material
.
roughnessMap
)
{
const
metalRoughTexture
=
this
.
buildMetalRoughTexture
(
material
.
metalnessMap
,
material
.
roughnessMap
);
const
metalRoughMapDef
=
{
index
:
this
.
processTexture
(
orm
Texture
)
index
:
this
.
processTexture
(
metalRough
Texture
)
};
this
.
applyTextureTransform
(
metalRoughMapDef
,
m
aterial
.
metalnessMap
||
material
.
roughnessMap
);
this
.
applyTextureTransform
(
metalRoughMapDef
,
m
etalRoughTexture
);
materialDef
.
pbrMetallicRoughness
.
metallicRoughnessTexture
=
metalRoughMapDef
;
}
// pbrMetallicRoughness.baseColorTexture or pbrSpecularGlossiness diffuseTexture
...
...
@@ -1217,7 +1206,7 @@
if
(
material
.
aoMap
)
{
const
occlusionMapDef
=
{
index
:
this
.
processTexture
(
ormTexture
),
index
:
this
.
processTexture
(
material
.
aoMap
),
texCoord
:
1
};
...
...
examples/js/helpers/OctreeHelper.js
0 → 100644
浏览文件 @
c12c9a16
(
function
()
{
class
OctreeHelper
extends
THREE
.
LineSegments
{
constructor
(
octree
,
color
=
0xffff00
)
{
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
(
octree
.
subTrees
);
const
geometry
=
new
THREE
.
BufferGeometry
();
geometry
.
setAttribute
(
'
position
'
,
new
THREE
.
Float32BufferAttribute
(
vertices
,
3
)
);
super
(
geometry
,
new
THREE
.
LineBasicMaterial
(
{
color
:
color
,
toneMapped
:
false
}
)
);
this
.
octree
=
octree
;
this
.
color
=
color
;
this
.
type
=
'
OctreeHelper
'
;
}
}
THREE
.
OctreeHelper
=
OctreeHelper
;
}
)();
examples/js/helpers/ViewHelper.js
0 → 100644
浏览文件 @
c12c9a16
(
function
()
{
const
vpTemp
=
new
THREE
.
Vector4
();
class
ViewHelper
extends
THREE
.
Object3D
{
constructor
(
editorCamera
,
dom
)
{
super
();
this
.
animating
=
false
;
this
.
controls
=
null
;
const
color1
=
new
THREE
.
Color
(
'
#ff3653
'
);
const
color2
=
new
THREE
.
Color
(
'
#8adb00
'
);
const
color3
=
new
THREE
.
Color
(
'
#2c8fff
'
);
const
interactiveObjects
=
[];
const
raycaster
=
new
THREE
.
Raycaster
();
const
mouse
=
new
THREE
.
Vector2
();
const
dummy
=
new
THREE
.
Object3D
();
const
camera
=
new
THREE
.
OrthographicCamera
(
-
2
,
2
,
2
,
-
2
,
0
,
4
);
camera
.
position
.
set
(
0
,
0
,
2
);
const
geometry
=
new
THREE
.
BoxGeometry
(
0.8
,
0.05
,
0.05
).
translate
(
0.4
,
0
,
0
);
const
xAxis
=
new
THREE
.
Mesh
(
geometry
,
getAxisMaterial
(
color1
)
);
const
yAxis
=
new
THREE
.
Mesh
(
geometry
,
getAxisMaterial
(
color2
)
);
const
zAxis
=
new
THREE
.
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
THREE
.
Sprite
(
getSpriteMaterial
(
color1
,
'
X
'
)
);
posXAxisHelper
.
userData
.
type
=
'
posX
'
;
const
posYAxisHelper
=
new
THREE
.
Sprite
(
getSpriteMaterial
(
color2
,
'
Y
'
)
);
posYAxisHelper
.
userData
.
type
=
'
posY
'
;
const
posZAxisHelper
=
new
THREE
.
Sprite
(
getSpriteMaterial
(
color3
,
'
Z
'
)
);
posZAxisHelper
.
userData
.
type
=
'
posZ
'
;
const
negXAxisHelper
=
new
THREE
.
Sprite
(
getSpriteMaterial
(
color1
)
);
negXAxisHelper
.
userData
.
type
=
'
negX
'
;
const
negYAxisHelper
=
new
THREE
.
Sprite
(
getSpriteMaterial
(
color2
)
);
negYAxisHelper
.
userData
.
type
=
'
negY
'
;
const
negZAxisHelper
=
new
THREE
.
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
THREE
.
Vector3
();
const
dim
=
128
;
const
turnRate
=
2
*
Math
.
PI
;
// turn rate in angles per second
this
.
render
=
function
(
renderer
)
{
this
.
quaternion
.
copy
(
editorCamera
.
quaternion
).
invert
();
this
.
updateMatrixWorld
();
point
.
set
(
0
,
0
,
1
);
point
.
applyQuaternion
(
editorCamera
.
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
=
dom
.
offsetWidth
-
dim
;
renderer
.
clearDepth
();
renderer
.
getViewport
(
vpTemp
);
renderer
.
setViewport
(
x
,
0
,
dim
,
dim
);
renderer
.
render
(
this
,
camera
);
renderer
.
setViewport
(
vpTemp
.
x
,
vpTemp
.
y
,
vpTemp
.
z
,
vpTemp
.
w
);
};
const
targetPosition
=
new
THREE
.
Vector3
();
const
targetQuaternion
=
new
THREE
.
Quaternion
();
const
q1
=
new
THREE
.
Quaternion
();
const
q2
=
new
THREE
.
Quaternion
();
let
radius
=
0
;
this
.
handleClick
=
function
(
event
)
{
if
(
this
.
animating
===
true
)
return
false
;
const
rect
=
dom
.
getBoundingClientRect
();
const
offsetX
=
rect
.
left
+
(
container
.
dom
.
offsetWidth
-
dim
);
const
offsetY
=
rect
.
top
+
(
container
.
dom
.
offsetHeight
-
dim
);
mouse
.
x
=
(
event
.
clientX
-
offsetX
)
/
(
rect
.
width
-
offsetX
)
*
2
-
1
;
mouse
.
y
=
-
(
(
event
.
clientY
-
offsetY
)
/
(
rect
.
bottom
-
offsetY
)
)
*
2
+
1
;
raycaster
.
setFromCamera
(
mouse
,
camera
);
const
intersects
=
raycaster
.
intersectObjects
(
interactiveObjects
);
if
(
intersects
.
length
>
0
)
{
const
intersection
=
intersects
[
0
];
const
object
=
intersection
.
object
;
prepareAnimationData
(
object
,
this
.
controls
.
center
);
this
.
animating
=
true
;
return
true
;
}
else
{
return
false
;
}
};
this
.
update
=
function
(
delta
)
{
const
step
=
delta
*
turnRate
;
const
focusPoint
=
this
.
controls
.
center
;
// animate position by doing a slerp and then scaling the position on the unit sphere
q1
.
rotateTowards
(
q2
,
step
);
editorCamera
.
position
.
set
(
0
,
0
,
1
).
applyQuaternion
(
q1
).
multiplyScalar
(
radius
).
add
(
focusPoint
);
// animate orientation
editorCamera
.
quaternion
.
rotateTowards
(
targetQuaternion
,
step
);
if
(
q1
.
angleTo
(
q2
)
===
0
)
{
this
.
animating
=
false
;
}
};
function
prepareAnimationData
(
object
,
focusPoint
)
{
switch
(
object
.
userData
.
type
)
{
case
'
posX
'
:
targetPosition
.
set
(
1
,
0
,
0
);
targetQuaternion
.
setFromEuler
(
new
THREE
.
Euler
(
0
,
Math
.
PI
*
0.5
,
0
)
);
break
;
case
'
posY
'
:
targetPosition
.
set
(
0
,
1
,
0
);
targetQuaternion
.
setFromEuler
(
new
THREE
.
Euler
(
-
Math
.
PI
*
0.5
,
0
,
0
)
);
break
;
case
'
posZ
'
:
targetPosition
.
set
(
0
,
0
,
1
);
targetQuaternion
.
setFromEuler
(
new
THREE
.
Euler
()
);
break
;
case
'
negX
'
:
targetPosition
.
set
(
-
1
,
0
,
0
);
targetQuaternion
.
setFromEuler
(
new
THREE
.
Euler
(
0
,
-
Math
.
PI
*
0.5
,
0
)
);
break
;
case
'
negY
'
:
targetPosition
.
set
(
0
,
-
1
,
0
);
targetQuaternion
.
setFromEuler
(
new
THREE
.
Euler
(
Math
.
PI
*
0.5
,
0
,
0
)
);
break
;
case
'
negZ
'
:
targetPosition
.
set
(
0
,
0
,
-
1
);
targetQuaternion
.
setFromEuler
(
new
THREE
.
Euler
(
0
,
Math
.
PI
,
0
)
);
break
;
default
:
console
.
error
(
'
ViewHelper: Invalid axis.
'
);
}
//
radius
=
editorCamera
.
position
.
distanceTo
(
focusPoint
);
targetPosition
.
multiplyScalar
(
radius
).
add
(
focusPoint
);
dummy
.
position
.
copy
(
focusPoint
);
dummy
.
lookAt
(
editorCamera
.
position
);
q1
.
copy
(
dummy
.
quaternion
);
dummy
.
lookAt
(
targetPosition
);
q2
.
copy
(
dummy
.
quaternion
);
}
function
getAxisMaterial
(
color
)
{
return
new
THREE
.
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
THREE
.
CanvasTexture
(
canvas
);
return
new
THREE
.
SpriteMaterial
(
{
map
:
texture
,
toneMapped
:
false
}
);
}
}
}
ViewHelper
.
prototype
.
isViewHelper
=
true
;
THREE
.
ViewHelper
=
ViewHelper
;
}
)();
examples/js/loaders/AMFLoader.js
浏览文件 @
c12c9a16
...
...
@@ -88,7 +88,7 @@
for
(
file
in
zip
)
{
if
(
file
.
toLowerCase
().
s
ubstr
(
-
4
)
===
'
.amf
'
)
{
if
(
file
.
toLowerCase
().
s
lice
(
-
4
)
===
'
.amf
'
)
{
break
;
...
...
examples/js/loaders/GLTFLoader.js
浏览文件 @
c12c9a16
...
...
@@ -1821,19 +1821,22 @@
let
hasMorphPosition
=
false
;
let
hasMorphNormal
=
false
;
let
hasMorphColor
=
false
;
for
(
let
i
=
0
,
il
=
targets
.
length
;
i
<
il
;
i
++
)
{
const
target
=
targets
[
i
];
if
(
target
.
POSITION
!==
undefined
)
hasMorphPosition
=
true
;
if
(
target
.
NORMAL
!==
undefined
)
hasMorphNormal
=
true
;
if
(
hasMorphPosition
&&
hasMorphNormal
)
break
;
if
(
target
.
COLOR_0
!==
undefined
)
hasMorphColor
=
true
;
if
(
hasMorphPosition
&&
hasMorphNormal
&&
hasMorphColor
)
break
;
}
if
(
!
hasMorphPosition
&&
!
hasMorphNormal
)
return
Promise
.
resolve
(
geometry
);
if
(
!
hasMorphPosition
&&
!
hasMorphNormal
&&
!
hasMorphColor
)
return
Promise
.
resolve
(
geometry
);
const
pendingPositionAccessors
=
[];
const
pendingNormalAccessors
=
[];
const
pendingColorAccessors
=
[];
for
(
let
i
=
0
,
il
=
targets
.
length
;
i
<
il
;
i
++
)
{
...
...
@@ -1853,14 +1856,23 @@
}
if
(
hasMorphColor
)
{
const
pendingAccessor
=
target
.
COLOR_0
!==
undefined
?
parser
.
getDependency
(
'
accessor
'
,
target
.
COLOR_0
)
:
geometry
.
attributes
.
color
;
pendingColorAccessors
.
push
(
pendingAccessor
);
}
}
return
Promise
.
all
(
[
Promise
.
all
(
pendingPositionAccessors
),
Promise
.
all
(
pendingNormalAccessors
)
]
).
then
(
function
(
accessors
)
{
return
Promise
.
all
(
[
Promise
.
all
(
pendingPositionAccessors
),
Promise
.
all
(
pendingNormalAccessors
)
,
Promise
.
all
(
pendingColorAccessors
)
]
).
then
(
function
(
accessors
)
{
const
morphPositions
=
accessors
[
0
];
const
morphNormals
=
accessors
[
1
];
const
morphColors
=
accessors
[
2
];
if
(
hasMorphPosition
)
geometry
.
morphAttributes
.
position
=
morphPositions
;
if
(
hasMorphNormal
)
geometry
.
morphAttributes
.
normal
=
morphNormals
;
if
(
hasMorphColor
)
geometry
.
morphAttributes
.
color
=
morphColors
;
geometry
.
morphTargetsRelative
=
true
;
return
geometry
;
...
...
@@ -1969,6 +1981,14 @@
}
}
function
getImageURIMimeType
(
uri
)
{
if
(
uri
.
search
(
/
\.
jpe
?
g
(
$|
\?)
/i
)
>
0
||
uri
.
search
(
/^data
\:
image
\/
jpeg/
)
===
0
)
return
'
image/jpeg
'
;
if
(
uri
.
search
(
/
\.
webp
(
$|
\?)
/i
)
>
0
||
uri
.
search
(
/^data
\:
image
\/
webp/
)
===
0
)
return
'
image/webp
'
;
return
'
image/png
'
;
}
/* GLTF PARSER */
...
...
@@ -2671,6 +2691,7 @@
}
texture
.
userData
.
mimeType
=
sourceDef
.
mimeType
||
getImageURIMimeType
(
sourceDef
.
uri
);
return
texture
;
}
).
catch
(
function
(
error
)
{
...
...
examples/js/loaders/SVGLoader.js
浏览文件 @
c12c9a16
...
...
@@ -53,7 +53,7 @@
if
(
node
.
nodeType
!==
1
)
return
;
const
transform
=
getNodeTransform
(
node
);
let
traverseChildNodes
=
tru
e
;
let
isDefsNode
=
fals
e
;
let
path
=
null
;
switch
(
node
.
nodeName
)
{
...
...
@@ -105,7 +105,7 @@
break
;
case
'
defs
'
:
traverseChildNodes
=
fals
e
;
isDefsNode
=
tru
e
;
break
;
case
'
use
'
:
...
...
@@ -147,16 +147,23 @@
}
if
(
traverseChildNodes
)
{
const
childNodes
=
node
.
childNodes
;
const
nodes
=
node
.
childNodes
;
for
(
let
i
=
0
;
i
<
childNodes
.
length
;
i
++
)
{
for
(
let
i
=
0
;
i
<
nodes
.
length
;
i
++
)
{
const
node
=
childNodes
[
i
];
parseNode
(
nodes
[
i
],
style
);
if
(
isDefsNode
&&
node
.
nodeName
!==
'
style
'
&&
node
.
nodeName
!==
'
defs
'
)
{
// Ignore everything in defs except CSS style definitions
// and nested defs, because it is OK by the standard to have
// <style/> there.
continue
;
}
parseNode
(
node
,
style
);
}
if
(
transform
)
{
...
...
examples/js/renderers/CSS2DRenderer.js
浏览文件 @
c12c9a16
...
...
@@ -102,17 +102,16 @@
if
(
object
.
isCSS2DObject
)
{
const
visible
=
object
.
visible
&&
_vector
.
z
>=
-
1
&&
_vector
.
z
<=
1
&&
object
.
layers
.
test
(
camera
.
layers
);
object
.
element
.
style
.
display
=
visible
?
''
:
'
none
'
;
_vector
.
setFromMatrixPosition
(
object
.
matrixWorld
);
if
(
visible
)
{
_vector
.
applyMatrix4
(
_viewProjectionMatrix
);
object
.
onBeforeRender
(
_this
,
scene
,
camera
);
_vector
.
setFromMatrixPosition
(
object
.
matrixWorld
);
const
visible
=
object
.
visible
===
true
&&
_vector
.
z
>=
-
1
&&
_vector
.
z
<=
1
&&
object
.
layers
.
test
(
camera
.
layers
)
===
true
;
object
.
element
.
style
.
display
=
visible
===
true
?
''
:
'
none
'
;
_vector
.
applyMatrix4
(
_viewProjectionMatrix
);
if
(
visible
===
true
)
{
object
.
onBeforeRender
(
_this
,
scene
,
camera
);
const
element
=
object
.
element
;
if
(
/apple/i
.
test
(
navigator
.
vendor
)
)
{
...
...
examples/js/renderers/CSS3DRenderer.js
浏览文件 @
c12c9a16
...
...
@@ -181,10 +181,10 @@
if
(
object
.
isCSS3DObject
)
{
const
visible
=
object
.
visible
&&
object
.
layers
.
test
(
camera
.
layers
)
;
object
.
element
.
style
.
display
=
visible
?
''
:
'
none
'
;
// only getObjectCSSMatrix when object.visible
const
visible
=
object
.
visible
===
true
&&
object
.
layers
.
test
(
camera
.
layers
)
===
true
;
object
.
element
.
style
.
display
=
visible
===
true
?
''
:
'
none
'
;
if
(
visible
)
{
if
(
visible
===
true
)
{
object
.
onBeforeRender
(
_this
,
scene
,
camera
);
let
style
;
...
...
package.json
浏览文件 @
c12c9a16
{
"name"
:
"three"
,
"version"
:
"0.138.
1
"
,
"version"
:
"0.138.
2
"
,
"description"
:
"JavaScript 3D library"
,
"type"
:
"module"
,
"main"
:
"./build/three.js"
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录