├── .gitignore
├── LICENSE.md
├── MANIFEST
├── README.md
├── build
└── lib
│ └── xNormal.py
├── dist
├── xNormal-1.1.0-py3-none-any.whl
├── xNormal-1.1.0-py3.5.egg
└── xNormal-1.1.0.tar.gz
├── examples
├── basic.py
├── extended1.py
├── extended2.py
├── piano_high.obj
└── piano_low.obj
├── setup.py
├── xNormal.egg-info
├── PKG-INFO
├── SOURCES.txt
├── dependency_links.txt
└── top_level.txt
└── xNormal.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.pyc
3 | *.png
4 | *.xml
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | License
2 | =======
3 |
4 | All source code is licensed under BSD
5 |
6 | Copyright (c) 2010, Daniel Holden
7 | All rights reserved.
8 |
9 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10 |
11 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15 |
--------------------------------------------------------------------------------
/MANIFEST:
--------------------------------------------------------------------------------
1 | # file GENERATED by distutils, do NOT edit
2 | LICENSE.md
3 | README.md
4 | setup.py
5 | xNormal.py
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | xNormal Wrapper
2 | ===============
3 |
4 | This is a wrapper for the program xNormal that allows for easy scripting and automation.
5 |
6 | It is licensed under BSD
7 |
8 | For any issues feel free to contact me at:
9 |
10 | `contact@theorangeduck.com`
11 |
12 | Basic Usage
13 | -----------
14 |
15 | The below generates a normal map and ambient occlusion map for the piano meshes.
16 |
17 | ```python
18 | import xNormal
19 | xNormal.run("piano_high.obj", "piano_low.obj", "piano.png",
20 | width=256, height=256, gen_normals = True, gen_ao = True)
21 | ```
22 |
23 | Extended Usage 1
24 | ----------------
25 |
26 | The belows shows some more features.
27 |
28 | First the path is set. By default the wrapper assumes `xNormal.exe` is in the PATH variable.
29 |
30 | Secondly it generates a normal map with a switch coordinate system and an AO map with fewer rays and jitter. Finally is generates a convexity map.
31 |
32 | For a full list of options have a look in the source code where they are listed and easy to see.
33 |
34 | Finally it stores the errorcode of xNormal in case of something being wrong.
35 |
36 | ```python
37 | import xNormal
38 | xNormal.path = "C:\\Program Files\\xNormal\\3.19.3\\x64\\xNormal.exe"
39 | err = xNormal.run("piano_high.obj", "piano_low.obj", "piano.png", width = 256, height = 256,
40 | gen_normals = True, normals_x = "+X", normals_y = "-Z", normals_z = "+Y",
41 | gen_ao = True, ao_rays = 64, ao_jitter = True,
42 | gen_convexity = True, convexity_scale = 0.75)
43 | ```
44 |
45 | Extended Usage 2
46 | ----------------
47 |
48 | Scripting xNormal goes via the form of supplying it with a configuration file. These can be generated and saved for later use.
49 |
50 | To build a configuration file `xNormal.config` it must be supplied with a list of high mesh options, a list of low mesh options and a list of generation options.
51 |
52 | ```python
53 | import xNormal
54 | high_config = xNormal.high_mesh_options("piano_high.obj", scale = 2.0)
55 | low_config = xNormal.low_mesh_options("piano_low.obj", scale = 2.0)
56 | generation_config = xNormal.generation_options("piano.png", gen_normals = True)
57 |
58 | config = xNormal.config([high_config], [low_config], generation_config)
59 |
60 | f = open("later.xml", 'w')
61 | f.write(config)
62 | f.close()
63 | ```
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/build/lib/xNormal.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | """
4 | path: The path to xNormal.exe
5 | version: The version of xNormal installed
6 | config_file: The default name for generated config files
7 | """
8 |
9 | path = "xNormal.exe"
10 | version = "3.19.3"
11 | config_file = "xNormal.xml"
12 |
13 |
14 | def run(high_path, low_path, out_path, **opts):
15 | """ Basic interface to run xNormal using a single high and low poly mesh """
16 |
17 | opts["out"] = os.path.abspath(out_path)
18 | conf = config([high_mesh_options(high_path)], [low_mesh_options(low_path)], generation_options(out_path, **opts))
19 | return run_config(conf)
20 |
21 |
22 | def run_config(conf):
23 | """ Runs xNormal using a provided configuration XML string. """
24 |
25 | f = open(config_file, 'w')
26 | f.write(conf)
27 | f.close()
28 |
29 | retcode = os.system("\"%s\" %s" % (path, config_file))
30 | os.remove(config_file)
31 |
32 | return retcode
33 |
34 |
35 | def run_config_filename(conf_filename):
36 | """ Runs xNormal using the path to a configuration file. """
37 | retcode = os.system("\"%s\" %s" % (path, conf_filename))
38 | return retcode
39 |
40 |
41 | def high_mesh_options(filename, **kwargs):
42 | """ Generates an options dictionary for a high mesh """
43 |
44 | kwargs["path"] = os.path.abspath(filename)
45 |
46 | """
47 | List of possible high mesh options in the form:
48 | (keyword_argument, xml_name, default_value)
49 | """
50 | high_opts = [
51 | ("visible", "Visible", True),
52 | ("scale", "Scale", 1.000000),
53 | ("ignore_vertex_colors", "IgnorePerVertexColor", True),
54 | ("average_normals", "AverageNormals", "UseExportedNormals"),
55 | ("texture_is_normalmap", "BaseTexIsTSNM", False),
56 | ("path", "File", ""),
57 | ("position_offset", "PositionOffset", "0.0000;0.0000;0.0000"),
58 | ("base_texture", "BaseTex", ""),
59 | ]
60 |
61 | def lookup(dict, key, default):
62 | if key in dict: return dict[key]
63 | else: return default
64 |
65 | high_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in high_opts ]
66 |
67 | return high_opts
68 |
69 |
70 | def low_mesh_options(filename, **kwargs):
71 | """ Generates an options dictionary for a low mesh """
72 |
73 | kwargs["path"] = os.path.abspath(filename)
74 |
75 | """
76 | List of possible low mesh options in the form:
77 | (keyword_argument, xml_name, default_value)
78 | """
79 | low_opts = [
80 | ("visible", "Visible", True),
81 | ("path", "File", ""),
82 | ("average_normals", "AverageNormals", "UseExportedNormals"),
83 | ("forward_ray_dist", "MaxRayDistanceFront", 0.5),
84 | ("backward_ray_dist", "MaxRayDistanceBack", 0.5),
85 | ("use_cage", "UseCage", False),
86 | ("normal_map_type", "NormapMapType", "Tangent-space"),
87 | ("vertex_colors", "UsePerVertexColors", True),
88 | ("fresnel", "UseFresnel", False),
89 | ("fresnel_index", "FresnelRefractiveIndex", 1.33),
90 | ("reflect_index", "ReflectHDRMult", 1.0),
91 | ("displace_tangent_space", "VectorDisplacementTS", False),
92 | ("x", "VDMSwizzleX", "X+"),
93 | ("y", "VDMSwizzleY", "Y+"),
94 | ("z", "VDMSwizzleZ", "Z+"),
95 | ("batch_protect", "BatchProtect", False),
96 | ("cast_shadows", "CastShadows", True),
97 | ("receive_shadows", "ReceiveShadows", True),
98 | ("cull_backfaces", "BackfaceCull", True),
99 | ("normals_x", "NMSwizzleX", "X+"),
100 | ("normals_y", "NMSwizzleY", "Y+"),
101 | ("normals_z", "NMSwizzleZ", "Z+"),
102 | ("cage_file", "CageFile", ""),
103 | ("high_override_tangent", "HighpolyNormalsOverrideTangentSpace", True),
104 | ("transparency", "TransparencyMode", None),
105 | ("alpha_test", "AlphaTestValue", 127),
106 | ("matte", "Matte", False),
107 | ("scale", "Scale", 1.0),
108 | ("match_uvs", "MatchUVs", False),
109 | ("offset_u", "UOffset", False),
110 | ("offset_v", "VOffset", False),
111 | ("position_offset", "PositionOffset", "0.0000;0.0000;0.0000"),
112 | ]
113 |
114 | def lookup(dict, key, default):
115 | if key in dict: return dict[key]
116 | else: return default
117 |
118 | low_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in low_opts ]
119 |
120 | return low_opts
121 |
122 |
123 | def generation_options(out_filename, **kwargs):
124 | """ Generates two options dictionaries for generation and colors """
125 |
126 | kwargs["out"] = os.path.abspath(out_filename)
127 |
128 | """
129 | List of possible generation options in the form:
130 | (keyword_argument, xml_name, default_value)
131 | """
132 | gen_opts = [
133 | ("out", "File", ""),
134 | ("width", "Width", 512),
135 | ("height", "Height", 512),
136 | ("edge_padding", "EdgePadding", 2),
137 | ("bucket_size", "BucketSize", 64),
138 |
139 | ("gen_normals", "GenNormals", False),
140 | ("aa", "AA", 4),
141 | ("tangent_space", "TangentSpace", True),
142 | ("closest_if_fails", "ClosestIfFails", False),
143 | ("discard_backface_hits", "DiscardRayBackFacesHits", True),
144 | ("normals_x", "SwizzleX", "X+"),
145 | ("normals_y", "SwizzleY", "Y+"),
146 | ("normals_z", "SwizzleZ", "Z+"),
147 | ("normals_high_texture", "BakeHighpolyBaseTex", False),
148 | ("normals_high_matid", "BakeHighpolyBaseTextureDrawObjectIDIfNoTexture", False),
149 |
150 | ("gen_heights", "GenHeights", False),
151 | ("heights_tonemap", "HeightTonemap", "Interactive"),
152 | ("heights_min", "HeightMinVal", -10.0),
153 | ("heights_max", "HeightMaxVal", 10.0),
154 |
155 | ("gen_ao", "GenAO", False),
156 | ("ao_rays", "AORaysPerSample", 128),
157 | ("ao_distribution", "AODistribution", "Cosine"),
158 | ("ao_cone_angle", "AOConeAngle", 162.0),
159 | ("ao_bias", "AOBias", 0.08),
160 | ("ao_pure_occlude", "AOAllowPureOccluded", True),
161 | ("ao_limit_ray_distance", "AOLimitRayDistance", False),
162 | ("ao_atten_const", "AOAttenConstant", 1.0),
163 | ("ao_atten_linear", "AOAttenLinear", 0.0),
164 | ("ao_atten_quadratic", "AOAttenCuadratic", 0.0),
165 | ("ao_jitter", "AOJitter", False),
166 | ("ao_ignore_backfaces", "AOIgnoreBackfaceHits", False),
167 |
168 | ("gen_bent", "GenBent", False),
169 | ("bent_rays", "BentRaysPerSample", 128),
170 | ("bent_cone_angle", "BentConeAngle", 162.0),
171 | ("bent_bias", "BentBias", 0.08),
172 | ("bent_tangent_space", "BentTangentSpace", False),
173 | ("bent_limit_ray_distance", "BentLimitRayDistance", True),
174 | ("bent_jitter", "BentJitter", False),
175 | ("bent_distribution", "BentDistribution", "Cosine"),
176 | ("bent_x", "BentSwizzleX", "X+"),
177 | ("bent_y", "BentSwizzleY", "Y+"),
178 | ("bent_z", "BentSwizzleZ", "Z+"),
179 |
180 | ("gen_prt", "GenPRT", False),
181 | ("prt_rays", "PRTRaysPerSample", 128),
182 | ("prt_cone_angle", "PRTConeAngle", 179.5),
183 | ("prt_bias", "PRTBias", 0.08),
184 | ("prt_limit_ray_distance", "PRTLimitRayDistance", True),
185 | ("prt_jitter", "PRTJitter", False),
186 | ("prt_normalize", "PRTNormalize", True),
187 | ("prt_threshold", "PRTThreshold", 0.005),
188 |
189 | ("gen_proximity","GenProximity", False),
190 | ("proximity_rays", "ProximityRaysPerSample", 128),
191 | ("proximity_cone_angle", "ProximityConeAngle", 80.0),
192 | ("proximity_limit_ray_distance", "ProximityLimitRayDistance", True),
193 |
194 | ("gen_convexity", "GenConvexity", False),
195 | ("convexity_scale", "ConvexityScale", 1.0),
196 |
197 | ("gen_thickness", "GenThickness", False),
198 |
199 | ("gen_cavity", "GenCavity", False),
200 | ("cavity_rays", "CavityRaysPerSample", 128),
201 | ("cavity_jitter", "CavityJitter", False),
202 | ("cavity_search_radius", "CavitySearchRadius", 0.5),
203 | ("cavity_contrast", "CavityContrast", 1.25),
204 | ("cavity_steps", "CavitySteps", 4),
205 |
206 | ("gen_wire", "GenWireRays", False),
207 | ("render_ray_fails", "RenderRayFails", True),
208 | ("render_wireframe", "RenderWireframe", True),
209 |
210 | ("gen_directions", "GenDirections", False),
211 | ("directions_tangent_space", "DirectionsTS", False),
212 | ("directions_x", "DirectionsSwizzleX", "X+"),
213 | ("directions_y", "DirectionsSwizzleY", "Y+"),
214 | ("directions_z", "DirectionsSwizzleZ", "Z+"),
215 | ("directions_tonemap", "DirectionsTonemap", "Interactive"),
216 | ("directions_min", "DirectionsMinVal", -10.0),
217 | ("directions_max", "DirectionsMaxVal", 10.0),
218 |
219 | ("gen_radiosity_normals", "GenRadiosityNormals", False),
220 | ("radiosity_normals_rays", "RadiosityNormalsRaysPerSample", 128),
221 | ("radiosity_normals_distribution", "RadiosityNormalsDistribution", "Cosine"),
222 | ("radiosity_normals_cone_angle", "RadiosityNormalsConeAngle", 162.0),
223 | ("radiosity_normals_bias", "RadiosityNormalsBias", 0.08),
224 | ("radiosity_normals_limit_ray_distance", "RadiosityNormalsLimitRayDistance", False),
225 | ("radiosity_normals_atten_const", "RadiosityNormalsAttenConstant", 1.0),
226 | ("radiosity_normals_atten_linear", "RadiosityNormalsAttenLinear", 0.0),
227 | ("radiosity_normals_atten_quadratic", "RadiosityNormalsAttenCuadratic", 0.0),
228 | ("radiosity_normals_jitter", "RadiosityNormalsJitter", False),
229 | ("radiosity_normals_contrast", "RadiosityNormalsContrast", 4.0),
230 | ("radiosity_normals_encode_ao", "RadiosityNormalsEncodeAO", True),
231 | ("radiosity_normals_coordsys", "RadiosityNormalsCoordSys", "AlibB"),
232 | ("radiosity_normals_pure_occlusion", "RadiosityNormalsAllowPureOcclusion", False),
233 | ("radiosity_normals_high_vcols", "BakeHighpolyVCols", False),
234 |
235 | ("gen_curve", "GenCurv", False),
236 | ("curve_rays", "CurvRaysPerSample", 128),
237 | ("curve_bias", "CurvBias", 0.0001),
238 | ("curve_cone_angle", "CurvConeAngle", 178.0),
239 | ("curve_jitter", "CurvJitter", False),
240 | ("curve_search_dist", "CurvSearchDistance", 0.5),
241 | ("curve_tonemap", "CurvTonemap", "2Col"),
242 | ("curve_distribution", "CurvDistribution", "Cosine"),
243 | ("curve_algorithm", "CurvAlgorithm", "Average"),
244 | ("curve_smoothing", "CurvSmoothing", True),
245 |
246 | ("gen_derivative_normals", "GenDerivNM", False),
247 |
248 | ("gen_translucency", "GenTranslu", False),
249 | ("translucency_rays", "TransluRaysPerSample", 128),
250 | ("translucency_distribution", "TransluDistribution", "Cosine"),
251 | ("translucency_cone_angle", "TransluConeAngle", 162.0),
252 | ("translucency_bias", "TransluBias", 0.0005),
253 | ("translucency_distance", "TransluDist", 1.0),
254 | ("translucency_jitter", "TransluJitter", False),
255 | ]
256 |
257 | """
258 | List of possible color options in the form:
259 | (keyword_argument, xml_name, default_value)
260 |
261 | While color options are created in a different set,
262 | conceptually they are the same as generation options
263 | """
264 | color_opts = [
265 | ("normals_background_color", "NMBackgroundColor",(127, 127, 255)),
266 | ("high_no_texture_color", "BakeHighpolyBaseTextureNoTexCol", (255, 0, 0)),
267 | ("high_background_color", "BakeHighpolyBaseTextureBackgroundColor", (0, 0, 0)),
268 | ("heights_background_color", "HMBackgroundColor", (0, 0, 0)),
269 | ("ao_occluded_color", "AOOccludedColor", (0, 0, 0)),
270 | ("ao_unoccluded_color", "AOUnoccludedColor", (255, 255, 255)),
271 | ("ao_background_color", "AOBackgroundColor", (255, 255, 255)),
272 | ("bent_background_color", "BentBackgroundColor", (127, 127, 255)),
273 | ("prt_background_color", "PRTBackgroundColor", (0, 0, 0)),
274 | ("proximity_background_color", "ProximityBackgroundColor", (255, 255, 255)),
275 | ("convexity_background_color", "ConvexityBackgroundColor", (255, 255, 255)),
276 | ("cavity_background_color", "CavityBackgroundColor", (255, 255, 255)),
277 | ("wireframe_color", "RenderWireframeCol", (255, 255, 255)),
278 | ("cw_color", "RenderCWCol", (0, 0, 255)),
279 | ("seam_color", "RenderSeamCol", (0, 255, 0)),
280 | ("ray_fail_color", "RenderRayFailsCol", (255, 0, 0)),
281 | ("wireframe_background_color", "RenderWireframeBackgroundColor", (0, 0, 0)),
282 | ("vdm_background_color", "VDMBackgroundColor", (0, 0, 0)),
283 | ("radiosity_normals_background_color", "RadNMBackgroundColor", (0, 0, 0)),
284 | ("high_vcols_background_color", "BakeHighpolyVColsBackgroundCol", (255, 255, 255)),
285 | ("curve_background_color", "CurvBackgroundColor", (0, 0, 0)),
286 | ("derivative_normals_background_color", "DerivNMBackgroundColor", (0, 0, 0)),
287 | ("translucency_background_color", "TransluBackgroundColor", (0, 0, 0)),
288 | ]
289 |
290 | def lookup(dict, key, default):
291 | if key in dict: return dict[key]
292 | else: return default
293 |
294 | gen_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in gen_opts ]
295 | color_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in color_opts ]
296 |
297 | return gen_opts, color_opts
298 |
299 |
300 | def config(high_meshes, low_meshes, opts):
301 | """ Builds a config file using a list of high mesh options, a list of low mesh options and generation options """
302 |
303 | def lowercase_bool(o):
304 | if o[1] is True: return (o[0], "true")
305 | elif o[1] is False: return (o[0], "false")
306 | else: return o
307 |
308 | gen_opts, color_opts = opts
309 |
310 | gen_opts = map(lowercase_bool, gen_opts)
311 | color_opts = map(lowercase_bool, color_opts)
312 |
313 | conf_string = "\n"
314 | conf_string += "\n" % version
315 |
316 | conf_string += " \n"
317 | for h in high_meshes:
318 | h = map(lowercase_bool, h)
319 | conf_string += " \n" % " ".join(['%s="%s"' % o for o in h])
320 | conf_string += " \n"
321 |
322 | conf_string += " \n"
323 | for l in low_meshes:
324 | l = map(lowercase_bool, l)
325 | conf_string += " \n" % " ".join(['%s="%s"' % o for o in l])
326 | conf_string += " \n"
327 |
328 | conf_string += " \n" % " ".join(['%s="%s"' % o for o in gen_opts])
329 |
330 | color_opt_string = "\n ".join(['<%s R="%i" G="%i" B="%i" />' % (o[0], o[1][0], o[1][1], o[1][2]) for o in color_opts])
331 | conf_string += " %s\n" % color_opt_string
332 |
333 | conf_string += " \n"
334 | conf_string += "\n"
335 |
336 | return conf_string
337 |
338 |
--------------------------------------------------------------------------------
/dist/xNormal-1.1.0-py3-none-any.whl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangeduck/Python-xNormal/06eb9aacee4859b1ba5b248fcb5a29acb40ee586/dist/xNormal-1.1.0-py3-none-any.whl
--------------------------------------------------------------------------------
/dist/xNormal-1.1.0-py3.5.egg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangeduck/Python-xNormal/06eb9aacee4859b1ba5b248fcb5a29acb40ee586/dist/xNormal-1.1.0-py3.5.egg
--------------------------------------------------------------------------------
/dist/xNormal-1.1.0.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/orangeduck/Python-xNormal/06eb9aacee4859b1ba5b248fcb5a29acb40ee586/dist/xNormal-1.1.0.tar.gz
--------------------------------------------------------------------------------
/examples/basic.py:
--------------------------------------------------------------------------------
1 | import xNormal
2 | xNormal.run("piano_high.obj", "piano_low.obj", "piano.png", width=256, height=256, gen_normals = True, gen_ao = True)
--------------------------------------------------------------------------------
/examples/extended1.py:
--------------------------------------------------------------------------------
1 | import xNormal
2 | xNormal.path = "C:\\Program Files\\xNormal\\3.19.3\\x64\\xNormal.exe"
3 | xNormal.run("piano_high.obj", "piano_low.obj", "piano.png", width = 256, height = 256,
4 | gen_normals = True, normals_x = "+X", normals_y = "-Z", normals_z = "+Y",
5 | gen_ao = True, ao_rays = 64, ao_jitter = True,
6 | gen_convexity = True, convexity_scale = 0.75)
--------------------------------------------------------------------------------
/examples/extended2.py:
--------------------------------------------------------------------------------
1 | import xNormal
2 | high_config = xNormal.high_mesh_options("piano_high.obj", scale = 2.0)
3 | low_config = xNormal.low_mesh_options("piano_low.obj", scale = 2.0)
4 | generation_config = xNormal.generation_options("piano.png", gen_normals = True)
5 |
6 | config = xNormal.config([high_config], [low_config], generation_config)
7 |
8 | f = open("later.xml", 'w')
9 | f.write(config)
10 | f.close()
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import setuptools
4 |
5 | with open("README.md", "r") as fh:
6 | long_description = fh.read()
7 |
8 | setuptools.setup(
9 | name="xNormal",
10 | version="1.1.0",
11 | author="Daniel Holden, Paul Greveson",
12 | author_email="contact@theorangeduck.com",
13 | description="Wrapper for xNormal",
14 | long_description=long_description,
15 | long_description_content_type="text/markdown",
16 | url="https://github.com/orangeduck/Python-xNormal",
17 | packages=setuptools.find_packages(),
18 | )
--------------------------------------------------------------------------------
/xNormal.egg-info/PKG-INFO:
--------------------------------------------------------------------------------
1 | Metadata-Version: 2.1
2 | Name: xNormal
3 | Version: 1.1.0
4 | Summary: Wrapper for xNormal
5 | Home-page: https://github.com/orangeduck/Python-xNormal
6 | Author: Daniel Holden, Paul Greveson
7 | Author-email: contact@theorangeduck.com
8 | License: UNKNOWN
9 | Description: xNormal Wrapper
10 | ===============
11 |
12 | This is a wrapper for the program xNormal that allows for easy scripting and automation.
13 |
14 | It is licensed under BSD
15 |
16 | For any issues feel free to contact me at:
17 |
18 | `contact@theorangeduck.com`
19 |
20 | Basic Usage
21 | -----------
22 |
23 | The below generates a normal map and ambient occlusion map for the piano meshes.
24 |
25 | ```python
26 | import xNormal
27 | xNormal.run("piano_high.obj", "piano_low.obj", "piano.png",
28 | width=256, height=256, gen_normals = True, gen_ao = True)
29 | ```
30 |
31 | Extended Usage 1
32 | ----------------
33 |
34 | The belows shows some more features.
35 |
36 | First the path is set. By default the wrapper assumes `xNormal.exe` is in the PATH variable.
37 |
38 | Secondly it generates a normal map with a switch coordinate system and an AO map with fewer rays and jitter. Finally is generates a convexity map.
39 |
40 | For a full list of options have a look in the source code where they are listed and easy to see.
41 |
42 | Finally it stores the errorcode of xNormal in case of something being wrong.
43 |
44 | ```python
45 | import xNormal
46 | xNormal.path = "C:\\Program Files\\xNormal\\3.19.3\\x64\\xNormal.exe"
47 | err = xNormal.run("piano_high.obj", "piano_low.obj", "piano.png", width = 256, height = 256,
48 | gen_normals = True, normals_x = "+X", normals_y = "-Z", normals_z = "+Y",
49 | gen_ao = True, ao_rays = 64, ao_jitter = True,
50 | gen_convexity = True, convexity_scale = 0.75)
51 | ```
52 |
53 | Extended Usage 2
54 | ----------------
55 |
56 | Scripting xNormal goes via the form of supplying it with a configuration file. These can be generated and saved for later use.
57 |
58 | To build a configuration file `xNormal.config` it must be supplied with a list of high mesh options, a list of low mesh options and a list of generation options.
59 |
60 | ```python
61 | import xNormal
62 | high_config = xNormal.high_mesh_options("piano_high.obj", scale = 2.0)
63 | low_config = xNormal.low_mesh_options("piano_low.obj", scale = 2.0)
64 | generation_config = xNormal.generation_options("piano.png", gen_normals = True)
65 |
66 | config = xNormal.config([high_config], [low_config], generation_config)
67 |
68 | f = open("later.xml", 'w')
69 | f.write(config)
70 | f.close()
71 | ```
72 |
73 |
74 |
75 |
76 | Platform: UNKNOWN
77 | Description-Content-Type: text/markdown
78 |
--------------------------------------------------------------------------------
/xNormal.egg-info/SOURCES.txt:
--------------------------------------------------------------------------------
1 | README.md
2 | setup.py
3 | xNormal.egg-info/PKG-INFO
4 | xNormal.egg-info/SOURCES.txt
5 | xNormal.egg-info/dependency_links.txt
6 | xNormal.egg-info/top_level.txt
--------------------------------------------------------------------------------
/xNormal.egg-info/dependency_links.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/xNormal.egg-info/top_level.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/xNormal.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | """
4 | path: The path to xNormal.exe
5 | version: The version of xNormal installed
6 | config_file: The default name for generated config files
7 | """
8 |
9 | path = "xNormal.exe"
10 | version = "3.19.3"
11 | config_file = "xNormal.xml"
12 |
13 |
14 | def run(high_path, low_path, out_path, **opts):
15 | """ Basic interface to run xNormal using a single high and low poly mesh """
16 |
17 | opts["out"] = os.path.abspath(out_path)
18 | conf = config([high_mesh_options(high_path, **opts)], [low_mesh_options(low_path, **opts)], generation_options(out_path, **opts))
19 | return run_config(conf)
20 |
21 |
22 | def run_config(conf):
23 | """ Runs xNormal using a provided configuration XML string. """
24 |
25 | f = open(config_file, 'w')
26 | f.write(conf)
27 | f.close()
28 |
29 | retcode = os.system("\"%s\" %s" % (path, config_file))
30 | os.remove(config_file)
31 |
32 | return retcode
33 |
34 |
35 | def run_config_filename(conf_filename):
36 | """ Runs xNormal using the path to a configuration file. """
37 | retcode = os.system("\"%s\" %s" % (path, conf_filename))
38 | return retcode
39 |
40 |
41 | def high_mesh_options(filename, **kwargs):
42 | """ Generates an options dictionary for a high mesh """
43 |
44 | kwargs["path"] = os.path.abspath(filename)
45 |
46 | """
47 | List of possible high mesh options in the form:
48 | (keyword_argument, xml_name, default_value)
49 | """
50 | high_opts = [
51 | ("visible", "Visible", True),
52 | ("scale", "Scale", 1.000000),
53 | ("ignore_vertex_colors", "IgnorePerVertexColor", True),
54 | ("average_normals", "AverageNormals", "UseExportedNormals"),
55 | ("texture_is_normalmap", "BaseTexIsTSNM", False),
56 | ("path", "File", ""),
57 | ("position_offset", "PositionOffset", "0.0000;0.0000;0.0000"),
58 | ("base_texture", "BaseTex", ""),
59 | ]
60 |
61 | def lookup(dict, key, default):
62 | if key in dict: return dict[key]
63 | else: return default
64 |
65 | high_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in high_opts ]
66 |
67 | return high_opts
68 |
69 |
70 | def low_mesh_options(filename, **kwargs):
71 | """ Generates an options dictionary for a low mesh """
72 |
73 | kwargs["path"] = os.path.abspath(filename)
74 |
75 | """
76 | List of possible low mesh options in the form:
77 | (keyword_argument, xml_name, default_value)
78 | """
79 | low_opts = [
80 | ("visible", "Visible", True),
81 | ("path", "File", ""),
82 | ("average_normals", "AverageNormals", "UseExportedNormals"),
83 | ("forward_ray_dist", "MaxRayDistanceFront", 0.5),
84 | ("backward_ray_dist", "MaxRayDistanceBack", 0.5),
85 | ("use_cage", "UseCage", False),
86 | ("normal_map_type", "NormapMapType", "Tangent-space"),
87 | ("vertex_colors", "UsePerVertexColors", True),
88 | ("fresnel", "UseFresnel", False),
89 | ("fresnel_index", "FresnelRefractiveIndex", 1.33),
90 | ("reflect_index", "ReflectHDRMult", 1.0),
91 | ("displace_tangent_space", "VectorDisplacementTS", False),
92 | ("x", "VDMSwizzleX", "X+"),
93 | ("y", "VDMSwizzleY", "Y+"),
94 | ("z", "VDMSwizzleZ", "Z+"),
95 | ("batch_protect", "BatchProtect", False),
96 | ("cast_shadows", "CastShadows", True),
97 | ("receive_shadows", "ReceiveShadows", True),
98 | ("cull_backfaces", "BackfaceCull", True),
99 | ("normals_x", "NMSwizzleX", "X+"),
100 | ("normals_y", "NMSwizzleY", "Y+"),
101 | ("normals_z", "NMSwizzleZ", "Z+"),
102 | ("cage_file", "CageFile", ""),
103 | ("high_override_tangent", "HighpolyNormalsOverrideTangentSpace", True),
104 | ("transparency", "TransparencyMode", None),
105 | ("alpha_test", "AlphaTestValue", 127),
106 | ("matte", "Matte", False),
107 | ("scale", "Scale", 1.0),
108 | ("match_uvs", "MatchUVs", False),
109 | ("offset_u", "UOffset", False),
110 | ("offset_v", "VOffset", False),
111 | ("position_offset", "PositionOffset", "0.0000;0.0000;0.0000"),
112 | ]
113 |
114 | def lookup(dict, key, default):
115 | if key in dict: return dict[key]
116 | else: return default
117 |
118 | low_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in low_opts ]
119 |
120 | return low_opts
121 |
122 |
123 | def generation_options(out_filename, **kwargs):
124 | """ Generates two options dictionaries for generation and colors """
125 |
126 | kwargs["out"] = os.path.abspath(out_filename)
127 |
128 | """
129 | List of possible generation options in the form:
130 | (keyword_argument, xml_name, default_value)
131 | """
132 | gen_opts = [
133 | ("out", "File", ""),
134 | ("width", "Width", 512),
135 | ("height", "Height", 512),
136 | ("edge_padding", "EdgePadding", 2),
137 | ("bucket_size", "BucketSize", 64),
138 |
139 | ("gen_normals", "GenNormals", False),
140 | ("aa", "AA", 4),
141 | ("tangent_space", "TangentSpace", True),
142 | ("closest_if_fails", "ClosestIfFails", False),
143 | ("discard_backface_hits", "DiscardRayBackFacesHits", True),
144 | ("normals_x", "SwizzleX", "X+"),
145 | ("normals_y", "SwizzleY", "Y+"),
146 | ("normals_z", "SwizzleZ", "Z+"),
147 | ("normals_high_texture", "BakeHighpolyBaseTex", False),
148 | ("normals_high_matid", "BakeHighpolyBaseTextureDrawObjectIDIfNoTexture", False),
149 |
150 | ("gen_heights", "GenHeights", False),
151 | ("heights_tonemap", "HeightTonemap", "Interactive"),
152 | ("heights_min", "HeightMinVal", -10.0),
153 | ("heights_max", "HeightMaxVal", 10.0),
154 |
155 | ("gen_ao", "GenAO", False),
156 | ("ao_rays", "AORaysPerSample", 128),
157 | ("ao_distribution", "AODistribution", "Cosine"),
158 | ("ao_cone_angle", "AOConeAngle", 162.0),
159 | ("ao_bias", "AOBias", 0.08),
160 | ("ao_pure_occlude", "AOAllowPureOccluded", True),
161 | ("ao_limit_ray_distance", "AOLimitRayDistance", False),
162 | ("ao_atten_const", "AOAttenConstant", 1.0),
163 | ("ao_atten_linear", "AOAttenLinear", 0.0),
164 | ("ao_atten_quadratic", "AOAttenCuadratic", 0.0),
165 | ("ao_jitter", "AOJitter", False),
166 | ("ao_ignore_backfaces", "AOIgnoreBackfaceHits", False),
167 |
168 | ("gen_bent", "GenBent", False),
169 | ("bent_rays", "BentRaysPerSample", 128),
170 | ("bent_cone_angle", "BentConeAngle", 162.0),
171 | ("bent_bias", "BentBias", 0.08),
172 | ("bent_tangent_space", "BentTangentSpace", False),
173 | ("bent_limit_ray_distance", "BentLimitRayDistance", True),
174 | ("bent_jitter", "BentJitter", False),
175 | ("bent_distribution", "BentDistribution", "Cosine"),
176 | ("bent_x", "BentSwizzleX", "X+"),
177 | ("bent_y", "BentSwizzleY", "Y+"),
178 | ("bent_z", "BentSwizzleZ", "Z+"),
179 |
180 | ("gen_prt", "GenPRT", False),
181 | ("prt_rays", "PRTRaysPerSample", 128),
182 | ("prt_cone_angle", "PRTConeAngle", 179.5),
183 | ("prt_bias", "PRTBias", 0.08),
184 | ("prt_limit_ray_distance", "PRTLimitRayDistance", True),
185 | ("prt_jitter", "PRTJitter", False),
186 | ("prt_normalize", "PRTNormalize", True),
187 | ("prt_threshold", "PRTThreshold", 0.005),
188 |
189 | ("gen_proximity","GenProximity", False),
190 | ("proximity_rays", "ProximityRaysPerSample", 128),
191 | ("proximity_cone_angle", "ProximityConeAngle", 80.0),
192 | ("proximity_limit_ray_distance", "ProximityLimitRayDistance", True),
193 |
194 | ("gen_convexity", "GenConvexity", False),
195 | ("convexity_scale", "ConvexityScale", 1.0),
196 |
197 | ("gen_thickness", "GenThickness", False),
198 |
199 | ("gen_cavity", "GenCavity", False),
200 | ("cavity_rays", "CavityRaysPerSample", 128),
201 | ("cavity_jitter", "CavityJitter", False),
202 | ("cavity_search_radius", "CavitySearchRadius", 0.5),
203 | ("cavity_contrast", "CavityContrast", 1.25),
204 | ("cavity_steps", "CavitySteps", 4),
205 |
206 | ("gen_wire", "GenWireRays", False),
207 | ("render_ray_fails", "RenderRayFails", True),
208 | ("render_wireframe", "RenderWireframe", True),
209 |
210 | ("gen_directions", "GenDirections", False),
211 | ("directions_tangent_space", "DirectionsTS", False),
212 | ("directions_x", "DirectionsSwizzleX", "X+"),
213 | ("directions_y", "DirectionsSwizzleY", "Y+"),
214 | ("directions_z", "DirectionsSwizzleZ", "Z+"),
215 | ("directions_tonemap", "DirectionsTonemap", "Interactive"),
216 | ("directions_min", "DirectionsMinVal", -10.0),
217 | ("directions_max", "DirectionsMaxVal", 10.0),
218 |
219 | ("gen_radiosity_normals", "GenRadiosityNormals", False),
220 | ("radiosity_normals_rays", "RadiosityNormalsRaysPerSample", 128),
221 | ("radiosity_normals_distribution", "RadiosityNormalsDistribution", "Cosine"),
222 | ("radiosity_normals_cone_angle", "RadiosityNormalsConeAngle", 162.0),
223 | ("radiosity_normals_bias", "RadiosityNormalsBias", 0.08),
224 | ("radiosity_normals_limit_ray_distance", "RadiosityNormalsLimitRayDistance", False),
225 | ("radiosity_normals_atten_const", "RadiosityNormalsAttenConstant", 1.0),
226 | ("radiosity_normals_atten_linear", "RadiosityNormalsAttenLinear", 0.0),
227 | ("radiosity_normals_atten_quadratic", "RadiosityNormalsAttenCuadratic", 0.0),
228 | ("radiosity_normals_jitter", "RadiosityNormalsJitter", False),
229 | ("radiosity_normals_contrast", "RadiosityNormalsContrast", 4.0),
230 | ("radiosity_normals_encode_ao", "RadiosityNormalsEncodeAO", True),
231 | ("radiosity_normals_coordsys", "RadiosityNormalsCoordSys", "AlibB"),
232 | ("radiosity_normals_pure_occlusion", "RadiosityNormalsAllowPureOcclusion", False),
233 | ("radiosity_normals_high_vcols", "BakeHighpolyVCols", False),
234 |
235 | ("gen_curve", "GenCurv", False),
236 | ("curve_rays", "CurvRaysPerSample", 128),
237 | ("curve_bias", "CurvBias", 0.0001),
238 | ("curve_cone_angle", "CurvConeAngle", 178.0),
239 | ("curve_jitter", "CurvJitter", False),
240 | ("curve_search_dist", "CurvSearchDistance", 0.5),
241 | ("curve_tonemap", "CurvTonemap", "2Col"),
242 | ("curve_distribution", "CurvDistribution", "Cosine"),
243 | ("curve_algorithm", "CurvAlgorithm", "Average"),
244 | ("curve_smoothing", "CurvSmoothing", True),
245 |
246 | ("gen_derivative_normals", "GenDerivNM", False),
247 |
248 | ("gen_translucency", "GenTranslu", False),
249 | ("translucency_rays", "TransluRaysPerSample", 128),
250 | ("translucency_distribution", "TransluDistribution", "Cosine"),
251 | ("translucency_cone_angle", "TransluConeAngle", 162.0),
252 | ("translucency_bias", "TransluBias", 0.0005),
253 | ("translucency_distance", "TransluDist", 1.0),
254 | ("translucency_jitter", "TransluJitter", False),
255 | ]
256 |
257 | """
258 | List of possible color options in the form:
259 | (keyword_argument, xml_name, default_value)
260 |
261 | While color options are created in a different set,
262 | conceptually they are the same as generation options
263 | """
264 | color_opts = [
265 | ("normals_background_color", "NMBackgroundColor",(127, 127, 255)),
266 | ("high_no_texture_color", "BakeHighpolyBaseTextureNoTexCol", (255, 0, 0)),
267 | ("high_background_color", "BakeHighpolyBaseTextureBackgroundColor", (0, 0, 0)),
268 | ("heights_background_color", "HMBackgroundColor", (0, 0, 0)),
269 | ("ao_occluded_color", "AOOccludedColor", (0, 0, 0)),
270 | ("ao_unoccluded_color", "AOUnoccludedColor", (255, 255, 255)),
271 | ("ao_background_color", "AOBackgroundColor", (255, 255, 255)),
272 | ("bent_background_color", "BentBackgroundColor", (127, 127, 255)),
273 | ("prt_background_color", "PRTBackgroundColor", (0, 0, 0)),
274 | ("proximity_background_color", "ProximityBackgroundColor", (255, 255, 255)),
275 | ("convexity_background_color", "ConvexityBackgroundColor", (255, 255, 255)),
276 | ("cavity_background_color", "CavityBackgroundColor", (255, 255, 255)),
277 | ("wireframe_color", "RenderWireframeCol", (255, 255, 255)),
278 | ("cw_color", "RenderCWCol", (0, 0, 255)),
279 | ("seam_color", "RenderSeamCol", (0, 255, 0)),
280 | ("ray_fail_color", "RenderRayFailsCol", (255, 0, 0)),
281 | ("wireframe_background_color", "RenderWireframeBackgroundColor", (0, 0, 0)),
282 | ("vdm_background_color", "VDMBackgroundColor", (0, 0, 0)),
283 | ("radiosity_normals_background_color", "RadNMBackgroundColor", (0, 0, 0)),
284 | ("high_vcols_background_color", "BakeHighpolyVColsBackgroundCol", (255, 255, 255)),
285 | ("curve_background_color", "CurvBackgroundColor", (0, 0, 0)),
286 | ("derivative_normals_background_color", "DerivNMBackgroundColor", (0, 0, 0)),
287 | ("translucency_background_color", "TransluBackgroundColor", (0, 0, 0)),
288 | ]
289 |
290 | def lookup(dict, key, default):
291 | if key in dict: return dict[key]
292 | else: return default
293 |
294 | gen_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in gen_opts ]
295 | color_opts = [(xml, lookup(kwargs, kw, val)) for kw, xml, val in color_opts ]
296 |
297 | return gen_opts, color_opts
298 |
299 |
300 | def config(high_meshes, low_meshes, opts):
301 | """ Builds a config file using a list of high mesh options, a list of low mesh options and generation options """
302 |
303 | def lowercase_bool(o):
304 | if o[1] is True: return (o[0], "true")
305 | elif o[1] is False: return (o[0], "false")
306 | else: return o
307 |
308 | gen_opts, color_opts = opts
309 |
310 | gen_opts = map(lowercase_bool, gen_opts)
311 | color_opts = map(lowercase_bool, color_opts)
312 |
313 | conf_string = "\n"
314 | conf_string += "\n" % version
315 |
316 | conf_string += " \n"
317 | for h in high_meshes:
318 | h = map(lowercase_bool, h)
319 | conf_string += " \n" % " ".join(['%s="%s"' % o for o in h])
320 | conf_string += " \n"
321 |
322 | conf_string += " \n"
323 | for l in low_meshes:
324 | l = map(lowercase_bool, l)
325 | conf_string += " \n" % " ".join(['%s="%s"' % o for o in l])
326 | conf_string += " \n"
327 |
328 | conf_string += " \n" % " ".join(['%s="%s"' % o for o in gen_opts])
329 |
330 | color_opt_string = "\n ".join(['<%s R="%i" G="%i" B="%i" />' % (o[0], o[1][0], o[1][1], o[1][2]) for o in color_opts])
331 | conf_string += " %s\n" % color_opt_string
332 |
333 | conf_string += " \n"
334 | conf_string += "\n"
335 |
336 | return conf_string
337 |
338 |
--------------------------------------------------------------------------------