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:        # objectobj = 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()# 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) 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 Falseev2 = True if tri.GetEdgeVis(1) else Falseev3 = True if tri.GetEdgeVis(2) else Falsent = 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_submatnum_faces = mesh.GetNumFaces()for i in range(num_faces):f = mesh.GetFace(i)mid = f.GetMatID()refcount[mid] += 1print("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_submatselse: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()# 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)if tex:bitmap = MaxPlus.BitmapTex._CastFrom(tex)texmap = bitmap.GetMapName()mo.tex_filepath = texmapmo.tex_bindata = self.loadFileData(texmap)# Add to listitem = (mi, mo)self.list_mats.append(item)return modef 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 = mprint("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 = pself.an = Nonedef normals(self):if self.an is None:return ()elif type(self.an) is list:return self.anelse:return (self.an, )def addNormal(self, n, sg):for vn in self.normals():if vn.sg & sg:vn.n = vn.n + nreturn#not findvn = NormalBuilder.VNormal(n, sg)if self.an is None:self.an = vnelif type(self.an) is list:self.an.append(vn)else:self.an = [self.an, vn]class VNormal:def __init__(self, n, sg):self.n = nself.sg = sgself.idx = -1def add(self, n):self.n =  self.n + ndef 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 = 0for v in self.list_verts:for vn in v.normals():vn.n = vn.n.Normalize()self.list_normals.append(vn.n)vn.idx = idxidx += 1def GetNormal(self, vi, sg):v = self.list_verts[vi]for vn in v.normals():if vn.sg & sg:return vn.idxassert(False)############
# Main
#   1、创建插件导出对象CModelExport
#   2、执行export
############
modelExport = CModelExport()
modelExport.export()

2、CModel.py

import mathclass 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 = Noneclass 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 = 0self.smGroup = 0self.edgevis=(True, True, True)class CVector3:def __init__(self, x, y, z):self.x=xself.y=yself.z=z@classmethoddef zero2(cls):return CVector3(0.0, 0.0, 0.0)@staticmethoddef 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 *= sself.y *= sself.z *= sdef __add__(self,  R):return CVector3(self.x + R.x, self.y + R.y, self.z + R.z)

系列文章预告

目标是一个完善的Viewer,能够显示Sketchup的.skp文件中的3D模型
在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis:原理速成+项目实战——Redis的Java客户端

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:Redis:原理速成项目实战——Redis常见命令(数据结构、常见命令总结) 📚订阅专栏&…

【网络安全 | XCTF】simple_transfer

考察kali基本工具的使用 方法一 打开文件如图: 存在较多协议,将协议分级: 可以看到DLEP协议占比最大: 将其作为过滤器应用: 搜索DLEP: 并没有有利信息,但观察到多数数据包损坏: 执行…

一语道破爬虫,来揭开爬虫面纱

目录 一、爬虫(网络蜘蛛(Spider)) 1.1、是什么: 1.2、学习的原因 1.3、用在地方: 1.4、是否合法: 1.5、后果 案例: 二、应用领域 三、Robots协议 四、抓包 4.1、浏览器抓包 4.2、抓包工具 常见…

性能优化-如何提高cache命中率

本文主要介绍性能优化领域常见的cache的命中率问题,旨在全面的介绍提高cache命中率的方法,以供大家编写出性能友好的代码,并且可以应对性能优化领域的面试问题。 🎬个人简介:一个全栈工程师的升级之路! &am…

C++图论之强连通图

1. 连通性 什么是连通性? 连通,字面而言,类似于自来水管道中的水流,如果水能从某一个地点畅通流到另一个地点,说明两点之间是连通的。也说明水管具有连通性,图中即如此。 无向图和有向图的连通概念稍有差…

【owt-server】一些构建项目梳理

【owt-server】清理日志:owt、srs、ffmpeg 【owt】p2p client mfc 工程梳理【m98】webrtc vs2017构建带符号的debug库【OWT】梳理构建的webrtc和owt mfc工程 m79的mfc客户端及owt-client

在VMware安装CentOS 7:详细教程

安装准备工作 本地虚拟机:我这里使用的是VMware Workstation 17 Pro centos7系统ISO镜像:我这里使用的是CentOS-7-x86_64-DVD-2009.iso,具体的下载地址是在阿里云官方镜像站:centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿…

TV端Web页面性能优化实践

01 背景 随着互联网技术的持续创新和电视行业的高速发展,通过电视观看在线视频已经逐渐成为大众的重要娱乐方式。奇异果App作为在TV设备上用户活跃度最高的应用之一,为广大用户提供了丰富的内容播放服务,除此之外,同样有会员运营、…

Qt QAction添加图片

QAction用的时候,时常需要添加图片,如上图所示,代码如下所示: 测试的图片格式包含png,jpg,bmp,svg,其他未测试

年终跑步总结

第一个365天无间断年 以前也跑步很频繁,但今年是第一次365天未缺勤。年跑步量也是历来个人最多:2900km以上。 连续跑步天数累积超700天了 这里出现的签到天数累加只有666次,因为中间有跑步、但没有到app上签到,实际最近一次停…

RabbitMQ消息确认机制

介绍 在使用RabbitMQ发送消息如果出现消息没有发送到,队列没有接收到情况。需要消息确认来排错。 RabbitMQ发送端确认 ConfirmCallback 确认模式 和 ReturnCallback 未投递到 queue 退回模式 ConfirmCallback 确认模式 是生产者发送消息 被broker接收 会触发ConfirmCallba…

kafka实现延迟消息

背景 我们知道消息中间件mq是支持延迟消息的发送功能的,但是kafka不支持这种直接的用法,所以我们需要独立实现这个功能,以下是在kafka中实现消息延时投递功能的一种方案 kafka实现延时消息 主要的思路是增加一个检测服务,这个检…

条款 12:拷贝对象的所有部分

编译器生成的拷贝函数(拷贝构造函数,拷贝赋值运算符),会拷贝对象的所有数据,当你声明自己的拷贝函数时,就是在告诉编译器,默认实现中有你不喜欢的地方。 void logCall(const std::string& …

Apple Unity Plugins 接入GameCenter 崩溃解决方案

目录 问题问题原因解决方案可直接使用的UnityPlugins 问题 调用 GKLocalPlayer.Local.FetchItems() 程序崩溃,报错:Thread 1: EXC_BAD_ACCESS (code257, address0x8000000000000002) 启动崩溃,报错:Library not loaded: rpath/Ap…

【Electron】webview 实现网页内嵌

实现效果: 当在输入框内输入某个网址后并点击button按钮 , 该网址内容就展示到下面 踩到的坑:之前通过web技术实现 iframe 标签内嵌会出现 同源策略,同时尝试过 vue.config.ts 内配置跨域项 那样确实 是实现啦 但不知道如何动态切换 tagert …

sklearn学习的一个例子用pycharm jupyter

环境 运行在jupyter 进行开发。即一个WEB端的开发工具。能适时显示开发的输出。后缀用的是ipynb.pycharm也可以支持。但也要提示按装jupyter. 或直接用andcoda 这里我们用pycharm进行项目创建 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyterlab pip ins…

基于Python的电商手机数据可视化分析和推荐系统

1. 项目简介 本项目旨在通过Python技术栈对京东平台上的手机数据进行抓取、分析并构建一个简单的手机推荐系统。主要功能包括: 网络爬虫:从京东获取手机数据;数据分析:统计各厂商手机销售分布、市场占有率、价格区间和好评率&am…

Java进阶(第八期): Java中递归的的使用和递归解决一些算法问题 Java中的异常机制、异常的处理逻辑 自定义异常

文章目录 一、递归1.1 递归的介绍1.2 递归的简单练习1.3 图解递归执行流程:1.4 使用递归完成悲波那契数列1.5 猴子吃桃子问题 二、异常三 、异常的处理逻辑3.1 try catch 捕获异常3.2 throws抛出异常 四、自定义异常 Java进阶(第八期) 一、递…

科技云报道:开源才是大模型的未来?

科技云报道原创。 一年前,ChatGPT横空出世;7个多月后,Meta宣布开源LLaMA 2,并且可免费商用。 这一天,也成为大模型发展的分水岭。短时间内,LLaMA 2对一些闭源的大模型厂商造成了致命性的打击。 随后&…

SpringMVC源码解析——DispatcherServlet初始化

在Spring中,ContextLoaderListener只是辅助功能,用于创建WebApplicationContext类型的实例,而真正的逻辑实现其实是在DispatcherServlet中进行的,DispatcherServlet是实现Servlet接口的实现类。Servlet是一个JAVA编写的程序&#…