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


Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射

运行效果:
在这里插入图片描述

文章目录

  • 系列文章
  • 前言
  • Python+OpenGL绘制3D模型(六)材质文件载入和贴图映射
    • 一、从文件读取贴图
    • 二、glBindTexture
    • 三、指定贴图坐标
    • 四、运行图效果
    • 五、2个问题和原因
    • 六、源代码
      • 1、Draw1.py
      • 2、tOpenGLqt5.py
  • 系列文章预告

一、从文件读取贴图

记得以前用c++写的时候,要编译链接图像库,用于对应图片格式的加载,每个用到的格式都要单独搞一遍,在网上找开源的图片库,下载,编译,测试,一套下来搞的人很累,现在,因为有Qt的加持,载入贴图变得非常简单,全部交给Qt来做,只需要几行代码

def load_texture_from_file( filepath ):with open(filepath, 'rb') as hf:data = hf.read()image = QImage()valid = image.loadFromData(data)if not valid:return Falsegl_tex_obj = QOpenGLTexture(image.mirrored())return gl_tex_obj

图片载入到Qt后,还需要调用OpenGL的库加载贴图数据,另外Qt的图片坐标Y轴是向下增加的,符合显示屏幕坐标的习惯,而OpenGL中的Y轴坐标是正常向上的,所以Y轴需要mirror处理一下

is_built = False
gl_tex = Nonedef draw(gl):global is_built, gl_texif not is_built:gl_tex = load_texture_from_file("c:/temp/cg.jpg")is_built = True

为了代码保持简洁,简化接口,不要把简单的事情复杂化:
1、载入贴图后,对象保存到全局对象中,
2、载入模型的初始工作也内嵌到draw函数中执行,这种思维在项目越做越大也仍然实用
3、贴图路径使用了硬编码方式的绝对路径,在没有形成一个模型框架的时候,暂且用这种方式对测试减少不少工作量

演示用到的贴图文件下载:
在这里插入图片描述

二、glBindTexture

在绘制模型前,设置一下OpenGL的状态机,调用glBindTexture指定当前纹理单元的数据,然后调用glEnable(GL_TEXTURE)激活纹理单元,因为有贴图作为像素的输入颜色,所以颜色设为纯白

    gl.glDisable(gl.GL_TEXTURE_2D)gl.glColor3f(0.9, 0.83, 0.6)# 绘制上下2个面draw_single_face(p1, p2, p3, p4, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p5, p6, p7, p8, uv1,  uv2,  uv3,  uv4,  gl)gl_tex.bind()gl.glEnable(gl.GL_TEXTURE_2D)gl.glColor3f(1, 1, 1)# 绘制其他4个面,前,后,左,有draw_single_face(p1, p2, p6, p5, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p3, p4, p8, p7, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p2, p3, p7, p6, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p4, p1, p5, p8, uv1,  uv2,  uv3,  uv4, gl)

在本例中,前后左右的面映射了贴图,上下2个面仍然用了原来的模型颜色(淡黄色)

三、指定贴图坐标

在调用glVertex3f之前,需要先指定顶点的贴图坐标属性,调用glTexCoord2f

    gl.glBegin(gl.GL_TRIANGLES)# 第一个顶点gl.glTexCoord2f( ... )gl.glVertex3f( ... )# 第二个顶点gl.glTexCoord2f( ... )gl.glVertex3f( ... )# 第三个顶点gl.glTexCoord2f( ... )gl.glVertex3f( ... )# 完成gl.glEnd()

四、运行图效果

在这里插入图片描述

五、2个问题和原因

第一、模型的位置有点高:

这是应为camera坐标系的中心为(0,0,0), 而模型高度是从 0.0 到 2.0,要调整一下中心坐标,可以通过设置概念上的world matrix沿Z轴往下移动1.0个单位,模型就居中了,目前程序框架里控制视角的平移,也可以通过这个方法来解决,不过我还是打算在之后引入Camera对象的计算,计算的逻辑思维更清晰,不容易出错

    # Cameraself.gl.glMatrixMode(self.gl.GL_MODELVIEW)self.gl.glLoadIdentity()self.gl.glTranslatef(0.0,0.0,-self.zoom)self.gl.glRotatef(self.rotX-90,1.0,0.0,0.0)self.gl.glRotatef(self.rotZ,0.0,0.0,1.0)# Worldself.gl.glTranslatef(0.0,0.0,-1.0)  # 在这里加上对world matrix的改变

这里的矩阵叠加,是按照相反的顺序乘积的,比较容易搞错,OpenGL中有个机制 PushMatrix PopMatrix,也就是说后面加入的矩阵,可以通过PopMatrix来恢复到之前的矩阵乘积的状态,越靠后面加进来的矩阵,代表子物体的矩阵,在运算中最先乘这个矩阵

第二、当模型放大的时候,近的地方会被切掉

调整下几行代码,

    # Projectionself.gl.glMatrixMode(self.gl.GL_PROJECTION)pm = QMatrix4x4()aspectRatio = w/hfov = 45 / aspectRatio if w < h else 45pm.perspective(fov,  w/h,  2,  5000)self.gl.glLoadMatrixf(pm.data())

这里是设置透视投影矩阵的代码,pm.perspective(fov, w/h, 2, 5000),这个函数调用的最后2个参数,分别代表了近裁剪平面(z-buff=0.0),和远裁剪平面(z-buff=1.0),这2个值明显不太匹配当前场景的大小,每次应该根据当前场景大小来适当选择取值范围

    pm.perspective(fov,  w/h,  0.1,  100)

改成 0.1 到 100 的范围,就能够比较适配当前的测试模型
在这里插入图片描述
到此我们已经能够绘制一个完整的模型,不过模型数据来源还没解决,不能显示复杂的模型,下节我们要讲一个模型数据来源的通用方法,通过编写一个3dsmax插件导出模型,因为3dsmax也支持python,所以下节我们用很少的python代码来完成一个复杂的模型导出插件

六、源代码

1、Draw1.py

from PyQt5.QtGui import QVector3D, QVector2D, QImage,  QOpenGLTexture################################
#                   FILE DESCRIPTION
#  文件描述:load_texture_and_bind()
#  对应文章:Python+OpenGL绘制3D模型(六) 载入贴图及映射到模型
#  作者:李航 Lihang
#
################################is_built = False
gl_tex = Nonedef draw(gl):global is_built, gl_texif not is_built:gl_tex = load_texture_from_file("c:/temp/cg.jpg")is_built = True# 设置z-buff偏移gl.glEnable(gl.GL_POLYGON_OFFSET_FILL)gl.glPolygonOffset(1, 1)# 绘制填充面draw_box_faces(gl_tex, gl)# 关闭z-buff偏移gl.glDisable(gl.GL_POLYGON_OFFSET_FILL)# 绘制线框gl.glColor3f(0.0, 0.0, 0.0)draw_box_lines(gl)def load_texture_from_file( filepath ):with open(filepath, 'rb') as hf:data = hf.read()image = QImage()valid = image.loadFromData(data)if not valid:return Falsegl_tex_obj = QOpenGLTexture(image.mirrored())return gl_tex_obj############
# draw_box_faces
#   画面 - 中间的填充部分
############
def draw_box_faces(gl_tex, gl):p1 = QVector3D(-1, -1, 0 )p2 = QVector3D(+1, -1, 0 )p3 = QVector3D(+1, +1, 0 )p4 = QVector3D(-1, +1, 0 )p5 = QVector3D(-1, -1, 2 )p6 = QVector3D(+1, -1, 2 )p7 = QVector3D(+1, +1, 2 )p8 = QVector3D(-1, +1, 2 )uv1 = QVector2D(0, 0)uv2 = QVector2D(1, 0)uv3 = QVector2D(1, 1)uv4 = QVector2D(0, 1)gl.glDisable(gl.GL_TEXTURE_2D)gl.glColor3f(0.9, 0.83, 0.6)draw_single_face(p1, p2, p3, p4, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p5, p6, p7, p8, uv1,  uv2,  uv3,  uv4,  gl)gl_tex.bind()gl.glEnable(gl.GL_TEXTURE_2D)gl.glColor3f(1, 1, 1)draw_single_face(p1, p2, p6, p5, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p3, p4, p8, p7, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p2, p3, p7, p6, uv1,  uv2,  uv3,  uv4, gl)draw_single_face(p4, p1, p5, p8, uv1,  uv2,  uv3,  uv4, gl)def draw_single_face(p1, p2, p3, p4, uv1,  uv2,  uv3,  uv4, gl):gl.glBegin(gl.GL_TRIANGLES)gl.glTexCoord2f(uv1.x(), uv1.y())gl.glVertex3f(p1.x(),  p1.y(), p1.z())gl.glTexCoord2f(uv2.x(), uv2.y())gl.glVertex3f(p2.x(),  p2.y(), p2.z())gl.glTexCoord2f(uv3.x(), uv3.y())gl.glVertex3f(p3.x(),  p3.y(), p3.z())gl.glEnd()gl.glBegin(gl.GL_TRIANGLES)gl.glTexCoord2f(uv3.x(), uv3.y())gl.glVertex3f(p3.x(),  p3.y(), p3.z())gl.glTexCoord2f(uv4.x(), uv4.y())gl.glVertex3f(p4.x(),  p4.y(), p4.z())gl.glTexCoord2f(uv1.x(), uv1.y())gl.glVertex3f(p1.x(),  p1.y(), p1.z())gl.glEnd()############
# draw_box_lines
#   画面 - 外侧的线
############
def draw_box_lines(gl):p1 = QVector3D(-1, -1, 0 )p2 = QVector3D(+1, -1, 0 )p3 = QVector3D(+1, +1, 0 )p4 = QVector3D(-1, +1, 0 )p5 = QVector3D(-1, -1, 2 )p6 = QVector3D(+1, -1, 2 )p7 = QVector3D(+1, +1, 2 )p8 = QVector3D(-1, +1, 2 )# 一个立方体有12条边draw_single_line( p1, p2, gl )draw_single_line( p2, p3, gl )draw_single_line( p3, p4, gl )draw_single_line( p4, p1, gl )draw_single_line( p5, p6, gl )draw_single_line( p6, p7, gl )draw_single_line( p7, p8, gl )draw_single_line( p8, p5, gl )draw_single_line( p1, p5, gl )draw_single_line( p2, p6, gl )draw_single_line( p3, p7, gl )draw_single_line( p4, p8, gl )def draw_single_line(p1, p2, gl):gl.glBegin(gl.GL_LINES)gl.glVertex3d(p1.x(),  p1.y(), p1.z())gl.glVertex3d(p2.x(),  p2.y(), p2.z())gl.glEnd()

2、tOpenGLqt5.py

import sys
from PyQt5.QtCore import (QPoint)
from PyQt5.QtGui import (QMatrix4x4, QVector3D, QOpenGLVersionProfile)
from PyQt5.QtWidgets import QApplication, QOpenGLWidgetfrom Draw1 import draw############
# GLWidget
# OpenGL 窗口通用程序框架
#   1、创建OpenGL环境
#   2、设置矩阵
#   3、控制窗口视角
#   4、调用 draw 绘图主函数
############
class GLWidget(QOpenGLWidget):def __init__(self, parent):super(GLWidget, self).__init__( parent)self.dragPressPos = QPoint()self.rotX=45self.rotZ=0self.ps_button = 0self.ps_rotX = 0self.ps_rotZ = 0self.zoom=10############# 创建OpenGL环境# Qt6 和 Qt5的主要区别在这里############def initializeGL(self):version_profile = QOpenGLVersionProfile()version_profile.setVersion(2, 0)self.gl = self.context().versionFunctions(version_profile)self.gl.initializeOpenGLFunctions()############# paintEvent############def paintEvent(self,  event):# Step 0self.makeCurrent()# Step 1self.gl.glClearColor(0.85, 0.85, 0.85, 1.0)self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)self.gl.glEnable(self.gl.GL_DEPTH_TEST)# Step 2self.SetupMatrix()# Step 3draw(self.gl)#self.drawTarget(self.gl)############# 绘图# 这里是个绘图的简单测试代码############def drawTarget(self, gl):p = QVector3D(0, 0, 0)gl.glColor3f(1.0, 0.0, 0.0);gl.glBegin(gl.GL_LINES)gl.glVertex3d(p.x()-1,  p.y(),  p.z()  )gl.glVertex3d(p.x()+1,  p.y(),  p.z() )gl.glEnd()gl.glColor3f(0.0, 1.0, 0.0);gl.glBegin(gl.GL_LINES)gl.glVertex3d(p.x(),  p.y()-1,  p.z()  )gl.glVertex3d(p.x(),  p.y()+1,  p.z() )gl.glEnd()gl.glColor3f(0.0, 0.0, 1.0);gl.glBegin(gl.GL_LINES)gl.glVertex3d(p.x(),  p.y(),  p.z()  )gl.glVertex3d(p.x(),  p.y(),  p.z() +4 )gl.glEnd()############# 设置矩阵# 透视矩阵和Camera矩阵############def SetupMatrix(self):# ViewPortw = self.width()h = self.height()self.gl.glViewport(0, 0, w, h)  # Projectionself.gl.glMatrixMode(self.gl.GL_PROJECTION)pm = QMatrix4x4()aspectRatio = w/hfov = 45 / aspectRatio if w < h else 45pm.perspective(fov,  w/h,  0.1,  100)self.gl.glLoadMatrixf(pm.data())# Cameraself.gl.glMatrixMode(self.gl.GL_MODELVIEW)self.gl.glLoadIdentity()self.gl.glTranslatef(0.0,0.0,-self.zoom)self.gl.glRotatef(self.rotX-90,1.0,0.0,0.0)self.gl.glRotatef(self.rotZ,0.0,0.0,1.0)# Worldself.gl.glTranslatef(0.0,0.0,-1)  # 在这里加上对world matrix的改变############# 视角控制# 1、左键旋转# 2、中间缩放# 3、平移 **TODO**############def mousePressEvent(self,event):self.dragPressPos = event.pos()self.ps_button = event.button()self.ps_rotX = self.rotXself.ps_rotZ = self.rotZdef mouseMoveEvent(self, event):diff = event.pos() - self.dragPressPosif self.ps_button == 1:self.rotX = self.ps_rotX + diff.y()*0.5if self.rotX > 90:self.rotX = 90if self.rotX < -90:self.rotX = -90;            # rotZself.rotZ = self.ps_rotZ + diff.x()*0.5self.repaint()def wheelEvent(self, event):delta = event.angleDelta().y()if delta < 0 :self.zoom += self.zoom * 0.2else:self.zoom -= self.zoom * 0.2self.repaint()############
# App
# 创建主窗口应用程序
# 并且进入消息循环
############
if __name__ == '__main__':app = QApplication(sys.argv)widget = GLWidget(None)widget.resize(640, 480)widget.show()sys.exit(app.exec())

系列文章预告

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

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

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

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

相关文章

Java强软弱虚引用

面试&#xff1a; 1.强引用&#xff0c;软引用&#xff0c;弱引用&#xff0c;虚引用分别是什么&#xff1f; 2.软引用和弱引用适用的场景&#xff1f; 3.你知道弱引用的话&#xff0c;能谈谈WeakHashMap吗&#xff1f; 目录 一、Java引用 1、强引用&#xff08;默认支持模式…

05-C++ 类和对象-继承

类与对象-03 继承与派生 1. 继承的概念 c最重要的特征是代码重用&#xff0c;通过继承机制可以利用已有的数据类型&#xff0c;来定义新的数据类型&#xff0c;新的类不仅拥有旧类的成员&#xff0c;还拥有新定义的成员。 一个 B 类继承于 A 类&#xff0c;或称从类 A 派生…

菜鸟网络Java实习一面面经

自我介绍&#xff0c;做过的项目 巴拉巴拉 你项目中用到redis&#xff0c;可以介绍一下为什么使用它吗&#xff1f; 基于内存操作&#xff0c;内存读写速度快。 支持多种数据类型&#xff0c;包括String、Hash、List、Set、ZSet等。 支持持久化。Redis支持RDB和AOF两种持久…

K8S异常处理

一、概述 1、k8s有时候会报错The connection to the server ip:6443 was refused - did you specify the right host or port &#xff0c;本文档提供几种可能产生该报错的原因和排障思路。 二、发现问题 使用任意Kubectl 命令会报错&#xff1a;The connection to the serv…

JVM GC 算法原理概述

对于JVM的垃圾收集&#xff08;GC&#xff09;&#xff0c;这是一个作为Java开发者必须了解的内容&#xff0c;那么&#xff0c;我们需要去了解哪些内容呢&#xff0c;其实&#xff0c;GC主要是解决下面的三个问题&#xff1a; 哪些内存需要回收&#xff1f; 什么时候回收&…

透过许战海矩阵洞察安记食品增长战略

引言&#xff1a;安记食品如果想实施增长战略&#xff0c;建议深耕招牌产品,走向全国市场,目前招牌产品咖哩和复合调味粉市场空间没有被全面释放出来,需要科学的产品战略作为支撑。安记食品选择功能性产品方向是正确的,但“功能性”需要一个大品类作为载体,牛奶,饮料是最大的载…

SpringBoot - Maven 打包合并一个胖 JAR 以及主项目 JAR 依赖 JAR 分离打包解决方案

问题描述 <plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.1.18.RELEASE</version><configuration><!--<classifier>exec</classifier>--…

产品经理学习-怎么写PRD文档

目录 瀑布流方法论介绍 产品需求文档&#xff08;PRD&#xff09;介绍 产品需求文档的基本要素 撰写产品需求文档 优先产品需求文档的特点 其他相关文档 瀑布流方法论介绍 瀑布流模型是一种项目的开发和管理的方法论&#xff0c;是敏捷的开发管理方式相对应的另一种方法…

Google机器人团队获ICRA 2023 机器人学习方向最佳论文奖:机器人实体控制的大语言模型程序

这篇论文主要讨论了大型语言模型&#xff08;LLM&#xff09;在机器人控制方面的应用。作者们指出&#xff0c;尽管LLM在理解和生成自然语言方面表现出色&#xff0c;但其在实际应用中&#xff0c;如机器人控制等领域的应用仍然有限。因此&#xff0c;他们提出了一种新的方法&a…

通过栈将中缀表达式转换为等价的后缀表达式

为什么要从中缀表达式转换为等价的后缀表达式&#xff1f; →使后缀表达式包含运算符优先级的信息&#xff0c;方便计算机能理解进行运算。 什么是中缀表达式&#xff1f; →中缀表达式虽然是人类看得懂的简单算术形式&#xff0c;但对计算机来说反而复杂难以理解 什么是后缀表…

【头歌实训】PySpark Streaming 入门

文章目录 第1关&#xff1a;SparkStreaming 基础 与 套接字流任务描述相关知识Spark Streaming 简介Python 与 Spark StreamingPython Spark Streaming APISpark Streaming 初体验&#xff08;套接字流&#xff09; 编程要求测试说明答案代码 第2关&#xff1a;文件流任务描述相…

<JavaEE> TCP 的通信机制(三) -- 滑动窗口

目录 TCP的通信机制的核心特性 四、滑动窗口 1&#xff09;什么是滑动窗口&#xff1f; 2&#xff09;滑动窗口的作用是什么&#xff1f; 3&#xff09;批量传输出现丢包如何处理&#xff1f; 1> 接收端ACK丢包 2> 发送端数据包丢包 4&#xff09;适用性 TCP的通…

Mysql 容易忘的 sql 指令总结

目录 一、操作数据库的基本指令 二、查询语句的指令 1、基本查询语句 2、模糊查询 3、分支查询 4、 分组查询 5、分组查询 6、基本查询总结&#xff1a; 7、子查询 8、连接查询 三、MySQL中的常用函数 1、时间函数 2、字符串函数 3、聚合函数 4、运算函数 四、表…

【elk-day01】es和kibana搭建及验证---Mac-Docker

Mac系统使用Docker下载搭建和验证eskibana Docker下载安装es安装es验证kibana安装kibana验证 Docker下载安装 Docker Desktop官网安装下载地址 说明一下为什么要安装desktop版本的docker&#xff0c;因为docker作为工具使用&#xff0c;我们需要的是开箱即用&#xff0c;没有必…

QT中的信号与槽的讲解

文章目录 信号及其特点槽及其特点代码演示标准信号与标准槽函数方式一方式二 自定义信号和槽connect()函数信号和槽函数存在函数重载的情况下Qt的信号槽机制注意事项 信号及其特点 信号&#xff1a;是一种特殊的函数&#xff0c;又称信号函数&#xff0c;俗称信号&#xff0c;…

2023年新一代开发者工具 Vue ,正式开源!

以下文章来源于前端充电宝 &#xff0c;作者CUGGZ 近日&#xff0c;Vue 新一代开发者工具&#xff08;DevTools&#xff09;正式开源&#xff01;Vue DevTools 是一个旨在增强 Vue 开发人员体验的工具&#xff0c;它提供了一些功能来帮助开发者更好地了解 Vue 应用。下面就来看…

第九部分 图论

目录 例 相关概念 握手定理 例1 图的度数列 例 无向图的连通性 无向图的连通度 例2 例3 有向图D如图所示&#xff0c;求 A, A2, A3, A4&#xff0c;并回答诸问题&#xff1a; 中间有几章这里没有写&#xff0c;感兴趣可以自己去学&#xff0c;组合数学跟高中差不多&#xff0c…

记录汇川:MODBUS TCP-梯形图

H5U的MODBUS通信不需要编写程序&#xff0c;通过组态MODBUS通信配置表&#xff0c;实现数据通信。 Modbus-TCP 主站即Modbus-TCP客户端&#xff0c;通过Modbus-TCP配置&#xff0c;可最多支持同时与31个 Modbus-TCP服务器&#xff08;从站&#xff09;进行通讯。 …

【51单片机系列】DS18B20温度传感器模块

本文是关于温度传感器的相关内容。 文章目录 一、 DS18B20数字温度传感器介绍1.1、 DS18B20温度传感器的特点1.2、DA18B20内部结构1.3、 DS18B20的温度转换规则1.4、 DS18B20的ROM指令表1.6、 计算温度1.7、 读写时序 二、DS18B20使用示例 一、 DS18B20数字温度传感器介绍 DS1…

CloneNotSupportedException的解决方案 + Object的clone方法分析

CloneNotSupportedException的解决方案 引入问题&#xff1a; 在一次测试clone方法时&#xff0c;D类Override了Object类的clone方法 public class D {private Integer A1;private Integer A2;public D() {}public D(Integer a1, Integer a2 {A1 a1;A2 a2;}Overrideprotec…