Last active
November 24, 2025 01:29
-
-
Save aniongithub/961bfcc07e0d89db511be3df4131db64 to your computer and use it in GitHub Desktop.
Revisions
-
aniongithub revised this gist
Sep 6, 2023 . No changes.There are no files selected for viewing
-
aniongithub revised this gist
Sep 6, 2023 . 1 changed file with 32 additions and 15 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -11,16 +11,17 @@ }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Download this file from https://paste.c-net.org/ScreechGrubby\n", "gltf = GLTF2.load(\"assets/X Bot.glb\")" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +84,7 @@ }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -100,7 +101,7 @@ }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -110,7 +111,7 @@ }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -129,7 +130,7 @@ }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -146,7 +147,7 @@ }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -155,7 +156,7 @@ }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -170,7 +171,7 @@ }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -195,7 +196,7 @@ }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -206,7 +207,7 @@ }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -219,7 +220,7 @@ }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -231,9 +232,25 @@ }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "c8293ea23cf64061be2e4ae2b5b95b9a", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Renderer(camera=PerspectiveCamera(aspect=1.3333333333333333, position=(10.0, 6.0, 10.0), projectionMatrix=(1.0…" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "view_width = 800\n", "view_height = 600\n", -
aniongithub revised this gist
Sep 6, 2023 . No changes.There are no files selected for viewing
-
aniongithub created this gist
Sep 6, 2023 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,282 @@ { "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pygltflib import GLTF2, Accessor, Skin, Node" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "gltf = GLTF2.load(\"assets/X Bot.glb\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from enum import IntEnum\n", "import struct\n", "\n", "class GLTFComponentType(IntEnum):\n", " BYTE = 5120\n", " UNSIGNED_BYTE = 5121\n", " SHORT = 5122\n", " UNSIGNED_SHORT = 5123\n", " UNSIGNED_INT = 5125\n", " FLOAT = 5126\n", "\n", "GLTF_COMPONENTTYPE_SIZES = {\n", " GLTFComponentType.BYTE: 1,\n", " GLTFComponentType.UNSIGNED_BYTE: 1,\n", " GLTFComponentType.SHORT: 2,\n", " GLTFComponentType.UNSIGNED_SHORT: 2,\n", " GLTFComponentType.UNSIGNED_INT: 4,\n", " GLTFComponentType.FLOAT: 4\n", "}\n", "\n", "GLTF_ACCESSORTYPE_COUNTS = {\n", " \"SCALAR\": 1,\n", " \"VEC2\": 2,\n", " \"VEC3\": 3,\n", " \"VEC4\": 4,\n", " \"MAT2\": 4,\n", " \"MAT3\": 9,\n", " \"MAT4\": 16\n", "}\n", "\n", "GLTF_COMPONENT_UNPACK_FORMATS = {\n", " GLTFComponentType.BYTE: \"b\",\n", " GLTFComponentType.UNSIGNED_BYTE: \"B\",\n", " GLTFComponentType.SHORT: \"h\",\n", " GLTFComponentType.UNSIGNED_SHORT: \"H\",\n", " GLTFComponentType.UNSIGNED_INT: \"I\",\n", " GLTFComponentType.FLOAT: \"f\"\n", "}\n", "\n", "def get_dense_data(gltf: GLTF2, accessor: Accessor):\n", " bufferView = gltf.bufferViews[accessor.bufferView]\n", " buffer = gltf.buffers[bufferView.buffer]\n", " buffer_data = gltf.get_data_from_buffer_uri(buffer.uri)\n", " result = []\n", " elem_stride = GLTF_ACCESSORTYPE_COUNTS[accessor.type] * GLTF_COMPONENTTYPE_SIZES[int(accessor.componentType)]\n", " for i in range(accessor.count):\n", " index = bufferView.byteOffset + accessor.byteOffset + i * elem_stride\n", " base64_elem_data = buffer_data[index:index + elem_stride]\n", " elem_data = struct.unpack(f\"<{GLTF_COMPONENT_UNPACK_FORMATS[accessor.componentType] * GLTF_ACCESSORTYPE_COUNTS[accessor.type]}\", base64_elem_data)\n", " if len(elem_data) == 1:\n", " result.append(elem_data[0])\n", " else:\n", " result.append(elem_data)\n", " \n", " return result" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "primitive = gltf.meshes[gltf.scenes[gltf.scene].nodes[0]].primitives[0]\n", "\n", "indices = get_dense_data(gltf, gltf.accessors[primitive.indices])\n", "positions = get_dense_data(gltf, gltf.accessors[primitive.attributes.POSITION])\n", "normals = get_dense_data(gltf, gltf.accessors[primitive.attributes.NORMAL])\n", "texcoords = get_dense_data(gltf, gltf.accessors[primitive.attributes.TEXCOORD_0])\n", "joints = get_dense_data(gltf, gltf.accessors[primitive.attributes.JOINTS_0])\n", "joint_weights = get_dense_data(gltf, gltf.accessors[primitive.attributes.WEIGHTS_0])\n", "inverse_bind_matrices = get_dense_data(gltf, gltf.accessors[gltf.skins[0].inverseBindMatrices])\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pythreejs import *\n", "from IPython.display import display" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def rgba_to_html_color(rgba_tuple):\n", " # Convert each RGBA value to its corresponding 8-bit integer representation\n", " r = int(rgba_tuple[0] * 255)\n", " g = int(rgba_tuple[1] * 255)\n", " b = int(rgba_tuple[2] * 255)\n", " a = int(rgba_tuple[3] * 255)\n", "\n", " # Format the values as a hexadecimal color string\n", " color_string = \"#{:02X}{:02X}{:02X}{:02X}\".format(r, g, b, a)\n", "\n", " return color_string" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def create_material(material: Material):\n", " result = MeshPhongMaterial(\n", " color = rgba_to_html_color(material.pbrMetallicRoughness.baseColorFactor),\n", " metallicFactor = material.pbrMetallicRoughness.metallicFactor,\n", " roughnessFactor = material.pbrMetallicRoughness.roughnessFactor,\n", " emissiveFactor = material.emissiveFactor,\n", " skinning = True)\n", "\n", " return result" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mesh_material = create_material(gltf.materials[0])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mesh_geometry = BufferGeometry()\n", "mesh_geometry.attributes[\"position\"] = BufferAttribute(positions)\n", "mesh_geometry.attributes[\"index\"] = BufferAttribute(indices)\n", "mesh_geometry.attributes[\"uv\"] = BufferAttribute(texcoords)\n", "mesh_geometry.attributes[\"normal\"] = BufferAttribute(normals)\n", "mesh_geometry.attributes[\"skinIndex\"] = BufferAttribute(joints)\n", "mesh_geometry.attributes[\"skinWeight\"] = BufferAttribute(joint_weights)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from typing import List\n", "\n", "def create_skeleton(gltf: GLTF2, skin: Skin, bones: List[Bone]):\n", " def create_bone(gltf: GLTF2, node: Node):\n", " bone = Bone(name = node.name, \n", " position = node.translation, \n", " scale = node.scale, \n", " quaternion = node.rotation)\n", " if bones is not None:\n", " bones.insert(0, bone)\n", " for child_node in node.children:\n", " child_bone = create_bone(gltf, gltf.nodes[child_node])\n", " bone.add(child_bone)\n", "\n", " return bone\n", "\n", " return create_bone(gltf, gltf.nodes[skin.skeleton])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bones = []\n", "root_bone = create_skeleton(gltf, gltf.skins[0], bones)\n", "skeleton = Skeleton(bones = bones, boneInverses = inverse_bind_matrices)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "mesh = SkinnedMesh(mesh_geometry, mesh_material)\n", "mesh.add(root_bone)\n", "mesh.skeleton = skeleton\n", "\n", "helper = SkeletonHelper(mesh)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sphere = Mesh(\n", " SphereBufferGeometry(1, 32, 16),\n", " MeshStandardMaterial(color='red')\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "view_width = 800\n", "view_height = 600\n", "\n", "key_light = DirectionalLight(position=[0, 10, 10], intensity=0.6)\n", "ambient_light = AmbientLight()\n", "\n", "camera = PerspectiveCamera(position=[10, 6, 10], aspect=view_width/view_height)\n", "\n", "# This line doesn't work, draws a small white rectangle\n", "scene_elems = [camera, key_light, ambient_light, mesh, helper]\n", "\n", "# This line works, draws a red sphere\n", "# scene_elems = [camera, key_light, ambient_light, sphere]\n", "\n", "scene = Scene(children = scene_elems)\n", "renderer = Renderer(camera=camera, scene=scene,\n", " controls=[OrbitControls(controlling=camera)],\n", " width=view_width, height=view_height)\n", "renderer" ] } ], "metadata": { "kernelspec": { "display_name": "base", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }