1.文件的读写
支持的文件格式
- 三维模型文件格式
- 常见格式:.obj(Wavefront OBJ)、.fbx(Autodesk FBX)、.dae(Collada)、.gltf(GL Transmission Format)。
- 特点:.obj格式简单易用,适合静态模型;.fbx支持动画和复杂场景,广泛用于游戏和影视;.gltf是现代Web应用中常用的格式,支持PBR(物理基础渲染)。
- 图片及视频文件格式
- 图片格式:.jpg(有损压缩,适合照片)、.png(无损压缩,支持透明)、.bmp(位图,未压缩)。
- 视频格式:.mp4(广泛支持,良好的压缩比)、.avi(较老的格式,支持多种编码)、.mov(Apple的格式,常用于视频编辑)。
- 打包及网络传输格式
- 打包格式:.zip、.tar、.rar(数据压缩和归档)。
- 网络传输格式:JSON、XML(用于数据交换),适用于API和网络通信。
- 字体文件格式
- 常见字体格式:.ttf(TrueType Font,广泛支持)、.otf(OpenType Font,支持更多功能)、.woff(Web Open Font Format,专为网页设计)。
- 伪插件文件格式
- 伪插件通常是为了兼容性和扩展性而设计的,允许开发者在不修改核心代码的情况下添加新功能。
- .osg文件和.ive文件
- .osg文件是OpenSceneGraph的场景描述文件,包含场景的几何和材质信息。
- .ive文件是二进制格式,相比于.osg文件,加载速度更快,适合大场景的实时渲染。
文件读写的流程
- osgDB库
- osgDB是OpenSceneGraph的数据库接口,支持多种文件格式的读写,提供统一的API接口,简化了文件操作。
- 文件的读取与保存
- 读取文件时,首先通过osgDB库确定文件格式,然后解析文件内容并生成相应的场景图节点。
- 保存文件时,需将场景图节点序列化为目标格式,确保数据完整性。
- 文件读写示例
- 文件读写进度
- 可以通过回调函数或进度条来显示文件读写的进度,提升用户体验。
- 文件读取进度示例
插件的工作机制 - 插件的搜索和注册
- 插件通常放置在特定目录下,osgDB会在初始化时扫描这些目录并注册可用插件。
- osgArchive读写流程
- osgArchive用于处理打包文件,支持读取和写入,能够处理内部文件结构。
- 自定义文件插件
- 开发者可以根据需要实现自定义文件格式的读写插件,需实现特定的接口。
- 自定义文件格式读写插件示例
读写中文文件名及中文路径问题 - 使用UTF-8编码处理中文文件名和路径,确保在不同操作系统上都能正确识别。
- 在Windows平台上,可能需要使用特定的API(如_wfopen)来处理宽字符。
osgEXP导出文件 - osgEXP是OpenSceneGraph的导出工具,支持将场景导出为多种文件格式。
- 使用osgEXP时,可以设置导出选项,如是否导出纹理、动画等。
要点总结 - 了解各种文件格式的特性及适用场景,有助于选择合适的格式进行数据交换。
- 掌握文件的读写流程和示例代码,能够在项目中灵活运用。
- 理解插件机制,能够扩展和自定义功能,提升系统灵活性。
- 处理中文文件名和路径问题时,需注意编码方式,确保跨平台兼容性。
- 熟悉osgEXP的使用,能够有效地导出场景数据,便于后续处理和共享。
2.C++语法
osgDB::Registry::instance() 一个典型的单例模式实现
class Registry {
public:
static Registry& instance() {
static Registry instance; // 局部静态变量,保证只初始化一次
return instance;
}
private:
Registry() {} // 构造函数私有化,防止外部创建实例
Registry(const Registry&) = delete; // 禁止拷贝构造
Registry& operator=(const Registry&) = delete; // 禁止赋值
};
// 拷贝构造函数的实现
class MyClass {
public:
int data;
// 默认构造函数
MyClass(int value) : data(value) {}
// 拷贝构造函数 浅拷贝
MyClass(const MyClass& other) {
data = other.data; // 复制状态
}
};
// 深拷贝示例
class MyClass {
public:
int* data;
// 默认构造函数
MyClass(int value) {
data = new int(value);
}
// 拷贝构造函数(深拷贝)
MyClass(const MyClass& other) {
data = new int(*other.data); // 分配新的内存并复制值
}
// 析构函数
~MyClass() {
delete data; // 释放内存
}
};
std::deque<std::string> C++ 标准库中的一个双端队列(double-ended queue),可以存储 std::string 类型的元素。std::deque 是一个序列容器,允许在两端高效地插入和删除元素。
#include <iostream>
#include <deque>
#include <string>
int main() {
std::deque<std::string> myDeque = {"Hello", "World", "C++"};
for (const auto& str : myDeque) {
// std::cout 被调用时,str 被解引用,输出的是 myDeque 中当前字符串的内容。
std::cout << str << " "; // 输出每个字符串
}
std::cout << std::endl;
return 0;
}
// 模板定义
#include <iostream>
#include <string>
#include <cstring>
#include <type_traits>
template<typename Elem, typename Tr = std::char_traits<Elem>> // 属于模板参数
class MyString {
public:
MyString(const Elem* str) {
size = Tr::length(str); // 使用 Tr 来获取字符串长度
data = new Elem[size + 1];
Tr::copy(data, str, size); // 使用 Tr 来复制字符串
data[size] = Tr::to_char_type(0); // 添加字符串结束符
}
~MyString() {
delete[] data;
}
void print() const {
std::cout << data << std::endl;
}
private:
Elem* data;
size_t size;
};
int main() {
MyString<char> myStr("Hello, World!");
myStr.print(); // 输出: Hello, World!
return 0;
}
// 函数模板
#include <iostream>
// 定义一个函数模板
template<typename T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(5, 3) << std::endl; // 输出: 8
std::cout << add(5.5, 3.2) << std::endl; // 输出: 8.7
return 0;
}
// 类模板
#include <iostream>
template<typename T>
class Box {
public:
Box(T value) : value(value) {}
T getValue() const {
return value;
}
private:
T value;
};
int main() {
Box<int> intBox(123);
Box<std::string> strBox("Hello, Templates!");
std::cout << intBox.getValue() << std::endl; // 输出: 123
std::cout << strBox.getValue() << std::endl; // 输出: Hello, Templates!
return 0;
}
// 模板参数
#include <iostream>
template<typename T, int size>
class Array {
public:
Array() {
std::cout << "Array size: " << size << std::endl;
}
private:
T arr[size]; // 使用非类型参数定义数组大小
};
int main() {
Array<int, 10> intArray; // 创建一个大小为 10 的整数数组
return 0;
}
// 模板特化
#include <iostream>
template<typename T>
class Printer {
public:
void print(T value) {
std::cout << "Value: " << value << std::endl;
}
};
// 针对 std::string 类型的特化
template<>
class Printer<std::string> {
public:
void print(std::string value) {
std::cout << "String Value: " << value << std::endl;
}
};
int main() {
Printer<int> intPrinter;
intPrinter.print(42); // 输出: Value: 42
Printer<std::string> strPrinter;
strPrinter.print("Hello, World!"); // 输出: String Value: Hello, World!
return 0;
}
总结
函数模板 允许你定义通用函数,接受多种数据类型。
类模板 允许你定义通用类,处理不同类型的对象。
模板参数 可以是类型参数和非类型参数。
模板特化 允许你为特定类型提供不同的实现。