I'm trying to register a custom geometry in A-Frame using THREE.BufferGeometry using two or more indexed groups of faces, because many vertices will be shared, and I want to change the mesh AFTER creation, WITHOUT having to move multiple points and worry about holes appearing in the model. I want several groups of faces, and I want to assign a different material to EACH GROUP.
I know it might be easier to make nested groups of entities with different materials, but I am trying to save CPU and GPU time, as well as memory.
ALL the old examples I could find online used THREE.Geometry, NOT THREE.BufferGeometry. None are working with the current versions, as THREE.Geometry has been depreciated.
I have made eight or nine variations based on the new formatting info I found in the online docs. I even tried 20+ new versions of the code with the "help" of ChatGPT.
NONE of it's code worked either.
AFRAME.registerGeometry('my-custom-geometry', {
init: function () {
// Define the vertices of the geometry
const vertices = [
// First set of five shared points
-1, 1, 0,
-1, -1, 0,
0, -1, 0,
1, -1, 0,
1, 1, 0,
// Second set of five shared points
-1, 1, 1,
-1, -1, 1,
0, -1, 1,
1, -1, 1,
1, 1, 1,
];
// Define the indices of the faces
const indices = [
// Group 1
0, 1, 2,
2, 3, 4,
0, 2, 4,
// Group 2
5, 6, 7,
7, 8, 9,
5, 7, 9,
];
// Create a buffer geometry object
const geometry = new THREE.BufferGeometry();
// Set the attributes of the buffer geometry
const positionAttribute = new THREE.Float32BufferAttribute(vertices, 3);
geometry.setAttribute('position', positionAttribute);
const indexAttribute = new THREE.Uint16BufferAttribute(indices, 1);
geometry.setIndex(indexAttribute);
// Define the groups of faces
geometry.addGroup(0, 3, 0); // Group 1, material 1
geometry.addGroup(3, 3, 1); // Group 1, material 2
geometry.addGroup(6, 3, 2); // Group 1, material 3
geometry.addGroup(9, 3, 3); // Group 2, material 4
geometry.addGroup(12, 3, 4); // Group 2, material 5
geometry.addGroup(15, 3, 5); // Group 2, material 6
// Register the geometry with A-Frame
this.geometry = geometry;
}
});
// Define the materials for the groups of faces
const materials = [
new THREE.MeshBasicMaterial({ color: 'red' }),
new THREE.MeshBasicMaterial({ color: 'green' }),
new THREE.MeshBasicMaterial({ color: 'blue' }),
new THREE.MeshBasicMaterial({ color: 'yellow' }),
new THREE.MeshBasicMaterial({ color: 'purple' }),
new THREE.MeshBasicMaterial({ color: 'orange' }),
];
// Create a mesh object with the registered geometry and materials
const mesh = new THREE.Mesh(
AFRAME.scenes[0].systems.geometry.getGeometry('my-custom-geometry'),materials);
// Add the mesh object to the scene
AFRAME.scenes[0].object3D.add(mesh);
I ALSO tried the following:
AFRAME.registerGeometry('custom-geometry', {
schema: {},
init: function () {
var geometry = new THREE.BufferGeometry();
// Define vertices and indices
var vertices = [
// Define vertices here
];
var indices = [
// Define indices here
];
// Set vertex and index data
geometry.setIndex(indices);
geometry.addAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
// Create two groups of faces
geometry.addGroup(0, 3, 0); // Group 1: 3 faces, starting at index 0
geometry.addGroup(3, 3, 1); // Group 2: 3 faces, starting at index 3
// Define materials for each group
var material1 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var material2 = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
// Create mesh and apply materials to groups
var mesh = new THREE.Mesh(geometry, [material1, material2]);
mesh.geometry.groups.forEach(function (group) {
mesh.geometry.addGroup(group.start, group.count, group.materialIndex);
});
// Add mesh to A-Frame entity
this.geometry = mesh.geometry;
}
});
The first code piece creates the groups correctly. If you want your mesh to work properly with lights, you need to:
THREE.MeshStandardMaterial
instead of the THREE.MeshBasicMaterial
.mesh.castShadow = mesh.receiveShadow = true;
Once you've done it - it should be working:
// define colors for the groups
const colors = ["red", "green", "blue", "yellow", "purple", "orange"];
// create the mesh and append it to the scene when its ready
const scene = document.querySelector("a-scene")
scene.addEventListener("loaded", () => {
const mesh = new THREE.Mesh(
scene.systems.geometry.getOrCreateGeometry({
"primitive": 'my-custom-geometry'
}),
colors.map(color => new THREE.MeshStandardMaterial({
color,
side: THREE.DoubleSide
}))
);
mesh.position.set(0, 1, -4);
mesh.rotation.set(0, Math.PI / 4, 0);
mesh.castShadow = mesh.receiveShadow = true;
scene.object3D.add(mesh);
});
// "fix" self - shadowing
const dlight = document.querySelector("#dlight");
dlight.addEventListener("loaded", () => {
dlight.object3D.children[0].shadow.bias = -0.0001;
})
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
<script>
AFRAME.registerGeometry('my-custom-geometry', {
init: function() {
// Define the vertices of the geometry
const vertices = [
// First set of five shared points
-1, 1, 0, -1, -1, 0,
0, -1, 0,
1, -1, 0,
1, 1, 0,
// Second set of five shared points
-1, 1, 1, -1, -1, 1,
0, -1, 1,
1, -1, 1,
1, 1, 1,
];
// Define the indices of the faces
const indices = [
// Group 1
0, 1, 2,
2, 3, 4,
0, 2, 4,
// Group 2
5, 6, 7,
7, 8, 9,
5, 7, 9,
];
// Create a buffer geometry object
const geometry = new THREE.BufferGeometry();
// Set the attributes of the buffer geometry
const positionAttribute = new THREE.Float32BufferAttribute(vertices, 3);
geometry.setAttribute('position', positionAttribute);
const indexAttribute = new THREE.Uint16BufferAttribute(indices, 1);
geometry.setIndex(indexAttribute);
// Define the groups of faces
geometry.addGroup(0, 3, 0); // Group 1, material 1
geometry.addGroup(3, 3, 1); // Group 1, material 2
geometry.addGroup(6, 3, 2); // Group 1, material 3
geometry.addGroup(9, 3, 3); // Group 2, material 4
geometry.addGroup(12, 3, 4); // Group 2, material 5
geometry.addGroup(15, 3, 5); // Group 2, material 6
geometry.computeVertexNormals(); // compute normals
// Register the geometry with A-Frame
this.geometry = geometry;
}
});
</script>
<a-scene renderer="colorManagement: true">
<a-entity position="-2 1.5 -2" animation="property: position; to: 2 1.5 -2; dir: alternate; loop: true; dur: 1500; easing: linear">
<a-sphere radius="0.25" color="yellow" position="0 0 0">></a-sphere>
<a-light type="point" cast-shadow="true" distance="3"></a-light>
</a-entity>
<a-entity id="dlight" light="type:directional; castShadow:true; intensity: 0.2" position="1 1 1"></a-entity>
<a-plane position="0 0 -4" rotation="-90 0 0" width="4" height="4" color="#7BC8A4" shadow="cast: true; receive: true"></a-plane>
<a-sky color="#ECECEC"></a-sky>
</a-scene>