Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线
2026/9/18 13:16:49 网站建设 项目流程

系列文章

基础
Python+OpenGL绘制3D模型(一)Python 和 PyQt环境搭建
Python+OpenGL绘制3D模型(二)程序框架PyQt5
Python+OpenGL绘制3D模型(三)程序框架PyQt6
Python+OpenGL绘制3D模型(四)绘制线段
Python+OpenGL绘制3D模型(五)绘制三角型
Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射
Python+OpenGL绘制3D模型(七)制作3dsmax导出插件

高级
用 DeepDraw 几何引擎开发建筑设计软件(一):SketchUp 替代
用 DeepDraw 几何引擎开发建筑设计软件(二):下载DeepDraw
用 DeepDraw 几何引擎开发建筑设计软件(三):用代码绘制线段
用 DeepDraw 几何引擎开发建筑设计软件(四):用代码绘制面
用 DeepDraw 几何引擎开发建筑设计软件(五):创建选择工具
用 DeepDraw 几何引擎开发建筑设计软件(六):读取模型文件
用 DeepDraw 几何引擎开发建筑设计软件(七):InputPoint输入捕获器
用 DeepDraw 几何引擎开发建筑设计软件(八):画线工具
未完待续…

前言

Sketchup作为目前设计院最为流行的设计软件(非工程制图软件),深受设计师的喜爱,软件小巧,而功能强大,有不少为之开发的插件应运而生,不过呢,关于底层数据结构和工作原理相关的文章少之又少,本文意在填补一下这方面的空缺。


文章目录

  • 系列文章
  • 前言
    • 一、模型的变换矩阵
    • 二、导出材质、贴图
    • 三、导出法线
    • 四、 源代码
      • 1、maxplus_export_sel_model.py
      • 2、CModel.py
  • 系列文章预告

一、模型的变换矩阵

TODO

二、导出材质、贴图

TODO

三、导出法线

TODO

四、 源代码

1、maxplus_export_sel_model.py

importMaxPlusimportpickleimportbase64fromCModelimportCModel,CMaterial,CMesh,CTriangle,CVector3################################# FILE DESCRIPTION# 文件描述:CModelExport# 对应文章:Python+OpenGL绘制3D模型(九) 完善插件功能: 矩阵,材质,法线# 作者:李航 Lihang# 使用方法:# 1、选择要导出的模型# 2、在命令行窗口中输入# python.ExecuteFile "C:/_proj/SuViewer/articles/step3/maxplus_export_sel_model.py"# 3、把命令行拖入工具栏可以生成快捷方式,方便下次使用# 4、测试可使用的版本:3dsmax2016# ELSE..################################# 注意: 保证c:/temp目录存在,否则无法导出EXPORT_PATH_FILE="c:/temp/CModel.pickle"############# CModelExport############classCModelExport:def__init__(self):self.list_mats=[]############# export# 插件主入口############defexport(self):model=CModel()forn,obj,triObjinself.EnumSeletciontGeometry():print("find %s, class:%s"%(n,obj.GetClassName()))mesh=triObj.GetMesh()out_mesh=self.ProcMesh(n,mesh)model.list_mesh.append(out_mesh)self.printMats()model.list_mats=[moformi,moinself.list_mats]hf=open("c:/temp/CModel.pickle","wb")pickle.dump(model,hf,2)hf.close()############# EnumSeletciontGeometry# 1、遍历选择的物体# 2、生成列表sel_list并返回############defEnumSeletciontGeometry(self):sel_list=[]forninMaxPlus.SelectionManager.Nodes:# objectobj=n.EvalWorldState().Getobj()ifobj.CanConvertToType(MaxPlus.ClassIds.TriMeshGeometry):triObj=obj.ConvertToType(MaxPlus.ClassIds.TriMeshGeometry)item=(n,obj,MaxPlus.TriObject._CastFrom(triObj))sel_list.append(item)#print ("find %s, class:%s"%(n, obj.GetClassName()) )else:print("can't conv triMesh, Type is:%s"%obj.GetClassName())returnsel_list############# ProcMesh# 1、处理max中的模型# 2、转成需要的CModel数据############defProcMesh(self,node,mesh):print("---ProcMesh()---")#=====================# Collect Infomation#=====================tm=node.GetWorldTM()# colornew_mesh=CMesh()color1=node.GetWireColor()new_mesh.color=(color1.GetR(),color1.GetG(),color1.GetB())# materialm=node.GetMaterial()new_mesh.mat=self.ProcMeshMaterial(m,mesh)ifmelseNone#=====================# Vertices#=====================num_verts=mesh.GetNumVertices()print("numVertics:%d"%num_verts)foriinrange(num_verts):loc_pos=mesh.GetVertex(i)point=tm.PointTransform(loc_pos)nv=CVector3(point.X,point.Y,point.Z)new_mesh.list_vertices.append(nv)#=====================# Normals#=====================normalBuilder=NormalBuilder()normalBuilder.BuildNormals(mesh)num_normals=len(normalBuilder.list_normals)print("numNormals:%d"%num_normals)foriinrange(num_normals):ln=normalBuilder.list_normals[i]nm=tm.VectorTransform(ln).Normalize()new_mesh.list_normals.append(CVector3(nm.X,nm.Y,nm.Z))#=====================# UV#=====================num_tverts=mesh.GetNumTVerts()print("numTVerts:%d"%num_tverts)foriinrange(num_tverts):uv=mesh.GetTVert(i)nv=CVector3(uv.X,uv.Y,uv.Z)new_mesh.list_uvs.append(nv)#=====================# Triangles#=====================num_faces=mesh.GetNumFaces()print("numFaces:%d"%num_faces)foriinrange(num_faces):tri=mesh.GetFace(i)smGroup=tri.GetSmGroup()vi1=tri.GetVert(0)vi2=tri.GetVert(1)vi3=tri.GetVert(2)ni1=normalBuilder.GetNormal(vi1,smGroup)ni2=normalBuilder.GetNormal(vi2,smGroup)ni3=normalBuilder.GetNormal(vi3,smGroup)tface=mesh.GetTVFace(i)ui1=tface.GetA()ui2=tface.GetB()ui3=tface.GetC()ev1=Trueiftri.GetEdgeVis(0)elseFalseev2=Trueiftri.GetEdgeVis(1)elseFalseev3=Trueiftri.GetEdgeVis(2)elseFalsent=CTriangle()nt.a=(vi1,ni1,ui1)nt.b=(vi2,ni2,ui2)nt.c=(vi3,ni3,ui3)norm=mesh.FaceNormal(i)nt.matId=tri.GetMatID()nt.faceNormal=CVector3(norm.X,norm.Y,norm.Z)nt.smGroup=tri.GetSmGroup()nt.edgevis=(ev1,ev2,ev3)new_mesh.list_tris.append(nt)#print(" Tri: %d %d %d"%(tri.GetVert(0), tri.GetVert(1), tri.GetVert(2) ))returnnew_mesh############# Material############defProcMeshMaterial(self,m,mesh):print("---CollectMeshMaterials()---")ifm.IsMultiMtl():num_submat=m.GetNumSubMtls()refcount=[0]*num_submat num_faces=mesh.GetNumFaces()foriinrange(num_faces):f=mesh.GetFace(i)mid=f.GetMatID()refcount[mid]+=1print("refcount:"+str(refcount))list_submats=[]foriinrange(num_submat):ifrefcount[i]:mMat=m.GetSubMtl(i)ifmMat:cm=self.addMaterial(mMat)list_submats.append(cm)else:list_submats.append(None)else:list_submats.append(None)returnlist_submatselse:returnself.addMaterial(m)defaddMaterial(self,mi):#==============# Find Exist#==============formin,moutinself.list_mats:ifmin==mi:returnmout#==============# Create Out Material#==============mo=CMaterial()# Diffusecolor1=mi.GetDiffuse()mo.diffuse=(color1.GetR(),color1.GetG(),color1.GetB())# Textureisubmap=MaxPlus.ISubMap._CastFrom(mi)#assert(isubmap, "can't cast to ISubMap")tex=isubmap.GetSubTexmap(1)iftex:bitmap=MaxPlus.BitmapTex._CastFrom(tex)texmap=bitmap.GetMapName()mo.tex_filepath=texmap mo.tex_bindata=self.loadFileData(texmap)# Add to listitem=(mi,mo)self.list_mats.append(item)returnmodefloadFileData(self,filename):file1=open(filename,mode='rb')imgdata=bytes(file1.read())file1.close()returnbase64.b64encode(imgdata)defprintMats(self):print("---printMats()---")print("Number of Materials: %d"%len(self.list_mats))print("----------------")formatId,minenumerate(self.list_mats):mi,mo=mprint("ID: %d"%matId)print("source:"+str(mi))print("diffuse: "+str(mo.diffuse))print("texture: "+mo.tex_filepath)############# NormalBuilder############classNormalBuilder:def__init__(self):self.list_verts=[]self.list_normals=[]classVertex:def__init__(self,p):self.v=p self.an=Nonedefnormals(self):ifself.anisNone:return()eliftype(self.an)islist:returnself.anelse:return(self.an,)defaddNormal(self,n,sg):forvninself.normals():ifvn.sg&sg:vn.n=vn.n+nreturn#not findvn=NormalBuilder.VNormal(n,sg)ifself.anisNone:self.an=vneliftype(self.an)islist:self.an.append(vn)else:self.an=[self.an,vn]classVNormal:def__init__(self,n,sg):self.n=n self.sg=sg self.idx=-1defadd(self,n):self.n=self.n+ndefBuildNormals(self,mesh):print("-----BuildNormals------")#=====================# Vertices#=====================num_verts=mesh.GetNumVertices()foriinrange(num_verts):point=mesh.GetVertex(i)v=NormalBuilder.Vertex(point)self.list_verts.append(v)num_faces=mesh.GetNumFaces()foriinrange(num_faces):f=mesh.GetFace(i)faceNormal=mesh.FaceNormal(i)smGroup=f.GetSmGroup()v1=f.GetVert(0)self.list_verts[v1].addNormal(faceNormal,smGroup)v2=f.GetVert(1)self.list_verts[v2].addNormal(faceNormal,smGroup)v3=f.GetVert(2)self.list_verts[v3].addNormal(faceNormal,smGroup)idx=0forvinself.list_verts:forvninv.normals():vn.n=vn.n.Normalize()self.list_normals.append(vn.n)vn.idx=idx idx+=1defGetNormal(self,vi,sg):v=self.list_verts[vi]forvninv.normals():ifvn.sg&sg:returnvn.idxassert(False)############# Main# 1、创建插件导出对象CModelExport# 2、执行export############modelExport=CModelExport()modelExport.export()

2、CModel.py

importmathclassCModel:def__init__(self):self.list_mats=[]self.list_mesh=[]classCMaterial:def__init__(self):self.diffuse=(0,0,0)self.tex_filepath=""self.tex_bindata=NoneclassCMesh:def__init__(self):self.name=""self.color=(0,0,0)self.list_vertices=[]self.list_normals=[]self.list_uvs=[]self.list_tris=[]classCTriangle:def__init__(self):self.a=(0,0,0)self.b=(0,0,0)self.c=(0,0,0)self.faceNormal=CVector3.zero()self.matId=0self.smGroup=0self.edgevis=(True,True,True)classCVector3:def__init__(self,x,y,z):self.x=x self.y=y self.z=z@classmethoddefzero2(cls):returnCVector3(0.0,0.0,0.0)@staticmethoddefzero():returnCVector3(0.0,0.0,0.0)defnormalize(self):s=1.0/math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z)self.x*=s self.y*=s self.z*=sdef__add__(self,R):returnCVector3(self.x+R.x,self.y+R.y,self.z+R.z)

系列文章预告

目标是开发一个Sketchup Viewer,能够显示Sketchup的.skp文件中的3D模型

Corona渲染器照片级渲染效果

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询