-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathtexture_manager.py
260 lines (191 loc) · 9.16 KB
/
texture_manager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
import FreeCAD
import math
import json
from pivy import coin
import arch_texture_utils.faceset_utils as faceset_utils
import arch_texture_utils.py2_utils as py2_utils
class TextureConfigEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, FreeCAD.Vector):
return [obj.x, obj.y, obj.z]
return json.JSONEncoder.default(self, obj)
class TextureConfigDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
json.JSONDecoder.__init__(
self, object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, dct):
if 'vertices' in dct:
vectors = [FreeCAD.Vector(vertices[0], vertices[1], vertices[2])
for vertices in dct['vertices']]
dct['vertices'] = vectors
return dct
class TextureManager():
def __init__(self, fileObject=None):
if fileObject is None:
self.textureData = {
'materials': {
# '<mat_name>': {
# 'file': 'path_to_texture',
# 'bumpMap': 'path_to_texture',
# 'realSize': None | {
# 's': <length_in_mm>,
# 't': <height_in_mm>
# }
# }
},
'faceOverrides': [
# {
# 'vertices': [],
# 'objectName': '<name_of_object_the_face_belongs_to>',
# 'rotation': <rotation_in_degrees>
# }
]
}
else:
try:
self.textureData = json.load(
fileObject, encoding='utf-8', cls=TextureConfigDecoder)
finally:
fileObject.close()
self.textureCache = {
# '<file_name>': texture
}
self.bumpMapCache = {
# '<file_name>': bumpmap
}
self.texturedObjects = [
#(object, shadedNode, (textureUnit, texture, transform), (material, originalDiffuseColor))
]
def export(self, fileObject):
try:
json.dump(self.textureData, fileObject, sort_keys=True,
indent=4, ensure_ascii=False, cls=TextureConfigEncoder)
finally:
fileObject.close()
def serializeTextureData(self):
return json.dumps(self.textureData, sort_keys=True, indent=4, ensure_ascii=False, cls=TextureConfigEncoder)
def deserializeTextureData(self, textureDataAsString):
self.textureData = json.loads(
textureDataAsString, encoding='utf-8', cls=TextureConfigDecoder)
def textureObjects(self, debug=False):
# Make sure that no old textures are left. Otherwise we could end up with duplicate textures
self.removeTextures()
FreeCAD.Console.PrintMessage('Texturing objects\n')
for o in FreeCAD.ActiveDocument.Objects:
if self.isTexturable(o):
# Test Script for bump mapping is here: https://forum.freecadweb.org/viewtopic.php?f=10&t=37255&p=319329#p319329
texture, bumpMap, textureConfig = self.getTextureForMaterial(
o.Material)
if texture is not None:
print('Texturing %s' % (o.Label,))
textureUnit = None
rootnode = o.ViewObject.RootNode
switch = faceset_utils.findSwitch(rootnode)
shadedNode = faceset_utils.findShadedNode(switch)
if shadedNode is None:
print('Object %s has no shaded node. Skipping...' % (o.Label,))
continue
brep = faceset_utils.findBrepFaceset(shadedNode)
material = faceset_utils.findMaterial(shadedNode)
vertexCoordinates = faceset_utils.findVertexCoordinates(
rootnode)
transform = faceset_utils.findTransform(rootnode)
originalDiffuseColor = self.updateMaterialColors(material)
faceSet = faceset_utils.buildFaceSet(
brep, vertexCoordinates, self.getFaceOverrides(), transform)
textureCoords = faceSet.calculateTextureCoordinates(
textureConfig['realSize'])
if debug:
faceSet.printData(textureConfig['realSize'], 4)
self.setupTextureCoordinateIndex(brep)
shadedNode.insertChild(texture, 1)
shadedNode.insertChild(textureCoords, 1)
# Only add the texture unit when the bump map is set
# Otherwise the default is OK
if bumpMap is not None:
textureUnit = coin.SoTextureUnit()
textureUnit.unit.setValue(1)
shadedNode.insertChild(textureUnit, 1)
if bumpMap is not None:
# Bump map coordinates do not work, we have to use texture coordinates
# Skipping the coordinates also ends in an access violation
shadedNode.insertChild(textureCoords, 1)
shadedNode.insertChild(bumpMap, 1)
self.texturedObjects.append(
(o, shadedNode, (textureUnit, texture, textureCoords, bumpMap), (material, originalDiffuseColor)))
def updateMaterialColors(self, material):
originalDiffuseColor = coin.SoMFColor()
originalDiffuseColor.copyFrom(material.diffuseColor)
material.diffuseColor.setValue(1.0, 1.0, 1.0)
return originalDiffuseColor
def ensureFaceOverrides(self):
if 'faceOverrides' not in self.textureData:
self.textureData['faceOverrides'] = []
return self.textureData['faceOverrides']
def getFaceOverrides(self):
if 'faceOverrides' in self.textureData:
return self.textureData['faceOverrides']
else:
return None
def removeTextures(self):
FreeCAD.Console.PrintMessage('Removing Textures\n')
for o, shadedNode, coinData, materialData in self.texturedObjects:
if coinData[0] is not None:
shadedNode.removeChild(coinData[0])
if coinData[1] is not None:
shadedNode.removeChild(coinData[1])
if coinData[2] is not None:
shadedNode.removeChild(coinData[2])
if coinData[3] is not None:
shadedNode.removeChild(coinData[3])
# When a bump map is set, the texture coordinate is added twice. So remove it again
shadedNode.removeChild(coinData[2])
material = materialData[0]
material.diffuseColor.deleteValues(0)
material.diffuseColor.setValues(
0, len(materialData[1]), materialData[1])
self.texturedObjects = []
def isTexturable(self, o):
if not hasattr(o, 'Shape') or o.Shape is None or o.Shape.isNull():
return False
if not hasattr(o, 'Material') or o.Material is None or o.Material == '':
return False
if not hasattr(o.Material, 'Name') or o.Material.Name is None or o.Material.Name == '':
return False
return o.ViewObject.Visibility
def setupTextureCoordinateIndex(self, brep):
coordinateIndex = brep.coordIndex.getValues()
brep.textureCoordIndex.setValues(
0, len(coordinateIndex), coordinateIndex)
def getTextureForMaterial(self, material):
materialName = material.Name
if materialName in self.textureData['materials']:
materialConfig = self.textureData['materials'][materialName]
imageFile = py2_utils.textureFileString(materialConfig['file'])
bumpMapFile = None
bumpMap = None
if 'bumpMap' in materialConfig:
bumpMapFile = py2_utils.textureFileString(
materialConfig['bumpMap'])
if imageFile not in self.textureCache:
tex = coin.SoTexture2()
tex.filename = imageFile
self.textureCache[imageFile] = tex
if bumpMapFile is not None:
if bumpMapFile not in self.bumpMapCache:
bumpMap = coin.SoBumpMap()
bumpMap.filename.setValue(bumpMapFile)
self.bumpMapCache[bumpMapFile] = bumpMap
bumpMap = self.bumpMapCache[bumpMapFile]
texture = self.textureCache[imageFile]
return (texture, bumpMap, materialConfig)
return (None, None, None)
if __name__ == "__main__":
from os import path
import arch_texture_utils.qtutils as qtutils
if FreeCAD.ActiveDocument is None:
qtutils.showInfo('No Document', 'Create a document to continue.')
else:
configFile = path.join(path.dirname(path.realpath(
__file__)), 'textures', FreeCAD.ActiveDocument.Name + '.json')
TextureManager(open(configFile, 'r')).textureObjects(debug=False)