obj2array.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. # Python tool to convert a 3D model from the text obj format to C arrays to be
  2. # used with small3dlib.
  3. #
  4. # by drummyfish
  5. # released under CC0 1.0.
  6. # NOTE: this script doesn't work with any OBJ file! If this doesn't work, the
  7. # format is probably wrong. If you're exporting the OBJ from Blender, you may
  8. # have to play around with the export options. Try this: unwrap model UVs
  9. # (the script expects UV coords to exist), check "selection only", "apply
  10. # modifiers", "include UVs", "triangulate faces", uncheck other things.
  11. #
  12. # Yes, I'll fix all this later :)
  13. import sys
  14. def printHelp():
  15. print("Convert 3D model in OBJ format (text, triangulated) to C array for small3dlib.")
  16. print("usage:\n")
  17. print(" python obj2array.py [-c -sX -uY -vZ -n] file\n")
  18. print(" -c compact format (off by default)")
  19. print(" -t use direct instead of indexed UV coords (off by default)")
  20. print(" -h include header guards (for model per file)")
  21. print(" -m include a material array (per-triangle)")
  22. print(" -nS use the name S for the model (defaut: \"model\")")
  23. print(" -sX scale the model by X (default: 512)")
  24. print(" -uY scale the U texture coord by Y (default: 512)")
  25. print(" -vZ scale the V texture coord by Z (default: 512)")
  26. print("");
  27. print("by Miloslav \"drummyfish\" Ciz")
  28. print("released under CC0 1.0")
  29. if len(sys.argv) < 2:
  30. printHelp()
  31. quit()
  32. FILENAME = ""
  33. VERTEX_SCALE = 512
  34. U_SCALE = 512
  35. V_SCALE = 512
  36. NAME = "model"
  37. GUARDS = False
  38. COMPACT = False
  39. INDEXED_UVS = True
  40. MATERIALS = False
  41. for s in sys.argv:
  42. if s == "-c":
  43. COMPACT = True
  44. elif s == "-t":
  45. INDEXED_UVS = False
  46. elif s == "-h":
  47. GUARDS = True
  48. elif s == "-m":
  49. MATERIALS = True
  50. elif s[:2] == "-s":
  51. VERTEX_SCALE = int(s[2:])
  52. elif s[:2] == "-u":
  53. U_SCALE = int(s[2:])
  54. elif s[:2] == "-v":
  55. V_SCALE = int(s[2:])
  56. elif s[:2] == "-n":
  57. NAME = s[2:]
  58. else:
  59. FILENAME = s
  60. objFile = open(FILENAME)
  61. vertices = []
  62. uvs = []
  63. triangles = []
  64. triangleUVs = []
  65. materials = []
  66. materialNames = []
  67. currentMatrial = 0
  68. def getMaterialIndex(materialName):
  69. try:
  70. return materialNames.index(materialName)
  71. except Exception:
  72. materialNames.append(materialName)
  73. return len(materialNames) - 1
  74. # parse the file:
  75. for line in objFile:
  76. if line[:2] == "v ":
  77. coords = line[2:].split()
  78. vertex = [float(coords[i]) for i in range(3)]
  79. vertex[2] *= -1
  80. vertices.append(vertex)
  81. elif line[:3] == "vt ":
  82. coords = line[3:].split()
  83. vertex = [float(coords[i]) for i in range(2)]
  84. vertex[1] = 1.0 - vertex[1]
  85. uvs.append(vertex)
  86. elif line[:2] == "f ":
  87. indices = line[2:].split()
  88. if len(indices) != 3:
  89. raise(Exception("The model is not triangulated!"))
  90. t = []
  91. u = []
  92. for i in indices:
  93. components = i.split("/")
  94. t.append(int(components[0]) - 1)
  95. try:
  96. u.append(int(components[1]) - 1)
  97. except Exception as e:
  98. u.append(int(components[2]) - 1)
  99. triangles.append(t)
  100. triangleUVs.append(u)
  101. materials.append([currentMatrial])
  102. elif line[:7] == "usemtl ":
  103. currentMatrial = getMaterialIndex(line[7:])
  104. # print the result:
  105. def arrayString(name, array, components, scales, align, short, dataType, sizeStr):
  106. result = "const " + dataType + " " + name + "[" + sizeStr + "] = {\n"
  107. if COMPACT:
  108. lineLen = 0
  109. first = True
  110. n = 0
  111. for v in array:
  112. for c in v:
  113. item = ""
  114. if first:
  115. first = False
  116. else:
  117. result += ","
  118. lineLen += 1
  119. if lineLen >= 80:
  120. result += "\n"
  121. lineLen = 0
  122. num = c * scales[n % len(scales)]
  123. if short:
  124. item += str(num)
  125. else:
  126. item += ("" if num >= 0 else "-") + "0x%x" % abs(num)
  127. if lineLen + len(item) >= 80:
  128. result += "\n"
  129. lineLen = 0
  130. result += item
  131. lineLen += len(item)
  132. n += 1
  133. result += "};\n"
  134. else: # non-compact
  135. n = 0
  136. endIndex = len(array) - 1
  137. for v in array:
  138. line = " " + ", ".join([str(int(v[c] * scales[c % len(scales)])).rjust(align) for c in range(components)])
  139. if n < endIndex:
  140. line += ","
  141. line = line.ljust((components + 2) * (align + 1)) + "// " + str(n * components) + "\n"
  142. result += line
  143. n += 1
  144. result += "}; // " + name + "\n"
  145. return result
  146. result = ""
  147. if GUARDS:
  148. print("#ifndef " + NAME.upper() + "_MODEL_H")
  149. print("#define " + NAME.upper() + "_MODEL_H\n")
  150. print("#define " + NAME.upper() + "_VERTEX_COUNT " + str(len(vertices)))
  151. print(arrayString(NAME + "Vertices",vertices,3,[VERTEX_SCALE],5,False,"S3L_Unit",NAME.upper() + "_VERTEX_COUNT * 3"))
  152. print("#define " + NAME.upper() + "_TRIANGLE_COUNT " + str(len(triangles)))
  153. print(arrayString(NAME + "TriangleIndices",triangles,3,[1],5,True,"S3L_Index",NAME.upper() + "_TRIANGLE_COUNT * 3"))
  154. if MATERIALS:
  155. print(arrayString(NAME + "Materials",materials,1,[1],5,True,"uint8_t",NAME.upper() + "_TRIANGLE_COUNT"))
  156. if INDEXED_UVS:
  157. print("#define " + NAME.upper() + "_UV_COUNT " + str(len(uvs)))
  158. print(arrayString(NAME + "UVs",uvs,2,[U_SCALE,V_SCALE],5,False,"S3L_Unit",NAME.upper() + "_UV_COUNT * 2"))
  159. print("#define " + NAME.upper() + "_UV_INDEX_COUNT " + str(len(triangleUVs)))
  160. print(arrayString(NAME + "UVIndices",triangleUVs,3,[1],5,True,"S3L_Index",NAME.upper() + "_UV_INDEX_COUNT * 3"))
  161. else:
  162. uvs2 = []
  163. for item in triangleUVs:
  164. uvs2.append([
  165. uvs[item[0]][0],
  166. uvs[item[0]][1],
  167. uvs[item[1]][0],
  168. uvs[item[1]][1],
  169. uvs[item[2]][0],
  170. uvs[item[2]][1]])
  171. print("#define " + NAME.upper() + "_DIRECT_UV_COUNT " + str(len(uvs2)))
  172. print(arrayString(NAME + "DirectUVs",uvs2,6,[U_SCALE,V_SCALE],5,False,"S3L_Unit",NAME.upper() + "_DIRECT_UV_COUNT * 6"))
  173. print("S3L_Model3D " + NAME + "Model;\n")
  174. print("void " + NAME + "ModelInit()")
  175. print("{")
  176. print(" S3L_model3DInit(")
  177. print(" " + NAME + "Vertices,")
  178. print(" " + NAME.upper() + "_VERTEX_COUNT,")
  179. print(" " + NAME + "TriangleIndices,")
  180. print(" " + NAME.upper() + "_TRIANGLE_COUNT,")
  181. print(" &" + NAME + "Model);")
  182. print("}")
  183. if GUARDS:
  184. print("\n#endif // guard")