Python+OpenGL绘制3D模型(七)制作3dsmax导出插件

系列文章

一、逆向工程
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 杂谈(一)

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

前言

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


在这里插入图片描述

文章目录

  • 系列文章
  • 前言
    • 一、介绍
    • 二、使用方法
    • 三、插件源代码
  • 系列文章预告

一、介绍

3dsmax支持python2.7,所以同样可以用python编写插件,不需要编译安装,一键执行,非常方便,
编写插件是个比较复杂的过程,涉及3dsmax中模型、材质、贴图、等数据的组织结构,以后专门用一个专题详细介绍,有兴趣的同学可以仔细读一下插件源代码
插件和程序需要交换文件数据格式,这里定义一个CModel,数据保存使用了python内置的pickle,也是一键保存,在程序里一键载入,不需要专门编写读写文件的代码

二、使用方法

1、下载源代码,保存到:c:/temp/maxplus_export_sel_model.py
2、打开3dsmax,菜单 》Scripting 》 MAXScript Listener
3、运行 python.ExecuteFile “C:/temp/maxplus_export_sel_model.py”

如果看到如下输出,说明已经导出成功

find INode: Teapot001, <0X0000000048D3CBB0>, class:Teapot
---ProcMesh()---
numVertics:530
numFaces:1024
#success

模型导出到 “c:/temp/CModel.pickle”

注意:保证c:/temp存在,否则导出失败

三、插件源代码

maxplus_export_sel_model.py

import MaxPlus
import pickle
from CModel import CModel, CMesh, CTriangle, CVector3

################################
#                   FILE DESCRIPTION
#  文件描述:CModelExport
#  对应文章:Python+OpenGL绘制3D模型(七) 制作3dsmax导出插件
#  作者:李航 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)
        
        with open(EXPORT_PATH_FILE,  "wb") as hf:
            pickle.dump(model,  hf,  2)

    ############
    # 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()---")

        #=====================
        # Color
        #=====================
        new_mesh = CMesh()
        color1 = node.GetWireColor()
        new_mesh.color = (color1.GetR(), color1.GetG(), color1.GetB())
        
        #=====================
        # Vertices
        #=====================
        num_verts = mesh.GetNumVertices()
        print ("numVertics:%d"%num_verts)
        for i in range(num_verts):
            point = mesh.GetVertex(i)
            nv = CVector3(point.X, point.Y, point.Z)
            new_mesh.list_vertices.append(nv)

        #=====================
        # Triangles
        #=====================
        num_faces = mesh.GetNumFaces()
        print ("numFaces:%d"%num_faces)
        for i in range(num_faces):
            tri = mesh.GetFace(i)
            vi1 = tri.GetVert(0)
            vi2= tri.GetVert(1)
            vi3 = tri.GetVert(2)
            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, 0, 0)
            nt.b = (vi2, 0, 0)
            nt.c = (vi3, 0, 0)
            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)
            
        return new_mesh

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

CModel.py

class CModel:
    def __init__(self):
        self.list_mesh = []
        
class CMesh:
    def __init__(self):
        self.name = ""
        self.color = (0, 0, 0)
        self.list_vertices = []
        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(0, 0, 0)
        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

系列文章预告

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

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

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

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

相关文章

Zookeeper之快速入门

前言 本篇文章主要还是让人快速上手入门&#xff0c;想要深入的话可以通过书籍系统的学习。 简介 是什么 可用于协调、构建分布式应用。 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储&#xff0c;并且可以对树中的节点进行有效管理…

Unity中Shader裁剪空间推导(在Shader中使用)

文章目录 前言一、在Shader中使用转化矩阵1、在顶点着色器中定义转化矩阵2、用 UNITY_NEAR_CLIP_VALUE 区分平台矩阵3、定义一个枚举用于区分当前是处于什么相机 二、我们在DirectX平台下&#xff0c;看看效果1、正交相机下2、透视相机下3、最终代码 前言 在上一篇文章中&…

Linux驱动开发学习笔记6《蜂鸣器实验》

目录 一、蜂鸣器驱动原理 二、硬件原理分析 三、实验程序编写 1、 修改设备树文件 &#xff08;1&#xff09;添加pinctrl节点 &#xff08;2&#xff09;添加BEEP设备节点 &#xff08;3&#xff09;检查PIN 是否被其他外设使用 2、蜂鸣器驱动程序编写 3、编写测试AP…

【JS逆向学习】快乐学堂

逆向目标 登陆接口&#xff1a;https://www.91118.com/passport/Account/LoginPost?r0.20790763112591337&kdsyes&username13127519353&passbb3mlkFBqqo%3D&recordPwd1&ckcode5719&fscodeklxt&invite 加密参数&#xff1a; r&#xff1a;0.2079…

机器学习模型可解释性的结果分析

模型的可解释性是机器学习领域的一个重要分支&#xff0c;随着 AI 应用范围的不断扩大&#xff0c;人们越来越不满足于模型的黑盒特性&#xff0c;与此同时&#xff0c;金融、自动驾驶等领域的法律法规也对模型的可解释性提出了更高的要求&#xff0c;在可解释 AI 一文中我们已…

Linux开发工具——gdb篇

Linux下调试工具——gdb 文章目录 makefile自动化构建工具 gdb背景 gdb的使用 常用命令 总结 前言&#xff1a; 编写代码我们使用vim&#xff0c;编译代码我们使用gcc/g&#xff0c;但是我们&#xff0c;不能保证代码没问题&#xff0c;所以调试是必不可少的。与gcc/vim一样&…

Python中使用SQLite数据库的方法2-2

3.3.2 创建表单及字段 通过“3.2 创建Cursor类的对象”中创建的Cursor类的对象cur创建表单及字段&#xff0c;代码如图5所示。 图5 创建表单及字段 从图5中可以看出&#xff0c;通过Cursor类的对象cur调用了Cursor类的execute()方法来执行SQL语句。该方法的参数即为要指定的S…

在电商行业中,如何采集电商数据使用数据分析提高业务绩效

数据分析丨知识点丨电商数据采集 福利指路&#xff1a;文章底部领取《数据分析全家桶》 随着电子商务的不断发展&#xff0c;越来越多的企业开始使用数据分析来提高业务绩效。数据分析可以帮助电商企业更好地理解市场和客户&#xff0c;以制定更有针对性的营销策略和产品方案。…

ksuser.dll文件缺失怎么办?软件或游戏无法启动,一键自动修复

很多小伙伴反馈&#xff0c;自己的电脑中了病毒&#xff0c;被杀毒软件清理后&#xff0c;在打开游戏或软件的时候&#xff0c;经常会报错“提示无法找到ksuser.dll文件&#xff0c;建议重新安装软件或游戏”。自己根据提示重装后&#xff0c;还是报错&#xff0c;不知道应该怎…

两向量叉乘值为对应平行四边形面积--公式推导

两向量叉乘值为对应平行四边形面积--公式推导 介绍 介绍

[电磁学]大学物理陈秉乾老师课程笔记

主页有博主其他上万字的精品笔记,都在不断完善ing~ 第一讲 绪论,库仑定律 主要讲解了电磁学中的库伦定律和电场的相关概念&#xff0c;介绍了电荷和电磁相互作用的规律&#xff0c;并讲解了电场强度和电势的概念。 03:14 &#x1f393; 库伦定律&#xff1a;电势能与电荷的关…

【JAVA核心知识】分布式事务框架Seata

Seata 基本信息 GitHub&#xff1a;https://github.com/seata/seatastars: 20.6k 最新版本&#xff1a; v1.6.1 Dec 22, 2022 官方文档&#xff1a;http://seata.io/zh-cn/index.html 注意 官方仅仅支持同步调用。 官方在FAQ中表示对于异步框架需要自行支持。 具体的扩展思…

【Maven】<scope>provided</scope>

在Maven中&#xff0c;“provided”是一个常用的依赖范围&#xff0c;它表示某个依赖项在编译和测试阶段是必需的&#xff0c;但在运行时则由外部环境提供&#xff0c;不需要包含在最终的项目包中。下面是对Maven scope “provided”的详细解释&#xff1a; 编译和测试阶段可用…

关于2024年度PMI认证考试计划的通知

尊敬的考生&#xff1a; 经PMI和中国国际人才交流基金会研究决定&#xff0c;2024年度中国大陆地区计划举办四次PMI认证考试&#xff0c;3月、6月、8月、11月各举办一次&#xff0c;具体考试日期另行公布。如遇特殊情况需变更考试计划的&#xff0c;将提前另行通知。 PMI&#…

Ubuntu安装K8S(1.28版本,基于containrd)

原文网址&#xff1a;Ubuntu安装K8S(1.28版本&#xff0c;基于containrd&#xff09;-CSDN博客 简介 本文介绍Ubuntu安装K8S的方法。 官网文档&#xff1a;这里 1.安装K8S 1.让apt支持SSL传输 sudo apt-get update sudo apt-get -y install apt-transport-https ca-certi…

超声波清洗机实测!北柏、希亦、洁盟超声波清洗机哪款清洁强?

眼镜清洗其实是一件非常重要的事情&#xff0c;一定不要忽视&#xff0c;表面看眼镜已经清洗干净了&#xff0c;实际眼镜缝隙中的杂污渍还没清洁到位&#xff0c;时间一长就会非常容易滋生细菌以及长螨虫&#xff01;为了杜绝这种情况发生&#xff0c;大家务必重视起清洗眼镜&a…

SpringBoot2.7.12整合Knife4j

SpringBoot2.7.12整合Knife4j 是什么 Knife4j是一个集Swagger2 和 OpenAPI3为一体的增强解决方案 添加依赖 <!--引入Knife4j的官方start包,该指南选择Spring Boot版本<3.0,开发者需要注意--> <dependency><groupId>com.github.xiaoymin</groupId>&l…

Zookeeper-Zookeeper特性与节点数据类型详解

1.Zookeeper介绍 ZooKeeper 是一个开源的分布式协调框架&#xff0c;是Apache Hadoop 的一个子项目&#xff0c;主要用来解决分布式集群中应用系统的一致性问题。Zookeeper 的设计目标是将那些复杂目容易出错的分布式一致性服务封装起来&#xff0c;构成一高效可靠的原语集&…

如何本地搭建FastDFS文件服务器并实现远程访问【内网穿透】

文章目录 前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx 2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.…

交换两个数字的三种方法-LeetCode做题总结 344

344. 反转字符串 题解Java知识点交换两个数字的三种方法1、temp2、异或3、 题解 class Solution {public void reverseString(char[] s) {char temp;for(int i0,js.length-1; i<j; i,j--) {temp s[i];s[i] s[j];s[j] temp;}} }Java知识点 交换两个数字的三种方法 1、t…