antvis / antvis/G6

[Feat]: 请问g6的拖拽库可以自定义3d节点吗;比如我将glb格式的文件通过three转换为所需要的信息;然后通过g6内置渲染来渲染

Open
#7,330 3 comments 0 reactions 0 assignees View on GitHub
question 💬
Dominant language
TypeScript
Stars
12.3k
Forks
1.6k
PR merge metrics
No merged PRs in 30d

Description

### Describe the feature / 功能描述

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
export function convertThreeJsToG6Data(geometry) {
// 获取 Three.js 中的几何数据
const positions = geometry.attributes.position.array;
const normals = geometry.attributes.normal ? geometry.attributes.normal.array : [];
const uvs = geometry.attributes.uv ? geometry.attributes.uv.array : [];
const indices = geometry.index ? geometry.index.array : [];

// 初始化 G6 所需的数据结构
const g6Data = {
positions: [],
normals: [],
uvs: [],
uv1s: [], // G6 需要 uv1s,用于特殊的 UV 处理
indices: [],
};

// 转换 positions 数据:每 3 个值为一个顶点的坐标 (x, y, z)
for (let i = 0; i < positions.length; i += 3) {
g6Data.positions.push(positions[i], positions[i + 1], positions[i + 2]);
}

// 转换 normals 数据:每 3 个值为一个顶点的法线 (nx, ny, nz)
if (normals.length > 0) {
for (let i = 0; i < normals.length; i += 3) {
g6Data.normals.push(normals[i], normals[i + 1], normals[i + 2]);
}
}

// 转换 uvs 数据:每 2 个值为一个顶点的 UV 坐标 (u, v)
if (uvs.length > 0) {
for (let i = 0; i < uvs.length; i += 2) {
g6Data.uvs.push(uvs[i], uvs[i + 1]);
}
}

// 转换 uv1s 数据:将 UV 坐标稍微调整并存储
if (uvs.length > 0) {
for (let i = 0; i < uvs.length; i += 2) {
let u = uvs[i];
let v = uvs[i + 1];

// 这里可能需要对 UV 坐标进行一些缩放和偏移,以符合 G6 的要求
u = u / 3;
v = v / 3;

// 通过某些公式处理 u 和 v
u = u * 1.5 + 0.1; // 假设我们需要缩放和偏移
v = v * 1.5 + 0.1;

g6Data.uv1s.push(u, 1.0 - v); // G6 使用的是反转的 v 坐标
}
}

// 转换 indices 数据:每 3 个值为一个三角形的三个顶点索引
for (let i = 0; i < indices.length; i++) {
g6Data.indices.push(indices[i]);
}

return g6Data;
}

export async function loadModelData(){
// let g6Data
// 异步数据使用promise
return new Promise((resolve,reject)=>{

const loader = new GLTFLoader();
loader.load('/info/1.glb', (gltf) => {
const model = gltf.scene;

// 遍历模型中的所有 Mesh 对象
model.traverse((child) => {
if (child.isMesh) {
const geometry = child.geometry;

// 调用转换函数
let g6Data = convertThreeJsToG6Data(geometry);
resolve(g6Data)

// 输出转换后的数据
// console.log("G6 Data:", g6Data);

// 可以将 g6Data 用于 G6 3D 节点或其他用途
}
});
});
})

// console.log(g6Data)
}目前知道的香港方法是
// 内置的Cube基类
var CubeGeometry = /*#__PURE__*/function (_ProceduralGeometry) {
function CubeGeometry(device) {
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
_classCallCheck(this, CubeGeometry);
return _callSuper(this, CubeGeometry, [device, _objectSpread({
width: 1,
height: 1,
depth: 1,
widthSegments: 1,
heightSegments: 1,
depthSegments: 1
}, props)]);
}
_inherits(CubeGeometry, _ProceduralGeometry);
return _createClass(CubeGeometry, [{
key: "width",
get: function get() {
return this.props.width;
},
set: function set(v) {
if (this.props.width !== v) {
this.props.width = v;
this.rebuildPosition();
}
}
}, {
key: "height",
get: function get() {
return this.props.height;
},
set: function set(v) {
if (this.props.height !== v) {
this.props.height = v;
this.rebuildPosition();
}
}
}, {
key: "depth",
get: function get() {
return this.props.depth;
},
set: function set(v) {
if (this.props.depth !== v) {
this.props.depth = v;
this.rebuildPosition();
}
}
}, {
key: "widthSegments",
get: function get() {
return this.props.widthSegments;
},
set: function set(v) {
if (this.props.widthSegments !== v) {
this.props.widthSegments = v;
this.build();
}
}
}, {
key: "heightSegments",
get: function get() {
return this.props.heightSegments;
},
set: function set(v) {
if (this.props.heightSegments !== v) {
this.props.heightSegments = v;
this.build();
}
}
}, {
key: "depthSegments",
get: function get() {
return this.props.depthSegments;
},
set: function set(v) {
if (this.props.depthSegments !== v) {
this.props.depthSegments = v;
this.build();
}
}
}, {
key: "createTopology",
value: function createTopology() {
var _this$props = this.props,
_this$props$widthSegm = _this$props.widthSegments,
widthSegments = _this$props$widthSegm === void 0 ? 1 : _this$props$widthSegm,
_this$props$heightSeg = _this$props.heightSegments,
heightSegments = _this$props$heightSeg === void 0 ? 1 : _this$props$heightSeg,
_this$props$depthSegm = _this$props.depthSegments,
depthSegments = _this$props$depthSegm === void 0 ? 1 : _this$props$depthSegm,
_this$props$height = _this$props.height,
height = _this$props$height === void 0 ? 1 : _this$props$height,
_this$props$width = _this$props.width,
width = _this$props$width === void 0 ? 1 : _this$props$width,
_this$props$depth = _this$props.depth,
depth = _this$props$depth === void 0 ? 1 : _this$props$depth;
var ws = widthSegments;
var hs = heightSegments;
var ds = depthSegments;
var hex = width / 2;
var hey = height / 2;
var hez = depth / 2;
var corners = [glMatrix.vec3.fromValues(-hex, -hey, hez), glMatrix.vec3.fromValues(hex, -hey, hez), glMatrix.vec3.fromValues(hex, hey, hez), glMatrix.vec3.fromValues(-hex, hey, hez), glMatrix.vec3.fromValues(hex, -hey, -hez), glMatrix.vec3.fromValues(-hex, -hey, -hez), glMatrix.vec3.fromValues(-hex, hey, -hez), glMatrix.vec3.fromValues(hex, hey, -hez)];
var faceAxes = [[0, 1, 3],
// FRONT
[4, 5, 7],
// BACK
[3, 2, 6],
// TOP
[1, 0, 4],
// BOTTOM
[1, 4, 2],
// RIGHT
[5, 0, 6] // LEFT
];
var faceNormals = [[0, 0, 1],
// FRONT
[0, 0, -1],
// BACK
[0, 1, 0],
// TOP
[0, -1, 0],
// BOTTOM
[1, 0, 0],
// RIGHT
[-1, 0, 0] // LEFT
];
var sides = {
FRONT: 0,
BACK: 1,
TOP: 2,
BOTTOM: 3,
RIGHT: 4,
LEFT: 5
};
var positions = [];
var normals = [];
var uvs = [];
var uvs1 = [];
var indices = [];
var vcounter = 0;
var generateFace = function generateFace(side, uSegments, vSegments) {
var u;
var v;
var i;
var j;
for (i = 0; i <= uSegments; i++) {
for (j = 0; j <= vSegments; j++) {
var temp1 = glMatrix.vec3.create();
var temp2 = glMatrix.vec3.create();
var temp3 = glMatrix.vec3.create();
var r = glMatrix.vec3.create();
glMatrix.vec3.lerp(temp1, corners[faceAxes[side][0]], corners[faceAxes[side][1]], i / uSegments);
glMatrix.vec3.lerp(temp2, corners[faceAxes[side][0]], corners[faceAxes[side][2]], j / vSegments);
glMatrix.vec3.sub(temp3, temp2, corners[faceAxes[side][0]]);
glMatrix.vec3.add(r, temp1, temp3);
u = i / uSegments;
v = j / vSegments;
positions.push(r[0], r[1], r[2]);
normals.push(faceNormals[side][0], faceNormals[side][1], faceNormals[side][2]);
uvs.push(u, 1.0 - v);
// pack as 3x2
// 1/3 will be empty, but it's either that or stretched pixels
// TODO: generate non-rectangular lightMaps, so we could use space without stretching
u /= 3;
v /= 3;
u = u * primitiveUv1PaddingScale + primitiveUv1Padding;
v = v * primitiveUv1PaddingScale + primitiveUv1Padding;
u += side % 3 / 3;
v += Math.floor(side / 3) / 3;
uvs1.push(u, 1.0 - v);
if (i < uSegments && j < vSegments) {
indices.push(vcounter + vSegments + 1, vcounter + 1, vcounter);
indices.push(vcounter + vSegments + 1, vcounter + vSegments + 2, vcounter + 1);
}
vcounter++;
}
}
};
generateFace(sides.FRONT, ws, hs);
generateFace(sides.BACK, ws, hs);
generateFace(sides.TOP, ws, ds);
generateFace(sides.BOTTOM, ws, ds);
generateFace(sides.RIGHT, ds, hs);
generateFace(sides.LEFT, ds, hs);
return {
indices: indices,
positions: positions,
normals: normals,
uvs: uvs,
uv1s: uvs1
};
}
}]);
}(ProceduralGeometry);

let DEVICE;
const GEOMETRY_CACHE = new Map();
/**
* 创建几何体
*
* Create geometry
* 创建几何体的方法
* @param type - 几何体类型 geometry type
* @param device - 设备对象 device object
* @param Ctor - 几何体构造函数 geometry constructor
* @param style - 几何体样式 geometry style
* @returns 几何体对象 geometry object
*/
export function createGeometry(type, device, Ctor, style) {
if (!DEVICE)
DEVICE = device;
else if (DEVICE !== device) {
DEVICE = device;
GEOMETRY_CACHE.clear();
}
const cacheKey = type +
'|' +
Object.entries(style)
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}:${v}`)
.join(',');
if (GEOMETRY_CACHE.has(cacheKey)) {
return GEOMETRY_CACHE.get(cacheKey);
}
const geometry = new Ctor(device, style);
GEOMETRY_CACHE.set(cacheKey, geometry);
return geometry;
}
//# sourceMappingURL=geometry.js.map

// ****************************************************1**********************************************************

import { CubeGeometry } from '@antv/g-plugin-3d';
import { deepMix } from '@antv/util';
import { createGeometry } from '../utils/geometry';
import { BaseNode3D } from './base-node-3d';
/**
* 立方体节点
*
* Cube Node
*/
export class Cube extends BaseNode3D {
constructor(options) {
super(deepMix({}, { style: Cube.defaultStyleProps }, options));
}
getGeometry(attributes) {
const size = this.getSize();
const { width = size[0], height = size[1], depth = size[2], widthSegments, heightSegments, depthSegments, } = attributes;
return createGeometry('cube', this.device, CubeGeometry, {
width,
height,
depth,
widthSegments,
heightSegments,
depthSegments,
});
}
}
Cube.defaultStyleProps = {
widthSegments: 1,
heightSegments: 1,
depthSegments: 1,
};
//# sourceMappingURL=cube.js.map

### Are you willing to contribute? / 是否愿意参与贡献?

Please select / 请选择

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the shown BaseNode3D, Cube, CubeGeometry, and createGeometry entry points, then compare their positions, normals, uvs, uv1s, and indices data with the Three.js conversion example. Determine the supported scope for GLB-derived custom geometry and define completion around a reproducible custom 3D-node path; no test file is named in the issue.

Written by the indexing model from the issue text.

Assessment

Tech stack
three.js, typescript
Domain
computer-graphics, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.