-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit ed48b35
Showing
144 changed files
with
14,572 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
MView File Viewer | ||
================= | ||
|
||
This project consists of 2 scripts. `extract_mview` to extract files inside the mview archive and `extract_model` to convert the `.dat` files to `.obj`. Also includes a Noesis plugin. (see below) | ||
|
||
**Example** | ||
|
||
File downloaded from [ArtStation](https://www.artstation.com/artwork/3LBbA). | ||
|
||
**Result** | ||
![](http://i.imgur.com/EFu0Hg1.png) | ||
|
||
Requirements | ||
============ | ||
|
||
- Python 3.6.1 or later [[download](https://www.python.org/downloads/)] | ||
|
||
Usage | ||
===== | ||
|
||
// extract mview archive | ||
// python extract_mview.py <filename> | ||
$ python extract_mview.py test_data/test_file1.mview | ||
$ > thumbnail.jpg image/jpeg | ||
$ > sky.dat image/derp | ||
$ > mesh0.dat model/mset | ||
$ > mesh1.dat model/mset | ||
$ > mesh2.dat model/mset | ||
$ > mesh3.dat model/mset | ||
$ > mesh4.dat model/mset | ||
$ > mesh5.dat model/mset | ||
$ > mesh6.dat model/mset | ||
$ > mat0_c.jpg image/jpeg | ||
$ > mat0_r.jpg image/jpeg | ||
$ > mat0_n.jpg image/jpeg | ||
$ > mat0_a.jpg image/jpeg | ||
$ > mat0_g.jpg image/jpeg | ||
$ > mat0_s.jpg image/jpeg | ||
$ > mat1_c.jpg image/jpeg | ||
$ > mat1_n.jpg image/jpeg | ||
$ > mat2_c.jpg image/jpeg | ||
$ > mat3_c.jpg image/jpeg | ||
$ > mat4_c.jpg image/jpeg | ||
$ > mat4_a.jpg image/jpeg | ||
$ > mat5_c.jpg image/jpeg | ||
$ > mat5_a.jpg image/jpeg | ||
$ > mat5_s.jpg image/jpeg | ||
$ > mat6_c.jpg image/jpeg | ||
$ > mat6_a.jpg image/jpeg | ||
$ > scene.json.sig application/json | ||
$ > scene.json application/json | ||
$ > COMPLETED!!! | ||
|
||
// convert dat files to obj (wavefront) | ||
// python extract_model.py <folder_containing_scene.json> | ||
$ python extract_model.py test_data/test_file1 | ||
$ > COMPLETED!!! | ||
|
||
Viewer | ||
====== | ||
|
||
You can use the included viewer called Noesis. Just drag and drop the `.obj` file into Noesis. | ||
You can also open the `.obj` file with any 3D program that supports wavefront format. @[richwhitehouse](http://richwhitehouse.com/index.php?content=inc_projects.php) | ||
|
||
A Noesis plugin is also available at `noesis/plugins/python/fmt_artstation_mview.py`. Just open the `.mview` files with Noesis. (no texture support) | ||
|
||
![](http://i.imgur.com/4F1azVi.png) | ||
|
||
Notes | ||
===== | ||
|
||
To download an `.mview` file: | ||
|
||
1. Open url with 3D viewer in browser. (do not click play yet) | ||
2. Open **Developer Tools** and go to **Network** tab. | ||
3. Click the play button on the 3D viewer. | ||
4. Type or search for `mview` on the **Developer Tools' Network** tab. | ||
5. Right click on the file and select `open in new tab`. (will start download) | ||
|
||
**[FIXED]** ~~[BUG] There is currently no support for huge files that uses uint32 indices. Pull requests are welcome.~~ | ||
|
||
Community | ||
========= | ||
|
||
- [Xentax Forum](http://forum.xentax.com) @majidemo, @shakotay2, @TaylorMouse | ||
- [Marmoset Toolbag](https://www.marmoset.co/viewer) | ||
- [ArtStation](https://www.artstation.com/artwork/3LBbA) |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
#!/usr/bin/python | ||
#@majidemo | ||
|
||
import json, os, sys | ||
from struct import * | ||
|
||
def main(folder): | ||
f = open("%s/scene.json" % (folder)) | ||
data = json.load(f) | ||
f.close() | ||
|
||
omtl = open("%s/master.mtl" % (folder), "w") | ||
for mat in data["materials"]: | ||
name = mat["name"] | ||
diffuse = mat["albedoTex"] | ||
# specular = mat["extrasTex"] | ||
|
||
# write to file | ||
omtl.write("newmtl {0}\n".format(name)) | ||
omtl.write("map_Ka {0}\n".format(diffuse)) | ||
omtl.write("map_Kd {0}\n".format(diffuse)) | ||
# omtl.write("map_Ks {0}\n\n".format(specular)) | ||
|
||
omtl.close() | ||
|
||
for mesh in data["meshes"]: | ||
name = mesh["name"] | ||
dat = mesh["file"] | ||
print("converting %s" % dat) | ||
# transform = mesh["transform"] | ||
wire_count = mesh["wireCount"] | ||
index_count = mesh["indexCount"] | ||
vertex_count = mesh["vertexCount"] | ||
|
||
tex_coord_2 = 0 | ||
if "secondaryTexCoord" in mesh: | ||
tex_coord_2 = mesh["secondaryTexCoord"] | ||
|
||
vertex_color = 0 | ||
if "vertexColor" in mesh: | ||
vertex_color = mesh["vertexColor"] | ||
|
||
index_type_size = mesh["indexTypeSize"] | ||
# consts | ||
stride = 32 | ||
if vertex_color > 0: stride = stride + 4 | ||
if tex_coord_2 > 0: stride = stride + 8 | ||
|
||
# TODO: BUG LONG INDICES | ||
# if index_type_size == 4: | ||
# raise Exception("ERROR! Currently can't process any large files with long (uint32) indices... To Be Updated!!!") | ||
|
||
# read stream | ||
df = open("%s/%s" % (folder, dat), "rb") | ||
# write stream | ||
output = open("{0}/{1}.obj".format(folder, dat), "w") | ||
output.write("mtllib master.mtl\n") | ||
|
||
# lists | ||
face_list = [] | ||
vert_list = [] | ||
uv_list = [] | ||
materials_list = [] | ||
|
||
for sub_mesh in mesh["subMeshes"]: | ||
faces = [] | ||
material = sub_mesh["material"] | ||
index_count_2 = sub_mesh["indexCount"] | ||
wire_count_2 = sub_mesh["wireIndexCount"] | ||
|
||
face_count = int((index_count_2 * index_type_size) / 6) | ||
if index_type_size == 4: | ||
face_count = int((index_count_2 * index_type_size) / 12) | ||
|
||
# faces | ||
for f in range(face_count): | ||
if index_type_size == 2: | ||
faces.append(unpack("<HHH", df.read(6))) | ||
else: | ||
faces.append(unpack("<III", df.read(12))) | ||
|
||
# set submesh data | ||
face_list.append(faces) | ||
materials_list.append(material) | ||
|
||
# skip unknown wire count | ||
df.seek(wire_count * index_type_size, 1) | ||
|
||
# vertices | ||
for v in range(vertex_count): | ||
# position | ||
pos = unpack("<fff", df.read(12)) | ||
# texcoord | ||
texpos = unpack("<ff", df.read(8)) | ||
# stride | ||
df.read(stride - 20) | ||
|
||
vert_list.append(pos) | ||
uv_list.append(texpos) | ||
|
||
for vert in vert_list: | ||
output.write("v {0} {1} {2}\n".format(vert[0], vert[1], vert[2])) | ||
|
||
for uv in uv_list: | ||
output.write("vt {0} {1}\n".format(uv[0], -uv[1])) | ||
|
||
for x, faces in enumerate(face_list): | ||
output.write("\n") | ||
output.write("g {0}-{1}-{2}\n".format(dat, name, x)) | ||
output.write("usemtl {0}\n".format(materials_list[x])) | ||
|
||
for face in faces: | ||
output.write("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n".format(face[0]+1, face[1]+1, face[2]+1)) | ||
|
||
df.close() | ||
output.close() | ||
|
||
print("COMPLETED!!!") | ||
|
||
def mkDIR(dir): | ||
if not os.path.exists(dir): | ||
os.makedirs(dir) | ||
|
||
main(sys.argv[1]) |
This file contains 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 characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#!/usr/bin/python | ||
# @majidemo | ||
|
||
import os, io, sys, struct | ||
|
||
def main(filename): | ||
folder = filename.split(".")[0] | ||
mkDIR(folder) | ||
|
||
bin = load(filename) | ||
|
||
bin.seek(0, 2) | ||
end = bin.tell() | ||
bin.seek(0) | ||
|
||
while bin.tell() < end: | ||
name = readcstr(bin) | ||
ftype = readcstr(bin) | ||
c = readuint32(bin) | ||
d = readuint32(bin) | ||
e = readuint32(bin) | ||
|
||
data = bin.read(d) | ||
|
||
if c & 1: | ||
data = decompress(data, e) | ||
|
||
output = open("%s/%s" % (folder, name), "wb") | ||
output.write(data) | ||
output.close() | ||
|
||
print(name, ftype) | ||
|
||
print("COMPLETED!!!") | ||
|
||
def decompress(a, b): | ||
c = bytearray(b) | ||
d = 0 | ||
e = [0] * 4096 | ||
f = [0] * 4096 | ||
g = 256 | ||
h = len(a) | ||
k = 0 | ||
l = 1 | ||
m = 0 | ||
n = 1 | ||
|
||
c[d] = a[0] | ||
d += 1 | ||
|
||
r = 1 | ||
while True: | ||
n = r + (r >> 1) | ||
if (n + 1) >= h: | ||
break | ||
m = a[n + 1] | ||
n = a[n] | ||
p = (m << 4 | n >> 4) if r & 1 else ((m & 15) << 8 | n) | ||
if p < g: | ||
if 256 > p: | ||
m = d | ||
n = 1 | ||
c[d] = p | ||
d += 1 | ||
else: | ||
m = d | ||
n = f[p] | ||
p = e[p] | ||
q = p + n | ||
while p < q: | ||
c[d] = c[p] | ||
d += 1 | ||
p += 1 | ||
elif p == g: | ||
m = d | ||
n = l + 1 | ||
p = k | ||
q = k + l | ||
while p < q: | ||
c[d] = c[p] | ||
d += 1 | ||
p += 1 | ||
c[d] = c[k] | ||
d += 1 | ||
else: | ||
break | ||
|
||
e[g] = k | ||
f[g] = l + 1 | ||
g += 1 | ||
k = m | ||
l = n | ||
g = 256 if 4096 <= g else g | ||
r += 1 | ||
|
||
return c if d == b else None | ||
|
||
def readuint32(f): | ||
return struct.unpack("<I", f.read(4))[0] | ||
|
||
def readcstr(f): | ||
buf = [] | ||
while True: | ||
b = struct.unpack("<b", f.read(1))[0] | ||
if b == 0: | ||
return "".join(map(chr, buf)) | ||
else: | ||
buf.append(b) | ||
|
||
def mkDIR(dir): | ||
if not os.path.exists(dir): | ||
os.makedirs(dir) | ||
|
||
def load(file): | ||
return open(file, "rb") | ||
|
||
main(sys.argv[1]) |
Binary file not shown.
Oops, something went wrong.