three-base 1.129.9 → 1.129.10

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.
@@ -0,0 +1,209 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-05-04 23:01:36
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2022-06-06 13:32:08
6
+ */
7
+
8
+ import { Camera, Clock, EventDispatcher, Object3D, Vector3 } from "three";
9
+ import { Capsule } from "three/examples/jsm/math/Capsule";
10
+ import { Octree } from "three/examples/jsm/math/Octree";
11
+ import { clamp, degToRad } from "three/src/math/MathUtils";
12
+
13
+
14
+ /**
15
+ * 第一人称控制器
16
+ *
17
+ * @export
18
+ * @class FPScontrol
19
+ * @extends {EventDispatcher}
20
+ */
21
+ export default class FPScontrol extends EventDispatcher {
22
+
23
+ canvas!: HTMLCanvasElement
24
+
25
+ constructor(camera: Camera, canvas: HTMLCanvasElement) {
26
+ super();
27
+
28
+ // 相机对象
29
+ this.camera = camera;
30
+
31
+ this.camera.rotation.order = 'YXZ';
32
+
33
+ this.canvas = canvas;
34
+
35
+ this.init(canvas);
36
+ }
37
+
38
+ // 是否启用
39
+ enable = false;
40
+
41
+ GRAVITY = 30; // 重力
42
+ STEPS_PER_FRAME = 5; // 速度
43
+
44
+ camera!: Camera;
45
+
46
+ // 世界八叉树对象
47
+ worldOctree = new Octree();
48
+
49
+ // 用户自己的碰撞体积
50
+ playerCollider = new Capsule(new Vector3(0, 0.35, 0), new Vector3(0, 1, 0), 0.35);
51
+
52
+ // 时钟对象
53
+ clock = new Clock();
54
+
55
+ playerVelocity = new Vector3();
56
+ playerDirection = new Vector3();
57
+
58
+ playerOnFloor = false;
59
+ mouseTime = 0;
60
+ isMousedown = false;
61
+ keyStates: { [key: string]: boolean } = {};
62
+
63
+
64
+ init(canvas: HTMLCanvasElement) {
65
+ this.playerCollider.end.set(0, 1, 0);
66
+ document.addEventListener('keydown', this.keydown);
67
+ document.addEventListener('keyup', this.keyup);
68
+ canvas.addEventListener('pointerdown', this.mousedown);
69
+ document.addEventListener('pointerup', this.mouseup);
70
+ document.body.addEventListener('pointermove', this.mousemove);
71
+ this.enable = true;
72
+ }
73
+
74
+
75
+ keydown = (event: KeyboardEvent) => {
76
+ this.keyStates[event.code] = true;
77
+ }
78
+
79
+ keyup = (event: KeyboardEvent) => {
80
+ this.keyStates[event.code] = false;
81
+ }
82
+
83
+ mousedown = () => {
84
+ this.isMousedown = true;
85
+ this.mouseTime = performance.now();
86
+ }
87
+
88
+ mouseup = () => {
89
+ this.isMousedown = false;
90
+ }
91
+
92
+ mousemove = (event: MouseEvent) => {
93
+ if (this.enable && this.isMousedown) {
94
+ this.camera.rotation.y -= event.movementX / 500;
95
+ this.camera.rotation.x -= event.movementY / 500;
96
+
97
+ }
98
+ }
99
+
100
+ // 设置 八叉树 节点
101
+ fromGraphNode(object3d: Object3D) {
102
+ this.worldOctree.fromGraphNode(object3d);
103
+ }
104
+
105
+ // 获取前进的向量
106
+ getForwardVector = () => {
107
+ this.camera.getWorldDirection(this.playerDirection);
108
+ this.playerDirection.y = 0;
109
+ this.playerDirection.normalize();
110
+ return this.playerDirection;
111
+ }
112
+
113
+ // 获取 后退的向量
114
+ getSideVector = () => {
115
+ this.camera.getWorldDirection(this.playerDirection);
116
+ this.playerDirection.y = 0;
117
+ this.playerDirection.normalize();
118
+ this.playerDirection.cross(this.camera.up);
119
+ return this.playerDirection;
120
+ }
121
+
122
+ // 玩家移动控制器
123
+ controls = (deltaTime: number) => {
124
+ const speedDelta = deltaTime * (this.playerOnFloor ? 25 : 8);
125
+ if (this.keyStates['KeyW']) {
126
+ this.playerVelocity.add(this.getForwardVector().multiplyScalar(speedDelta));
127
+ }
128
+ if (this.keyStates['KeyS']) {
129
+ this.playerVelocity.add(this.getForwardVector().multiplyScalar(-speedDelta));
130
+ }
131
+ if (this.keyStates['KeyA']) {
132
+ this.playerVelocity.add(this.getSideVector().multiplyScalar(-speedDelta));
133
+ }
134
+ if (this.keyStates['KeyD']) {
135
+ this.playerVelocity.add(this.getSideVector().multiplyScalar(speedDelta));
136
+ }
137
+
138
+ if (this.playerOnFloor) {
139
+ if (this.keyStates['Space']) {
140
+ this.playerVelocity.y = 15;
141
+ }
142
+ }
143
+
144
+ }
145
+
146
+ // 更新玩家的位置
147
+ updatePlayer = (deltaTime: number) => {
148
+ let damping = Math.exp(-4 * deltaTime) - 1;
149
+ if (!this.playerOnFloor) {
150
+ this.playerVelocity.y -= this.GRAVITY * deltaTime;
151
+ damping *= 0.1;
152
+ }
153
+ this.playerVelocity.addScaledVector(this.playerVelocity, damping);
154
+ const deltaPosition = this.playerVelocity.clone().multiplyScalar(deltaTime);
155
+ this.playerCollider.translate(deltaPosition);
156
+ this.playerCollisions();
157
+ this.camera.position.copy(this.playerCollider.end);
158
+ this.camera.position.y += 1;
159
+ this.camera.rotation.x = clamp(this.camera.rotation.x, degToRad(-20), degToRad(20));
160
+ }
161
+
162
+ // 玩家的碰撞检测
163
+ playerCollisions() {
164
+ const result = this.worldOctree.capsuleIntersect(this.playerCollider);
165
+ this.playerOnFloor = false;
166
+ if (result) {
167
+ this.playerOnFloor = result.normal.y > 0;
168
+ if (!this.playerOnFloor) {
169
+ this.playerVelocity.addScaledVector(result.normal, -result.normal.dot(this.playerVelocity));
170
+
171
+ }
172
+ this.playerCollider.translate(result.normal.multiplyScalar(result.depth));
173
+ }
174
+ }
175
+ // 更新控制器的位置
176
+ teleportPlayerIfOob = () => {
177
+ if (this.camera.position.y <= -10) {
178
+ this.playerCollider.start.set(0, 0.35, 0);
179
+ this.playerCollider.end.set(0, 1, 10);
180
+ this.playerCollider.radius = 0.35;
181
+ this.camera.position.copy(this.playerCollider.end);
182
+ // this.camera.position.set(0, 3, 0);
183
+ }
184
+ }
185
+
186
+ // 更新
187
+ update() {
188
+ if (this.enable) {
189
+ const deltaTime = Math.min(0.05, this.clock.getDelta()) / this.STEPS_PER_FRAME;
190
+ for (let i = 0; i < this.STEPS_PER_FRAME; i++) {
191
+ this.controls(deltaTime);
192
+ this.updatePlayer(deltaTime);
193
+ this.teleportPlayerIfOob();
194
+ }
195
+ }
196
+ }
197
+
198
+ // 销毁事件
199
+ disposer() {
200
+ this.enable = false;
201
+ document.removeEventListener('keydown', this.keydown);
202
+ document.removeEventListener('keyup', this.keyup);
203
+ this.canvas.removeEventListener('pointerdown', this.mousedown);
204
+ document.removeEventListener('pointerup', this.mouseup);
205
+ document.body.removeEventListener('pointermove', this.mousemove);
206
+ }
207
+
208
+
209
+ }
@@ -0,0 +1,80 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2023-02-21 09:55:52
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2023-02-26 14:42:21
6
+ */
7
+
8
+ import * as THREE from 'three'
9
+
10
+ // 模型组转成 实例对象
11
+ export default class InstancedGroupMesh extends THREE.Mesh {
12
+ isInstancedGroupMesh = true
13
+
14
+ count: number = 0
15
+
16
+ meshCollect: Record<string, THREE.Mesh[]> = {}
17
+
18
+ instanceCollect: Record<string, THREE.InstancedMesh> = {}
19
+
20
+ static matrix = new THREE.Matrix4()
21
+
22
+ constructor(group: THREE.Group, count: number) {
23
+ super()
24
+
25
+ this.count = count
26
+
27
+ const { meshCollect, instanceCollect } = this
28
+
29
+ group.updateMatrixWorld(true);
30
+
31
+ // collect all mesh counts, in case there are more than one copy of the same mesh in this group
32
+ group.traverse((obj) => {
33
+ const mesh = obj as THREE.Mesh
34
+ if (mesh.isMesh) {
35
+
36
+ mesh.matrixWorld.decompose(mesh.position, mesh.quaternion, mesh.scale);
37
+ mesh.rotation.setFromQuaternion(mesh.quaternion);
38
+ mesh.updateMatrix();
39
+
40
+ // same geometry and same material consider as the same mesh
41
+ const uuid = mesh.geometry.uuid + '/' + (mesh.material as THREE.Material).uuid
42
+
43
+ if (meshCollect[uuid]) {
44
+ meshCollect[uuid].push(mesh)
45
+ } else {
46
+ meshCollect[uuid] = [mesh]
47
+ }
48
+ } else {
49
+ obj.position.set(0, 0, 0);
50
+ obj.scale.set(1, 1, 1);
51
+ obj.rotation.set(0, 0, 0);
52
+ }
53
+
54
+ })
55
+
56
+ // create all instances
57
+ Object.keys(meshCollect).forEach(uuid => {
58
+ const meshes = meshCollect[uuid]
59
+ const mesh = meshes[0]
60
+
61
+ const instancedMesh = new THREE.InstancedMesh(mesh.geometry, mesh.material, meshes.length * count)
62
+ instanceCollect[uuid] = instancedMesh
63
+ this.add(instancedMesh)
64
+ })
65
+ }
66
+
67
+ setMatrixAt(index: number, matrix: THREE.Matrix4) {
68
+ Object.keys(this.meshCollect).forEach(uuid => {
69
+ const instancedMesh = this.instanceCollect[uuid]
70
+ const collect = this.meshCollect[uuid]
71
+ collect.forEach((mesh: THREE.Mesh, i: number) => {
72
+ InstancedGroupMesh.matrix.copy(mesh.matrix)
73
+ InstancedGroupMesh.matrix.premultiply(matrix)
74
+
75
+ instancedMesh.setMatrixAt(collect.length * index + i, InstancedGroupMesh.matrix);
76
+ instancedMesh.instanceMatrix.needsUpdate = true;
77
+ })
78
+ })
79
+ }
80
+ }
@@ -0,0 +1,122 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2023-02-16 00:22:31
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2023-02-16 19:08:55
6
+ */
7
+
8
+ import { last } from "lodash";
9
+ import { BufferAttribute, Euler, Line, LineBasicMaterial, Object3D, Vector3 } from "three";
10
+ import threeUtils from "./threeUtils";
11
+
12
+
13
+ // 关键帧类型
14
+ export type Keyframe = {
15
+ position: Vector3;
16
+ scale: Vector3;
17
+ rotation: Euler;
18
+ timeTemp: number; //时间节点 单位毫秒.
19
+ }
20
+
21
+
22
+ export type KeyframeAnimationProps = {
23
+ speedScale?: number; // 速度调整
24
+ object3d: Object3D; // 需要移动的对象
25
+ keyframes: Array<Keyframe>,
26
+ }
27
+
28
+ // 关键帧动画对象
29
+ export default class KeyframeAnimation extends Object3D {
30
+
31
+ constructor(props: KeyframeAnimationProps) {
32
+ super();
33
+
34
+ // 速度调整
35
+ this.speedScale = props.speedScale || 1;
36
+
37
+ // 设置关键帧对象数组
38
+ this.setKeyframes(props.keyframes);
39
+
40
+
41
+ // 设置对象
42
+ this.setObject3D(props.object3d);
43
+
44
+ }
45
+
46
+ // 速度调整
47
+ speedScale = 1;
48
+
49
+ // 需要需要移动的对象
50
+ object3d!: Object3D;
51
+
52
+ //路径辅助线
53
+ halperLine = new Line(undefined, new LineBasicMaterial({ color: "#f00" }));
54
+
55
+ // 关键帧数组
56
+ keyframes: Array<Keyframe> = [];
57
+
58
+ // 设置需要移动的对象
59
+ setObject3D(object3d: Object3D) {
60
+ this.object3d = object3d;
61
+ if (threeUtils.isDev) {
62
+ this.children = [object3d, this.halperLine];
63
+ } else {
64
+ this.children = [object3d];
65
+ }
66
+ }
67
+
68
+ // 获取单个关键帧
69
+ getKeyframe(i: number) {
70
+ return this.keyframes[i];
71
+ }
72
+
73
+ // 设置单个关键帧
74
+ setKeyframe(i: number, keyframe: Keyframe) {
75
+ this.keyframes[i] = keyframe;
76
+ this.setKeyframes(this.keyframes);
77
+ }
78
+
79
+ //设置全部关键帧
80
+ setKeyframes(keyframes: Array<Keyframe>) {
81
+ this.keyframes = keyframes;
82
+ const position: Array<number> = [];
83
+ keyframes.map(k => {
84
+ position.push(k.position.x);
85
+ position.push(k.position.y);
86
+ position.push(k.position.z);
87
+ });
88
+ this.halperLine.geometry.setAttribute("position", new BufferAttribute(new Float32Array(position), 3, false));
89
+ }
90
+
91
+ // 更具关键帧的信息更新 对象的位置
92
+ update() {
93
+ const { _timeTemp } = threeUtils;
94
+ const maxtimetemp = last(this.keyframes)!.timeTemp;
95
+ const currentTime = _timeTemp % maxtimetemp;
96
+
97
+ const currentKeyframeIndex = this.keyframes.findIndex(k => currentTime < k.timeTemp);
98
+
99
+ const startKeyframe = this.keyframes[currentKeyframeIndex - 1];
100
+ const endKeyframe = this.keyframes[currentKeyframeIndex];
101
+ const progress = (currentTime - startKeyframe.timeTemp) / (endKeyframe.timeTemp - startKeyframe.timeTemp);
102
+
103
+ this.object3d.position.copy(endKeyframe.position).sub(startKeyframe.position).multiplyScalar(progress).add(startKeyframe.position);
104
+ this.object3d.scale.copy(endKeyframe.scale).sub(startKeyframe.scale).multiplyScalar(progress).add(startKeyframe.scale);
105
+
106
+ this.object3d.rotation.set(
107
+ (endKeyframe.rotation.x - startKeyframe.rotation.x) * progress + startKeyframe.rotation.x,
108
+ (endKeyframe.rotation.y - startKeyframe.rotation.y) * progress + startKeyframe.rotation.y,
109
+ (endKeyframe.rotation.z - startKeyframe.rotation.z) * progress + startKeyframe.rotation.z,
110
+ );
111
+
112
+ this.dispatchEvent({ type: "update" });
113
+ }
114
+
115
+ //更新关键帧的实例矩阵
116
+ updateMatrix(): void {
117
+ this.update();
118
+ super.updateMatrix();
119
+ }
120
+
121
+
122
+ }
@@ -0,0 +1,59 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-05-23 13:53:06
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2022-05-23 19:38:24
6
+ */
7
+
8
+ import { DoubleSide, Mesh, MeshBasicMaterial, PlaneGeometry, Vector3 } from "three";
9
+ import SpriteText from "three-spritetext";
10
+
11
+
12
+
13
+ type Props = {
14
+ text?: string;
15
+ textHeight?: number;
16
+ color?: string;
17
+ backgroundColor?: string;
18
+ fontFace?: string;
19
+ fontSize?: number;
20
+ fontWeight?: string;
21
+ padding?: number | [number, number];
22
+ borderWidth?: number;
23
+ borderRadius?: number;
24
+ borderColor?: string;
25
+ strokeWidth?: number;
26
+ strokeColor?: string;
27
+ }
28
+
29
+ export default class PlaneText extends Mesh {
30
+
31
+ constructor(text: string = "planeText") {
32
+ super(PlaneText.geometry);
33
+ this.set({ text });
34
+ }
35
+
36
+ type = "PlaneText";
37
+
38
+ static geometry = new PlaneGeometry(1, 1, 1, 1);
39
+
40
+ spriteText = new SpriteText("");
41
+
42
+ material = new MeshBasicMaterial({ transparent: true, side: DoubleSide, map: this.spriteText.material.map });
43
+
44
+ set(parame: Props) {
45
+ for (let key in parame) {
46
+ //@ts-ignore
47
+ this.spriteText[key] = parame[key];
48
+ }
49
+
50
+ this.scale.copy(this.spriteText.scale);
51
+ this.scale.max(new Vector3(1, 1, 1));
52
+ this.material.map = this.spriteText.material.map;
53
+ }
54
+
55
+ get(prop: keyof SpriteText) {
56
+ return this.spriteText[prop];
57
+ }
58
+
59
+ }
@@ -0,0 +1,78 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-05-18 16:02:55
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2022-06-23 21:02:45
6
+ */
7
+
8
+ import { Mesh, MeshBasicMaterial, PointLight as THREEPointLight, SphereGeometry } from "three";
9
+ import threeSpritetext from "three-spritetext";
10
+ import threeUtils from "./threeUtils";
11
+
12
+
13
+ /**
14
+ * 点光源
15
+ * @export
16
+ * @class PointLight
17
+ * @extends {THREEPointLight}
18
+ */
19
+ export default class PointLight extends THREEPointLight {
20
+
21
+ constructor(color = "#fff", intensity?: number, distance?: number, decay?: number) {
22
+ super(color, intensity, distance, decay);
23
+
24
+ if (threeUtils.isDev) {
25
+ if (this.label) {
26
+ this.label.borderColor = color;
27
+ this.label.color = color;
28
+ this.label.scale.multiplyScalar(0.03);
29
+ }
30
+
31
+ }
32
+
33
+ }
34
+
35
+ sphere = threeUtils.isDev && (() => {
36
+ // 点光源辅助小球
37
+ const sphere = new SphereGeometry(0.1, 10, 10);
38
+ const materils = new MeshBasicMaterial({
39
+ color: this.color,
40
+ wireframe: true
41
+ });
42
+ const mesh = new Mesh(sphere, materils);
43
+
44
+ mesh.userData = {
45
+ cursor: "pointer"
46
+ };
47
+
48
+ mesh.addEventListener("mouseenter", () => {
49
+ if (this.label) {
50
+ this.label.visible = true;
51
+ }
52
+ });
53
+
54
+ mesh.addEventListener("mouseleave", () => {
55
+ if (this.label) {
56
+ this.label.visible = false;
57
+ }
58
+ });
59
+
60
+ this.add(mesh);
61
+ })();
62
+
63
+ label = threeUtils.isDev && (() => {
64
+ const label = new threeSpritetext("点光源");
65
+ label.renderOrder = Infinity;
66
+ label.backgroundColor = "rgba(0, 0, 0, 0.5)";
67
+ label.padding = 4;
68
+ label.borderRadius = 4;
69
+ label.borderWidth = 1;
70
+ label.position.set(0, 0.5, 0);
71
+ label.visible = false;
72
+ label.material.depthTest = false;
73
+ label.scale.multiplyScalar(0.001);
74
+ this.add(label);
75
+ return label;
76
+ })();
77
+
78
+ }
package/lib/Sky/Sky.ts ADDED
@@ -0,0 +1,36 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-05-20 18:03:58
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2022-12-27 09:54:23
6
+ */
7
+
8
+ import { DoubleSide, Mesh, MeshBasicMaterial, SphereGeometry } from "three";
9
+ import threeLoader from "../threeLoader";
10
+
11
+ import blueskyJPG from "./blueSky.jpg";
12
+
13
+ /**
14
+ * 天空球
15
+ */
16
+ export default class Sky extends Mesh {
17
+
18
+ constructor() {
19
+ super(Sky.geometry);
20
+ }
21
+
22
+ static geometry = new SphereGeometry(500000, 100, 100);
23
+
24
+ material = new MeshBasicMaterial({
25
+ side: DoubleSide,
26
+ map: threeLoader.getTexture(blueskyJPG)
27
+ });
28
+
29
+ /**
30
+ *设置贴图
31
+ */
32
+ setMap(src: string) {
33
+ this.material.map = threeLoader.getTexture(src);
34
+ }
35
+
36
+ }
@@ -0,0 +1,91 @@
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2022-06-14 09:48:00
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2023-04-04 19:26:36
6
+ */
7
+
8
+ import { Mesh, MeshStandardMaterial, TextGeometry } from "three";
9
+ import fontContorller from "./fontController";
10
+
11
+
12
+
13
+ export type Text3DProps = {
14
+ text?: string,
15
+ fontType?: string,
16
+ size?: number | undefined;
17
+ height?: number | undefined;
18
+ curveSegments?: number | undefined;
19
+ bevelEnabled?: boolean | undefined;
20
+ bevelThickness?: number | undefined;
21
+ bevelSize?: number | undefined;
22
+ bevelOffset?: number | undefined;
23
+ bevelSegments?: number | undefined;
24
+ }
25
+
26
+
27
+ // 3D文字模型
28
+ export default class Text3D extends Mesh {
29
+
30
+ constructor(text: string = "new Text3D()") {
31
+ super();
32
+ this.setText({ text });
33
+ }
34
+
35
+ // 字体的存储路径 例如 : "http://drm.gitee.io/three-base-font/simhei/字.js"
36
+ static path = "http://drm.gitee.io/three-base-font/";
37
+
38
+
39
+ material = new MeshStandardMaterial({
40
+ metalness: 0.8, // 金属度,值为0到1
41
+ roughness: 0.5, // 粗糙度,值为0到1
42
+ });
43
+
44
+ // 配置
45
+ _config: Text3DProps = {
46
+ text: "",
47
+ fontType: "simhei",
48
+ size: 1,
49
+ curveSegments: 10,
50
+ height: 0.1, // 文字的厚度
51
+ bevelEnabled: true, // 斜角开启
52
+ bevelThickness: 0.02, // 斜角厚度
53
+ bevelSize: 0.015, // 斜角大小
54
+ bevelOffset: 0,// 斜角偏移
55
+ bevelSegments: 3,//斜角分段
56
+ }
57
+
58
+ // 设置文字
59
+ setText(text3DProps: Text3DProps) {
60
+
61
+ if (Object.keys(text3DProps).some(key => text3DProps[key as keyof typeof text3DProps] !== this._config[key as keyof typeof this._config])) {
62
+
63
+ Object.assign(this._config, text3DProps);
64
+
65
+ fontContorller.loadText({
66
+ fontBasePath: Text3D.path,
67
+ text: this._config.text,
68
+ fontType: this._config.fontType,
69
+ callback: THREEFont => {
70
+ this.geometry.dispose();
71
+ this.geometry = new TextGeometry(this._config.text || "", {
72
+ font: THREEFont,
73
+ size: this._config.size,
74
+ curveSegments: this._config.curveSegments,
75
+ height: this._config.height, // 文字的厚度
76
+ bevelEnabled: this._config.bevelEnabled, // 斜角开启
77
+ bevelThickness: this._config.bevelThickness, // 斜角厚度
78
+ bevelSize: this._config.bevelSize, // 斜角大小
79
+ bevelOffset: this._config.bevelOffset,// 斜角偏移
80
+ bevelSegments: this._config.bevelSegments,//斜角分段
81
+ });
82
+ this.geometry.center();
83
+ }
84
+ })
85
+
86
+ }
87
+
88
+ }
89
+
90
+
91
+ }