refactor: Start on cleanup and polish
This commit is contained in:
1 parent
77796e8761
commit
075a9d597a
7 files changed
+344
-838
No files matched your search
@@ -1 +1,2 @@
|
||||
**/dist
|
||||
*.gltf
|
||||
@@ -1,641 +1 @@
|
||||
{
|
||||
"asset": { "version": "2.0", "generator": "Blockbench 4.12.4 glTF exporter" },
|
||||
"scenes": [{ "nodes": [18], "name": "blockbench_export" }],
|
||||
"scene": 0,
|
||||
"nodes": [
|
||||
{ "translation": [0, -1.5, 0], "name": "Head", "mesh": 0 },
|
||||
{ "translation": [0, -1.5, 0], "name": "Hat Layer", "mesh": 1 },
|
||||
{
|
||||
"rotation": [
|
||||
0.05228614400929896, -0.043559608510925024, 0.002282862348496242, 0.997679060715675
|
||||
],
|
||||
"translation": [0, 1.5, 0],
|
||||
"name": "Head",
|
||||
"children": [0, 1]
|
||||
},
|
||||
{ "translation": [0, -1.5, 0], "name": "Body", "mesh": 2 },
|
||||
{ "translation": [0, -1.5, 0], "name": "Body Layer", "mesh": 3 },
|
||||
{ "translation": [0, 1.5, 0], "name": "Body", "children": [3, 4] },
|
||||
{ "translation": [-0.3125, -1.375, 0], "name": "Right Arm", "mesh": 4 },
|
||||
{ "translation": [-0.3125, -1.375, 0], "name": "Right Arm Layer", "mesh": 5 },
|
||||
{
|
||||
"rotation": [0.08715574274765817, 0, 0, 0.9961946980917455],
|
||||
"translation": [0.3125, 1.375, 0],
|
||||
"name": "Right Arm",
|
||||
"children": [6, 7]
|
||||
},
|
||||
{ "translation": [0.3125, -1.375, 0], "name": "Left Arm", "mesh": 6 },
|
||||
{ "translation": [0.3125, -1.375, 0], "name": "Left Arm Layer", "mesh": 7 },
|
||||
{
|
||||
"rotation": [-0.10452846326765346, 0, 0, 0.9945218953682733],
|
||||
"translation": [-0.3125, 1.375, 0],
|
||||
"name": "Left Arm",
|
||||
"children": [9, 10]
|
||||
},
|
||||
{ "translation": [-0.11875, -0.75, 0], "name": "Right Leg", "mesh": 8 },
|
||||
{ "translation": [-0.11875, -0.75, 0], "name": "Right Leg Layer", "mesh": 9 },
|
||||
{
|
||||
"rotation": [
|
||||
-0.09583115474787583, -0.0016727390282702394, 0.01737205902003089, 0.9952445947048578
|
||||
],
|
||||
"translation": [0.11875, 0.75, 0],
|
||||
"name": "Right Leg",
|
||||
"children": [12, 13]
|
||||
},
|
||||
{ "translation": [0.11875, -0.75, 0], "name": "Left Leg", "mesh": 10 },
|
||||
{ "translation": [0.11875, -0.75, 0], "name": "Left Leg Layer", "mesh": 11 },
|
||||
{
|
||||
"rotation": [
|
||||
0.08714246850588939, -0.0015210774457754552, -0.017385994761764084, 0.9960429728140489
|
||||
],
|
||||
"translation": [-0.11875, 0.75, 0],
|
||||
"name": "Left Leg",
|
||||
"children": [15, 16]
|
||||
},
|
||||
{ "children": [2, 5, 8, 11, 14, 17] }
|
||||
],
|
||||
"bufferViews": [
|
||||
{ "buffer": 0, "byteOffset": 0, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 288, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 576, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 768, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 840, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 1128, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 1416, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 1608, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 1680, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 1968, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 2256, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 2448, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 2520, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 2808, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 3096, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 3288, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 3360, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 3648, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 3936, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 4128, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 4200, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 4488, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 4776, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 4968, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 5040, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 5328, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 5616, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 5808, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 5880, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 6168, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 6456, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 6648, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 6720, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 7008, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 7296, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 7488, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 7560, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 7848, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 8136, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 8328, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 8400, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 8688, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 8976, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 9168, "byteLength": 72, "target": 34963 },
|
||||
{ "buffer": 0, "byteOffset": 9240, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 9528, "byteLength": 288, "target": 34962, "byteStride": 12 },
|
||||
{ "buffer": 0, "byteOffset": 9816, "byteLength": 192, "target": 34962, "byteStride": 8 },
|
||||
{ "buffer": 0, "byteOffset": 10008, "byteLength": 72, "target": 34963 }
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 10080,
|
||||
"uri": "data:application/octet-stream;base64,AACAPgAAAEAAAIA+AACAPgAAAEAAAIC+AACAPgAAwD8AAIA+AACAPgAAwD8AAIC+AACAvgAAAEAAAIC+AACAvgAAAEAAAIA+AACAvgAAwD8AAIC+AACAvgAAwD8AAIA+AACAvgAAAEAAAIC+AACAPgAAAEAAAIC+AACAvgAAAEAAAIA+AACAPgAAAEAAAIA+AACAvgAAwD8AAIA+AACAPgAAwD8AAIA+AACAvgAAwD8AAIC+AACAPgAAwD8AAIC+AACAvgAAAEAAAIA+AACAPgAAAEAAAIA+AACAvgAAwD8AAIA+AACAPgAAwD8AAIA+AACAPgAAAEAAAIC+AACAvgAAAEAAAIC+AACAPgAAwD8AAIC+AACAvgAAwD8AAIC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAOQBAAD4AgP89AEAAPgAAgDkAwH8+AID/PQDAfz4AIIA+AEAAPgDgvz4AQAA+ACCAPgDAfz4A4L8+AMB/PgDAfz4AgP89AEAAPgCA/z0AwH8+AACAOQBAAD4AAIA5AOC/PgAAgDkAIIA+AACAOQDgvz4AgP89ACCAPgCA/z0AIMA+AEAAPgDg/z4AQAA+ACDAPgDAfz4A4P8+AMB/PgBAAD4AQAA+AMB/PgBAAD4AQAA+AMB/PgDAfz4AwH8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACQPgAAAkAAAJA+AACQPgAAAkAAAJC+AACQPgAAvD8AAJA+AACQPgAAvD8AAJC+AACQvgAAAkAAAJC+AACQvgAAAkAAAJA+AACQvgAAvD8AAJC+AACQvgAAvD8AAJA+AACQvgAAAkAAAJC+AACQPgAAAkAAAJC+AACQvgAAAkAAAJA+AACQPgAAAkAAAJA+AACQvgAAvD8AAJA+AACQPgAAvD8AAJA+AACQvgAAvD8AAJC+AACQPgAAvD8AAJC+AACQvgAAAkAAAJA+AACQPgAAAkAAAJA+AACQvgAAvD8AAJA+AACQPgAAvD8AAJA+AACQPgAAAkAAAJC+AACQvgAAAkAAAJC+AACQPgAAvD8AAJC+AACQvgAAvD8AAJC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABAAPwBAAD4A8B8/AEAAPgAQAD8AwH8+APAfPwDAfz4AEEA/AEAAPgDwXz8AQAA+ABBAPwDAfz4A8F8/AMB/PgDwPz8AgP89ABAgPwCA/z0A8D8/AACAOQAQID8AAIA5APBfPwAAgDkAEEA/AACAOQDwXz8AgP89ABBAPwCA/z0AEGA/AEAAPgDwfz8AQAA+ABBgPwDAfz4A8H8/AMB/PgAQID8AQAA+APA/PwBAAD4AECA/AMB/PgDwPz8AwH8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACAPgAAwD8AAAA+AACAPgAAwD8AAAC+AACAPgAAQD8AAAA+AACAPgAAQD8AAAC+AACAvgAAwD8AAAC+AACAvgAAwD8AAAA+AACAvgAAQD8AAAC+AACAvgAAQD8AAAA+AACAvgAAwD8AAAC+AACAPgAAwD8AAAC+AACAvgAAwD8AAAA+AACAPgAAwD8AAAA+AACAvgAAQD8AAAA+AACAPgAAQD8AAAA+AACAvgAAQD8AAAC+AACAPgAAQD8AAAC+AACAvgAAwD8AAAA+AACAPgAAwD8AAAA+AACAvgAAQD8AAAA+AACAPgAAQD8AAAA+AACAPgAAwD8AAAC+AACAvgAAwD8AAAC+AACAPgAAQD8AAAC+AACAvgAAQD8AAAC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ACCAPgAgoD4A4J8+ACCgPgAggD4A4P8+AOCfPgDg/z4AIOA+ACCgPgDg/z4AIKA+ACDgPgDg/z4A4P8+AOD/PgDg3z4A4J8+ACCgPgDgnz4A4N8+ACCAPgAgoD4AIIA+APAPPwAggD4AIOA+ACCAPgDwDz8A4J8+ACDgPgDgnz4AEAA/ACCgPgDwHz8AIKA+ABAAPwDg/z4A8B8/AOD/PgAgoD4AIKA+AODfPgAgoD4AIKA+AOD/PgDg3z4A4P8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACIPgAAwj8AABA+AACIPgAAwj8AABC+AACIPgAAPD8AABA+AACIPgAAPD8AABC+AACIvgAAwj8AABC+AACIvgAAwj8AABA+AACIvgAAPD8AABC+AACIvgAAPD8AABA+AACIvgAAwj8AABC+AACIPgAAwj8AABC+AACIvgAAwj8AABA+AACIPgAAwj8AABA+AACIvgAAPD8AABA+AACIPgAAPD8AABA+AACIvgAAPD8AABC+AACIPgAAPD8AABC+AACIvgAAwj8AABA+AACIPgAAwj8AABA+AACIvgAAPD8AABA+AACIPgAAPD8AABA+AACIPgAAwj8AABC+AACIvgAAwj8AABC+AACIPgAAPD8AABC+AACIvgAAPD8AABC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ACCAPgAQED8A4J8+ABAQPwAggD4A8D8/AOCfPgDwPz8AIOA+ABAQPwDg/z4AEBA/ACDgPgDwPz8A4P8+APA/PwDg3z4A8A8/ACCgPgDwDz8A4N8+ABAAPwAgoD4AEAA/APAPPwAQAD8AIOA+ABAAPwDwDz8A8A8/ACDgPgDwDz8AEAA/ABAQPwDwHz8AEBA/ABAAPwDwPz8A8B8/APA/PwAgoD4AEBA/AODfPgAQED8AIKA+APA/PwDg3z4A8D8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAADgPgAAwD8AAAA+AADgPgAAwD8AAAC+AADgPgAAQD8AAAA+AADgPgAAQD8AAAC+AACAPgAAwD8AAAC+AACAPgAAwD8AAAA+AACAPgAAQD8AAAC+AACAPgAAQD8AAAA+AACAPgAAwD8AAAC+AADgPgAAwD8AAAC+AACAPgAAwD8AAAA+AADgPgAAwD8AAAA+AACAPgAAQD8AAAA+AADgPgAAQD8AAAA+AACAPgAAQD8AAAC+AADgPgAAQD8AAAC+AACAPgAAwD8AAAA+AADgPgAAwD8AAAA+AACAPgAAQD8AAAA+AADgPgAAQD8AAAA+AADgPgAAwD8AAAC+AACAPgAAwD8AAAC+AADgPgAAQD8AAAC+AACAPgAAQD8AAAC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACALine truncated
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.25, 2, 0.25],
|
||||
"min": [-0.25, 1.5, -0.25],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.499755859375, 0.249755859375],
|
||||
"min": [0.000244140625, 0.000244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 4,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.28125, 2.03125, 0.28125],
|
||||
"min": [-0.28125, 1.46875, -0.28125],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 5,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 6,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.999755859375, 0.249755859375],
|
||||
"min": [0.500244140625, 0.000244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 7,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 8,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.25, 1.5, 0.125],
|
||||
"min": [-0.25, 0.75, -0.125],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 9,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 10,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.624755859375, 0.499755859375],
|
||||
"min": [0.250244140625, 0.250244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 11,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 12,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.265625, 1.515625, 0.140625],
|
||||
"min": [-0.265625, 0.734375, -0.140625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 13,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 14,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.624755859375, 0.749755859375],
|
||||
"min": [0.250244140625, 0.500244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 15,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 16,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.4375, 1.5, 0.125],
|
||||
"min": [0.25, 0.75, -0.125],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 17,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 18,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.843505859375, 0.499755859375],
|
||||
"min": [0.625244140625, 0.250244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 19,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 20,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.453125, 1.515625, 0.140625],
|
||||
"min": [0.234375, 0.734375, -0.140625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 21,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 22,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.843505859375, 0.749755859375],
|
||||
"min": [0.625244140625, 0.500244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 23,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 24,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [-0.25, 1.5, 0.125],
|
||||
"min": [-0.4375, 0.75, -0.125],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 25,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 26,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.718505859375, 0.999755859375],
|
||||
"min": [0.500244140625, 0.750244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 27,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 28,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [-0.234375, 1.515625, 0.140625],
|
||||
"min": [-0.453125, 0.734375, -0.140625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 29,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 30,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.968505859375, 0.999755859375],
|
||||
"min": [0.750244140625, 0.750244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 31,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 32,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.24375000596046448, 0.75, 0.125],
|
||||
"min": [-0.0062500000931322575, 0, -0.125],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 33,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 34,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.249755859375, 0.499755859375],
|
||||
"min": [0.000244140625, 0.250244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 35,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 36,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.2593750059604645, 0.765625, 0.140625],
|
||||
"min": [-0.02187499962747097, -0.015625, -0.140625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 37,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 38,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.249755859375, 0.749755859375],
|
||||
"min": [0.000244140625, 0.500244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 39,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 40,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.0062500000931322575, 0.75, 0.125],
|
||||
"min": [-0.24375000596046448, 0, -0.125],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 41,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 42,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.499755859375, 0.999755859375],
|
||||
"min": [0.250244140625, 0.750244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 43,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 44,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.02187499962747097, 0.765625, 0.140625],
|
||||
"min": [-0.2593750059604645, -0.015625, -0.140625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 45,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [1, 1, 1],
|
||||
"min": [-1, -1, -1],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 46,
|
||||
"componentType": 5126,
|
||||
"count": 24,
|
||||
"max": [0.249755859375, 0.999755859375],
|
||||
"min": [0.000244140625, 0.750244140625],
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 47,
|
||||
"componentType": 5123,
|
||||
"count": 36,
|
||||
"max": [23],
|
||||
"min": [0],
|
||||
"type": "SCALAR"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"metallicFactor": 0,
|
||||
"roughnessFactor": 1,
|
||||
"baseColorTexture": { "index": 0 }
|
||||
},
|
||||
"alphaMode": "MASK",
|
||||
"alphaCutoff": 0.05,
|
||||
"doubleSided": true
|
||||
}
|
||||
],
|
||||
"textures": [{ "sampler": 0, "source": 0 }],
|
||||
"samplers": [{ "magFilter": 9728, "minFilter": 9728, "wrapS": 33071, "wrapT": 33071 }],
|
||||
"images": [
|
||||
{
|
||||
"mimeType": "image/png",
|
||||
"uri": "data:image/png;base64,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"
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 0, "NORMAL": 1, "TEXCOORD_0": 2 },
|
||||
"indices": 3,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 4, "NORMAL": 5, "TEXCOORD_0": 6 },
|
||||
"indices": 7,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 8, "NORMAL": 9, "TEXCOORD_0": 10 },
|
||||
"indices": 11,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 12, "NORMAL": 13, "TEXCOORD_0": 14 },
|
||||
"indices": 15,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 16, "NORMAL": 17, "TEXCOORD_0": 18 },
|
||||
"indices": 19,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 20, "NORMAL": 21, "TEXCOORD_0": 22 },
|
||||
"indices": 23,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 24, "NORMAL": 25, "TEXCOORD_0": 26 },
|
||||
"indices": 27,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 28, "NORMAL": 29, "TEXCOORD_0": 30 },
|
||||
"indices": 31,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 32, "NORMAL": 33, "TEXCOORD_0": 34 },
|
||||
"indices": 35,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 36, "NORMAL": 37, "TEXCOORD_0": 38 },
|
||||
"indices": 39,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 40, "NORMAL": 41, "TEXCOORD_0": 42 },
|
||||
"indices": 43,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"mode": 4,
|
||||
"attributes": { "POSITION": 44, "NORMAL": 45, "TEXCOORD_0": 46 },
|
||||
"indices": 47,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
{"asset":{"version":"2.0","generator":"Blockbench 4.12.4 glTF exporter"},"scenes":[{"nodes":[18],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[0,-1.5,0],"name":"Head","mesh":0},{"translation":[0,-1.5,0],"name":"Hat Layer","mesh":1},{"rotation":[0.05228614400929896,-0.043559608510925024,0.002282862348496242,0.997679060715675],"translation":[0,1.5,0],"name":"Head","children":[0,1]},{"translation":[0,-1.5,0],"name":"Body","mesh":2},{"translation":[0,-1.5,0],"name":"Body Layer","mesh":3},{"translation":[0,1.5,0],"name":"Body","children":[3,4]},{"translation":[-0.3125,-1.375,0],"name":"Right Arm","mesh":4},{"translation":[-0.3125,-1.375,0],"name":"Right Arm Layer","mesh":5},{"rotation":[0.08715574274765817,0,0,0.9961946980917455],"translation":[0.3125,1.375,0],"name":"Right Arm","children":[6,7]},{"translation":[0.3125,-1.375,0],"name":"Left Arm","mesh":6},{"translation":[0.3125,-1.375,0],"name":"Left Arm Layer","mesh":7},{"rotation":[-0.10452846326765346,0,0,0.9945218953682733],"translation":[-0.3125,1.375,0],"name":"Left Arm","children":[9,10]},{"translation":[-0.11875,-0.75,0],"name":"Right Leg","mesh":8},{"translation":[-0.11875,-0.75,0],"name":"Right Leg Layer","mesh":9},{"rotation":[-0.09583115474787583,-0.0016727390282702394,0.01737205902003089,0.9952445947048578],"translation":[0.11875,0.75,0],"name":"Right Leg","children":[12,13]},{"translation":[0.11875,-0.75,0],"name":"Left Leg","mesh":10},{"translation":[0.11875,-0.75,0],"name":"Left Leg Layer","mesh":11},{"rotation":[0.08714246850588939,-0.0015210774457754552,-0.017385994761764084,0.9960429728140489],"translation":[-0.11875,0.75,0],"name":"Left Leg","children":[15,16]},{"children":[2,5,8,11,14,17]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":2520,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2808,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3096,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":3288,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":3360,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3648,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3936,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4128,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":4200,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4488,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4776,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4968,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5040,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5328,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5616,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":5808,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5880,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6168,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6456,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":6648,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":6720,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7008,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7296,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":7488,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":7560,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7848,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8136,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":8328,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":8400,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8688,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8976,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":9168,"byteLength":72,"target":34963},{"buffer"Line truncated
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ModalWrapper ref="modal" @on-modal-hide="resetState">
|
||||
<ModalWrapper ref="modal" @on-hide="resetState">
|
||||
<template #title>
|
||||
<span class="text-lg font-extrabold text-contrast">
|
||||
{{ mode === 'edit' ? 'Edit skin' : 'New skin' }}
|
||||
@@ -12,10 +12,12 @@
|
||||
<SkinPreviewRenderer
|
||||
slim-model-src="/src/assets/models/slim_player.gltf"
|
||||
wide-model-src="/src/assets/models/classic_player.gltf"
|
||||
cape-model-src="/src/assets/models/cape.gltf"
|
||||
:variant="variant"
|
||||
:texture-src="previewSkin"
|
||||
:scale="1.4"
|
||||
:fov="50"
|
||||
:cape-src="selectedCapeTexture"
|
||||
:scale="1.4" :fov="50"
|
||||
:initial-rotation="Math.PI / 8"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
@@ -35,19 +37,17 @@
|
||||
<UploadIcon aria-hidden="true" />
|
||||
</FileInput>
|
||||
|
||||
<div v-if="fileName || mode === 'new'" class="flex items-center gap-2">
|
||||
<div class="flex items-center gap-2" v-if="fileName || mode === 'new'">
|
||||
<InfoIcon v-if="!fileName" class="text-brand-blue" />
|
||||
<CheckCircleIcon v-else class="text-brand-green" />
|
||||
<small v-tooltip="fileName" class="truncate">{{
|
||||
fileName || 'No skin uploaded yet.'
|
||||
}}</small>
|
||||
<small class="truncate" v-tooltip="fileName">{{ fileName || 'No skin uploaded yet.' }}</small>
|
||||
</div>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2 class="text-base font-semibold mb-2">Arm style</h2>
|
||||
<RadioButtons v-model="variant" :items="['CLASSIC', 'SLIM']">
|
||||
<RadioButtons v-model="variant" :items="['CLASSIC','SLIM']">
|
||||
<template #default="{ item }">
|
||||
{{ item === 'CLASSIC' ? 'Wide' : 'Slim' }}
|
||||
</template>
|
||||
@@ -57,21 +57,33 @@
|
||||
<section>
|
||||
<h2 class="text-base font-semibold mb-2">Cape</h2>
|
||||
<div class="flex gap-2">
|
||||
<CapeLikeTextButton
|
||||
tooltip="No Cape"
|
||||
:highlighted="!selectedCape"
|
||||
@click="selectCape(undefined)"
|
||||
>
|
||||
<template #icon>
|
||||
<XIcon />
|
||||
</template>
|
||||
<span>No Cape</span>
|
||||
</CapeLikeTextButton>
|
||||
|
||||
<CapeButton
|
||||
v-for="cape in firstThreeCapes"
|
||||
:id="cape.id"
|
||||
v-for="cape in visibleCapeList"
|
||||
:key="cape.id"
|
||||
:id="cape.id"
|
||||
:texture="cape.texture"
|
||||
:name="cape.name || 'Cape'"
|
||||
:selected="selectedCape?.id === cape.id"
|
||||
@select="selectCape(cape)"
|
||||
/>
|
||||
|
||||
<CapeLikeTextButton
|
||||
v-if="capes?.length ?? 0 > 3"
|
||||
v-if="(capes?.length ?? 0) > 2"
|
||||
tooltip="View more capes"
|
||||
@click="viewMoreCapes"
|
||||
@mouseup="openSelectCapeModal"
|
||||
>
|
||||
<template #icon><ChevronRightIcon /></template>
|
||||
<template #icon><ChevronRightIcon/></template>
|
||||
<span>More</span>
|
||||
</CapeLikeTextButton>
|
||||
</div>
|
||||
@@ -81,62 +93,51 @@
|
||||
|
||||
<div class="flex gap-2 mt-6">
|
||||
<ButtonStyled color="brand" :disabled="disableSave">
|
||||
<Button
|
||||
v-tooltip="disableSave && 'Upload a skin first!'"
|
||||
:disabled="disableSave"
|
||||
@click="save"
|
||||
>
|
||||
<Button @click="save" :disabled="disableSave" v-tooltip="disableSave && 'Upload a skin first!'">
|
||||
<CheckIcon v-if="mode === 'new'" /><SaveIcon v-else />
|
||||
{{ mode === 'new' ? 'Add skin' : 'Save skin' }}
|
||||
</Button>
|
||||
</ButtonStyled>
|
||||
<Button @click="hide"><XIcon />Cancel</Button>
|
||||
<ButtonStyled v-if="mode === 'edit'" color="red">
|
||||
<Button @click="handleDelete"><TrashIcon />Delete</Button>
|
||||
<Button @click="hide"><XIcon/>Cancel</Button>
|
||||
<ButtonStyled color="red" v-if="mode==='edit'">
|
||||
<Button @click="handleDelete"><TrashIcon/>Delete</Button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
|
||||
<SelectCapeModal ref="selectCapeModal" :capes="capes || []" @select="handleCapeSelected" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, useTemplateRef } from 'vue'
|
||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
|
||||
import {
|
||||
Card,
|
||||
FileInput,
|
||||
SkinPreviewRenderer,
|
||||
Button,
|
||||
RadioButtons,
|
||||
CapeButton,
|
||||
CapeLikeTextButton,
|
||||
ButtonStyled,
|
||||
Card, FileInput, SkinPreviewRenderer,
|
||||
Button, RadioButtons, CapeButton, CapeLikeTextButton, ButtonStyled
|
||||
} from '@modrinth/ui'
|
||||
import {
|
||||
add_and_equip_custom_skin,
|
||||
remove_custom_skin,
|
||||
equip_skin,
|
||||
unequip_skin,
|
||||
type Skin,
|
||||
type Cape,
|
||||
type SkinModel,
|
||||
get_available_skins,
|
||||
type Skin, type Cape, type SkinModel
|
||||
} from '@/helpers/skins.ts'
|
||||
import { handleError } from '@/store/notifications'
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
InfoIcon,
|
||||
UploadIcon,
|
||||
CheckIcon,
|
||||
SaveIcon,
|
||||
XIcon,
|
||||
TrashIcon,
|
||||
ChevronRightIcon,
|
||||
CheckCircleIcon, InfoIcon, UploadIcon,
|
||||
CheckIcon, SaveIcon, XIcon, TrashIcon, ChevronRightIcon
|
||||
} from '@modrinth/assets'
|
||||
|
||||
const modal = useTemplateRef('modal')
|
||||
const mode = ref<'new' | 'edit'>('new')
|
||||
const currentSkin = ref<Skin | null>(null)
|
||||
const selectCapeModal = useTemplateRef('selectCapeModal')
|
||||
const mode = ref<'new'|'edit'>('new')
|
||||
const currentSkin = ref<Skin|null>(null)
|
||||
const shouldRestoreModal = ref(false)
|
||||
|
||||
const fileUploadTextureBlob = ref<Uint8Array | null>(null)
|
||||
const fileName = ref<string | null>(null)
|
||||
const fileUploadTextureBlob = ref<Uint8Array|null>(null)
|
||||
const fileName = ref<string|null>(null)
|
||||
watch(fileUploadTextureBlob, () => {
|
||||
if (fileName.value === null && fileUploadTextureBlob.value) {
|
||||
fileName.value = 'New upload'
|
||||
@@ -144,11 +145,54 @@ watch(fileUploadTextureBlob, () => {
|
||||
})
|
||||
|
||||
const variant = ref<SkinModel>('CLASSIC')
|
||||
const selectedCape = ref<Cape | undefined>(undefined)
|
||||
const selectedCape = ref<Cape|undefined>(undefined)
|
||||
const props = defineProps<{ capes?: Cape[] }>()
|
||||
const firstThreeCapes = computed(() => props.capes?.slice(0, 3) ?? [])
|
||||
|
||||
const localPreviewUrl = ref<string | null>(null)
|
||||
const selectedCapeTexture = computed(() => selectedCape.value?.texture)
|
||||
const visibleCapeList = ref<Cape[]>([])
|
||||
|
||||
function initVisibleCapeList() {
|
||||
if (!props.capes || props.capes.length === 0) {
|
||||
visibleCapeList.value = []
|
||||
return
|
||||
}
|
||||
|
||||
if (visibleCapeList.value.length === 0) {
|
||||
if (selectedCape.value) {
|
||||
const otherCape = getRandomCapeExcluding(selectedCape.value.id)
|
||||
visibleCapeList.value = otherCape ? [selectedCape.value, otherCape] : [selectedCape.value]
|
||||
} else {
|
||||
visibleCapeList.value = getRandomCapes(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomCapes(count: number): Cape[] {
|
||||
if (!props.capes || props.capes.length === 0) return []
|
||||
|
||||
const availableCapes = [...props.capes]
|
||||
const result: Cape[] = []
|
||||
|
||||
for (let i = 0; i < count && availableCapes.length > 0; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * availableCapes.length)
|
||||
const cape = availableCapes.splice(randomIndex, 1)[0]
|
||||
result.push(cape)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function getRandomCapeExcluding(excludeId: string): Cape | undefined {
|
||||
if (!props.capes || props.capes.length <= 1) return undefined
|
||||
|
||||
const availableCapes = props.capes.filter(cape => cape.id !== excludeId)
|
||||
if (availableCapes.length === 0) return undefined
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * availableCapes.length)
|
||||
return availableCapes[randomIndex]
|
||||
}
|
||||
|
||||
const localPreviewUrl = ref<string|null>(null)
|
||||
watch(fileUploadTextureBlob, (blob, prev) => {
|
||||
if (prev && localPreviewUrl.value) URL.revokeObjectURL(localPreviewUrl.value)
|
||||
if (blob) localPreviewUrl.value = URL.createObjectURL(new Blob([blob]))
|
||||
@@ -160,7 +204,9 @@ const previewSkin = computed(() => {
|
||||
return '/src/assets/skins/steve.png'
|
||||
})
|
||||
|
||||
const disableSave = computed(() => mode.value === 'new' && !fileUploadTextureBlob.value)
|
||||
const disableSave = computed(() =>
|
||||
mode.value==='new' && !fileUploadTextureBlob.value
|
||||
)
|
||||
|
||||
function resetState() {
|
||||
mode.value = 'new'
|
||||
@@ -169,6 +215,8 @@ function resetState() {
|
||||
fileName.value = null
|
||||
variant.value = 'CLASSIC'
|
||||
selectedCape.value = undefined
|
||||
visibleCapeList.value = []
|
||||
shouldRestoreModal.value = false
|
||||
if (localPreviewUrl.value) {
|
||||
URL.revokeObjectURL(localPreviewUrl.value)
|
||||
localPreviewUrl.value = null
|
||||
@@ -180,11 +228,13 @@ function show(e: MouseEvent, skin?: Skin) {
|
||||
currentSkin.value = skin ?? null
|
||||
if (skin) {
|
||||
variant.value = skin.variant
|
||||
selectedCape.value = props.capes?.find((c) => c.id === skin.cape_id)
|
||||
selectedCape.value = props.capes?.find(c=>c.id===skin.cape_id)
|
||||
} else {
|
||||
variant.value = 'CLASSIC'
|
||||
selectedCape.value = undefined
|
||||
}
|
||||
visibleCapeList.value = []
|
||||
initVisibleCapeList()
|
||||
modal.value?.show(e)
|
||||
}
|
||||
|
||||
@@ -193,7 +243,7 @@ function hide() {
|
||||
resetState()
|
||||
}
|
||||
|
||||
async function onTextureSelected(files: FileList | null) {
|
||||
async function onTextureSelected(files: FileList|null) {
|
||||
if (!files?.length) return
|
||||
const file = files[0]
|
||||
fileName.value = file.name
|
||||
@@ -201,11 +251,44 @@ async function onTextureSelected(files: FileList | null) {
|
||||
fileUploadTextureBlob.value = new Uint8Array(buf)
|
||||
}
|
||||
|
||||
function selectCape(c: Cape | undefined) {
|
||||
selectedCape.value = c
|
||||
function changeVariant(v: SkinModel){ variant.value = v }
|
||||
|
||||
function selectCape(cape: Cape|undefined) {
|
||||
if (cape && selectedCape.value?.id !== cape.id) {
|
||||
const isInVisibleList = visibleCapeList.value.some(c => c.id === cape.id)
|
||||
if (!isInVisibleList && visibleCapeList.value.length > 0) {
|
||||
visibleCapeList.value.splice(0, 1, cape)
|
||||
}
|
||||
}
|
||||
selectedCape.value = cape
|
||||
}
|
||||
function viewMoreCapes() {
|
||||
// TODO: later
|
||||
|
||||
function handleCapeSelected(cape: Cape | undefined) {
|
||||
selectCape(cape)
|
||||
|
||||
if (shouldRestoreModal.value) {
|
||||
setTimeout(() => {
|
||||
modal.value?.show()
|
||||
shouldRestoreModal.value = false
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
function openSelectCapeModal(e: MouseEvent) {
|
||||
if (!selectCapeModal.value) return
|
||||
|
||||
shouldRestoreModal.value = true
|
||||
modal.value?.hide()
|
||||
|
||||
setTimeout(() => {
|
||||
selectCapeModal.value?.show(
|
||||
e,
|
||||
currentSkin.value?.texture_key,
|
||||
selectedCape.value,
|
||||
previewSkin.value,
|
||||
variant.value
|
||||
)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
async function handleDelete() {
|
||||
@@ -233,7 +316,7 @@ async function save() {
|
||||
blob = new Uint8Array(buf)
|
||||
}
|
||||
|
||||
await unequip_skin()
|
||||
await unequip_skin();
|
||||
|
||||
if (mode.value === 'new') {
|
||||
await add_and_equip_custom_skin(blob, variant.value, selectedCape.value)
|
||||
@@ -250,8 +333,13 @@ async function save() {
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.capes, () => {
|
||||
initVisibleCapeList()
|
||||
}, { immediate: true })
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'saved' | 'deleted'): void
|
||||
(e:'saved'):void
|
||||
(e:'deleted', skin: Skin):void
|
||||
}>()
|
||||
|
||||
defineExpose({ show, hide })
|
||||
|
||||
@@ -63,7 +63,9 @@ defineExpose({
|
||||
<template>
|
||||
<ModalWrapper ref="modal">
|
||||
<template #title>
|
||||
<span class="text-lg font-extrabold text-contrast">Selecting a cape</span>
|
||||
<div class="flex flex-col">
|
||||
<span class="text-lg font-extrabold text-heading">Change cape</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex flex-col md:flex-row gap-6">
|
||||
<div class="max-h-[25rem] h-[25rem] w-[16rem] min-w-[16rem] overflow-hidden relative">
|
||||
@@ -78,7 +80,7 @@ defineExpose({
|
||||
:variant="currentSkinVariant"
|
||||
:scale="1.4"
|
||||
:fov="50"
|
||||
:initial-rotation="180"
|
||||
:initial-rotation="Math.PI + (Math.PI / 8)"
|
||||
class="h-full w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { UpdatedIcon, PlusIcon } from '@modrinth/assets'
|
||||
import {UpdatedIcon, PlusIcon, ExcitedRinthbot} from '@modrinth/assets'
|
||||
import { ButtonStyled, SkinPreviewRenderer, SkinButton, SkinLikeTextButton } from '@modrinth/ui'
|
||||
import { ref, computed, useTemplateRef, watch } from 'vue'
|
||||
import { ref, computed, useTemplateRef, watch, onMounted, onUnmounted } from 'vue'
|
||||
import EditSkinModal from '@/components/ui/skin/EditSkinModal.vue'
|
||||
import SelectCapeModal from '@/components/ui/skin/SelectCapeModal.vue'
|
||||
import { handleError } from '@/store/notifications'
|
||||
@@ -18,6 +18,7 @@ import type { Cape, Skin } from '@/helpers/skins.ts'
|
||||
import { get_default_user, users } from '@/helpers/auth'
|
||||
import type { RenderResult } from '@/helpers/rendering/batchSkinRenderer.ts'
|
||||
import { generateSkinPreviews, map } from '@/helpers/rendering/batchSkinRenderer.ts'
|
||||
import { WavingRinthbot } from '@modrinth/assets'
|
||||
|
||||
const editSkinModal = useTemplateRef('editSkinModal')
|
||||
const selectCapeModal = useTemplateRef('selectCapeModal')
|
||||
@@ -25,17 +26,14 @@ const selectCapeModal = useTemplateRef('selectCapeModal')
|
||||
const settings = ref(await getSettings())
|
||||
const skins = ref<Skin[]>([])
|
||||
const capes = ref<Cape[]>([])
|
||||
const username = ref<string | undefined>(undefined)
|
||||
const currentUser = ref(undefined)
|
||||
const currentUserId = ref<string | undefined>(undefined)
|
||||
|
||||
// @ts-ignore
|
||||
const username = computed(() => currentUser.value?.profile?.name ?? undefined)
|
||||
const selectedSkin = ref<Skin | null>(null)
|
||||
const defaultCape = ref<Cape>()
|
||||
|
||||
const forceSkinRefreshKey = ref(0)
|
||||
|
||||
const previewSkin = computed(() =>
|
||||
selectedSkin.value ? `https://vzge.me/processedskin/${selectedSkin.value.texture_key}.png` : '',
|
||||
)
|
||||
|
||||
const savedSkins = computed(() => filterSavedSkins(skins.value))
|
||||
const defaultSkins = computed(() => filterDefaultSkins(skins.value))
|
||||
|
||||
@@ -46,11 +44,39 @@ const currentCape = computed(() => {
|
||||
return overrideCape
|
||||
}
|
||||
}
|
||||
|
||||
return defaultCape.value
|
||||
})
|
||||
|
||||
await Promise.all([loadCapes(), loadSkins(), loadUsername()])
|
||||
const skinTexture = computed(() => selectedSkin.value?.texture ?? '')
|
||||
const capeTexture = computed(() => currentCape.value?.texture)
|
||||
const skinVariant = computed(() => selectedSkin.value?.variant)
|
||||
const skinNametag = computed(() => settings.value.hide_nametag_skins_page ? undefined : username.value)
|
||||
|
||||
await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
|
||||
let userCheckInterval: number | null = null
|
||||
|
||||
onMounted(() => {
|
||||
// TODO: Surely a better way to do this?
|
||||
userCheckInterval = window.setInterval(checkUserChanges, 250)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (userCheckInterval !== null) {
|
||||
window.clearInterval(userCheckInterval)
|
||||
}
|
||||
})
|
||||
|
||||
async function checkUserChanges() {
|
||||
try {
|
||||
const defaultId = await get_default_user()
|
||||
if (defaultId !== currentUserId.value) {
|
||||
await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
}
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCapes() {
|
||||
capes.value = (await get_available_capes().catch(handleError)) ?? []
|
||||
@@ -60,13 +86,7 @@ async function loadCapes() {
|
||||
async function loadSkins() {
|
||||
skins.value = (await get_available_skins().catch(handleError)) ?? []
|
||||
generateSkinPreviews(skins.value, capes.value)
|
||||
|
||||
const previousSkinId = selectedSkin.value?.texture_key
|
||||
selectedSkin.value = skins.value.find((s) => s.is_equipped) ?? null
|
||||
|
||||
if (previousSkinId && selectedSkin.value?.texture_key === previousSkinId) {
|
||||
forceSkinRefreshKey.value++
|
||||
}
|
||||
}
|
||||
|
||||
async function changeSkin(newSkin: Skin) {
|
||||
@@ -84,15 +104,18 @@ async function onSkinSaved() {
|
||||
await Promise.all([loadCapes(), loadSkins()])
|
||||
}
|
||||
|
||||
async function loadUsername() {
|
||||
async function loadCurrentUser() {
|
||||
try {
|
||||
const defaultId = await get_default_user()
|
||||
currentUserId.value = defaultId
|
||||
|
||||
const allAccounts = await users()
|
||||
const current = allAccounts.find((acc) => acc.profile.id === defaultId)
|
||||
username.value = current?.profile?.name ?? undefined
|
||||
// @ts-ignore
|
||||
currentUser.value = allAccounts.find((acc) => acc.profile.id === defaultId)
|
||||
} catch (e) {
|
||||
handleError(e)
|
||||
username.value = undefined
|
||||
currentUser.value = undefined
|
||||
currentUserId.value = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,9 +126,7 @@ function getBakedSkinTextures(skin: Skin): RenderResult | undefined {
|
||||
|
||||
watch(
|
||||
() => selectedSkin.value?.cape_id,
|
||||
() => {
|
||||
forceSkinRefreshKey.value++
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -117,21 +138,28 @@ watch(
|
||||
@deleted="() => loadSkins()"
|
||||
/>
|
||||
<SelectCapeModal ref="selectCapeModal" :capes="capes" @select="handleCapeSelected" />
|
||||
<div class="p-6 grid grid-cols-[300px_1fr] xl:grid-cols-[3fr_5fr] gap-6">
|
||||
|
||||
<div v-if="currentUser" class="p-6 grid grid-cols-[300px_1fr] xl:grid-cols-[3fr_5fr] gap-6">
|
||||
<div class="sticky top-6 self-start">
|
||||
<div class="flex justify-between gap-4">
|
||||
<h1 class="m-0 text-2xl font-bold">Skins</h1>
|
||||
<ButtonStyled :disabled="!!selectedSkin?.cape_id">
|
||||
<button
|
||||
v-tooltip="
|
||||
selectedSkin?.cape_id
|
||||
? 'The equipped skin is overriding the default cape.'
|
||||
: undefined
|
||||
"
|
||||
selectedSkin?.cape_id
|
||||
? 'The equipped skin is overriding the default cape.'
|
||||
: undefined
|
||||
"
|
||||
:disabled="!!selectedSkin?.cape_id"
|
||||
@click="
|
||||
(e: MouseEvent) => selectCapeModal?.show(e, selectedSkin?.texture_key, currentCape)
|
||||
"
|
||||
(e: MouseEvent) => selectCapeModal?.show(
|
||||
e,
|
||||
selectedSkin?.texture_key,
|
||||
currentCape,
|
||||
skinTexture,
|
||||
skinVariant
|
||||
)
|
||||
"
|
||||
>
|
||||
<UpdatedIcon />
|
||||
Change cape
|
||||
@@ -140,14 +168,14 @@ watch(
|
||||
</div>
|
||||
<div class="h-[80vh] flex items-center justify-center">
|
||||
<SkinPreviewRenderer
|
||||
:key="forceSkinRefreshKey"
|
||||
wide-model-src="/src/assets/models/classic_player.gltf"
|
||||
slim-model-src="/src/assets/models/slim_player.gltf"
|
||||
cape-model-src="/src/assets/models/cape.gltf"
|
||||
:cape-src="currentCape?.texture"
|
||||
:texture-src="previewSkin"
|
||||
:variant="selectedSkin?.variant"
|
||||
:nametag="settings.hide_nametag_skins_page ? undefined : username"
|
||||
:cape-src="capeTexture"
|
||||
:texture-src="skinTexture"
|
||||
:variant="skinVariant"
|
||||
:nametag="skinNametag"
|
||||
:initial-rotation="Math.PI / 8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -199,4 +227,18 @@ watch(
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="flex items-center justify-center min-h-[50vh] p-6 pt-28">
|
||||
<div class="bg-bg-raised rounded-lg p-7 flex flex-col gap-5 shadow-md relative max-w-xl w-full mx-auto">
|
||||
<img :src="ExcitedRinthbot" alt="Excited Modrinth Bot" class="absolute -top-28 right-8 md:right-20 h-28 w-auto" />
|
||||
<div class="absolute top-0 left-0 w-full h-[1px] opacity-40 bg-gradient-to-r from-transparent via-green-500 to-transparent" style="background: linear-gradient(to right, transparent 2rem, var(--color-green) calc(100% - 13rem), var(--color-green) calc(100% - 5rem), transparent calc(100% - 2rem))"></div>
|
||||
|
||||
<div class="flex flex-col gap-5">
|
||||
<h1 class="text-3xl font-extrabold m-0">Login Required</h1>
|
||||
<p class="text-lg m-0">
|
||||
Please log into your account to use the skin management features of the Modrinth app.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -13,14 +13,14 @@
|
||||
shadows
|
||||
alpha
|
||||
:antialias="antialias"
|
||||
:renderer-options="{
|
||||
outputColorSpace: THREE.SRGBColorSpace,
|
||||
toneMapping: THREE.NoToneMapping
|
||||
}"
|
||||
@pointerdown="onPointerDown"
|
||||
@pointermove="onPointerMove"
|
||||
@pointerup="onPointerUp"
|
||||
@pointerleave="onPointerUp"
|
||||
:rendererOptions="{
|
||||
outputColorSpace: THREE.SRGBColorSpace,
|
||||
toneMapping: THREE.NoToneMapping
|
||||
}"
|
||||
>
|
||||
<Suspense>
|
||||
<Group>
|
||||
@@ -55,7 +55,7 @@
|
||||
</Suspense>
|
||||
|
||||
<TresPerspectiveCamera
|
||||
:make-default="true"
|
||||
:makeDefault="true"
|
||||
:fov="fov"
|
||||
:position="[0, 1.5, -3.25]"
|
||||
:look-at="target"
|
||||
@@ -70,7 +70,7 @@
|
||||
import * as THREE from 'three'
|
||||
import { useGLTF } from '@tresjs/cientos'
|
||||
import { useTexture, TresCanvas } from '@tresjs/core'
|
||||
import { shallowRef, ref, computed, watch, markRaw } from 'vue'
|
||||
import { shallowRef, ref, computed, watch, markRaw, onBeforeMount } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -106,11 +106,33 @@ const capeScene = shallowRef<THREE.Object3D | null>(null)
|
||||
const bodyNode = shallowRef<THREE.Object3D | null>(null)
|
||||
const capeAttached = ref(false)
|
||||
const lastCapeSrc = ref<string | undefined>(undefined)
|
||||
const texture = shallowRef<THREE.Texture | null>(null)
|
||||
const capeTexture = shallowRef<THREE.Texture | null>(null)
|
||||
const transparentTexture = createTransparentTexture()
|
||||
|
||||
function createTransparentTexture(): THREE.Texture {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = canvas.height = 1
|
||||
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
|
||||
ctx.clearRect(0, 0, 1, 1)
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas)
|
||||
texture.needsUpdate = true
|
||||
texture.colorSpace = THREE.SRGBColorSpace
|
||||
texture.flipY = false
|
||||
texture.magFilter = THREE.NearestFilter
|
||||
texture.minFilter = THREE.NearestFilter
|
||||
|
||||
return texture
|
||||
}
|
||||
|
||||
async function loadModel(src: string) {
|
||||
const { scene: loadedScene } = await useGLTF(src)
|
||||
scene.value = markRaw(loadedScene)
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
|
||||
if (texture.value) {
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
}
|
||||
|
||||
bodyNode.value = null
|
||||
loadedScene.traverse(node => {
|
||||
@@ -121,7 +143,7 @@ async function loadModel(src: string) {
|
||||
|
||||
capeAttached.value = false
|
||||
|
||||
if (capeScene.value && bodyNode.value && props.capeSrc) {
|
||||
if (capeScene.value && bodyNode.value) {
|
||||
attachCapeToBody()
|
||||
}
|
||||
|
||||
@@ -137,14 +159,45 @@ async function loadCape(src: string) {
|
||||
const { scene: loadedCape } = await useGLTF(src)
|
||||
capeScene.value = markRaw(loadedCape)
|
||||
|
||||
if (props.capeSrc) {
|
||||
const tex = await useTexture([props.capeSrc])
|
||||
capeTexture.value = tex
|
||||
applyCapeTexture(capeScene.value, tex)
|
||||
applyCapeTexture(capeScene.value, capeTexture.value)
|
||||
|
||||
if (bodyNode.value && !capeAttached.value) {
|
||||
attachCapeToBody()
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAndApplyTexture(src: string) {
|
||||
if (!src) return null
|
||||
const tex = await useTexture([src])
|
||||
tex.colorSpace = THREE.SRGBColorSpace
|
||||
tex.flipY = false
|
||||
tex.magFilter = THREE.NearestFilter
|
||||
tex.minFilter = THREE.NearestFilter
|
||||
return tex
|
||||
}
|
||||
|
||||
async function loadAndApplyCapeTexture(src: string | undefined) {
|
||||
if (src === lastCapeSrc.value) return
|
||||
|
||||
lastCapeSrc.value = src
|
||||
|
||||
if (src) {
|
||||
capeTexture.value = await loadAndApplyTexture(src)
|
||||
} else {
|
||||
capeTexture.value = null
|
||||
}
|
||||
|
||||
if (bodyNode.value && capeScene.value && !capeAttached.value) {
|
||||
attachCapeToBody()
|
||||
if (capeScene.value) {
|
||||
applyCapeTexture(capeScene.value, capeTexture.value)
|
||||
}
|
||||
|
||||
if (capeScene.value && bodyNode.value) {
|
||||
if (!src && capeAttached.value && capeScene.value.parent) {
|
||||
capeScene.value.parent.remove(capeScene.value)
|
||||
capeAttached.value = false
|
||||
} else if (src && !capeAttached.value) {
|
||||
attachCapeToBody()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,106 +215,49 @@ function attachCapeToBody() {
|
||||
capeAttached.value = true
|
||||
}
|
||||
|
||||
watch(selectedModelSrc, src => loadModel(src), { immediate: true })
|
||||
function applyTextureToScene(root: THREE.Object3D | null, tex: THREE.Texture | null) {
|
||||
if (!root || !tex) return
|
||||
|
||||
watch(
|
||||
() => props.capeModelSrc,
|
||||
src => src && loadCape(src),
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const texture = ref<THREE.Texture>(await useTexture([props.textureSrc]))
|
||||
const capeTexture = shallowRef<THREE.Texture | null>(null)
|
||||
|
||||
watch(
|
||||
() => props.textureSrc,
|
||||
async newSrc => {
|
||||
texture.value = await useTexture([newSrc])
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.capeSrc,
|
||||
async newCapeSrc => {
|
||||
|
||||
if (newCapeSrc === lastCapeSrc.value) return;
|
||||
|
||||
lastCapeSrc.value = newCapeSrc;
|
||||
|
||||
if (newCapeSrc) {
|
||||
const newTexture = await useTexture([newCapeSrc]);
|
||||
capeTexture.value = newTexture;
|
||||
|
||||
if (capeScene.value && bodyNode.value && !capeAttached.value) {
|
||||
attachCapeToBody();
|
||||
}
|
||||
|
||||
if (capeScene.value) {
|
||||
applyCapeTexture(capeScene.value, newTexture);
|
||||
}
|
||||
} else {
|
||||
capeTexture.value = null;
|
||||
|
||||
if (capeAttached.value && capeScene.value && capeScene.value.parent) {
|
||||
capeScene.value.parent.remove(capeScene.value);
|
||||
capeAttached.value = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
function applyTextureToScene(root: THREE.Object3D | null, tex: THREE.Texture) {
|
||||
if (!root) return
|
||||
tex.colorSpace = THREE.SRGBColorSpace
|
||||
tex.flipY = false
|
||||
tex.magFilter = THREE.NearestFilter
|
||||
tex.minFilter = THREE.NearestFilter
|
||||
root.traverse(child => {
|
||||
if ((child as THREE.Mesh).isMesh) {
|
||||
const mesh = child as THREE.Mesh
|
||||
|
||||
if (mesh.name == "Cape") return;
|
||||
if (mesh.name === "Cape") return
|
||||
|
||||
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
||||
// @ts-ignore
|
||||
materials.forEach((mat: THREE.MeshStandardMaterial) => {
|
||||
mat.map = tex
|
||||
mat.metalness = 0
|
||||
mat.color.set(0xffffff)
|
||||
mat.toneMapped = false
|
||||
mat.roughness = 1
|
||||
mat.needsUpdate = true
|
||||
|
||||
materials.forEach((mat, index, array) => {
|
||||
const standardMat = mat as THREE.MeshStandardMaterial
|
||||
standardMat.map = tex
|
||||
standardMat.metalness = 0
|
||||
standardMat.color.set(0xffffff)
|
||||
standardMat.toneMapped = false
|
||||
standardMat.roughness = 1
|
||||
standardMat.needsUpdate = true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function applyCapeTexture(root: THREE.Object3D | null, tex: THREE.Texture | null) {
|
||||
if (!root || !tex) return
|
||||
|
||||
// Force texture settings
|
||||
tex.colorSpace = THREE.SRGBColorSpace
|
||||
tex.flipY = false
|
||||
tex.magFilter = THREE.NearestFilter
|
||||
tex.minFilter = THREE.NearestFilter
|
||||
tex.needsUpdate = true
|
||||
if (!root) return
|
||||
|
||||
root.traverse(child => {
|
||||
if ((child as THREE.Mesh).isMesh) {
|
||||
const mesh = child as THREE.Mesh
|
||||
|
||||
const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material]
|
||||
// @ts-ignore
|
||||
materials.forEach((mat: THREE.MeshStandardMaterial) => {
|
||||
mat.map = tex
|
||||
mat.metalness = 0
|
||||
mat.color.set(0xffffff)
|
||||
mat.toneMapped = false
|
||||
mat.roughness = 1
|
||||
mat.side = THREE.DoubleSide
|
||||
mat.needsUpdate = true
|
||||
|
||||
materials.forEach((mat, index, array) => {
|
||||
const standardMat = mat as THREE.MeshStandardMaterial
|
||||
standardMat.map = tex || transparentTexture
|
||||
standardMat.transparent = true
|
||||
standardMat.metalness = 0
|
||||
standardMat.color.set(0xffffff)
|
||||
standardMat.toneMapped = false
|
||||
standardMat.roughness = 1
|
||||
standardMat.side = THREE.DoubleSide
|
||||
standardMat.needsUpdate = true
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -320,9 +316,29 @@ function createRadialTexture(size: number): THREE.CanvasTexture {
|
||||
return new THREE.CanvasTexture(canvas)
|
||||
}
|
||||
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
loadModel(selectedModelSrc.value)
|
||||
if (props.capeModelSrc) {
|
||||
loadCape(props.capeModelSrc)
|
||||
}
|
||||
watch(selectedModelSrc, src => loadModel(src))
|
||||
watch(() => props.capeModelSrc, src => src && loadCape(src))
|
||||
watch(() => props.textureSrc, async newSrc => {
|
||||
texture.value = await loadAndApplyTexture(newSrc)
|
||||
if (scene.value && texture.value) {
|
||||
applyTextureToScene(scene.value, texture.value)
|
||||
}
|
||||
})
|
||||
watch(() => props.capeSrc, async newCapeSrc => {
|
||||
await loadAndApplyCapeTexture(newCapeSrc)
|
||||
})
|
||||
|
||||
onBeforeMount(async () => {
|
||||
texture.value = await loadAndApplyTexture(props.textureSrc)
|
||||
|
||||
await loadModel(selectedModelSrc.value)
|
||||
|
||||
if (props.capeSrc) {
|
||||
await loadAndApplyCapeTexture(props.capeSrc)
|
||||
}
|
||||
|
||||
if (props.capeModelSrc) {
|
||||
await loadCape(props.capeModelSrc)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Generated
-3
@@ -95,9 +95,6 @@ importers:
|
||||
posthog-js:
|
||||
specifier: ^1.158.2
|
||||
version: 1.158.2
|
||||
three:
|
||||
specifier: ^0.172.0
|
||||
version: 0.172.0
|
||||
vite-svg-loader:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0(vue@3.5.13(typescript@5.5.4))
|
||||
|
||||
Reference in new issue
Block a user