Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线

这篇具有很好参考价值的文章主要介绍了Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章

一、逆向工程
Sketchup 逆向工程(一)破解.skp文件数据结构
Sketchup 逆向工程(二)分析三维模型数据结构
Sketchup 逆向工程(三)软件逆向工程从何处入手
Sketchup 逆向工程(四)破解的乐趣 钩子 外挂 代码注入

二、OpenGL渲染模型
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导出插件
Python+OpenGL绘制3D模型(八)绘制插件导出的插件
Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线
Python+OpenGL 杂谈(一)

三、成果
疫情期间关在家里实在没事干,破解了Sketchup,成功做出可以读取并显示.skp文件的程序SuViewer

前言

Sketchup作为目前设计院最为流行的设计软件(非工程制图软件),深受设计师的喜爱,软件小巧,而功能强大,有不少为之开发的插件应运而生,不过呢,关于底层数据结构和工作原理相关的文章少之又少,本文意在填补一下这方面的空缺,通过逆向软件分析,展示软件内部奥秘。本文用到的工具:IDA Pro,Immunity Debugger,Visual Studio (逆向工程三件套)数据结构属于知识产权的核心机密:


一、模型的变换矩阵

TODO

二、导出材质、贴图

TODO

三、导出法线

TODO

四、 源代码

1、maxplus_export_sel_model.py

import MaxPlus
import pickle
import base64
from CModel import CModel, 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
############  
class CModelExport:
    def __init__(self):
        self.list_mats = []
        
    ############
    # export
    #   插件主入口
    ############  
    def export(self):
        model = CModel()

        for n, obj, triObj in self.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 = [mo for mi, mo in self.list_mats]
        
        hf = open("c:/temp/CModel.pickle",  "wb")
        pickle.dump(model,  hf,  2)
        hf.close()

    ############
    # EnumSeletciontGeometry
    #   1、遍历选择的物体
    #   2、生成列表sel_list并返回
    ############  
    def EnumSeletciontGeometry(self):
        sel_list = []
        for n in MaxPlus.SelectionManager.Nodes:        
            # object
            obj = n.EvalWorldState().Getobj()
            if obj.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())
         
        return sel_list

    ############
    # ProcMesh
    #   1、处理max中的模型
    #   2、转成需要的CModel数据
    ############
    def ProcMesh(self, node, mesh):
        print("---ProcMesh()---")

        #=====================
        # Collect Infomation
        #=====================
        tm = node.GetWorldTM()

        # color
        new_mesh = CMesh()
        color1 = node.GetWireColor()
        new_mesh.color = (color1.GetR(), color1.GetG(), color1.GetB())
        
        # material
        m = node.GetMaterial()
        new_mesh.mat = self.ProcMeshMaterial(m, mesh) if m else None

        #=====================
        # Vertices
        #=====================
        num_verts = mesh.GetNumVertices()
        print ("numVertics:%d"%num_verts)
        for i in range(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)
        for i in range(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)
        for i in range(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)
        for i in range(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 = True if tri.GetEdgeVis(0) else False
            ev2 = True if tri.GetEdgeVis(1) else False
            ev3 = True if tri.GetEdgeVis(2) else False
            nt = 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) ))
            
        return new_mesh

    ############
    # Material
    ############
    def ProcMeshMaterial(self, m, mesh):
        print("---CollectMeshMaterials()---")

        if m.IsMultiMtl():
            num_submat = m.GetNumSubMtls()
            refcount = [0] * num_submat
            
            num_faces = mesh.GetNumFaces()
            for i in range(num_faces):
                f = mesh.GetFace(i)
                mid = f.GetMatID()
                refcount[mid] += 1
            print("refcount:"+str(refcount))
            
            list_submats = []
            for i in range(num_submat):
                if refcount[i]:
                    mMat = m.GetSubMtl(i)
                    if mMat:
                        cm = self.addMaterial(mMat)
                        list_submats.append(cm)
                    else:
                        list_submats.append(None)
                else:
                    list_submats.append(None)
            return list_submats
        else:
            return self.addMaterial(m)

    def addMaterial(self, mi):
        #==============
        #  Find Exist
        #==============
        for min, mout in self.list_mats:
            if min == mi:
                return mout
        
        #==============
        #  Create Out Material
        #==============
        mo = CMaterial()
        
        # Diffuse
        color1 = mi.GetDiffuse()
        mo.diffuse = (color1.GetR(), color1.GetG(), color1.GetB())

        # Texture
        isubmap = MaxPlus.ISubMap._CastFrom(mi)
        #assert(isubmap, "can't cast to ISubMap")
        tex = isubmap.GetSubTexmap(1)
        if tex:
            bitmap = MaxPlus.BitmapTex._CastFrom(tex)
            texmap = bitmap.GetMapName()
            mo.tex_filepath = texmap
            mo.tex_bindata = self.loadFileData(texmap)
        
        # Add to list
        item = (mi, mo)
        self.list_mats.append(item)

        return mo

    def loadFileData(self, filename):
        
        file1 = open(filename,  mode='rb')
        imgdata = bytes(file1.read())
        file1.close()
        
        return base64.b64encode(imgdata)

    def printMats(self):
        print("---printMats()---")
        print("Number of Materials: %d"% len(self.list_mats))
        print("----------------")
        for matId, m in enumerate(self.list_mats):
            mi, mo = m
            print("ID: %d"% matId)
            print("source:" + str(mi))
            print("diffuse: " + str(mo.diffuse))
            print("texture: " + mo.tex_filepath)

############
# NormalBuilder
############
class NormalBuilder:
    def __init__(self):
        self.list_verts = []
        self.list_normals = []

    class Vertex:
        def __init__(self, p):
            self.v = p
            self.an = None
            
        def normals(self):
            if self.an is None:
                return ()
            elif type(self.an) is list:
                return self.an
            else:
                return (self.an, )

        def addNormal(self, n, sg):
            for vn in self.normals():
                if vn.sg & sg:
                    vn.n = vn.n + n
                    return
            
            #not find
            vn = NormalBuilder.VNormal(n, sg)
            if self.an is None:
                self.an = vn
            elif type(self.an) is list:
                self.an.append(vn)
            else:
                self.an = [self.an, vn]
      
    class VNormal:
        def __init__(self, n, sg):
            self.n = n
            self.sg = sg
            self.idx = -1
            
        def add(self, n):
            self.n =  self.n + n
        
    def BuildNormals(self, mesh):
        print("-----BuildNormals------")
        #=====================
        # Vertices
        #=====================
        num_verts = mesh.GetNumVertices()
        for i in range(num_verts):
            point = mesh.GetVertex(i)
            v = NormalBuilder.Vertex(point)
            self.list_verts.append(v)

        num_faces = mesh.GetNumFaces()
        for i in range(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 = 0
        for v in self.list_verts:
            for vn in v.normals():
                vn.n = vn.n.Normalize()
                self.list_normals.append(vn.n)
                vn.idx = idx
                idx += 1
    
    def GetNormal(self, vi, sg):
        v = self.list_verts[vi]
        for vn in v.normals():
            if vn.sg & sg:
                return vn.idx
        
        assert(False)

############
# Main
#   1、创建插件导出对象CModelExport
#   2、执行export
############
modelExport = CModelExport()
modelExport.export()

2、CModel.py

import math

class CModel:
    def __init__(self):
        self.list_mats = []
        self.list_mesh = []
        
class CMaterial:
    def __init__(self):
        self.diffuse = (0, 0, 0)
        self.tex_filepath = ""
        self.tex_bindata = None

class CMesh:
    def __init__(self):
        self.name = ""
        self.color = (0, 0, 0)
        self.list_vertices = []
        self.list_normals = []
        self.list_uvs = []
        self.list_tris = []

class CTriangle:
    def __init__(self):
        self.a=(0, 0, 0)
        self.b=(0, 0, 0)
        self.c=(0, 0, 0)
        self.faceNormal = CVector3.zero()
        self.matId = 0
        self.smGroup = 0
        self.edgevis=(True, True, True)

class CVector3:
    def __init__(self, x, y, z):
        self.x=x
        self.y=y
        self.z=z
    
    @classmethod
    def zero2(cls):
        return CVector3(0.0, 0.0, 0.0)

    @staticmethod
    def zero():
        return CVector3(0.0, 0.0, 0.0)

    def normalize(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 *= s
        
    def __add__(self,  R):
        return CVector3(self.x + R.x, self.y + R.y, self.z + R.z)

系列文章预告

目标是一个完善的Viewer,能够显示Sketchup的.skp文件中的3D模型
Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线,Sketchup源代码分析,python,3d,矩阵,3dsmax,unity

Corona渲染器照片级渲染效果
Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线,Sketchup源代码分析,python,3d,矩阵,3dsmax,unity文章来源地址https://www.toymoban.com/news/detail-772689.html

到了这里,关于Python+OpenGL绘制3D模型(九)完善插件功能: 矩阵,材质,法线的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt OpenGL 3D模型

    这次教程中,我们将之前几课的基础上,教大家如何创建立体的3D模型。我们将开始生成真正的3D对象,而不是像之前那几课那样3D世界中的2D对象。我们会把之前的三角形变为立体的金字塔模型,把四边形变为立方体。 我们给三角形增加左侧面、右侧面、后侧面来生成一个金

    2024年02月11日
    浏览(49)
  • 第十三章 opengl之模型(导入3D模型)

    使用Assimp并创建实际的加载和转换代码。Model类结构如下: Model类包含一个Mesh对象的vector,构造器参数需要一个文件路径。 构造器通过loadModel来加载文件。私有函数将会处理Assimp导入过程中的一部分,私有函数还存储了 文件路径的目录,加载纹理时会用到。 Draw函数的作用:

    2024年02月05日
    浏览(48)
  • OpenGL ES相关库加载3D 车辆模型

    需求类似奇瑞的这个效果,就是能全方位旋转拖拽看车,以及点击开关车门车窗后备箱等 瑞虎9全景看车 (chery.cn) 最开始收到这个需求的时候还有点无所适从,因为以前没有做过类似的效果,后面一经搜索后发现实现的方式五花八门,但始终绕不过opengl,于是通过opengl 逐步展

    2024年02月06日
    浏览(36)
  • 【我的OpenGL学习进阶之旅】Assimp库支持哪些3D模型格式?

    在通过LearnOpenGL学习 OpenGL 知识的时候,有介绍如何通过Assimp来加载3D模型,并了解了Mesh网格的概念。 Assimp Mesh网格 3D模型 在 https://learnopengl-cn.github.io/03%20Model%20Loading/01%20Assimp/ 中有介绍使用 Assimp 库来加载 3D 模型 .obj 格式。 一个非常流行的模型导入库是Assimp,它是 Open Asse

    2024年02月05日
    浏览(42)
  • 【100天精通Python】Day61:Python 数据分析_Pandas可视化功能:绘制饼图,箱线图,散点图,散点图矩阵,热力图,面积图等(示例+代码)

    目录 1 Pandas 可视化功能 2 Pandas绘图实例 2.1 绘制线图 2.2 绘制柱状图 2.3 绘制随机散点图/

    2024年02月08日
    浏览(54)
  • 基于Django的Python应用—学习笔记—功能完善

    创建基于表单的页面的方法几乎与前面创建网页一样:定义一个 URL ,编写一个视图函数并编写一个模板。一个主要差别是,需要导入包含表单 的模块forms.py 。 edit_entry.html new_entry.html new_topic.html

    2024年01月20日
    浏览(44)
  • 使用Python和OpenGL渲染PS2存档3D图标

    经过前面一系列文章的铺垫,PS2存档3D图标的文件已经全部解析完毕。本篇开始将介绍使用如下工具将3D图标渲染出来,并尽可能接近PS2主机原生的效果。 Python3 PyGame Numpy ModernGL PyGLM 第一步先初始化 PyGame ,设置窗口大小为 640x480 , FPS 为 60 。开启 OpenGL 渲染模式, OpenGL 的版

    2024年02月03日
    浏览(46)
  • WxGL应用实例:绘制高精度的3D太阳系模型

    天何所沓?十二焉分?日月安属?列星安陈?—— 屈原 远古时期的人类就对神秘幽邃的星空充满了好奇与敬畏。仰望星空,宇宙浩瀚。比宇宙更壮阔的,是人类对宇宙的不懈追问和沉淀在基因中的探索精神。 本文尝试用WxGL来回答“日月安属、列星安陈”这个古老的问题。太

    2023年04月21日
    浏览(52)
  • 利用python绘制混淆矩阵

    利用python绘制混淆矩阵,直接附代码。 简单理解混淆矩阵的元素含义,例如下面第一行第二个的“3”表示实际标签为类别0的预测成类别1的有3个样本。 附效果图: 如果需要切换不同的颜色,如:

    2024年02月16日
    浏览(50)
  • python绘制3D条形图

    python条形图系列: 带有误差棒的条形图💎 分组条形图💎 3D条形图 尽管在matplotlib支持在一个坐标系中绘制多组条形图,效果如下 其中,蓝色表示中国,橘色表示美国,绿色表示欧盟。从这个图就可以非常直观地看出,三者自2018到2022年的GDP变化情况。但相比之下,通过增加

    2024年02月15日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包