跟着cherno手搓游戏引擎【16】Camera和Uniform变量的封装

相机封装:

OrthographicCamera.h:

#pragma once
#include <glm/glm.hpp>
namespace YOTO {
	class OrthographicCamera
	{
	public:
		OrthographicCamera(float left,float right , float bottom,float top);

		const glm::vec3& GetPosition()const { return m_Position; }
		void SetPosition(const glm::vec3& position) { 
			m_Position = position; 
			RecalculateViewMatrix();
		}

		float GetRotation()const { return m_Rotation; }
		void SetRotation(float rotation) {
			m_Rotation = rotation; 
			RecalculateViewMatrix();
		}
		const glm::mat4& GetProjectionMatrix()const { return m_ProjectionMatrix; }
		const glm::mat4& GetViewMatrix()const { return m_ViewMatrix; }
		const glm::mat4& GetViewProjectionMatrix()const { return m_ViewProjectionMatrix; }
	private:
		void RecalculateViewMatrix();
	private:
		glm::mat4 m_ProjectionMatrix;
		glm::mat4 m_ViewMatrix;
		glm::mat4 m_ViewProjectionMatrix;
		glm::vec3 m_Position = { 0.0f ,0.0f ,0.0f };
		float m_Rotation = 0.0f;

	};
}


OrthographicCamera.cpp: 

#include "ytpch.h"
#include "OrthographicCamera.h"
#include <glm/gtc/matrix_transform.hpp>
namespace YOTO {
	OrthographicCamera::OrthographicCamera(float left, float right, float bottom, float top)
		:m_ProjectionMatrix(glm::ortho(left,right,bottom,top)),m_ViewMatrix(1.0f) 
	{
		m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
	}
	void OrthographicCamera::RecalculateViewMatrix()
	{
		glm::mat4 transform = glm::translate(glm::mat4(1.0f), m_Position)*
			glm::rotate(glm::mat4(1.0f),glm::radians(m_Rotation),glm::vec3(0,0,1));
		m_ViewMatrix = glm::inverse(transform);
		m_ViewProjectionMatrix = m_ProjectionMatrix * m_ViewMatrix;
	}
}

Uniform的封装:

Shader.h:

#pragma once
#include <string>
#include <glm/glm.hpp>
namespace YOTO {

	class Shader {

	public:
		Shader(const std::string& vertexSrc, const std::string& fragmentSrc);
		~Shader();
		void Bind()const;
		void UnBind()const;
		
		void UploadUniformMat4(const std::string&name,  const glm::mat4 & matrix);

	private:
		uint32_t m_RendererID;
	}
;
}

Shader.cpp:

#include"ytpch.h"
#include "Shader.h"
#include <glad/glad.h>
#include <YOTO/Log.h>
#include<glm/gtc/type_ptr.hpp>
namespace YOTO {
	Shader::Shader(const std::string& vertexSrc, const std::string& fragmentSrc)
	{
		// 1.1.创建顶点着色器对象
		GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
		// Send the vertex shader source code to GL
		// Note that std::string's .c_str is NULL character terminated.
		// 1.2.附加顶点着色器源码到顶点着色器对象中
		const GLchar* source = vertexSrc.c_str();
		glShaderSource(vertexShader, 1, &source, 0);
		// 1.3.编译顶点着色器对象
		glCompileShader(vertexShader);


		// 1.4.检查是否编译成功
		GLint isCompiled = 0;
		glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isCompiled);
		if (isCompiled == GL_FALSE) {
			// 1.4.2编译失败可以打印报错信息
			GLint maxLength = 0;
			glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength);
			// The maxLength includes the NULL character
			std::vector<GLchar> infoLog(maxLength);
			glGetShaderInfoLog(vertexShader, maxLength, &maxLength, &infoLog[0]);
			// We don't need the shader anymore.
			glDeleteShader(vertexShader);
			YT_CORE_ERROR("{0}", infoLog.data());
			YT_CORE_ASSERT(false, "Vertex shader compilation failure!");
			return;
		}
		// 片段着色器一样
		// 2.1.创建片段着色器对象
		GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
		// Send the fragment shader source code to GL
		// Note that std::string's .c_str is NULL character terminated.
		// 2.2.附加片段着色器源码到片段着色器对象中
		source = fragmentSrc.c_str();
		glShaderSource(fragmentShader, 1, &source, 0);
		// 2.3.编译片段着色器对象
		glCompileShader(fragmentShader);
		// 2.4.检查是否编译成功
		glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isCompiled);
		if (isCompiled == GL_FALSE) {
			// 2.4.2编译失败可以打印报错信息
			GLint maxLength = 0;
			glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength);
			// The maxLength includes the NULL character
			std::vector<GLchar> infoLog(maxLength);
			glGetShaderInfoLog(fragmentShader, maxLength, &maxLength, &infoLog[0]);
			// We don't need the shader anymore.
			glDeleteShader(fragmentShader);
			// Either of them. Don't leak shaders.
			glDeleteShader(vertexShader);
			YT_CORE_ERROR("{0}", infoLog.data());
			YT_CORE_ASSERT(false, "Fragment shader compilation failure!");
			return;
		}
		// Vertex and fragment shaders are successfully compiled.
		// Now time to link them together into a program.
		// Get a program object.
		// 3.1创建着色器程序对象
		m_RendererID = glCreateProgram();
		GLuint program = m_RendererID;
		// 3.2附加着色器对象给着色器程序对象
		glAttachShader(program, vertexShader);
		glAttachShader(program, fragmentShader);
		// 3.3链接着色器程序对象
		glLinkProgram(program);
		// 3.4可以检查链接是否成功
		// Note the different functions here: glGetProgram* instead of glGetShader*.
		GLint isLinked = 0;
		glGetProgramiv(program, GL_LINK_STATUS, (int*)&isLinked);
		if (isLinked == GL_FALSE) {
			GLint maxLength = 0;
			glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
			// The maxLength includes the NULL character
			std::vector<GLchar> infoLog(maxLength);
			glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
			// We don't need the program anymore.
			glDeleteProgram(program);
			// Don't leak shaders either.
			glDeleteShader(vertexShader);
			glDeleteShader(fragmentShader);
			YT_CORE_ERROR("{0}", infoLog.data());
			YT_CORE_ASSERT(false, "Shader link failure!");
			return;
		}
		// 4.删除着色器对象
		// Always detach shaders after a successful link.
		glDetachShader(program, vertexShader);
		glDetachShader(program, fragmentShader);

	
	}
	Shader::~Shader()
	{
		glDeleteProgram(m_RendererID);
	}
	void Shader::Bind() const
	{
		glUseProgram(m_RendererID);
	}
	void Shader::UnBind() const
	{
		glUseProgram(0);
	}
	void Shader::UploadUniformMat4(const std::string& name, const glm::mat4& matrix)
	{
		GLint loacation= glGetUniformLocation(m_RendererID, name.c_str());
		glUniformMatrix4fv(loacation, 1, GL_FALSE,glm::value_ptr(matrix));

	}
}

进一步封装ShaderUniform:

Renderer.h:

#pragma once
#include"RenderCommand.h"
#include "OrthographicCamera.h"
#include"Shader.h"
namespace YOTO {
	
	class Renderer {
	public:
		static void BeginScene(OrthographicCamera &camera);
		static void EndScene();
		static void Submit(const std::shared_ptr<Shader>& shader, const std::shared_ptr<VertexArray>& vertexArray);
		inline static RendererAPI::API GetAPI() {
			return RendererAPI::GetAPI();
		}
	private:
		struct SceneData {
			glm::mat4 ViewProjectionMatrix;
		};
		static SceneData* m_SceneData;
	};

}

Renderer.cpp:

#include"ytpch.h"
#include"Renderer.h"
namespace YOTO {
	Renderer::SceneData* Renderer::m_SceneData = new	Renderer::SceneData;
	void Renderer::BeginScene(OrthographicCamera& camera)
	{
		m_SceneData->ViewProjectionMatrix = camera.GetViewProjectionMatrix();
	}
	void Renderer::EndScene()
	{
	}
	void Renderer::Submit( const std::shared_ptr<Shader>& shader,const std::shared_ptr<VertexArray>& vertexArray)
	{
		shader->Bind();
		shader->UploadUniformMat4("u_ViewProjection", m_SceneData->ViewProjectionMatrix);
		vertexArray->Bind();
		RenderCommand::DrawIndexed(vertexArray);
	}
}

调用:

Application.h:

#pragma once
#include"Core.h"
#include"Event/Event.h"
#include"Event/ApplicationEvent.h"
#include "YOTO/Window.h"
#include"YOTO/LayerStack.h"
#include"YOTO/ImGui/ImGuiLayer.h"

#include <YOTO/Renderer/Shader.h>
#include <YOTO/Renderer/Buffer.h>
#include <YOTO/Renderer/VertexArray.h>

#include "YOTO/Renderer/OrthographicCamera.h"
namespace YOTO {
	class YOTO_API Application
	{
	public:
		Application();
		virtual ~Application();
		void Run();
		void OnEvent(Event &e);
		void PushLayer(Layer* layer);
		void PushOverlay(Layer* layer);

		inline static Application& Get() {return * s_Instance;}
		inline Window& GetWindow() { return *m_Window; }
	private:
		bool  OnWindowClosed(WindowCloseEvent& e);
		std::unique_ptr<Window>  m_Window;
		ImGuiLayer *  m_ImGuiLayer;
		bool m_Running = true;
		LayerStack m_LayerStack;
		
		//unsigned int m_VertexArray;
		std::shared_ptr<Shader> m_Shader;
		std::shared_ptr<VertexArray> m_VertexArray;


		std::shared_ptr<Shader> m_BlueShader;
		std::shared_ptr<VertexArray> m_SquareVA;
		OrthographicCamera m_Camera;

	private:
		static Application* s_Instance;
	};
	//在客户端定义
	Application* CreateApplication();
}

Application.cpp:

#include"ytpch.h"
#include "Application.h"

#include"Log.h"
#include "YOTO/Renderer/Renderer.h"
#include"Input.h"


namespace YOTO {
#define BIND_EVENT_FN(x) std::bind(&x, this, std::placeholders::_1)

	 Application* Application::s_Instance = nullptr;

	Application::Application()
		:m_Camera(-2.0f,2.0f,-2.0f,2.0f)
	{

		YT_CORE_ASSERT(!s_Instance, "Application需要为空!")
		s_Instance = this;
		//智能指针
		m_Window = std::unique_ptr<Window>(Window::Creat());
		//设置回调函数
		m_Window->SetEventCallback(BIND_EVENT_FN(Application::OnEvent));
		//new一个Layer,放在最后层进行渲染
		m_ImGuiLayer = new ImGuiLayer();
		PushOverlay(m_ImGuiLayer);  
		//unsigned int id;
		//glGenBuffers(1, &id);

		uint32_t indices[3] = { 0,1,2 };
		float vertices[3 * 7] = {
			-0.5f,-0.5f,0.0f, 0.8f,0.2f,0.8f,1.0f,
			0.5f,-0.5f,0.0f,  0.2f,0.3f,0.8f,1.0f,
			0.0f,0.5f,0.0f,   0.8f,0.8f,0.2f,1.0f,
		};

		m_VertexArray.reset(VertexArray::Create());


		std::shared_ptr<VertexBuffer> m_VertexBuffer;
		m_VertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));

		{
			BufferLayout setlayout = {

	{ShaderDataType::Float3,"a_Position"},
		{ShaderDataType::Float4,"a_Color"}
			};
			m_VertexBuffer->SetLayout(setlayout);

		}

		m_VertexArray->AddVertexBuffer(m_VertexBuffer);
		
		 
		std::shared_ptr<IndexBuffer>m_IndexBuffer;
		m_IndexBuffer.reset(IndexBuffer::Create(indices, sizeof(indices)/sizeof(uint32_t)));

		m_VertexArray->AddIndexBuffer(m_IndexBuffer);

		std::string vertexSource = R"(
		#version 330 core
		layout(location = 0) in vec3 a_Position;
		layout(location = 1) in vec4 a_Color;
		uniform mat4 u_ViewProjection;

		out vec3 v_Position;
		out vec4 v_Color;
		void main(){
		v_Position=a_Position;
		v_Color=a_Color;
		gl_Position =u_ViewProjection*vec4( a_Position,1.0);
		}
		)";
		//绘制颜色
		std::string fragmentSource = R"(
		#version 330 core
		layout(location = 0) out vec4 color;
		in vec3 v_Position;
		in vec4 v_Color;
		void main(){
		color=vec4(v_Color);
		}
		)";
		m_Shader.reset(new Shader(vertexSource, fragmentSource));

		///测试/

		m_SquareVA.reset(VertexArray::Create());

		float squareVertices[3 * 4] = {
			-0.5f,-0.5f,0.0f,
			0.5f,-0.5f,0.0f, 
			0.5f,0.5f,0.0f,
			-0.5f,0.5f,0.0f
		};
		std::shared_ptr<VertexBuffer> squareVB;
		squareVB.reset(VertexBuffer::Create(squareVertices, sizeof(squareVertices)));
		squareVB->SetLayout({
			{ShaderDataType::Float3,"a_Position"}
		});
		m_SquareVA->AddVertexBuffer(squareVB);
		uint32_t squareIndices[6] = { 0,1,2,2,3,0 };
		std::shared_ptr<IndexBuffer> squareIB; 

		squareIB.reset((IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t))));

		m_SquareVA->AddIndexBuffer(squareIB);
		
		//测试:
		std::string BlueShaderVertexSource = R"(
		#version 330 core
		layout(location = 0) in vec3 a_Position;
		uniform mat4 u_ViewProjection;
		out vec3 v_Position;
		void main(){
		v_Position=a_Position;
		gl_Position =u_ViewProjection*vec4( a_Position,1.0);
		}
		)";
		//绘制颜色
		std::string BlueShaderFragmentSource = R"(
		#version 330 core
		layout(location = 0) out vec4 color;
		in vec3 v_Position;
		void main(){
		color=vec4(0.2,0.3,0.8,1.0);
		}
		)";
		m_BlueShader.reset(new Shader(BlueShaderVertexSource, BlueShaderFragmentSource));
	}
	Application::~Application() {

	}
	/// <summary>
	/// 所有的Window事件都会在这触发,作为参数e
	/// </summary>
	/// <param name="e"></param>
	void Application::OnEvent(Event& e) {
		//根据事件类型绑定对应事件
		EventDispatcher dispatcher(e);
		dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(Application::OnWindowClosed));
		//输出事件信息
		YT_CORE_INFO("Application:{0}",e);
		for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();) {
			(*--it)->OnEvent(e);
			if (e.m_Handled)
				break;
		}
	}

	bool Application::OnWindowClosed(WindowCloseEvent& e) {
		m_Running = false;
		return true;
	}
	void Application::Run() {
		WindowResizeEvent e(1280, 720);
		if (e.IsInCategory(EventCategoryApplication)) {
			YT_CORE_TRACE(e);
		}
		if (e.IsInCategory(EventCategoryInput)) {
			YT_CORE_ERROR(e);
		}

		while (m_Running)
		{
			RenderCommand::SetClearColor({0.2f, 0.2f, 0.2f, 1.0f});
			RenderCommand::Clear();
			m_Camera.SetPosition({0.5f,0.5f, 0.0f });
			m_Camera.SetRotation(45);
			Renderer::BeginScene(m_Camera);
			{
			/*	m_BlueShader->Bind();
				m_BlueShader->UploadUniformMat4("u_ViewProjection",m_Camera.GetViewProjectionMatrix());*/
				Renderer::Submit(m_BlueShader,m_SquareVA);
			/*	m_Shader->Bind();
				m_Shader->UploadUniformMat4("u_ViewProjection", m_Camera.GetViewProjectionMatrix());*/
				Renderer::Submit(m_Shader,m_VertexArray);
				Renderer::EndScene();
			}

			for (Layer* layer : m_LayerStack) {
				layer->OnUpdate();
			}
			//将ImGui的刷新放到APP中,与Update分开
			m_ImGuiLayer->Begin();
			
			for (Layer* layer : m_LayerStack) {
				layer->OnImGuiRender();
			}
			m_ImGuiLayer->End();
			m_Window->OnUpdate();
		}
	}
	void Application::PushLayer(Layer* layer) {
		m_LayerStack.PushLayer(layer);
		layer->OnAttach();
	}
	void Application::PushOverlay(Layer* layer) {
		m_LayerStack.PushOverlay(layer);
		layer->OnAttach();
	}
}

测试:

cool!

目前代码比较潦草,甚至有些地方没有注释。计划在渲染出第一个兰伯特模型时对代码架构进行总结。

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

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

相关文章

阿赵UE学习笔记——13、贴花

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。这次介绍一种特殊的材质类型&#xff0c;贴花。 一、获取贴花资源 在没有分析贴花的原理之前&#xff0c;可以先去获得一些免费的贴花资源来使用&#xff0c;比如在Quixel上面就有专门的一个资源…

rp-bf:一款Windows下辅助进行ROP gadgets搜索的Rust库

关于rp-bf rp-bf是一款Windows下辅助进行ROP gadgets搜索的Rust库&#xff0c;该工具可以通过模拟Windows用户模式下的崩溃转储来爆破枚举ROP gadgets。 在很多系统安全测试场景中&#xff0c;研究人员成功劫持控制流后&#xff0c;通常需要将堆栈数据转移到他们所能够控制的…

iZotope RX 10.4.2 mac激活版 音频修复和增强工具

iZotope RX 10 for Mac是一款专业的音频修复软件&#xff0c;旨在提供强大、精确的工具&#xff0c;让用户能够清晰、纯净地处理音频。以下是其主要功能和特点&#xff1a; 软件下载&#xff1a;iZotope RX 10.4.2 mac激活版下载 强大的降噪功能&#xff1a;iZotope RX 10采用了…

P1228 地毯填补问题

地毯填补问题 题目描述 相传在一个古老的阿拉伯国家里&#xff0c;有一座宫殿。宫殿里有个四四方方的格子迷宫&#xff0c;国王选择驸马的方法非常特殊&#xff0c;也非常简单&#xff1a;公主就站在其中一个方格子上&#xff0c;只要谁能用地毯将除公主站立的地方外的所有地…

IMX6LL|打造自己的驱动总线

xbus&#xff1a;打造自属的驱动总线 驱动总线 软件与硬件代码分离&#xff0c;提高程序的复用性 device–关联硬件代码driver_devices–关联软件代码bus_type–统一管理、设置match匹配规则 设备驱动模型体现分离思想 bus-xbus-devices-drivers 总线管理 buses_init()函…

贪吃蛇---C语言---详解

引言 C语言已经学了不短的时间的&#xff0c;这期间已经开始C和Python的学习&#xff0c;想给我的C语言收个尾&#xff0c;想起了小时候见过别人的老人机上的贪吃蛇游戏&#xff0c;自己父母的手机又没有这个游戏&#xff0c;当时成为了我的一大遗憾&#xff0c;这两天发现C语…

C++ 之LeetCode刷题记录(二十四)

&#x1f604;&#x1f60a;&#x1f606;&#x1f603;&#x1f604;&#x1f60a;&#x1f606;&#x1f603; 开始cpp刷题之旅。 目标&#xff1a;执行用时击败90%以上使用 C 的用户。 119. 杨辉三角 II 给定一个非负索引 rowIndex&#xff0c;返回「杨辉三角」的第 rowI…

基于stm32F4卷积神经网络手写数字识别项目

加我微信hezkz17 可以申请加入嵌入式人工智能技术研究开发交流答疑群&#xff0c;赠送企业嵌入式AI 图像理解/音/视频项目核心开发资料 1 采用CNN BP反向传播算法更新权重系数 2 原理解析 3 实现策略 训练与识别分离&#xff0c;先在电脑上训练好CNN BP神经网络的模型&#…

音视频数字化(数字与模拟-音频广播)

在互联网飞速发展的今天,每晚能坐在电视机前面的人越来越少,但是每天收听广播仍旧是很多人的习惯。 从1906年美国费森登在实验室首次进行无线电广播算起,“广播”系统已经陪伴人们115年了。1916年,收音机开始上市,收音机核心是“矿石”。1920年开始“调幅”广播,1941年开…

又涨又跌 近期现货黄金价格波动怎么看?

踏入2024年一月的下旬&#xff0c;现货黄金价格可以说没了之前火热的状态&#xff0c;盘面上是又涨又跌。面对这样的行情&#xff0c;很多投资者不知道如何看了。下面我们就来讨论一下怎么把握近期的行情。 先区分走势类型。在现货黄金市场中有两种主要的走势类型&#xff0c;一…

WebAssembly核心编程[1]:wasm模块实例化的N种方式

当我们在一个Web应用中使用WebAssembly&#xff0c;最终的目的要么是执行wasm模块的入口程序&#xff08;通过start指令指定的函数&#xff09;&#xff0c;要么是调用其导出的函数&#xff0c;这一切的前提需要创建一个通过WebAssembly.Instance对象表示的wasm模块实例(源代码…

基于flask的个人博客项目从0到1

项目展示(持续完善中…) 首页 文章时间线页面 笔记页面 留言页面 关于页面 后台页面-文章管理 后台页面-笔记页面 后台页面-分类 后台管理-新增标签 后台管理-标签页面 后台管理-新增标签 后台管理-关于页面 2.项目详述 该博客开源地址点击跳转&#xff0c;该项目已部署上…

【代码随想录19】235.二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点

目录 235.二叉搜索树的最近公共祖先题目描述参考代码 701.二叉搜索树中的插入操作题目介绍参考代码 450.删除二叉搜索树中的节点题目描述参考代码 235.二叉搜索树的最近公共祖先 题目描述 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖…

自治系统 AS 、路由选择协议、

目录 路由算法分类&#xff08;自适应&#xff09; 分层次的路由选择协议 自治系统 AS (Autonomous System) 2 大类路由选择协议 自治系统和内部网关协议、外部网关协议 路由选择协议属于网络层控制层面的内容 路由算法分类&#xff08;自适应&#xff09; 分层次的路由选…

vue3中多个表格怎么同时滚动并且固定表头

说明&#xff1a;这里需分为两种情况来做。第一种亲情况就是没有修改过el-table这个组件的样式&#xff1b;第二种情况就是修改过el-table组件的样式。第一种较为简单就简单略过&#xff0c;这里主要提及第二种做法。 1.需求效果 2.第一种没有修改过el-table这个组件的样式的做…

存内计算技术—解决冯·诺依曼瓶颈的AI算力引擎

文章目录 存内计算技术背景CSDN首个存内计算开发者社区硅基光电子技术存内计算提升AI算力知存科技存算一体芯片技术基于存内计算的语音芯片的实现挑战 参考文献 存内计算技术背景 存内计算技术是一种革新性的计算架构&#xff0c;旨在克服传统冯诺依曼架构的瓶颈&#xff0c;并…

详讲api网关之kong的基本概念及安装和使用(二)

consul的服务注册与发现 如果不知道consul的使用&#xff0c;可以点击上方链接&#xff0c;这是我写的关于consul的一篇文档。 upstreamconsul实现负载均衡 我们知道&#xff0c;配置upstream可以实现负载均衡&#xff0c;而consul实现了服务注册与发现&#xff0c;那么接下来…

防御第五次作业-防火墙综合实验(nat、双机热备、带宽、选路)

目录 拓扑图 要求 1 2 3 4 5 办公区上网用户限制流量不超过60M 销售部限流 6 7 拓扑图 说明&#xff1a;基本配置完成。所有设备均可出网。 要求 1.办公区设备可以通过电信和移动两条链路上网&#xff0c;且需要保留一个公网ip不能用来转换。 2.分公司设备可以通…

CANoe实际项目中文件夹的规划

本人&#xff0c;之前设计了一个CANoe工程&#xff0c;由于工程设计之初没有设计好文档的归纳分类&#xff0c;导致文件查找起来非常费劲。 为了避免以后出现文件混乱&#xff0c;不可查找的问题&#xff0c;故特此归纳说明。 建立工程时&#xff1a; 第1步就应该设计好文档…

InputNumber数字输入框(antd-design组件库)简单使用

1.InputNumber数字输入框 通过鼠标或键盘&#xff0c;输入范围内的数值。 2.何时使用 当需要获取标准数值时。 组件代码来自&#xff1a; 数字输入框 InputNumber - Ant Design 3.本地验证前的准备 参考文章【react项目antd组件-demo:hello-world react项目antd组件-demo:hello…