Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
majimboo committed May 16, 2017
0 parents commit ed48b35
Show file tree
Hide file tree
Showing 144 changed files with 14,572 additions and 0 deletions.
87 changes: 87 additions & 0 deletions README.md
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)
124 changes: 124 additions & 0 deletions extract_model.py
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])
117 changes: 117 additions & 0 deletions extract_mview.py
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 added noesis/Noesis.exe
Binary file not shown.
Loading

0 comments on commit ed48b35

Please sign in to comment.