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 (逆向工程三件套)数据结构属于知识产权的核心机密:


文章目录

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

一、模型的变换矩阵

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模型
在这里插入图片描述

Corona渲染器照片级渲染效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/281154.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

gulimall-002 分布式基础概念

1、微服务概念 微服务是一种非常流行的架构风格。 拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。 每个服务运行在自己的单个进程使用轻量级机制通信可以使用不同的编程语言编写以及不同的数据存储技术 2、集群&分布式&…

力扣:63. 不同路径 II(动态规划)

题目: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。 现在考虑网格中有障碍物。那…

14个强大的JS库

文章目录 一、前言二、Handsontable:高效的数据网格处理库2.1、数据绑定和验证2.2、过滤和排序2.3、文件导出2.4、多框架兼容性2.5、丰富的附加功能2.6、GitHub受欢迎程度 三、Calendar:全功能可定制日历库3.1、多种视图类型3.2、任务和里程碑管理3.3、鼠…

【报错处理】opencv-3.4.1安装报错 error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]

description 最近在复现ORB-SLAM2的时候配置 opencv-3.4.1的环境, 官网下载的opencv-3.4.1 source文件, 原封不动地解压后按照该指导方法安装和编译, 在make的过程中, 出现了编译错误 (截图忘记了),具体报错如下: error: invalid conversion from ‘const char*’ …

【Linux操作系统】探秘Linux奥秘:用户、组、密码及权限管理的解密与实战

🌈个人主页:Sarapines Programmer🔥 系列专栏:《操作系统实验室》🔖诗赋清音:柳垂轻絮拂人衣,心随风舞梦飞。 山川湖海皆可涉,勇者征途逐星辉。 目录 🪐1 初识Linux OS &…

2023年03月09日_谷歌视觉语言模型PaLM-E的介绍

自从最近微软凭借OpenAI 和ChatGPT火了一把之后呢 老对手Google就总想着扳回一局 之前发布了硬刚ChatGPT的Bard 但是没想到翻车了 弄巧成拙 所以呢Google这一周又发了个大招 发布了史上最大的视觉语言模型PaLM-E 这个模型有多夸张呢 参数量高达5,620亿 是ChatGTP-3的三…

[spark] SaveMode

https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/sql/SaveMode.html Overwrite 覆盖模式是指将DataFrame保存到数据源时,如果数据/表已经存在,则现有数据将被DataFrame的内容覆盖。 注意: Overwrite 模式会覆盖已存在的表…

9种卷积注意力机制创新方法汇总,含2024最新

今天咱们来聊聊卷积注意力机制。 相信各位在写论文的时候都苦恼过怎么更好地改模型,怎么更高效地提高模型的性能和泛化能力吧?我的建议是,不妨考虑考虑卷积注意力。 卷积注意力机制是一种通过关注输入数据中的不同部分来改进模型性能的方法…

《Linux系统与网络管理》复习题库---shell编程题

1、shell 编程题:在根目录下有四个文件 m1.c,m2.c,m3.c,m4.c,用 Shell 编程,实现自动创建 m1,m2,m3,m4 四个目录,并将 m1.c,m2.c,m3.c,m4.c 四个文件分别剪贴到各自相应的目录下。 #!/bin/bash…

Termius for Mac/Win:一站式终端模拟器、SSH 和 SFTP 客户端软件的卓越选择

随着远程工作和云技术的普及,对于高效安全的远程访问和管理服务器变得至关重要。Termius,一款强大且易用的终端模拟器、SSH 和 SFTP 客户端软件,正是满足这一需求的理想选择。 Termius 提供了一站式的解决方案,允许用户通过单一平…

英语长难句分享第十五天解析

群公告 长难句分享第十五天解析 【词汇】: • mispredict [ˌmɪsprɪˈdɪkt] v. 错误预测 • mechanical [məˈknɪkl] adj. 机械的 • everyday [ˈevrideɪ] adj. 日常的 • helicopter [ˈhelɪkɑːptər] n. 直升机 • eventually [ɪˈventʃuəli] adv. …

AI 开发必看的 6 款开源矢量数据库

你好,我是坚持分享干货的 EarlGrey,翻译出版过《Python编程无师自通》、《Python并行计算手册》等技术书籍。 如果我的分享对你有帮助,请关注我,一起向上进击。 创作不易,希望大家给一点鼓励,把公众号设置为…

starrocks集群fe/be节点进程守护脚本

自建starrocks集群,有时候服务会挂掉,无法自动拉起服务,于是采用supervisor进行进程守护。可能是版本的原因,supervisor程序总是异常,无法对fe//be进行守护。于是写了个简易脚本。 #!/bin/bash AppNameFecom.starrock…

jmeter接口测试02

jmeter接口测试02 新增测试计划用户自定义变量http请求默认值http头部管理器线程组HTTP请求HTTP响应断言 创建查看结果树和总结报告启动线程组,查看结果树和总结报告 新增测试计划 用户自定义变量 定义测试计划常用的变量 例如token、接口的ip、端口等。 http请求…

cargo(rust包管理) 常见命令、包检索 (windows+linux)

rust环境和开发环境配置:rust开发环境配置 winlinux Cargo是Rust的构建系统和包管理器。 如果你的能力足够强也愿意,可以不用cargo进行rust开发,即从头开始敲代码 一、cargo包相关查询 1.查找包 查找cargo包链接:crates.io …

视频编辑与制作,视频尺寸修改器

你是否曾因为视频尺寸与平台不匹配无法上传而烦恼?这个时候一款视频尺寸修改工具,就能帮你轻松搞定。不论是为了适应不同的平台要求,还是为了获得不一样的观看体验,【视频剪辑高手】都能为你提供完美的解决方案。 所需工具&#…

layui表格中预览视频和图片

全代码 <!DOCTYPE html> <html><head><title>Layui&#xff1a;数据表格table中预览图片、视频</title><meta charset"utf-8"/><link rel"stylesheet" href"../dist/css/layui.css"><style>&l…

Python 下载与安装

1、下载 打开Python官网&#xff1a;Welcome to Python.org 点击下图所示的【Downloads】按钮进入下载页面。 ​ 进入下载页面后下拉至下图位置&#xff0c;选择版本&#xff0c;点击下载按钮下载。 页面会跳转至下一页下载页面&#xff0c;下拉到下图位置&#xff0c;选择…

【实用工具】Gradio快速部署深度学习应用1:图像分类

前言 在AI快速发展的今天&#xff0c;我们作为算法开发人员&#xff0c;也应该有一些趁手的工具帮助我们快速开发并验证自己的想法&#xff0c;Gradio可以实现快速搭建和共享的功能&#xff0c;能够展示出一个前端界面&#xff0c;把我们的算法包裹起来&#xff0c;快速验证算…

(JAVA)-(网络编程)-初始网络编程

网络编程就是在通信协议下&#xff0c;不同的计算机上运行的程序&#xff0c;进行的数据传输。 讲的通俗一点&#xff0c;就是以前我们写的代码是单机版的&#xff0c;网络编程就是联机版的。 应用场景&#xff1a;即时通信&#xff0c;网游对战&#xff0c;金融证券&#xf…
最新文章