https://github.com/vr-voyage/direct-model-importer-vrchat
A prefab to recreate image-encoded 3D models inside VRChat
https://github.com/vr-voyage/direct-model-importer-vrchat
prefab udon udonsharp unity vrchat
Last synced: about 1 year ago
JSON representation
A prefab to recreate image-encoded 3D models inside VRChat
- Host: GitHub
- URL: https://github.com/vr-voyage/direct-model-importer-vrchat
- Owner: vr-voyage
- License: mit
- Created: 2023-05-10T17:49:22.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2023-09-28T07:23:19.000Z (over 2 years ago)
- Last Synced: 2025-03-27T09:22:44.773Z (about 1 year ago)
- Topics: prefab, udon, udonsharp, unity, vrchat
- Language: C#
- Homepage:
- Size: 455 KB
- Stars: 22
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
README
# /!\ ALPHA SOFTWARE. CAN BREAK AND CHANGE COMPLETELY OVER TIME /!\
[blender-demo-en.webm](https://github.com/vr-voyage/direct-model-importer-vrchat/assets/84687350/421c49b2-435d-43da-9817-4e3d2e05ad1a)
# Direct Model Importer
This is currently a Proof of Concept of how to regenerate meshes encoded in EXR
inside VRChat.
This package actually provide an encoder within Unity, to encode any Mesh as
a file.
You'll still have to upload the generated EXR file somewhere (Discord being
the easiest solution) and then enter the URL inside the panel within the game.
> You can actually test it in the Editor, during 'Play' mode too.
An exporter for Blender is also available here :
https://github.com/vr-voyage/direct-model-exporter-blender
# Encoding documentation
The data are supposed to be encoded in RGBA, using 32-bits floating point
for each channel.
I have no idea how to only use one channel at the moment, so four channels
(RGBA) are used. This actually hinders the decoder implementation, but I
have no clear idea how to avoid that, beside using shader tricks that would
make the whole thing even more complex.
## Version 2
### Metadata
The first 64 bytes provide the file metadata
* Float 0 == `(float) 0x00564f59` (**VOY**)
* Float 1 == `(float) 0x00454741` (**AGE**)
* Float 2 == Float.INFINITY (*Currently unchecked*)
* Float 3 == Float.NAN (*Currently unchecked*)
* Float 4 == **Version number**
* Float 8 == **Number of vertices stored**
* Float 9 == **Number of normals stored**
* Float 10 == **Number of UVs stored** (Only one UV map is stored at the moment)
* Float 11 == **Number of indices stored**
The rest is unused at the moment.
### Vertices (from index 64)
Vertices are stored in the RGB channels.
Alpha is ignored.
```csharp
int metadataSize = 64;
int nfloatsInColor = 4;
int cursor = metadataSize / nfloatsInColor;
for (int v = 0; v < nVertices; v++, cursor++)
{
Color currentCol = colors[cursor];
Vector3 vertex = new Vector3(
currentCol.r,
currentCol.g,
currentCol.b);
vertices[v] = vertex;
}
```
### Normals (after the vertices)
Normals are stored in the RGB channels.
Alpha is ignored.
```csharp
for (int n = 0; n < nNormals; n++, cursor++)
{
Color currentCol = colors[cursor];
Vector3 normal = new Vector3(
currentCol.r,
currentCol.g,
currentCol.b);
normals[n] = normal;
}
```
### UVS (after the normals)
UV coordinates are stored in the RG channels.
Blue and Alpha are ignored.
```csharp
for (int u = 0; u < nUVS; u++)
{
Color currentCol = colors[cursor];
Vector2 uv = new Vector2(
currentCol.r,
currentCol.g);
uvs[u] = uv;
cursor++;
}
```
### Indices (after the UV)
Indices are packed.
The easiest way is to read 'nIndices / (float values in Color)' values.
Then, at the end, read the remaining parts depending on how much data
is left.
```csharp
/* This rounds the value to a multiple of 4.
* There are way smarter ways to do it, I'm just lazy
*/
int alignedIndices = nIndices / nfloatsInColor * nfloatsInColor;
int currentIndex = 0;
for (; currentIndex < alignedIndices; currentIndex += 4, cursor++)
{
Color currentCol = colors[cursor];
/*Debug.Log($"Index : {currentIndex} ({cursor*4}) - Indices : {(int)currentCol.r},{(int)currentCol.g},{(int)currentCol.b},{(int)currentCol.a}");*/
indices[currentIndex + 0] = (int)currentCol.r;
indices[currentIndex + 1] = (int)currentCol.g;
indices[currentIndex + 2] = (int)currentCol.b;
indices[currentIndex + 3] = (int)currentCol.a;
}
/*Debug.Log($"cursor after indices : {cursor - start} ({(cursor - start) * 4})");*/
int remainingIndices = nIndices - alignedIndices;
if (remainingIndices > 0)
{
Color currentCol = colors[cursor];
if (remainingIndices >= 1)
{
indices[currentIndex] = (int)currentCol.r;
currentIndex++;
}
if (remainingIndices >= 2)
{
indices[currentIndex] = (int)currentCol.g;
currentIndex++;
}
if (remainingIndices == 3)
{
indices[currentIndex] = (int)currentCol.b;
currentIndex++;
}
}
```