1 | """Material export classes. |
---|
2 | |
---|
3 | @author Michael Reimpell |
---|
4 | """ |
---|
5 | # Copyright (C) 2005 Michael Reimpell |
---|
6 | # |
---|
7 | # This library is free software; you can redistribute it and/or |
---|
8 | # modify it under the terms of the GNU Lesser General Public |
---|
9 | # License as published by the Free Software Foundation; either |
---|
10 | # version 2.1 of the License, or (at your option) any later version. |
---|
11 | # |
---|
12 | # This library is distributed in the hope that it will be useful, |
---|
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
15 | # Lesser General Public License for more details. |
---|
16 | # |
---|
17 | # You should have received a copy of the GNU Lesser General Public |
---|
18 | # License along with this library; if not, write to the Free Software |
---|
19 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
---|
20 | |
---|
21 | # epydoc doc format |
---|
22 | __docformat__ = "javadoc en" |
---|
23 | |
---|
24 | import base |
---|
25 | from base import * |
---|
26 | |
---|
27 | import os |
---|
28 | import shutil |
---|
29 | import Blender |
---|
30 | |
---|
31 | def clamp(value): |
---|
32 | if value < 0.0: |
---|
33 | value = 0.0 |
---|
34 | elif value > 1.0: |
---|
35 | value = 1.0 |
---|
36 | return value |
---|
37 | |
---|
38 | class MaterialInterface: |
---|
39 | def getName(self): |
---|
40 | """Returns the material name. |
---|
41 | |
---|
42 | @return Material name. |
---|
43 | """ |
---|
44 | return |
---|
45 | def write(self, f): |
---|
46 | """Write material script entry. |
---|
47 | |
---|
48 | All used texture files are registered at the MaterialManager. |
---|
49 | |
---|
50 | @param f Material script file object to write into. |
---|
51 | """ |
---|
52 | return |
---|
53 | |
---|
54 | class DefaultMaterial(MaterialInterface): |
---|
55 | def __init__(self, manager, name): |
---|
56 | self.manager = manager |
---|
57 | self.name = name |
---|
58 | return |
---|
59 | def getName(self): |
---|
60 | return self.name |
---|
61 | def write(self, f): |
---|
62 | f.write("material %s\n" % self.getName()) |
---|
63 | f.write("{\n") |
---|
64 | self.writeTechniques(f) |
---|
65 | f.write("}\n") |
---|
66 | return |
---|
67 | def writeTechniques(self, f): |
---|
68 | f.write(indent(1) + "technique\n" + indent(1) + "{\n") |
---|
69 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
70 | # empty pass |
---|
71 | f.write(indent(2) + "}\n") # pass |
---|
72 | f.write(indent(1) + "}\n") # technique |
---|
73 | return |
---|
74 | |
---|
75 | class GameEngineMaterial(DefaultMaterial): |
---|
76 | def __init__(self, manager, blenderMesh, blenderFace, colouredAmbient): |
---|
77 | self.mesh = blenderMesh |
---|
78 | self.face = blenderFace |
---|
79 | self.colouredAmbient = colouredAmbient |
---|
80 | # check if a Blender material is assigned |
---|
81 | try: |
---|
82 | blenderMaterial = self.mesh.materials[self.face.mat] |
---|
83 | except: |
---|
84 | blenderMaterial = None |
---|
85 | self.material = blenderMaterial |
---|
86 | DefaultMaterial.__init__(self, manager, self._createName()) |
---|
87 | return |
---|
88 | def writeTechniques(self, f): |
---|
89 | mat = self.material |
---|
90 | #Mesh# if (not(mat) |
---|
91 | #Mesh# and not(self.mesh.vertexColors) |
---|
92 | #Mesh# and not(self.mesh.vertexUV or self.mesh.faceUV)): |
---|
93 | if (not(mat) |
---|
94 | and not(self.mesh.hasVertexColours()) |
---|
95 | and not(self.mesh.hasVertexUV()) |
---|
96 | and not(self.mesh.hasFaceUV())): |
---|
97 | # default material |
---|
98 | DefaultMaterial.writeTechniques(self, f) |
---|
99 | else: |
---|
100 | # default material |
---|
101 | # SOLID, white, no specular |
---|
102 | f.write(indent(1)+"technique\n") |
---|
103 | f.write(indent(1)+"{\n") |
---|
104 | f.write(indent(2)+"pass\n") |
---|
105 | f.write(indent(2)+"{\n") |
---|
106 | # ambient |
---|
107 | # (not used in Blender's game engine) |
---|
108 | if mat: |
---|
109 | if (not(mat.mode & Blender.Material.Modes["TEXFACE"]) |
---|
110 | and not(mat.mode & Blender.Material.Modes["VCOL_PAINT"]) |
---|
111 | and (self.colouredAmbient)): |
---|
112 | ambientRGBList = mat.rgbCol |
---|
113 | else: |
---|
114 | ambientRGBList = [1.0, 1.0, 1.0] |
---|
115 | # ambient <- amb * ambient RGB |
---|
116 | ambR = clamp(mat.amb * ambientRGBList[0]) |
---|
117 | ambG = clamp(mat.amb * ambientRGBList[1]) |
---|
118 | ambB = clamp(mat.amb * ambientRGBList[2]) |
---|
119 | ##f.write(indent(3)+"ambient %f %f %f\n" % (ambR, ambG, ambB)) |
---|
120 | # diffuse |
---|
121 | # (Blender's game engine uses vertex colours |
---|
122 | # instead of diffuse colour. |
---|
123 | # |
---|
124 | # diffuse is defined as |
---|
125 | # (mat->r, mat->g, mat->b)*(mat->emit + mat->ref) |
---|
126 | # but it's not used.) |
---|
127 | #Mesh#if self.mesh.vertexColors: |
---|
128 | if self.mesh.hasVertexColours(): |
---|
129 | #TODO: Broken in Blender 2.36. |
---|
130 | # Blender does not handle "texface" mesh with vertex colours |
---|
131 | f.write(indent(3)+"diffuse vertexcolour\n") |
---|
132 | elif mat: |
---|
133 | if (not(mat.mode & Blender.Material.Modes["TEXFACE"]) |
---|
134 | and not(mat.mode & Blender.Material.Modes["VCOL_PAINT"])): |
---|
135 | # diffuse <- rgbCol |
---|
136 | diffR = clamp(mat.rgbCol[0]) |
---|
137 | diffG = clamp(mat.rgbCol[1]) |
---|
138 | diffB = clamp(mat.rgbCol[2]) |
---|
139 | f.write(indent(3)+"diffuse %f %f %f\n" % (diffR, diffG, diffB)) |
---|
140 | elif (mat.mode & Blender.Material.Modes["VCOL_PAINT"]): |
---|
141 | f.write(indent(3)+"diffuse vertexcolour\n") |
---|
142 | if mat: |
---|
143 | # specular <- spec * specCol, hard/4.0 |
---|
144 | specR = clamp(mat.spec * mat.specCol[0]) |
---|
145 | specG = clamp(mat.spec * mat.specCol[1]) |
---|
146 | specB = clamp(mat.spec * mat.specCol[2]) |
---|
147 | specShine = mat.hard/4.0 |
---|
148 | f.write(indent(3)+"specular %f %f %f %f\n" % (specR, specG, specB, specShine)) |
---|
149 | # emissive |
---|
150 | # (not used in Blender's game engine) |
---|
151 | if(not(mat.mode & Blender.Material.Modes["TEXFACE"]) |
---|
152 | and not(mat.mode & Blender.Material.Modes["VCOL_PAINT"])): |
---|
153 | # emissive <-emit * rgbCol |
---|
154 | emR = clamp(mat.emit * mat.rgbCol[0]) |
---|
155 | emG = clamp(mat.emit * mat.rgbCol[1]) |
---|
156 | emB = clamp(mat.emit * mat.rgbCol[2]) |
---|
157 | ##f.write(indent(3)+"emissive %f %f %f\n" % (emR, emG, emB)) |
---|
158 | #Mesh#if self.mesh.faceUV: |
---|
159 | if self.mesh.hasFaceUV(): |
---|
160 | # mesh has texture values, resp. tface data |
---|
161 | # scene_blend <- transp |
---|
162 | if (self.face.transp == Blender.Mesh.FaceTranspModes["ALPHA"]): |
---|
163 | f.write(indent(3)+"scene_blend alpha_blend \n") |
---|
164 | elif (self.face.transp == Blender.NMesh.FaceTranspModes["ADD"]): |
---|
165 | f.write(indent(3)+"scene_blend add\n") |
---|
166 | # cull_hardware/cull_software |
---|
167 | if (self.face.mode & Blender.Mesh.FaceModes['TWOSIDE']): |
---|
168 | f.write(indent(3) + "cull_hardware none\n") |
---|
169 | f.write(indent(3) + "cull_software none\n") |
---|
170 | # shading |
---|
171 | # (Blender's game engine is initialized with glShadeModel(GL_FLAT)) |
---|
172 | ##f.write(indent(3) + "shading flat\n") |
---|
173 | # texture |
---|
174 | if (self.face.mode & Blender.Mesh.FaceModes['TEX']) and (self.face.image): |
---|
175 | f.write(indent(3)+"texture_unit\n") |
---|
176 | f.write(indent(3)+"{\n") |
---|
177 | f.write(indent(4)+"texture %s\n" % self.manager.registerTextureFile(self.face.image.filename)) |
---|
178 | f.write(indent(3)+"}\n") # texture_unit |
---|
179 | f.write(indent(2)+"}\n") # pass |
---|
180 | f.write(indent(1)+"}\n") # technique |
---|
181 | return |
---|
182 | # private |
---|
183 | def _createName(self): |
---|
184 | """Create unique material name. |
---|
185 | |
---|
186 | The name consists of several parts: |
---|
187 | <OL> |
---|
188 | <LI>rendering material name/</LI> |
---|
189 | <LI>blend mode (ALPHA, ADD, SOLID)</LI> |
---|
190 | <LI>/TEX</LI> |
---|
191 | <LI>/texture file name</LI> |
---|
192 | <LI>/VertCol</LI> |
---|
193 | <LI>/TWOSIDE></LI> |
---|
194 | </OL> |
---|
195 | """ |
---|
196 | materialName = '' |
---|
197 | # nonempty rendering material? |
---|
198 | if self.material: |
---|
199 | materialName += self.material.getName() + '/' |
---|
200 | # blend mode |
---|
201 | #Mesh#if self.mesh.faceUV and (self.face.transp == Blender.Mesh.FaceTranspModes['ALPHA']): |
---|
202 | if self.mesh.hasFaceUV() and (self.face.transp == Blender.Mesh.FaceTranspModes['ALPHA']): |
---|
203 | materialName += 'ALPHA' |
---|
204 | #Mesh#elif self.mesh.faceUV and (self.face.transp == Blender.Mesh.FaceTranspModes['ADD']): |
---|
205 | elif self.mesh.hasFaceUV() and (self.face.transp == Blender.Mesh.FaceTranspModes['ADD']): |
---|
206 | materialName += 'ADD' |
---|
207 | else: |
---|
208 | materialName += 'SOLID' |
---|
209 | # TEX face mode and texture? |
---|
210 | #Mesh#if self.mesh.faceUV and (self.face.mode & Blender.Mesh.FaceModes['TEX']): |
---|
211 | if self.mesh.hasFaceUV() and (self.face.mode & Blender.Mesh.FaceModes['TEX']): |
---|
212 | materialName += '/TEX' |
---|
213 | if self.face.image: |
---|
214 | materialName += '/' + PathName(self.face.image.filename).basename() |
---|
215 | # vertex colours? |
---|
216 | #Mesh#if self.mesh.vertexColors: |
---|
217 | if self.mesh.hasVertexColours(): |
---|
218 | materialName += '/VertCol' |
---|
219 | # two sided? |
---|
220 | #Mesh#if self.mesh.faceUV and (self.face.mode & Blender.Mesh.FaceModes['TWOSIDE']): |
---|
221 | if self.mesh.hasFaceUV() and (self.face.mode & Blender.Mesh.FaceModes['TWOSIDE']): |
---|
222 | materialName += '/TWOSIDE' |
---|
223 | return materialName |
---|
224 | |
---|
225 | class RenderingMaterial(DefaultMaterial): |
---|
226 | def __init__(self, manager, blenderMesh, blenderFace, colouredAmbient): |
---|
227 | self.mesh = blenderMesh |
---|
228 | self.face = blenderFace |
---|
229 | self.colouredAmbient = colouredAmbient |
---|
230 | self.key = 0 |
---|
231 | self.mTexUVCol = None |
---|
232 | self.mTexUVNor = None |
---|
233 | self.mTexUVCsp = None |
---|
234 | self.material = None |
---|
235 | try: |
---|
236 | self.material = self.mesh.materials[self.face.mat] |
---|
237 | except IndexError: |
---|
238 | Log.getSingleton().logWarning("Can't get material for mesh \"%s\"! Are any materials linked to object instead of linked to mesh?" % self.mesh.name) |
---|
239 | if self.material: |
---|
240 | self._generateKey() |
---|
241 | DefaultMaterial.__init__(self, manager, self._createName()) |
---|
242 | else: |
---|
243 | DefaultMaterial.__init__(self, manager, 'None') |
---|
244 | return |
---|
245 | def writeTechniques(self, f): |
---|
246 | # parse material |
---|
247 | if self.key: |
---|
248 | if self.TECHNIQUES.has_key(self.key): |
---|
249 | techniques = self.TECHNIQUES[self.key] |
---|
250 | techniques(self, f) |
---|
251 | else: |
---|
252 | # default |
---|
253 | self.writeColours(f) |
---|
254 | else: |
---|
255 | # Halo or empty material |
---|
256 | DefaultMaterial('').writeTechniques(f) |
---|
257 | return |
---|
258 | def writeColours(self, f): |
---|
259 | # receive_shadows |
---|
260 | self.writeReceiveShadows(f, 1) |
---|
261 | f.write(indent(1) + "technique\n" + indent(1) + "{\n") |
---|
262 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
263 | # ambient |
---|
264 | if (self.colouredAmbient): |
---|
265 | col = self.material.getRGBCol() |
---|
266 | else: |
---|
267 | col = [1.0, 1.0, 1.0] |
---|
268 | self.writeAmbient(f, col, 3) |
---|
269 | # diffuse |
---|
270 | self.writeDiffuse(f, self.material.rgbCol, 3) |
---|
271 | # specular |
---|
272 | self.writeSpecular(f, 3) |
---|
273 | # emissive |
---|
274 | self.writeEmissive(f, self.material.rgbCol, 3) |
---|
275 | # blend mode |
---|
276 | self.writeSceneBlend(f,3) |
---|
277 | # options |
---|
278 | self.writeCommonOptions(f, 3) |
---|
279 | # texture units |
---|
280 | self.writeDiffuseTexture(f, 3) |
---|
281 | f.write(indent(2) + "}\n") # pass |
---|
282 | f.write(indent(1) + "}\n") # technique |
---|
283 | return |
---|
284 | def writeTexFace(self, f): |
---|
285 | # preconditions: TEXFACE set |
---|
286 | # |
---|
287 | # Note that an additional Col texture replaces the |
---|
288 | # TEXFACE texture instead of blend over according to alpha. |
---|
289 | # |
---|
290 | # (amb+emit)textureCol + diffuseLight*ref*textureCol + specular |
---|
291 | # |
---|
292 | imageFileName = None |
---|
293 | if self.mTexUVCol: |
---|
294 | # COL MTex replaces UV/Image Editor texture |
---|
295 | imageFileName = self.manager.registerTextureFile(self.mTexUVCol.tex.getImage().getFilename()) |
---|
296 | #Mesh#elif (self.mesh.faceUV and self.face.image): |
---|
297 | elif (self.mesh.hasFaceUV() and self.face.image): |
---|
298 | # UV/Image Editor texture |
---|
299 | imageFileName = self.manager.registerTextureFile(self.face.image.filename) |
---|
300 | |
---|
301 | self.writeReceiveShadows(f, 1) |
---|
302 | f.write(indent(1) + "technique\n" + indent(1) + "{\n") |
---|
303 | col = [1.0, 1.0, 1.0] |
---|
304 | # texture pass |
---|
305 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
306 | self.writeAmbient(f, col, 3) |
---|
307 | self.writeDiffuse(f, col, 3) |
---|
308 | if not(imageFileName): |
---|
309 | self.writeSpecular(f, 3) |
---|
310 | self.writeEmissive(f, col, 3) |
---|
311 | self.writeSceneBlend(f,3) |
---|
312 | self.writeCommonOptions(f, 3) |
---|
313 | if imageFileName: |
---|
314 | f.write(indent(3) + "texture_unit\n") |
---|
315 | f.write(indent(3) + "{\n") |
---|
316 | f.write(indent(4) + "texture %s\n" % imageFileName) |
---|
317 | if self.mTexUVCol: |
---|
318 | self.writeTextureAddressMode(f, self.mTexUVCol, 4) |
---|
319 | self.writeTextureFiltering(f, self.mTexUVCol, 4) |
---|
320 | # multiply with factors |
---|
321 | f.write(indent(4) + "colour_op modulate\n") |
---|
322 | f.write(indent(3) + "}\n") # texture_unit |
---|
323 | f.write(indent(2) + "}\n") # texture pass |
---|
324 | # specular pass |
---|
325 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
326 | f.write(indent(3) + "ambient 0.0 0.0 0.0\n") |
---|
327 | f.write(indent(3) + "diffuse 0.0 0.0 0.0\n") |
---|
328 | self.writeSpecular(f, 3) |
---|
329 | f.write(indent(3) + "scene_blend add\n") |
---|
330 | hasAlpha = 0 |
---|
331 | if (self.material.getAlpha() < 1.0): |
---|
332 | hasAlpha = 1 |
---|
333 | else: |
---|
334 | for mtex in self.material.getTextures(): |
---|
335 | if mtex: |
---|
336 | if ((mtex.tex.type == Blender.Texture.Types['IMAGE']) |
---|
337 | and (mtex.mapto & Blender.Texture.MapTo['ALPHA'])): |
---|
338 | hasAlpha = 1 |
---|
339 | if (hasAlpha): |
---|
340 | f.write(indent(3) + "depth_write off\n") |
---|
341 | self.writeCommonOptions(f, 3) |
---|
342 | f.write(indent(2) + "}\n") # pass |
---|
343 | f.write(indent(1) + "}\n") # technique |
---|
344 | return |
---|
345 | def writeVertexColours(self, f): |
---|
346 | # preconditions: VCOL_PAINT set |
---|
347 | # |
---|
348 | # ambient = Amb*White resp. Amb*VCol if "Coloured Ambient" |
---|
349 | # diffuse = Ref*VCol |
---|
350 | # specular = Spec*SpeRGB, Hard/4.0 |
---|
351 | # emissive = Emit*VCol |
---|
352 | # alpha = A |
---|
353 | # |
---|
354 | # Best match without vertex shader: |
---|
355 | # ambient = Amb*white |
---|
356 | # diffuse = Ref*VCol |
---|
357 | # specular = Spec*SpeRGB, Hard/4.0 |
---|
358 | # emissive = black |
---|
359 | # alpha = 1 |
---|
360 | # |
---|
361 | self.writeReceiveShadows(f, 1) |
---|
362 | f.write(indent(1) + "technique\n" + indent(1) + "{\n") |
---|
363 | if (self.material.mode & Blender.Material.Modes['SHADELESS']): |
---|
364 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
365 | self.writeCommonOptions(f,3) |
---|
366 | f.write(indent(2) + "}\n") |
---|
367 | else: |
---|
368 | # vertex colour pass |
---|
369 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
370 | f.write(indent(3) + "ambient 0.0 0.0 0.0\n") |
---|
371 | f.write(indent(3) + "diffuse vertexcolour\n") |
---|
372 | self.writeCommonOptions(f, 3) |
---|
373 | f.write(indent(2) + "}\n") # vertex colour pass |
---|
374 | |
---|
375 | # factor pass |
---|
376 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
377 | f.write(indent(3) + "ambient 0.0 0.0 0.0\n") |
---|
378 | ref = self.material.getRef() |
---|
379 | f.write(indent(3) + "diffuse %f %f %f\n" % (ref, ref, ref)) |
---|
380 | f.write(indent(3) + "scene_blend modulate\n") |
---|
381 | self.writeCommonOptions(f, 3) |
---|
382 | f.write(indent(2) + "}\n") # factor pass |
---|
383 | |
---|
384 | # ambient and specular pass |
---|
385 | f.write(indent(2) + "pass\n" + indent(2) + "{\n") |
---|
386 | self.writeAmbient(f, [1.0, 1.0, 1.0], 3) |
---|
387 | f.write(indent(3) + "diffuse 0.0 0.0 0.0\n") |
---|
388 | self.writeSpecular(f, 3) |
---|
389 | f.write(indent(3) + "scene_blend add\n") |
---|
390 | self.writeCommonOptions(f, 3) |
---|
391 | f.write(indent(2) + "}\n") # specular pass |
---|
392 | |
---|
393 | f.write(indent(1) + "}\n") # technique |
---|
394 | return |
---|
395 | def writeNormalMap(self, f): |
---|
396 | # preconditions COL and NOR textures |
---|
397 | colImage = self.manager.registerTextureFile(self.mTexUVCol.tex.image.filename) |
---|
398 | norImage = self.manager.registerTextureFile(self.mTexUVNor.tex.image.filename) |
---|
399 | f.write(""" technique |
---|
400 | { |
---|
401 | pass |
---|
402 | { |
---|
403 | ambient 1 1 1 |
---|
404 | diffuse 0 0 0 |
---|
405 | specular 0 0 0 0 |
---|
406 | vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture |
---|
407 | { |
---|
408 | param_named_auto worldViewProj worldviewproj_matrix |
---|
409 | param_named_auto ambient ambient_light_colour |
---|
410 | } |
---|
411 | } |
---|
412 | pass |
---|
413 | { |
---|
414 | ambient 0 0 0 |
---|
415 | iteration once_per_light |
---|
416 | scene_blend add |
---|
417 | vertex_program_ref Examples/BumpMapVPSpecular |
---|
418 | { |
---|
419 | param_named_auto lightPosition light_position_object_space 0 |
---|
420 | param_named_auto eyePosition camera_position_object_space |
---|
421 | param_named_auto worldViewProj worldviewproj_matrix |
---|
422 | } |
---|
423 | fragment_program_ref Examples/BumpMapFPSpecular |
---|
424 | { |
---|
425 | param_named_auto lightDiffuse light_diffuse_colour 0 |
---|
426 | param_named_auto lightSpecular light_specular_colour 0 |
---|
427 | } |
---|
428 | texture_unit |
---|
429 | { |
---|
430 | texture %s |
---|
431 | colour_op replace |
---|
432 | } |
---|
433 | texture_unit |
---|
434 | { |
---|
435 | cubic_texture nm.png combinedUVW |
---|
436 | tex_coord_set 1 |
---|
437 | tex_address_mode clamp |
---|
438 | } |
---|
439 | texture_unit |
---|
440 | { |
---|
441 | cubic_texture nm.png combinedUVW |
---|
442 | tex_coord_set 2 |
---|
443 | tex_address_mode clamp |
---|
444 | } |
---|
445 | } |
---|
446 | pass |
---|
447 | { |
---|
448 | lighting off |
---|
449 | vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture |
---|
450 | { |
---|
451 | param_named_auto worldViewProj worldviewproj_matrix |
---|
452 | param_named ambient float4 1 1 1 1 |
---|
453 | } |
---|
454 | scene_blend dest_colour zero |
---|
455 | texture_unit |
---|
456 | { |
---|
457 | texture %s |
---|
458 | } |
---|
459 | } |
---|
460 | } |
---|
461 | technique |
---|
462 | { |
---|
463 | pass |
---|
464 | { |
---|
465 | ambient 1 1 1 |
---|
466 | diffuse 0 0 0 |
---|
467 | specular 0 0 0 0 |
---|
468 | vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture |
---|
469 | { |
---|
470 | param_named_auto worldViewProj worldviewproj_matrix |
---|
471 | param_named_auto ambient ambient_light_colour |
---|
472 | } |
---|
473 | } |
---|
474 | pass |
---|
475 | { |
---|
476 | ambient 0 0 0 |
---|
477 | iteration once_per_light |
---|
478 | scene_blend add |
---|
479 | vertex_program_ref Examples/BumpMapVP |
---|
480 | { |
---|
481 | param_named_auto lightPosition light_position_object_space 0 |
---|
482 | param_named_auto eyePosition camera_position_object_space |
---|
483 | param_named_auto worldViewProj worldviewproj_matrix |
---|
484 | } |
---|
485 | texture_unit |
---|
486 | { |
---|
487 | texture %s |
---|
488 | colour_op replace |
---|
489 | } |
---|
490 | texture_unit |
---|
491 | { |
---|
492 | cubic_texture nm.png combinedUVW |
---|
493 | tex_coord_set 1 |
---|
494 | tex_address_mode clamp |
---|
495 | colour_op_ex dotproduct src_texture src_current |
---|
496 | colour_op_multipass_fallback dest_colour zero |
---|
497 | } |
---|
498 | } |
---|
499 | pass |
---|
500 | { |
---|
501 | lighting off |
---|
502 | vertex_program_ref Ogre/BasicVertexPrograms/AmbientOneTexture |
---|
503 | { |
---|
504 | param_named_auto worldViewProj worldviewproj_matrix |
---|
505 | param_named ambient float4 1 1 1 1 |
---|
506 | } |
---|
507 | scene_blend dest_colour zero |
---|
508 | texture_unit |
---|
509 | { |
---|
510 | texture %s |
---|
511 | } |
---|
512 | } |
---|
513 | } |
---|
514 | """ % (norImage, colImage, norImage, colImage)) |
---|
515 | return |
---|
516 | def writeReceiveShadows(self, f, indentation=0): |
---|
517 | if (self.material.mode & Blender.Material.Modes["SHADOW"]): |
---|
518 | f.write(indent(indentation)+"receive_shadows on\n") |
---|
519 | else: |
---|
520 | f.write(indent(indentation)+"receive_shadows off\n") |
---|
521 | return |
---|
522 | def writeAmbient(self, f, col, indentation=0): |
---|
523 | # ambient <- amb * ambient RGB |
---|
524 | ambR = clamp(self.material.getAmb() * col[0]) |
---|
525 | ambG = clamp(self.material.getAmb() * col[1]) |
---|
526 | ambB = clamp(self.material.getAmb() * col[2]) |
---|
527 | if len(col) < 4: |
---|
528 | alpha = self.material.getAlpha() |
---|
529 | else: |
---|
530 | alpha = col[3] |
---|
531 | f.write(indent(indentation)+"ambient %f %f %f %f\n" % (ambR, ambG, ambB, alpha)) |
---|
532 | return |
---|
533 | def writeDiffuse(self, f, col, indentation=0): |
---|
534 | # diffuse = reflectivity*colour |
---|
535 | diffR = clamp(col[0] * self.material.getRef()) |
---|
536 | diffG = clamp(col[1] * self.material.getRef()) |
---|
537 | diffB = clamp(col[2] * self.material.getRef()) |
---|
538 | if len(col) < 4: |
---|
539 | alpha = self.material.getAlpha() |
---|
540 | else: |
---|
541 | alpha = col[3] |
---|
542 | f.write(indent(indentation)+"diffuse %f %f %f %f\n" % (diffR, diffG, diffB, alpha)) |
---|
543 | return |
---|
544 | def writeSpecular(self, f, indentation=0): |
---|
545 | # specular <- spec * specCol, hard/4.0 |
---|
546 | specR = clamp(self.material.getSpec() * self.material.getSpecCol()[0]) |
---|
547 | specG = clamp(self.material.getSpec() * self.material.getSpecCol()[1]) |
---|
548 | specB = clamp(self.material.getSpec() * self.material.getSpecCol()[2]) |
---|
549 | specShine = self.material.getHardness()/4.0 |
---|
550 | alpha = self.material.getAlpha() |
---|
551 | f.write(indent(indentation)+"specular %f %f %f %f %f\n" % (specR, specG, specB, alpha, specShine)) |
---|
552 | return |
---|
553 | def writeEmissive(self, f, col, indentation=0): |
---|
554 | # emissive <-emit * rgbCol |
---|
555 | emR = clamp(self.material.getEmit() * col[0]) |
---|
556 | emG = clamp(self.material.getEmit() * col[1]) |
---|
557 | emB = clamp(self.material.getEmit() * col[2]) |
---|
558 | if len(col) < 4: |
---|
559 | alpha = self.material.getAlpha() |
---|
560 | else: |
---|
561 | alpha = col[3] |
---|
562 | f.write(indent(indentation)+"emissive %f %f %f %f\n" % (emR, emG, emB, alpha)) |
---|
563 | return |
---|
564 | def writeSceneBlend(self, f, indentation=0): |
---|
565 | hasAlpha = 0 |
---|
566 | if (self.material.getAlpha() < 1.0): |
---|
567 | hasAlpha = 1 |
---|
568 | else: |
---|
569 | for mtex in self.material.getTextures(): |
---|
570 | if mtex: |
---|
571 | if ((mtex.tex.type == Blender.Texture.Types['IMAGE']) |
---|
572 | and (mtex.mapto & Blender.Texture.MapTo['ALPHA'])): |
---|
573 | hasAlpha = 1 |
---|
574 | if (hasAlpha): |
---|
575 | f.write(indent(indentation) + "scene_blend alpha_blend\n") |
---|
576 | f.write(indent(indentation) + "depth_write off\n") |
---|
577 | return |
---|
578 | def writeCommonOptions(self, f, indentation=0): |
---|
579 | # Shadeless, ZInvert, NoMist, Env |
---|
580 | # depth_func <- ZINVERT; ENV |
---|
581 | if (self.material.mode & Blender.Material.Modes['ENV']): |
---|
582 | f.write(indent(indentation)+"depth_func always_fail\n") |
---|
583 | elif (self.material.mode & Blender.Material.Modes['ZINVERT']): |
---|
584 | f.write(indent(indentation)+"depth_func greater_equal\n") |
---|
585 | # twoside |
---|
586 | #Mesh#if self.mesh.faceUV and (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']): |
---|
587 | if self.mesh.hasFaceUV() and (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']): |
---|
588 | f.write(indent(3) + "cull_hardware none\n") |
---|
589 | f.write(indent(3) + "cull_software none\n") |
---|
590 | # lighting <- SHADELESS |
---|
591 | if (self.material.mode & Blender.Material.Modes['SHADELESS']): |
---|
592 | f.write(indent(indentation)+"lighting off\n") |
---|
593 | # fog_override <- NOMIST |
---|
594 | if (self.material.mode & Blender.Material.Modes['NOMIST']): |
---|
595 | f.write(indent(indentation)+"fog_override true\n") |
---|
596 | return |
---|
597 | def writeDiffuseTexture(self, f, indentation = 0): |
---|
598 | if self.mTexUVCol: |
---|
599 | f.write(indent(indentation)+"texture_unit\n") |
---|
600 | f.write(indent(indentation)+"{\n") |
---|
601 | f.write(indent(indentation + 1) + "texture %s\n" % self.manager.registerTextureFile(self.mTexUVCol.tex.getImage().getFilename())) |
---|
602 | self.writeTextureAddressMode(f, self.mTexUVCol, indentation + 1) |
---|
603 | self.writeTextureFiltering(f, self.mTexUVCol, indentation + 1) |
---|
604 | self.writeTextureColourOp(f, self.mTexUVCol, indentation + 1) |
---|
605 | f.write(indent(indentation)+"}\n") # texture_unit |
---|
606 | return |
---|
607 | def writeTextureAddressMode(self, f, blenderMTex, indentation = 0): |
---|
608 | # tex_address_mode inside texture_unit |
---|
609 | # |
---|
610 | # EXTEND | clamp |
---|
611 | # CLIP | |
---|
612 | # CLIPCUBE | |
---|
613 | # REPEAT | wrap |
---|
614 | # |
---|
615 | if (blenderMTex.tex.extend & Blender.Texture.ExtendModes['REPEAT']): |
---|
616 | f.write(indent(indentation) + "tex_address_mode wrap\n") |
---|
617 | elif (blenderMTex.tex.extend & Blender.Texture.ExtendModes['EXTEND']): |
---|
618 | f.write(indent(indentation) + "tex_address_mode clamp\n") |
---|
619 | return |
---|
620 | def writeTextureFiltering(self, f, blenderMTex, indentation = 0): |
---|
621 | # filtering inside texture_unit |
---|
622 | # |
---|
623 | # InterPol | MidMap | filtering |
---|
624 | # ---------+--------+---------- |
---|
625 | # yes | yes | trilinear |
---|
626 | # yes | no | linear linear none |
---|
627 | # no | yes | bilinear |
---|
628 | # no | no | none |
---|
629 | # |
---|
630 | if (blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['INTERPOL']): |
---|
631 | if (blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['MIPMAP']): |
---|
632 | f.write(indent(indentation) + "filtering trilinear\n") |
---|
633 | else: |
---|
634 | f.write(indent(indentation) + "filtering linear linear none\n") |
---|
635 | else: |
---|
636 | if (blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['MIPMAP']): |
---|
637 | f.write(indent(indentation) + "filtering bilinear\n") |
---|
638 | else: |
---|
639 | f.write(indent(indentation) + "filtering none\n") |
---|
640 | return |
---|
641 | def writeTextureColourOp(self, f, blenderMTex, indentation = 0): |
---|
642 | # colour_op inside texture_unit |
---|
643 | if ((blenderMTex.tex.imageFlags & Blender.Texture.ImageFlags['USEALPHA']) |
---|
644 | and not(blenderMTex.mapto & Blender.Texture.MapTo['ALPHA'])): |
---|
645 | f.write(indent(indentation) + "colour_op alpha_blend\n") |
---|
646 | return |
---|
647 | # private |
---|
648 | def _createName(self): |
---|
649 | # must be called after _generateKey() |
---|
650 | materialName = self.material.getName() |
---|
651 | # two sided? |
---|
652 | #Mesh#if self.mesh.faceUV and (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']): |
---|
653 | if self.mesh.hasFaceUV() and (self.face.mode & Blender.NMesh.FaceModes['TWOSIDE']): |
---|
654 | materialName += '/TWOSIDE' |
---|
655 | # use UV/Image Editor texture? |
---|
656 | if ((self.key & self.TEXFACE) and not(self.key & self.IMAGEUVCOL)): |
---|
657 | materialName += '/TEXFACE' |
---|
658 | if self.face.image: |
---|
659 | materialName += '/' + PathName(self.face.image.filename).basename() |
---|
660 | return materialName |
---|
661 | def _generateKey(self): |
---|
662 | # generates key and populates mTex fields |
---|
663 | if self.material: |
---|
664 | if not(self.material.mode & Blender.Material.Modes['HALO']): |
---|
665 | self.key |= self.NONHALO |
---|
666 | if (self.material.mode & Blender.Material.Modes['VCOL_LIGHT']): |
---|
667 | self.key |= self.VCOLLIGHT |
---|
668 | if (self.material.mode & Blender.Material.Modes['VCOL_PAINT']): |
---|
669 | self.key |= self.VCOLPAINT |
---|
670 | if (self.material.mode & Blender.Material.Modes['TEXFACE']): |
---|
671 | self.key |= self.TEXFACE |
---|
672 | # textures |
---|
673 | for mtex in self.material.getTextures(): |
---|
674 | if mtex: |
---|
675 | if (mtex.tex.type == Blender.Texture.Types['IMAGE']): |
---|
676 | if (mtex.texco & Blender.Texture.TexCo['UV']): |
---|
677 | if (mtex.mapto & Blender.Texture.MapTo['COL']): |
---|
678 | self.key |= self.IMAGEUVCOL |
---|
679 | self.mTexUVCol = mtex |
---|
680 | if (mtex.mapto & Blender.Texture.MapTo['NOR']): |
---|
681 | # Check "Normal Map" image option |
---|
682 | if (mtex.tex.imageFlags & 2048): |
---|
683 | self.key |= self.IMAGEUVNOR |
---|
684 | self.mTexUVNor = mtex |
---|
685 | # else bumpmap |
---|
686 | if (mtex.mapto & Blender.Texture.MapTo['CSP']): |
---|
687 | self.key |= self.IMAGEUVCSP |
---|
688 | self.mTexUVCsp = mtex |
---|
689 | return |
---|
690 | NONHALO = 1 |
---|
691 | VCOLLIGHT = 2 |
---|
692 | VCOLPAINT = 4 |
---|
693 | TEXFACE = 8 |
---|
694 | IMAGEUVCOL = 16 |
---|
695 | IMAGEUVNOR = 32 |
---|
696 | IMAGEUVCSP = 64 |
---|
697 | # material techniques export methods |
---|
698 | TECHNIQUES = { |
---|
699 | NONHALO|IMAGEUVCOL : writeColours, |
---|
700 | NONHALO|IMAGEUVCOL|IMAGEUVCSP : writeColours, |
---|
701 | NONHALO|TEXFACE : writeTexFace, |
---|
702 | NONHALO|TEXFACE|VCOLLIGHT : writeTexFace, |
---|
703 | NONHALO|TEXFACE|IMAGEUVCOL : writeTexFace, |
---|
704 | NONHALO|TEXFACE|IMAGEUVNOR : writeTexFace, |
---|
705 | NONHALO|TEXFACE|IMAGEUVCSP : writeTexFace, |
---|
706 | NONHALO|TEXFACE|VCOLLIGHT|IMAGEUVCOL : writeTexFace, |
---|
707 | NONHALO|TEXFACE|VCOLLIGHT|IMAGEUVNOR : writeTexFace, |
---|
708 | NONHALO|TEXFACE|VCOLLIGHT|IMAGEUVCSP : writeTexFace, |
---|
709 | NONHALO|TEXFACE|IMAGEUVCOL|IMAGEUVCSP : writeTexFace, |
---|
710 | NONHALO|TEXFACE|IMAGEUVNOR|IMAGEUVCSP : writeTexFace, |
---|
711 | NONHALO|TEXFACE|VCOLLIGHT|IMAGEUVCOL|IMAGEUVCSP : writeTexFace, |
---|
712 | NONHALO|TEXFACE|VCOLLIGHT|IMAGEUVNOR|IMAGEUVCSP : writeTexFace, |
---|
713 | NONHALO|VCOLPAINT : writeVertexColours, |
---|
714 | NONHALO|VCOLPAINT|VCOLLIGHT : writeVertexColours, |
---|
715 | NONHALO|IMAGEUVCOL|IMAGEUVNOR : writeNormalMap, |
---|
716 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT : writeNormalMap, |
---|
717 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT : writeNormalMap, |
---|
718 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|TEXFACE : writeNormalMap, |
---|
719 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|IMAGEUVCSP : writeNormalMap, |
---|
720 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT : writeNormalMap, |
---|
721 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|TEXFACE : writeNormalMap, |
---|
722 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|IMAGEUVCSP : writeNormalMap, |
---|
723 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT|TEXFACE : writeNormalMap, |
---|
724 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT|IMAGEUVCSP : writeNormalMap, |
---|
725 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|TEXFACE|IMAGEUVCSP : writeNormalMap, |
---|
726 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLPAINT|TEXFACE|IMAGEUVCSP : writeNormalMap, |
---|
727 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|TEXFACE|IMAGEUVCSP : writeNormalMap, |
---|
728 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT|IMAGEUVCSP : writeNormalMap, |
---|
729 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT|TEXFACE : writeNormalMap, |
---|
730 | NONHALO|IMAGEUVCOL|IMAGEUVNOR|VCOLLIGHT|VCOLPAINT|TEXFACE|IMAGEUVCSP : writeNormalMap |
---|
731 | } |
---|
732 | |
---|
733 | class MaterialManager: |
---|
734 | """Manages database of material definitions. |
---|
735 | """ |
---|
736 | #TODO append to existing material script |
---|
737 | def __init__(self, dir=None, file=None): |
---|
738 | """Constructor. |
---|
739 | |
---|
740 | @param path Directory to the material file. Default is directory of the last file read or written with Blender. |
---|
741 | @param file Material script file. Default is current scene name with ".material" prefix. |
---|
742 | """ |
---|
743 | self.dir = dir or Blender.sys.dirname(Blender.Get('filename')) |
---|
744 | self.file = file or (Blender.Scene.GetCurrent().getName() + ".material") |
---|
745 | # key=name, value=material |
---|
746 | self.materialsDict = {} |
---|
747 | # key=basename, value=path |
---|
748 | self.textureFilesDict = {} |
---|
749 | return |
---|
750 | def getMaterial(self, bMesh, bMFace, colouredAmbient, gameEngineMaterial): |
---|
751 | """Returns material of a given face or <code>None</code>. |
---|
752 | """ |
---|
753 | ## get name of export material for Blender's material settings of that face. |
---|
754 | faceMaterial = None |
---|
755 | if gameEngineMaterial: |
---|
756 | #Mesh#if bMesh.faceUV and not(bMFace.mode & Blender.Mesh.FaceModes['INVISIBLE']): |
---|
757 | if bMesh.hasFaceUV() and not(bMFace.mode & Blender.Mesh.FaceModes['INVISIBLE']): |
---|
758 | if (bMFace.image and (bMFace.mode & Blender.Mesh.FaceModes['TEX'])): |
---|
759 | # image texture |
---|
760 | faceMaterial = GameEngineMaterial(self, bMesh, bMFace, colouredAmbient) |
---|
761 | else: |
---|
762 | # material only |
---|
763 | bMaterial = None |
---|
764 | try: |
---|
765 | bMaterial = bMesh.materials[bMFace.mat] |
---|
766 | except: |
---|
767 | Log.getSingleton().logWarning("Face without material assignment in mesh \"%s\"!" % bMesh.name) |
---|
768 | if bMaterial: |
---|
769 | faceMaterial = GameEngineMaterial(self, bMesh, bMFace, colouredAmbient) |
---|
770 | else: |
---|
771 | faceMaterial = DefaultMaterial(self, 'default') |
---|
772 | else: |
---|
773 | # rendering material |
---|
774 | bMaterial = None |
---|
775 | try: |
---|
776 | bMaterial = bMesh.materials[bMFace.mat] |
---|
777 | except: |
---|
778 | Log.getSingleton().logError("Face without material assignment in mesh \"%s\"!" % bMesh.name) |
---|
779 | if bMaterial: |
---|
780 | faceMaterial = RenderingMaterial(self, bMesh, bMFace, colouredAmbient) |
---|
781 | else: |
---|
782 | faceMaterial = DefaultMaterial(self, 'default') |
---|
783 | ## return material or None |
---|
784 | material = None |
---|
785 | if faceMaterial: |
---|
786 | if not(self.materialsDict.has_key(faceMaterial.getName())): |
---|
787 | self.materialsDict[faceMaterial.getName()] = faceMaterial |
---|
788 | material = self.materialsDict[faceMaterial.getName()] |
---|
789 | return material |
---|
790 | def registerTextureFile(self, path): |
---|
791 | """Register texture for export. |
---|
792 | |
---|
793 | @param path Texture file path, i.e. dirname and basename. |
---|
794 | @return Basename of texture file. |
---|
795 | """ |
---|
796 | texturePath = PathName(path) |
---|
797 | key = texturePath.basename() |
---|
798 | if self.textureFilesDict.has_key(key): |
---|
799 | if (path != self.textureFilesDict[key]): |
---|
800 | Log.getSingleton().logWarning('Texture filename conflict: \"%s\"' % key) |
---|
801 | Log.getSingleton().logWarning(' Location: \"%s\"' % path) |
---|
802 | Log.getSingleton().logWarning(' Conflicting location: \"%s\"' % self.textureFilesDict[key]) |
---|
803 | self.textureFilesDict[key] = path |
---|
804 | return key |
---|
805 | def export(self, dir=None, file=None, copyTextures=False): |
---|
806 | exportDir = dir or self.dir |
---|
807 | exportFile = file or self.file |
---|
808 | Log.getSingleton().logInfo("Exporting materials \"%s\"" % exportFile) |
---|
809 | f = open(Blender.sys.join(exportDir, exportFile), "w") |
---|
810 | for material in self.materialsDict.values(): |
---|
811 | material.write(f) |
---|
812 | f.close() |
---|
813 | if copyTextures and os.path.exists(dir): |
---|
814 | baseDirname = os.path.dirname(Blender.Get("filename")) |
---|
815 | for path in self.textureFilesDict.values(): |
---|
816 | # convert Blender's relative paths "//" to absolute path |
---|
817 | if (path[0:2] == "//"): |
---|
818 | Log.getSingleton().logInfo("Converting relative image name \"%s\"" % path) |
---|
819 | path = os.path.join(baseDirname, path[2:]) |
---|
820 | if os.path.exists(path): |
---|
821 | # copy texture to dir |
---|
822 | Log.getSingleton().logInfo("Copying texture \"%s\"" % path) |
---|
823 | try: |
---|
824 | shutil.copy(path, dir) |
---|
825 | except (IOError, OSError), detail: |
---|
826 | Log.getSingleton().logError("Error copying \"%s\": %s" % (path, str(detail))) |
---|
827 | else: |
---|
828 | Log.getSingleton().logWarning("Can't copy texture \"%s\" because file does not exists!" % path) |
---|
829 | return |
---|