s3m-three 0.3.14 → 0.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/s3m-three.iife.ext-three.js +100 -24
- package/dist/browser/s3m-three.iife.js +100 -24
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/render/DatasetManager.d.ts +3 -1
- package/dist/render/DatasetManager.d.ts.map +1 -1
- package/dist/render/DatasetManager.js +2 -1
- package/dist/render/DatasetManager.js.map +1 -1
- package/dist/render/EdgeLines.d.ts +14 -2
- package/dist/render/EdgeLines.d.ts.map +1 -1
- package/dist/render/EdgeLines.js +45 -8
- package/dist/render/EdgeLines.js.map +1 -1
- package/dist/render/GpuEdgePass.d.ts +58 -0
- package/dist/render/GpuEdgePass.d.ts.map +1 -0
- package/dist/render/GpuEdgePass.js +207 -0
- package/dist/render/GpuEdgePass.js.map +1 -0
- package/dist/render/S3MViewer.d.ts +75 -7
- package/dist/render/S3MViewer.d.ts.map +1 -1
- package/dist/render/S3MViewer.js +270 -52
- package/dist/render/S3MViewer.js.map +1 -1
- package/dist/render/StyleMaterial.d.ts +108 -6
- package/dist/render/StyleMaterial.d.ts.map +1 -1
- package/dist/render/StyleMaterial.js +90 -10
- package/dist/render/StyleMaterial.js.map +1 -1
- package/docs/API.md +42 -5
- package/package.json +1 -1
- package/src/index.ts +6 -1
- package/src/render/DatasetManager.ts +5 -1
- package/src/render/EdgeLines.ts +50 -8
- package/src/render/GpuEdgePass.ts +236 -0
- package/src/render/S3MViewer.ts +310 -52
- package/src/render/StyleMaterial.ts +174 -13
|
@@ -61,6 +61,75 @@ export const SELECT_MODE_LABELS: Record<SelectMode, string> = {
|
|
|
61
61
|
[SELECT_MODE.GLOW_ONLY]: '仅描边发光 (Glow)',
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
/* ------------------------------------------------------------------ 棱线画法 */
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 全局棱线的画法。
|
|
68
|
+
*
|
|
69
|
+
* - `'line'` CPU 折边线:焊合 → 折边判定 → 屏幕空间等宽四边形(默认口径)
|
|
70
|
+
* - `'offset'` 同上,但整段沿「边法线」往外推 `edgeOffset` 米。贴在面上的线会被
|
|
71
|
+
* 面的明暗吃掉,推出去一点才看得清,代价是要多带一条 aNormal 属性
|
|
72
|
+
* - `'hull'` 背面外扩轮廓:把整块几何沿法线鼓一圈、只画背面,得到「白模 + 卡通
|
|
73
|
+
* 描边」的观感。不建任何折边几何,成本就是多画一遍网格
|
|
74
|
+
* - `'gpu'` 纯片元描边:在主材质里用屏幕导数找「法线突变」的像素,直接混成棱线色。
|
|
75
|
+
* 零几何、零额外 pass,代价是线宽约 1 个设备像素,且描不出几何外轮廓
|
|
76
|
+
*/
|
|
77
|
+
export type EdgeStyle = 'line' | 'offset' | 'hull' | 'gpu';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `style.edges` 的取值。
|
|
81
|
+
*
|
|
82
|
+
* boolean 是旧口径(`true` = `'line'`),字符串 / 数组用来选画法,数组可叠加,
|
|
83
|
+
* 例如 `['line', 'hull']` = 折边线 + 外扩轮廓同时上。
|
|
84
|
+
* 注意 `'offset'` 隐含折边线:外推是折边线的修饰,不是另一种线。
|
|
85
|
+
*/
|
|
86
|
+
export type EdgesOption = boolean | EdgeStyle | EdgeStyle[];
|
|
87
|
+
|
|
88
|
+
/** 归一化后的棱线开关 */
|
|
89
|
+
export interface EdgeModes {
|
|
90
|
+
/** 整体开关:任一画法命中就是 true */
|
|
91
|
+
enabled: boolean;
|
|
92
|
+
/** 是否建 CPU 折边线 */
|
|
93
|
+
line: boolean;
|
|
94
|
+
/** 折边线是否沿边法线外推 */
|
|
95
|
+
offset: boolean;
|
|
96
|
+
/** 是否挂背面外扩轮廓 */
|
|
97
|
+
hull: boolean;
|
|
98
|
+
/** 是否在材质里做片元级描边(零几何) */
|
|
99
|
+
gpu: boolean;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const EDGE_MODE_OFF: EdgeModes = { enabled: false, line: false, offset: false, hull: false, gpu: false };
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 把 `EdgesOption` 归一成四个开关。
|
|
106
|
+
*
|
|
107
|
+
* 未知值(宿主 JS 里传错字符串 / 脏数组)一律当关,不抛错也不误开——样式这种东西
|
|
108
|
+
* 出错时最该做的是「保持不动」,而不是把整个图层带崩。
|
|
109
|
+
*/
|
|
110
|
+
export function resolveEdgeModes(value: EdgesOption | null | undefined): EdgeModes {
|
|
111
|
+
if (value == null || value === false) return { ...EDGE_MODE_OFF };
|
|
112
|
+
if (value === true) return { ...EDGE_MODE_OFF, enabled: true, line: true };
|
|
113
|
+
|
|
114
|
+
const list = Array.isArray(value) ? value : [value];
|
|
115
|
+
const modes: EdgeModes = { ...EDGE_MODE_OFF };
|
|
116
|
+
for (const item of list) {
|
|
117
|
+
if (item === 'line') modes.line = true;
|
|
118
|
+
else if (item === 'offset') {
|
|
119
|
+
modes.line = true;
|
|
120
|
+
modes.offset = true;
|
|
121
|
+
} else if (item === 'hull') modes.hull = true;
|
|
122
|
+
else if (item === 'gpu') modes.gpu = true;
|
|
123
|
+
}
|
|
124
|
+
modes.enabled = modes.line || modes.hull || modes.gpu;
|
|
125
|
+
return modes;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** 两套开关是否等价(只比较有效位,用来判断要不要重建装饰) */
|
|
129
|
+
export function sameEdgeModes(a: EdgeModes, b: EdgeModes): boolean {
|
|
130
|
+
return a.line === b.line && a.offset === b.offset && a.hull === b.hull && a.gpu === b.gpu;
|
|
131
|
+
}
|
|
132
|
+
|
|
64
133
|
/* ------------------------------------------------------------------ 样式配置 */
|
|
65
134
|
|
|
66
135
|
/** 图层整体样式(宿主无关,S3MViewer 与 s3m-maplibre 的 S3MLayer 共用) */
|
|
@@ -105,14 +174,31 @@ export interface S3MStyle {
|
|
|
105
174
|
outlineWidth?: number;
|
|
106
175
|
/** 选中棱线的粗细(像素,默认 3,仅 outlineStyle: 'edges' 生效,缩放时保持恒定) */
|
|
107
176
|
outlineWidthPx?: number;
|
|
108
|
-
/**
|
|
109
|
-
|
|
177
|
+
/**
|
|
178
|
+
* 是否显示几何棱线。
|
|
179
|
+
*
|
|
180
|
+
* 取值是 `boolean | EdgeStyle | EdgeStyle[]`:`true` 等价于 `'line'`(旧口径),
|
|
181
|
+
* `'hull'` 走背面外扩轮廓,`'offset'` 是折边线 + 法线外推,数组可叠加。
|
|
182
|
+
* 见 `resolveEdgeModes`。
|
|
183
|
+
*/
|
|
184
|
+
edges: EdgesOption;
|
|
110
185
|
/** 全局棱线粗细(像素,默认 1.2) */
|
|
111
186
|
edgeWidth?: number;
|
|
112
187
|
edgeColor: string;
|
|
113
188
|
edgeOpacity?: number;
|
|
114
189
|
/** 棱线临界角(度):相邻面法线夹角超过该值才描边,过滤掉浅坡与曲面细分。默认 55° */
|
|
115
190
|
edgeThreshold?: number;
|
|
191
|
+
/** 折边线沿边法线外推的距离(米,默认 0.15,仅 edges 含 'offset' 生效) */
|
|
192
|
+
edgeOffset?: number;
|
|
193
|
+
/** 背面外扩轮廓的厚度(米,默认 0.9,仅 edges 含 'hull' 生效) */
|
|
194
|
+
edgeHullWidth?: number;
|
|
195
|
+
/**
|
|
196
|
+
* 忽略数据自带的棱线包(primitiveType 13),一律 CPU 现算。默认 false。
|
|
197
|
+
*
|
|
198
|
+
* 数据自带的棱线是 `LineBasicMaterial`,线宽恒 1px,`edgeWidth` / 外扩 / 单体跟随
|
|
199
|
+
* 全都吃不上。想让这些能力对所有数据统一生效就打开它,代价是 CPU 要现算折边。
|
|
200
|
+
*/
|
|
201
|
+
forceCpuEdges?: boolean;
|
|
116
202
|
roofThreshold?: number;
|
|
117
203
|
/** 是否与模型原色贴图叠加混合(0 = 纯色覆盖,1 = 叠乘原色贴图) */
|
|
118
204
|
texMix?: number;
|
|
@@ -161,6 +247,9 @@ export const DEFAULT_STYLE: S3MStyle = {
|
|
|
161
247
|
edgeColor: '#334155',
|
|
162
248
|
edgeOpacity: 0.6,
|
|
163
249
|
edgeThreshold: 55,
|
|
250
|
+
edgeOffset: 0.15,
|
|
251
|
+
edgeHullWidth: 0.9,
|
|
252
|
+
forceCpuEdges: false,
|
|
164
253
|
roofThreshold: 0.65,
|
|
165
254
|
texMix: 0,
|
|
166
255
|
ambientGain: 0,
|
|
@@ -168,7 +257,33 @@ export const DEFAULT_STYLE: S3MStyle = {
|
|
|
168
257
|
fadeIn: 0,
|
|
169
258
|
};
|
|
170
259
|
|
|
171
|
-
|
|
260
|
+
/** 光照模型:pbr(默认,MeshStandardMaterial)| lambert(省 GGX 与环境球谐) */
|
|
261
|
+
export type MaterialQuality = 'pbr' | 'lambert';
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 当前光照模型。模块级全局 —— 与 anisotropy / maxTextureSize 同一套设计,
|
|
265
|
+
* 同页多个图层会互相覆盖,后创建的那个决定全局。只影响**之后创建**的材质。
|
|
266
|
+
*/
|
|
267
|
+
let materialQuality: MaterialQuality = 'pbr';
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 切换光照模型。
|
|
271
|
+
*
|
|
272
|
+
* 'pbr'(默认)= `MeshStandardMaterial`,GGX + 环境球谐,质感最好也最贵。
|
|
273
|
+
* 'lambert' = `MeshLambertMaterial`,只保留漫反射 + 平行光,片元开销约省一半;
|
|
274
|
+
* 白模 / 纯色 / 按高度着色这类不需要高光的模式,肉眼差别很小。
|
|
275
|
+
*
|
|
276
|
+
* 注意:只影响之后创建的材质 —— 已加载的瓦片要重新加载才会换。
|
|
277
|
+
*/
|
|
278
|
+
export function setMaterialQuality(q: MaterialQuality): void {
|
|
279
|
+
materialQuality = q === 'lambert' ? 'lambert' : 'pbr';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function getMaterialQuality(): MaterialQuality {
|
|
283
|
+
return materialQuality;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export type StyledMaterial = (THREE.MeshStandardMaterial | THREE.MeshLambertMaterial) & {
|
|
172
287
|
userData: {
|
|
173
288
|
uSelBatch: { value: number };
|
|
174
289
|
uSelWhole: { value: number };
|
|
@@ -785,11 +900,11 @@ export interface StyledMaterialOptions {
|
|
|
785
900
|
*/
|
|
786
901
|
export function createStyledMaterial(opts: StyledMaterialOptions): StyledMaterial {
|
|
787
902
|
const uniforms = opts.uniforms;
|
|
788
|
-
|
|
903
|
+
// 两种光照模型共用的参数。注入的 chunk 只动 diffuseColor / normal / transformed,
|
|
904
|
+
// 在 Standard 与 Lambert 的 shader 里都存在同名 include,所以模板不用分支。
|
|
905
|
+
const baseParams = {
|
|
789
906
|
color: opts.color ?? new THREE.Color(0xffffff),
|
|
790
907
|
map: opts.map ?? null,
|
|
791
|
-
roughness: opts.roughness ?? 0.95,
|
|
792
|
-
metalness: 0,
|
|
793
908
|
side: opts.side ?? THREE.DoubleSide,
|
|
794
909
|
transparent: !!opts.transparent,
|
|
795
910
|
opacity: opts.opacity ?? 1,
|
|
@@ -800,7 +915,16 @@ export function createStyledMaterial(opts: StyledMaterialOptions): StyledMateria
|
|
|
800
915
|
// 顶点色、贴图全部在自定义 chunk 里处理,不让 three 插手
|
|
801
916
|
vertexColors: false,
|
|
802
917
|
alphaTest: opts.alphaTest ?? 0,
|
|
803
|
-
}
|
|
918
|
+
};
|
|
919
|
+
const mat = (
|
|
920
|
+
materialQuality === 'lambert'
|
|
921
|
+
? new THREE.MeshLambertMaterial(baseParams)
|
|
922
|
+
: new THREE.MeshStandardMaterial({
|
|
923
|
+
...baseParams,
|
|
924
|
+
roughness: opts.roughness ?? 0.95,
|
|
925
|
+
metalness: 0,
|
|
926
|
+
})
|
|
927
|
+
) as StyledMaterial;
|
|
804
928
|
|
|
805
929
|
// 选中单体编号:每个 Mesh 独立,默认 -1 表示无选中
|
|
806
930
|
const uSelBatch = { value: -1 };
|
|
@@ -994,18 +1118,34 @@ const OUTLINE_VERT = /* glsl */ `
|
|
|
994
1118
|
|
|
995
1119
|
const OUTLINE_FRAG = /* glsl */ `
|
|
996
1120
|
uniform vec3 uColor;
|
|
1121
|
+
uniform float uOpacity;
|
|
997
1122
|
varying float vSel;
|
|
998
1123
|
void main() {
|
|
999
1124
|
if ( vSel < 0.5 ) discard;
|
|
1000
|
-
|
|
1125
|
+
if ( uOpacity <= 0.003 ) discard;
|
|
1126
|
+
gl_FragColor = vec4( uColor, uOpacity );
|
|
1001
1127
|
}
|
|
1002
1128
|
`;
|
|
1003
1129
|
|
|
1130
|
+
/**
|
|
1131
|
+
* 外扩轮廓的「外观」uniform。
|
|
1132
|
+
*
|
|
1133
|
+
* 与棱线一样,全局外扩是逐网格一份材质(要各绑各的单体状态纹理),但颜色 / 厚度 /
|
|
1134
|
+
* 不透明度必须全局统一 —— 所以这些 `{ value }` 对象由调用方持有并共享给每一份材质,
|
|
1135
|
+
* 改一次全场景生效。
|
|
1136
|
+
*/
|
|
1137
|
+
export interface OutlineStyleUniforms {
|
|
1138
|
+
uColor: { value: THREE.Color };
|
|
1139
|
+
uWidth: { value: number };
|
|
1140
|
+
uOpacity: { value: number };
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1004
1143
|
export interface OutlineMaterial extends THREE.ShaderMaterial {
|
|
1005
1144
|
uniforms: {
|
|
1006
1145
|
uSelBatch: { value: number };
|
|
1007
1146
|
uSelWhole: { value: number };
|
|
1008
1147
|
uWidth: { value: number };
|
|
1148
|
+
uOpacity: { value: number };
|
|
1009
1149
|
uElevation: { value: number };
|
|
1010
1150
|
uBatchTex: { value: THREE.Texture };
|
|
1011
1151
|
uBatchTex2: { value: THREE.Texture };
|
|
@@ -1020,13 +1160,34 @@ export interface OutlineMaterial extends THREE.ShaderMaterial {
|
|
|
1020
1160
|
};
|
|
1021
1161
|
}
|
|
1022
1162
|
|
|
1023
|
-
/**
|
|
1024
|
-
|
|
1163
|
+
/**
|
|
1164
|
+
* 描边材质(背面外扩法)。
|
|
1165
|
+
*
|
|
1166
|
+
* 选中侧与全局棱线共用这一份实现,差别只在 uniform:
|
|
1167
|
+
* - 选中:`uSelBatch` = 选中单体的 batchId,只扩那一栋,不透明
|
|
1168
|
+
* - 全局:`uSelWhole` = 1,整块几何都扩,走 `edgeOpacity` 混合
|
|
1169
|
+
*
|
|
1170
|
+
* @param styleUniforms 传入时**直接复用这些 `{ value }` 对象**,让逐网格材质共享外观
|
|
1171
|
+
* @param transparent 全局外扩要能吃 `edgeOpacity`,选中侧保持不透明(默认 false)
|
|
1172
|
+
*/
|
|
1173
|
+
export function createOutlineMaterial(
|
|
1174
|
+
shared: StyleUniforms,
|
|
1175
|
+
initialWidth = 0.9,
|
|
1176
|
+
styleUniforms?: OutlineStyleUniforms,
|
|
1177
|
+
transparent = false
|
|
1178
|
+
): OutlineMaterial {
|
|
1179
|
+
const style: OutlineStyleUniforms = styleUniforms ?? {
|
|
1180
|
+
uColor: { value: shared.uSelColor.value.clone() },
|
|
1181
|
+
uWidth: { value: initialWidth },
|
|
1182
|
+
uOpacity: { value: 1 },
|
|
1183
|
+
};
|
|
1184
|
+
|
|
1025
1185
|
const mat = new THREE.ShaderMaterial({
|
|
1026
1186
|
uniforms: {
|
|
1027
1187
|
uSelBatch: { value: -1 },
|
|
1028
1188
|
uSelWhole: { value: 0 },
|
|
1029
|
-
uWidth:
|
|
1189
|
+
uWidth: style.uWidth,
|
|
1190
|
+
uOpacity: style.uOpacity,
|
|
1030
1191
|
uElevation: { value: 0 },
|
|
1031
1192
|
uBatchTex: { value: DUMMY_BATCH_TEX },
|
|
1032
1193
|
uBatchTex2: { value: DUMMY_BATCH_TEX },
|
|
@@ -1037,13 +1198,13 @@ export function createOutlineMaterial(shared: StyleUniforms, initialWidth = 0.9)
|
|
|
1037
1198
|
uPathCount: shared.uPathCount ?? { value: 1 },
|
|
1038
1199
|
uPathTexActive: shared.uPathTexActive ?? { value: 0 },
|
|
1039
1200
|
uPathTime: shared.uPathTime ?? { value: 0 },
|
|
1040
|
-
uColor:
|
|
1201
|
+
uColor: style.uColor,
|
|
1041
1202
|
},
|
|
1042
1203
|
vertexShader: OUTLINE_VERT,
|
|
1043
1204
|
fragmentShader: OUTLINE_FRAG,
|
|
1044
1205
|
side: THREE.BackSide,
|
|
1045
1206
|
depthWrite: false,
|
|
1046
|
-
transparent
|
|
1207
|
+
transparent,
|
|
1047
1208
|
// 描边要压在建筑后面一点,避免和本体 z-fighting
|
|
1048
1209
|
polygonOffset: true,
|
|
1049
1210
|
polygonOffsetFactor: 1,
|