173 lines
4.6 KiB
Dart
173 lines
4.6 KiB
Dart
import 'dart:io';
|
||
|
||
import 'package:animations/utils/binary_manager.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:path/path.dart' as p;
|
||
import 'package:path_provider/path_provider.dart';
|
||
import 'package:thermion_flutter/thermion_flutter.dart';
|
||
|
||
class LocalModelViewer extends StatefulWidget {
|
||
@override
|
||
_LocalModelViewerState createState() => _LocalModelViewerState();
|
||
}
|
||
|
||
class _LocalModelViewerState extends State<LocalModelViewer> {
|
||
ThermionViewer? _viewer;
|
||
ThermionAsset? _asset;
|
||
bool _isLoading = true;
|
||
bool _hasError = false;
|
||
String _statusMessage = "初始化中...";
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
|
||
_initializeViewerAndLoadModel();
|
||
}
|
||
|
||
Future<void> _initializeViewerAndLoadModel() async {
|
||
try {
|
||
// 获取应用支持目录并构建模型文件路径
|
||
final supportDir = await getApplicationSupportDirectory();
|
||
final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf'));
|
||
|
||
// 检查文件是否存在
|
||
if (!await modelFile.exists()) {
|
||
await BinaryManager.migrate3DModels();
|
||
await Future.delayed(const Duration(milliseconds: 3000));
|
||
|
||
// throw Exception("模型文件不存在于路径: ${modelFile.path}");
|
||
}
|
||
setState(() {
|
||
_statusMessage = "创建3D查看器...";
|
||
});
|
||
|
||
// 创建ThermionViewer实例
|
||
_viewer = await ThermionFlutterPlugin.createViewer();
|
||
|
||
setState(() {
|
||
_statusMessage = "获取模型路径...";
|
||
});
|
||
|
||
setState(() {
|
||
_statusMessage = "加载环境光和天空盒...";
|
||
});
|
||
|
||
// 加载环境光和天空盒(确保在pubspec.yaml中注册)
|
||
await _viewer!.loadSkybox("assets/default_env_skybox.ktx");
|
||
await _viewer!.loadIbl("assets/default_env_ibl.ktx");
|
||
|
||
setState(() {
|
||
_statusMessage = "加载3D模型...";
|
||
});
|
||
|
||
// 加载glTF模型 - 使用文件URI格式
|
||
_asset = await _viewer!.loadGltf("file://${modelFile.path}");
|
||
|
||
// 可选:将模型缩放到单位立方体
|
||
await _asset!.transformToUnitCube();
|
||
|
||
// 配置场景
|
||
final camera = await _viewer!.getActiveCamera();
|
||
await camera.lookAt(Vector3(0, 0, 6));
|
||
|
||
// 开始渲染
|
||
await _viewer!.setRendering(true);
|
||
|
||
setState(() {
|
||
_isLoading = false;
|
||
_hasError = false;
|
||
_statusMessage = "加载完成";
|
||
});
|
||
} catch (e) {
|
||
print("加载模型失败: $e");
|
||
setState(() {
|
||
_isLoading = false;
|
||
_hasError = true;
|
||
_statusMessage = "加载失败: $e";
|
||
});
|
||
}
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
if (_isLoading) {
|
||
return Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
CircularProgressIndicator(),
|
||
SizedBox(height: 16),
|
||
Text(_statusMessage),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
if (_hasError || _viewer == null) {
|
||
return Center(
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Icon(Icons.error_outline, color: Colors.red, size: 48),
|
||
SizedBox(height: 16),
|
||
Text("加载3D模型失败"),
|
||
Text(_statusMessage, style: TextStyle(color: Colors.red)),
|
||
SizedBox(height: 16),
|
||
ElevatedButton(
|
||
onPressed: _initializeViewerAndLoadModel,
|
||
child: Text("重试"),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
|
||
return Scaffold(
|
||
appBar: AppBar(
|
||
title: const Text('本地模型查看'),
|
||
),
|
||
body: ThermionListenerWidget(
|
||
inputHandler: DelegateInputHandler.fixedOrbit(_viewer!),
|
||
child: ThermionWidget(
|
||
viewer: _viewer!,
|
||
showFpsCounter: true, // 可选:显示FPS计数器
|
||
),
|
||
),
|
||
);
|
||
|
||
// 使用ThermionWidget显示3D内容
|
||
return ThermionListenerWidget(
|
||
inputHandler: DelegateInputHandler.fixedOrbit(_viewer!),
|
||
child: ThermionWidget(
|
||
viewer: _viewer!,
|
||
showFpsCounter: true, // 可选:显示FPS计数器
|
||
),
|
||
);
|
||
}
|
||
|
||
Future<void> deleteModelFile() async {
|
||
// 获取应用支持目录
|
||
final supportDir = await getApplicationSupportDirectory();
|
||
|
||
// 构建 model.gltf 的完整路径
|
||
final modelFile = File(p.join(supportDir.path, 'cup', 'model.gltf'));
|
||
|
||
// 检查文件是否存在,然后删除
|
||
if (await modelFile.exists()) {
|
||
await modelFile.delete();
|
||
print('model.gltf 已删除');
|
||
} else {
|
||
print('model.gltf 文件不存在');
|
||
}
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
deleteModelFile();
|
||
// 清理资源
|
||
_viewer?.dispose();
|
||
super.dispose();
|
||
}
|
||
}
|