# Effects API

`u-space` 提供了两个基于 Three.js 着色语言（TSL/WebGPU 节点）的静态特效工具：`MaterialEffects` 用于为对象应用高亮、呼吸等材质特效，`TSLEffects` 用于生成动态颜色节点模式和可组合的 Outline 输出效果。

## `MaterialEffects`

静态工具类，将基于 TSL 的视觉特效直接应用于对象的材质。适用于任何 `Object3D`（支持单个或数组），会自动遍历所有子网格。
当对象是 `InstanceObject`（例如 `SceneInstanceObject`、`FacilityInstanceObject` 或楼层里的 `FloorSemanticInstanceObject`）时，`highlightColor()` / `removeHighlightColor()` 会通过 `setInstanceHighlight()` / `clearInstanceHighlight()` 改写该实例的颜色和透明度，而不是改动共享 batch 材质。
`InstanceObject` 支持 `overwrite` 的替换/染色语义；`depthWrite` 属于共享 batch 材质状态，不能按单个实例设置，因此对该类对象不会生效。

特性：
- **效果可叠加**：高亮和呼吸效果可同时作用于同一对象，呼吸在高亮结果之上混合
- **共享材质安全**：按材质跟踪引用计数，多个 Mesh 共享材质时互不干扰
- **完整还原**：移除效果时自动恢复材质原始的 `colorNode`、`opacityNode`、`transparent` 和 `depthWrite` 状态

### `MaterialEffects.highlightColor(object, options?)`

为对象中所有网格应用颜色/透明度高亮。

```typescript
import { MaterialEffects } from 'u-space';

// 以 50% 透明度高亮为红色（叠加模式）
MaterialEffects.highlightColor(myModel, {
  color: 0xff0000,
  opacity: 0.5,
  overwrite: false, // false = 叠加（相乘），true = 完全替换颜色
});

// 半透明效果
MaterialEffects.highlightColor(myModel, { opacity: 0.3 });

// X 光效果（替换颜色 + 关闭深度写入）
MaterialEffects.highlightColor(myModel, {
  color: 0x0088ff,
  opacity: 0.25,
  overwrite: true,
  depthWrite: false,
});

// 支持数组
MaterialEffects.highlightColor([model1, model2], { color: 0x00ff00 });
```

#### `HighlightColorOptions`

| 属性        | 类型                  | 默认值      | 说明                                                                         |
| :---------- | :-------------------- | :---------- | :--------------------------------------------------------------------------- |
| `color`      | `ColorRepresentation` | `0xff0000`  | 高亮颜色。                                                                   |
| `opacity`    | `number`              | `0.5`       | 高亮时材质的透明度。                                                         |
| `overwrite`  | `boolean`             | `false`     | `false` = 与原始颜色相乘（叠加）；`true` = 完全替换颜色。                    |
| `depthWrite` | `boolean`             | —           | 可选。设为 `false` 可产生 X 光透视效果。不设置时保持原始值。                  |
| `multiplyOpacity` | `boolean`       | `false`     | 可选。设为 `true` 时高亮透明度会与材质原始透明度相乘，主要用于语义 fallback 模型内部。 |

### `MaterialEffects.removeHighlightColor(object)`

移除高亮效果。当对象上所有效果都被移除后，材质将完整恢复到原始状态。

```typescript
MaterialEffects.removeHighlightColor(myModel);
```

### `MaterialEffects.breatheColor(object, options?)`

为对象应用呼吸灯效果，颜色在材质原色与目标颜色之间随时间脉冲变化。需要 `viewer.frameloop = 'always'`。

```typescript
MaterialEffects.breatheColor(myModel, {
  color: 0x00ff00,
  speed: 1.0,
  intensity: 2.0,
});
viewer.frameloop = 'always';
```

#### `BreatheColorOptions`

| 属性        | 类型                  | 默认值      | 说明                               |
| :---------- | :-------------------- | :---------- | :--------------------------------- |
| `color`     | `ColorRepresentation` | `0x00ff00`  | 呼吸目标颜色。                     |
| `speed`     | `number`              | `1.0`       | 振荡速度。                         |
| `intensity` | `number`              | `2.0`       | 控制峰值的锐度。                   |

### `MaterialEffects.removeBreatheColor(object)`

移除呼吸效果。

```typescript
MaterialEffects.removeBreatheColor(myModel);
```

### `MaterialEffects.wireframe(object, enabled?)`

开启或关闭线框渲染模式。

```typescript
MaterialEffects.wireframe(myModel);           // 开启线框
MaterialEffects.wireframe(myModel, false);    // 关闭线框
MaterialEffects.removeWireframe(myModel);     // 等同于 wireframe(obj, false)
```

### `MaterialEffects.fadeIn(object, options?)` / `MaterialEffects.fadeOut(object, options?)`

淡入/淡出动画效果。返回 `Promise`，在动画完成后 resolve。淡出后对象材质保持透明状态。

```typescript
// 淡出
await MaterialEffects.fadeOut(myModel, { duration: 1000 });

// 淡入
await MaterialEffects.fadeIn(myModel, { duration: 500 });
```

> 动画期间需要持续渲染，建议配合 `viewer.frameloop = 'always'` 使用。

#### `FadeOptions`

| 属性       | 类型     | 默认值 | 说明                |
| :--------- | :------- | :----- | :------------------ |
| `duration` | `number` | `500`  | 动画时长（毫秒）。 |

---

## `TSLEffects`

静态工厂类。`flow()`、`breathe()` 和 `fluid()` 返回可赋给 `NodeMaterial.colorNode` 的 TSL 颜色节点；`outline()` 返回接入 `RenderPipeline` 的输出效果。持续动画需要 `viewer.frameloop = 'always'`。

### `TSLEffects.outline(options?)`

创建基于 Three.js `OutlineNode` 的屏幕空间描边效果。它不会修改对象材质，而是通过 `RenderPipeline.addOutputEffect()` 叠加在默认渲染结果上，因此可以与内置 Bloom、SSGI、SSR 以及其他 output effects 组合。

```typescript
import { TSLEffects } from 'u-space';

const outline = TSLEffects.outline({
  selectedObjects: [model],
  edgeStrength: 3,
  edgeThickness: 1.5,
  edgeGlow: 0,
  visibleEdgeColor: 0x00e5ff,
  hiddenEdgeColor: 0xff4d6d,
  downSampleRatio: 2,
  instanceBoundsProxy: true,
  instanceBoundsPadding: 0.05,
});

viewer.renderPipeline.addOutputEffect(outline);

// 替换当前描边对象，不需要重建后处理链。
outline.setSelectedObjects([anotherObject]);

// 运行时更新样式与 InstanceObject bounds proxy 参数。
outline.update({ edgeStrength: 4, edgeGlow: 0.25 });
viewer.render();

// 销毁前先从 RenderPipeline 移除。
viewer.renderPipeline.removeOutputEffect(outline);
outline.dispose();
```

#### `TSLOutlineOptions`

| 属性 | 类型 | 默认值 | 说明 |
| :--- | :--- | :--- | :--- |
| `selectedObjects` | `readonly Object3D[]` | `[]` | 初始描边对象。普通 Mesh、Group 与核心 `InstanceObject` 均可传入。 |
| `edgeStrength` | `number` | `3` | 边缘颜色强度。 |
| `edgeThickness` | `number` | `1` | 边缘厚度。 |
| `edgeGlow` | `number` | `0` | 描边向外扩散的发光强度。 |
| `visibleEdgeColor` | `ColorRepresentation` | `0xffffff` | 可见边缘颜色。 |
| `hiddenEdgeColor` | `ColorRepresentation` | `0x4e3636` | 被遮挡边缘颜色。 |
| `downSampleRatio` | `number` | `2` | Outline pass 降采样比例；非有限值或 `<= 0` 时恢复为 `2`。值越大成本越低，但边缘精度也越低。 |
| `instanceBoundsProxy` | `boolean` | `true` | `InstanceObject` 没有可直接描边的渲染子树时，是否用世界包围盒代理生成轮廓。 |
| `instanceBoundsPadding` | `number` | `0` | bounds proxy 每个方向额外扩张的世界单位。 |
| `instanceBoundsMinSize` | `number` | `0.001` | bounds proxy 每个轴向的最小尺寸，避免退化包围盒无法描边。 |

#### 返回的 `TSLOutlineEffect`

| 成员 | 说明 |
| :--- | :--- |
| `selectedObjects` | 当前逻辑选择对象的只读快照。 |
| `outlineNode` | 当前内部 `OutlineNode`；在 RenderPipeline 首次构建前为 `null`，Scene 或 Camera 变化后可能被替换。 |
| `setSelectedObjects(objects)` | 替换当前选择并返回自身，便于链式调用。空数组会清除描边。 |
| `update(options)` | 更新除 `selectedObjects` 外的全部样式和 bounds proxy 选项，并返回自身。 |
| `dispose()` | 释放内部 OutlineNode、代理 Geometry/Material 和 dirty 订阅。调用前应先 `removeOutputEffect()`。 |

对于 `InstanceObject`，效果会优先描边 `getInstanceRenderObject()` 返回的可渲染对象；如果逻辑对象自身包含 Mesh/Sprite，则直接使用该对象；只有两者都不可用时才根据 `getInstanceBoundingBox()` 创建不可见 bounds proxy。代理会监听 `onInstanceRenderDirty()`，在实例显隐、变换或包围盒变化后同步，而且不会参与拾取、颜色输出或阴影。

> 如果业务设置了自定义 `viewer.renderPipeline.setOutputComposer()`，RenderPipeline 会按自定义 composer 语义跳过所有 output effects，Outline 也不会显示。更新选择或参数后，`frameloop = 'demand'` 的 Viewer 需要调用 `viewer.render()` 或 `viewer.invalidate()` 请求新帧。

[在线示例](https://u-space-phi.vercel.app/examples/test_outline.html)

### `TSLEffects.flow(parameters?)`

沿网格 UV X 轴方向的定向光扫效果，适用于道路、管道和流线。

```typescript
import { TSLEffects } from 'u-space';

myTubeMesh.material.colorNode = TSLEffects.flow({
  baseColor: 0x001133,
  flowColor: 0x00aaff,
  speed: 1.5,
  scale: 4.0,
  intensity: 6.0,
});
viewer.frameloop = 'always';
```

**参数：**

| 属性        | 类型                  | 默认值      | 说明                                              |
| :---------- | :-------------------- | :---------- | :------------------------------------------------ |
| `baseColor` | `ColorRepresentation` | `0xffffff`  | 背景/底色。                                       |
| `flowColor` | `ColorRepresentation` | `0x00ff00`  | 扫光高亮颜色。                                    |
| `speed`     | `number`              | `1.0`       | 动画速度（越高扫光越快）。                        |
| `scale`     | `number`              | `3.0`       | 图案的空间频率。                                  |
| `intensity` | `number`              | `4.0`       | 峰值锐度，值越高光束越细。                        |

### `TSLEffects.breathe(parameters?)`

在两种颜色之间随时间振荡的脉冲发光效果，适合状态指示器和警报。

```typescript
myMesh.material.colorNode = TSLEffects.breathe({
  baseColor: 0x333333,
  breathColor: 0x00ff88,
  speed: 2.0,
  intensity: 3.0,
});
```

**参数：**

| 属性          | 类型                  | 默认值      | 说明                               |
| :------------ | :-------------------- | :---------- | :--------------------------------- |
| `baseColor`   | `ColorRepresentation` | `0xffffff`  | 低/静息状态的颜色。                |
| `breathColor` | `ColorRepresentation` | `0x00ff00`  | 峰值亮度时的颜色。                 |
| `speed`       | `number`              | `1.0`       | 振荡速度。                         |
| `intensity`   | `number`              | `2.0`       | 控制峰值的锐度。                   |

### `TSLEffects.fluid(parameters?)`

噪声扭曲的流动效果，适用于水面、等离子体或有机流动材质。

```typescript
myPlaneMesh.material.colorNode = TSLEffects.fluid({
  baseColor: 0x002244,
  flowColor: 0x0066ff,
  speed: 0.5,
  scale: 2.0,
  intensity: 1.5,
  distortion: 0.3,
});
```

**参数：**

| 属性         | 类型                  | 默认值      | 说明                                          |
| :----------- | :-------------------- | :---------- | :-------------------------------------------- |
| `baseColor`  | `ColorRepresentation` | `0xffffff`  | 基础颜色。                                    |
| `flowColor`  | `ColorRepresentation` | `0x0000ff`  | 流体高亮颜色。                                |
| `speed`      | `number`              | `1.0`       | 动画速度。                                    |
| `scale`      | `number`              | `1.0`       | 噪声图案的 UV 缩放比例。                      |
| `intensity`  | `number`              | `1.0`       | 流体图案的锐度。                              |
| `distortion` | `number`              | `0.5`       | 采样前噪声对 UV 的扭曲程度。                  |
