rest
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
57
CMakeLists.txt
Normal file
57
CMakeLists.txt
Normal file
@@ -0,0 +1,57 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(VRModelViewer VERSION 0.1.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
find_package(OpenSceneGraph REQUIRED COMPONENTS
|
||||
osgDB osgGA osgUtil osgViewer osgText osgSim)
|
||||
find_package(assimp REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(IMGUI REQUIRED imgui)
|
||||
|
||||
set(SOURCES
|
||||
src/main.cpp
|
||||
src/Application.cpp
|
||||
src/ModelLoader.cpp
|
||||
src/MorphManager.cpp
|
||||
src/ImGuiLayer.cpp
|
||||
src/SceneBuilder.cpp
|
||||
src/OrbitManipulator.cpp
|
||||
src/ShaderManager.cpp
|
||||
src/AppConfig.cpp
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${OPENSCENEGRAPH_INCLUDE_DIRS}
|
||||
${ASSIMP_INCLUDE_DIRS}
|
||||
${IMGUI_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||
${OPENSCENEGRAPH_LIBRARIES}
|
||||
${ASSIMP_LIBRARIES}
|
||||
${IMGUI_LIBRARIES}
|
||||
OpenGL::GL
|
||||
)
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall -Wextra -Wpedantic>
|
||||
${IMGUI_CFLAGS_OTHER}
|
||||
)
|
||||
|
||||
add_custom_target(copy_assets ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}/assets
|
||||
COMMENT "Copying assets to build directory"
|
||||
)
|
||||
add_dependencies(${PROJECT_NAME} copy_assets)
|
||||
71
README.md
71
README.md
@@ -0,0 +1,71 @@
|
||||
# VR Model Viewer
|
||||
|
||||
Interactive 3-D model viewer built with **OpenSceneGraph** and **Assimp**.
|
||||
Designed for inspecting PMX (MikuMikuDance), FBX, OBJ and other model formats.
|
||||
|
||||
## Dependencies (already installed on Gentoo)
|
||||
|
||||
| Library | Gentoo package |
|
||||
|---------|----------------|
|
||||
| OpenSceneGraph ≥ 3.6 | `dev-games/openscenegraph` |
|
||||
| Assimp ≥ 5.0 | `media-libs/assimp` |
|
||||
| CMake ≥ 3.16 | `dev-build/cmake` |
|
||||
| GCC / Clang (C++17) | `sys-devel/gcc` |
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# From the project root:
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake --build build --parallel $(nproc)
|
||||
```
|
||||
|
||||
Or inside VSCodium press **Ctrl+Shift+B** → *Configure + Build*.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
./build/VRModelViewer path/to/model.pmx
|
||||
# or
|
||||
./build/VRModelViewer path/to/model.fbx
|
||||
```
|
||||
|
||||
## Controls
|
||||
|
||||
| Input | Action |
|
||||
|-------|--------|
|
||||
| **LMB drag** | Orbit camera |
|
||||
| **MMB drag** | Pan |
|
||||
| **Scroll** | Zoom (dolly) |
|
||||
| **R** | Reset camera to default humanoid view |
|
||||
| **F / Space** | Frame whole scene |
|
||||
| **S** | Toggle stats overlay (FPS, draw calls) |
|
||||
| **Esc** | Quit |
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
vr_model_viewer/
|
||||
├── CMakeLists.txt
|
||||
├── include/
|
||||
│ ├── Application.h # Top-level app / viewer owner
|
||||
│ ├── ModelLoader.h # Assimp → OSG conversion
|
||||
│ ├── SceneBuilder.h # Grid, axes, lights helpers
|
||||
│ └── OrbitManipulator.h # Tweaked orbit camera
|
||||
├── src/
|
||||
│ ├── main.cpp
|
||||
│ ├── Application.cpp
|
||||
│ ├── ModelLoader.cpp
|
||||
│ ├── SceneBuilder.cpp
|
||||
│ └── OrbitManipulator.cpp
|
||||
└── assets/
|
||||
└── models/ # drop your .pmx / .fbx files here
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [x] Phase 1 – Basic render pipeline + PMX/FBX loading
|
||||
- [ ] Phase 2 – Model placement & transform gizmos
|
||||
- [ ] Phase 3 – Bone / pose inspector
|
||||
- [ ] Phase 4 – ImGui UI panel
|
||||
- [ ] Phase 5 – VR headset integration (OpenXR)
|
||||
|
||||
21
assets/config.ini
Normal file
21
assets/config.ini
Normal file
@@ -0,0 +1,21 @@
|
||||
# VR Model Viewer configuration
|
||||
# Lines starting with # are comments.
|
||||
|
||||
[ui]
|
||||
# Path to a TTF/OTF font file with CJK coverage.
|
||||
# Kochi Gothic is a good choice on Gentoo:
|
||||
# /usr/share/fonts/kochi-substitute/kochi-gothic-subst.ttf
|
||||
# Leave blank to use ImGui's built-in ASCII-only font.
|
||||
font_path = /usr/share/fonts/kochi-substitute/kochi-gothic-subst.ttf
|
||||
|
||||
# Font size in pixels
|
||||
font_size = 14
|
||||
|
||||
# Starting width of the morph panel in pixels
|
||||
panel_width = 380
|
||||
|
||||
[model]
|
||||
# Initial scale applied to the loaded model.
|
||||
# FBX exports from Blender are often 100x too large (cm vs m units).
|
||||
# Try 0.01 if your model appears huge, 1.0 if it looks correct.
|
||||
scale = 0.01
|
||||
37
assets/shaders/cel.frag
Normal file
37
assets/shaders/cel.frag
Normal file
@@ -0,0 +1,37 @@
|
||||
#version 130
|
||||
|
||||
varying vec3 v_normalVS;
|
||||
varying vec3 v_posVS;
|
||||
varying vec2 v_uv;
|
||||
varying vec4 v_color;
|
||||
|
||||
uniform sampler2D osg_Sampler0;
|
||||
uniform bool u_hasTexture;
|
||||
uniform vec3 u_lightDirVS;
|
||||
uniform vec3 u_lightColor;
|
||||
uniform vec3 u_ambientColor;
|
||||
uniform int u_bands;
|
||||
uniform float u_bandSharpness;
|
||||
|
||||
float celQuantise(float value, int bands) {
|
||||
float b = float(bands);
|
||||
return floor(value * b + 0.5) / b;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 N = normalize(v_normalVS);
|
||||
vec3 L = normalize(u_lightDirVS);
|
||||
|
||||
// Half-lambert so shadows aren't pitch black
|
||||
float NdL = dot(N, L) * 0.5 + 0.5;
|
||||
float celVal = celQuantise(NdL, u_bands);
|
||||
|
||||
vec4 baseColor = u_hasTexture
|
||||
? texture2D(osg_Sampler0, v_uv)
|
||||
: v_color;
|
||||
|
||||
vec3 diffuse = baseColor.rgb * u_lightColor * celVal;
|
||||
vec3 ambient = baseColor.rgb * u_ambientColor;
|
||||
|
||||
gl_FragColor = vec4(ambient + diffuse, baseColor.a);
|
||||
}
|
||||
19
assets/shaders/cel.vert
Normal file
19
assets/shaders/cel.vert
Normal file
@@ -0,0 +1,19 @@
|
||||
#version 130
|
||||
|
||||
// OSG binds these automatically via Program::addBindAttribLocation
|
||||
// or via the fixed-function compatibility aliases in core profile.
|
||||
// Using built-in compatibility varyings is the safest approach with OSG 3.6.
|
||||
|
||||
varying vec3 v_normalVS;
|
||||
varying vec3 v_posVS;
|
||||
varying vec2 v_uv;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main() {
|
||||
vec4 posVS = gl_ModelViewMatrix * gl_Vertex;
|
||||
v_posVS = posVS.xyz;
|
||||
v_normalVS = normalize(gl_NormalMatrix * gl_Normal);
|
||||
v_uv = gl_MultiTexCoord0.xy;
|
||||
v_color = gl_Color;
|
||||
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||
}
|
||||
56
assets/shaders/toon.frag
Normal file
56
assets/shaders/toon.frag
Normal file
@@ -0,0 +1,56 @@
|
||||
#version 130
|
||||
|
||||
varying vec3 v_normalVS;
|
||||
varying vec3 v_posVS;
|
||||
varying vec2 v_uv;
|
||||
varying vec4 v_color;
|
||||
|
||||
uniform sampler2D osg_Sampler0;
|
||||
uniform bool u_hasTexture;
|
||||
uniform vec3 u_lightDirVS;
|
||||
uniform vec3 u_lightColor;
|
||||
uniform vec3 u_ambientColor;
|
||||
uniform vec3 u_outlineColor;
|
||||
uniform bool u_outlinePass;
|
||||
uniform int u_bands;
|
||||
uniform float u_specularThreshold;
|
||||
uniform float u_specularIntensity;
|
||||
uniform float u_rimThreshold;
|
||||
uniform float u_rimIntensity;
|
||||
uniform vec3 u_rimColor;
|
||||
|
||||
float celStep(float value, int bands) {
|
||||
return floor(value * float(bands) + 0.5) / float(bands);
|
||||
}
|
||||
|
||||
void main() {
|
||||
if (u_outlinePass) {
|
||||
gl_FragColor = vec4(u_outlineColor, 1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 N = normalize(v_normalVS);
|
||||
vec3 L = normalize(u_lightDirVS);
|
||||
vec3 V = normalize(-v_posVS);
|
||||
|
||||
// Cel diffuse
|
||||
float NdL = dot(N, L) * 0.5 + 0.5;
|
||||
float celDif = celStep(NdL, u_bands);
|
||||
|
||||
// Snap specular
|
||||
vec3 H = normalize(L + V);
|
||||
float NdH = max(dot(N, H), 0.0);
|
||||
float spec = step(u_specularThreshold, NdH) * u_specularIntensity;
|
||||
|
||||
// Rim light
|
||||
float rim = 1.0 - max(dot(N, V), 0.0);
|
||||
rim = step(u_rimThreshold, rim) * u_rimIntensity;
|
||||
|
||||
vec4 base = u_hasTexture ? texture2D(osg_Sampler0, v_uv) : v_color;
|
||||
vec3 ambient = base.rgb * u_ambientColor;
|
||||
vec3 diffuse = base.rgb * u_lightColor * celDif;
|
||||
vec3 specCol = u_lightColor * spec;
|
||||
vec3 rimCol = u_rimColor * rim;
|
||||
|
||||
gl_FragColor = vec4(ambient + diffuse + specCol + rimCol, base.a);
|
||||
}
|
||||
25
assets/shaders/toon.vert
Normal file
25
assets/shaders/toon.vert
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 130
|
||||
|
||||
uniform bool u_outlinePass;
|
||||
uniform float u_outlineWidth;
|
||||
|
||||
varying vec3 v_normalVS;
|
||||
varying vec3 v_posVS;
|
||||
varying vec2 v_uv;
|
||||
varying vec4 v_color;
|
||||
|
||||
void main() {
|
||||
vec3 pos = gl_Vertex.xyz;
|
||||
|
||||
if (u_outlinePass) {
|
||||
pos += gl_Normal * u_outlineWidth;
|
||||
}
|
||||
|
||||
vec4 posVS = gl_ModelViewMatrix * vec4(pos, 1.0);
|
||||
v_posVS = posVS.xyz;
|
||||
v_normalVS = normalize(gl_NormalMatrix * gl_Normal);
|
||||
v_uv = gl_MultiTexCoord0.xy;
|
||||
v_color = gl_Color;
|
||||
|
||||
gl_Position = gl_ModelViewProjectionMatrix * vec4(pos, 1.0);
|
||||
}
|
||||
35
include/AppConfig.h
Normal file
35
include/AppConfig.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
/**
|
||||
* AppConfig
|
||||
* ---------
|
||||
* Minimal INI-style config file reader.
|
||||
* Supports [sections], key = value pairs, and # comments.
|
||||
*
|
||||
* Keys are accessed as "section.key", e.g. "ui.font_path".
|
||||
* Missing keys return a provided default value.
|
||||
*
|
||||
* The config file is searched in this order:
|
||||
* 1. Next to the executable: <exeDir>/assets/config.ini
|
||||
* 2. Current working dir: assets/config.ini
|
||||
*/
|
||||
class AppConfig {
|
||||
public:
|
||||
/// Load config. Returns true if a file was found and parsed.
|
||||
bool load();
|
||||
|
||||
std::string getString(const std::string& key,
|
||||
const std::string& defaultVal = "") const;
|
||||
|
||||
float getFloat(const std::string& key,
|
||||
float defaultVal = 0.f) const;
|
||||
|
||||
int getInt(const std::string& key,
|
||||
int defaultVal = 0) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> m_values;
|
||||
};
|
||||
59
include/Application.h
Normal file
59
include/Application.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include <osgViewer/Viewer>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Group>
|
||||
#include <osg/Node>
|
||||
#include <osg/NodeCallback>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <osgGA/GUIEventHandler>
|
||||
|
||||
#include "ShaderManager.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
class MorphManager;
|
||||
class ImGuiLayer;
|
||||
|
||||
class Application : public osgGA::GUIEventHandler {
|
||||
public:
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
bool init(int width = 1280, int height = 720,
|
||||
const std::string& title = "VR Model Viewer");
|
||||
bool loadModel(const std::string& filepath);
|
||||
int run();
|
||||
|
||||
bool handle(const osgGA::GUIEventAdapter& ea,
|
||||
osgGA::GUIActionAdapter& aa) override;
|
||||
|
||||
void applyPendingShader(); // called by update callback
|
||||
void applyMorphWeights(); // called by update callback
|
||||
|
||||
private:
|
||||
void setupLighting();
|
||||
void setupGrid();
|
||||
void requestShader(const std::string& mode);
|
||||
void setModelScale(float scale);
|
||||
|
||||
osg::ref_ptr<osgViewer::Viewer> m_viewer;
|
||||
osg::ref_ptr<osg::Group> m_sceneRoot;
|
||||
osg::ref_ptr<osg::Group> m_shaderGroup;
|
||||
osg::ref_ptr<osg::Node> m_modelNode;
|
||||
osg::ref_ptr<osg::MatrixTransform> m_modelXform; // scale/transform wrapper
|
||||
|
||||
AppConfig m_config;
|
||||
std::unique_ptr<ShaderManager> m_shaderMgr;
|
||||
std::unique_ptr<MorphManager> m_morphMgr;
|
||||
std::unique_ptr<ImGuiLayer> m_imguiLayer;
|
||||
|
||||
std::mutex m_shaderMutex;
|
||||
std::string m_pendingShader;
|
||||
std::string m_currentShader = "toon";
|
||||
bool m_shaderDirty = false;
|
||||
bool m_reloadShaders = false;
|
||||
};
|
||||
51
include/ImGuiLayer.h
Normal file
51
include/ImGuiLayer.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <osg/Camera>
|
||||
#include <osgViewer/Viewer>
|
||||
#include <osgGA/GUIEventAdapter>
|
||||
|
||||
class MorphManager;
|
||||
class AppConfig;
|
||||
|
||||
class ImGuiLayer {
|
||||
public:
|
||||
explicit ImGuiLayer(MorphManager* morphMgr, const AppConfig* cfg);
|
||||
~ImGuiLayer();
|
||||
|
||||
void init(osgViewer::Viewer* viewer);
|
||||
bool handleEvent(const osgGA::GUIEventAdapter& ea);
|
||||
|
||||
void renderPanel(); // called by ImGuiDrawCallback each frame
|
||||
void markGLInitialized();
|
||||
|
||||
// Callbacks set by Application so ImGui can drive app state
|
||||
std::function<void(const std::string&)> onShaderChange;
|
||||
std::function<void()> onShaderReload;
|
||||
std::function<void(float)> onScaleChange;
|
||||
|
||||
// Called by Application each frame so the shader tab shows current state
|
||||
void setCurrentShader(const std::string& s) { m_currentShader = s; }
|
||||
void setInitialScale(float s) { m_scale = s; m_scaleBuf[0] = 0; }
|
||||
|
||||
private:
|
||||
void renderMorphTab();
|
||||
void renderShaderTab();
|
||||
void renderTransformTab();
|
||||
|
||||
MorphManager* m_morphMgr = nullptr;
|
||||
const AppConfig* m_cfg = nullptr;
|
||||
osgViewer::Viewer* m_viewer = nullptr;
|
||||
osg::ref_ptr<osg::Camera> m_camera;
|
||||
|
||||
bool m_contextCreated = false;
|
||||
bool m_glInitialized = false;
|
||||
float m_panelWidth = 380.f; // wider default so names are visible
|
||||
|
||||
char m_searchBuf[256] = {};
|
||||
bool m_showOnlyActive = false;
|
||||
std::string m_currentShader = "toon";
|
||||
float m_scale = 1.0f;
|
||||
char m_scaleBuf[32] = {};
|
||||
};
|
||||
20
include/ModelLoader.h
Normal file
20
include/ModelLoader.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Node>
|
||||
|
||||
class MorphManager;
|
||||
|
||||
class ModelLoader {
|
||||
public:
|
||||
ModelLoader() = default;
|
||||
|
||||
osg::ref_ptr<osg::Node> load(const std::string& filepath,
|
||||
MorphManager* morphMgr = nullptr);
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Node> buildOsgScene(const struct aiScene* scene,
|
||||
const std::string& baseDir,
|
||||
MorphManager* morphMgr);
|
||||
};
|
||||
83
include/MorphManager.h
Normal file
83
include/MorphManager.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Array>
|
||||
|
||||
/**
|
||||
* MorphManager
|
||||
* ------------
|
||||
* Stores morph target data for all meshes in a loaded model and applies
|
||||
* weighted blends every frame on the CPU.
|
||||
*
|
||||
* Usage:
|
||||
* // At load time (ModelLoader calls these):
|
||||
* mgr.registerMesh(geom, baseVerts, baseNormals);
|
||||
* mgr.addTarget(geom, "blink", deltaVerts, deltaNormals);
|
||||
*
|
||||
* // Every frame (update callback calls this):
|
||||
* mgr.applyWeights();
|
||||
*
|
||||
* // From ImGui (slider changed):
|
||||
* mgr.setWeight("blink", 0.75f);
|
||||
*/
|
||||
class MorphManager {
|
||||
public:
|
||||
MorphManager() = default;
|
||||
|
||||
// ── Registration (called at load time) ───────────────────────────────────
|
||||
|
||||
/// Register a geometry's base vertex/normal arrays.
|
||||
/// Must be called before addTarget() for this geometry.
|
||||
void registerMesh(osg::Geometry* geom,
|
||||
osg::ref_ptr<osg::Vec3Array> baseVerts,
|
||||
osg::ref_ptr<osg::Vec3Array> baseNormals);
|
||||
|
||||
/// Add one morph target for a geometry.
|
||||
/// deltaVerts/deltaNormals are OFFSETS from the base (not absolute positions).
|
||||
void addTarget(osg::Geometry* geom,
|
||||
const std::string& name,
|
||||
osg::ref_ptr<osg::Vec3Array> deltaVerts,
|
||||
osg::ref_ptr<osg::Vec3Array> deltaNormals);
|
||||
|
||||
// ── Weight control ───────────────────────────────────────────────────────
|
||||
|
||||
void setWeight(const std::string& name, float weight);
|
||||
float getWeight(const std::string& name) const;
|
||||
void resetAll();
|
||||
|
||||
/// Returns all unique morph names across all meshes, sorted.
|
||||
const std::vector<std::string>& morphNames() const { return m_morphNames; }
|
||||
|
||||
// ── Per-frame update ─────────────────────────────────────────────────────
|
||||
|
||||
/// Blend all active morphs into each geometry's live vertex/normal arrays
|
||||
/// and dirty them so OSG re-uploads to the GPU.
|
||||
void applyWeights();
|
||||
|
||||
private:
|
||||
// One morph target contribution for a single geometry
|
||||
struct Target {
|
||||
std::string name;
|
||||
osg::ref_ptr<osg::Vec3Array> deltaVerts;
|
||||
osg::ref_ptr<osg::Vec3Array> deltaNormals;
|
||||
};
|
||||
|
||||
// Per-geometry morph data
|
||||
struct MeshEntry {
|
||||
osg::Geometry* geom = nullptr;
|
||||
osg::ref_ptr<osg::Vec3Array> baseVerts;
|
||||
osg::ref_ptr<osg::Vec3Array> baseNormals;
|
||||
std::vector<Target> targets;
|
||||
};
|
||||
|
||||
std::vector<MeshEntry> m_meshes;
|
||||
std::unordered_map<std::string, float> m_weights; // name → 0..1
|
||||
std::vector<std::string> m_morphNames; // sorted unique list
|
||||
|
||||
void rebuildNameList();
|
||||
};
|
||||
29
include/OrbitManipulator.h
Normal file
29
include/OrbitManipulator.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <osgGA/OrbitManipulator>
|
||||
|
||||
/**
|
||||
* OrbitManipulator
|
||||
* ----------------
|
||||
* Thin subclass of osgGA::OrbitManipulator that tweaks defaults for
|
||||
* inspecting character models:
|
||||
* - LMB drag → orbit
|
||||
* - MMB drag → pan
|
||||
* - Scroll → dolly
|
||||
* - F key → frame the whole scene
|
||||
* - R key → reset to default view
|
||||
*
|
||||
* The manipulator targets the scene centre, with a configurable
|
||||
* initial eye offset.
|
||||
*/
|
||||
class OrbitManipulator : public osgGA::OrbitManipulator {
|
||||
public:
|
||||
OrbitManipulator();
|
||||
|
||||
/// Set a comfortable starting position for viewing a humanoid model.
|
||||
void setDefaultHumanoidView();
|
||||
|
||||
protected:
|
||||
bool handleKeyDown(const osgGA::GUIEventAdapter& ea,
|
||||
osgGA::GUIActionAdapter& aa) override;
|
||||
};
|
||||
34
include/SceneBuilder.h
Normal file
34
include/SceneBuilder.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Group>
|
||||
#include <osg/Geode>
|
||||
#include <osg/Geometry>
|
||||
#include <osg/Light>
|
||||
#include <osg/LightSource>
|
||||
|
||||
/**
|
||||
* SceneBuilder
|
||||
* ------------
|
||||
* Factory helpers for common scene-graph elements:
|
||||
* - reference floor grid
|
||||
* - ambient + directional lights
|
||||
* - axes gizmo (X/Y/Z)
|
||||
*/
|
||||
class SceneBuilder {
|
||||
public:
|
||||
/// Create a flat grid centred at the origin.
|
||||
/// @param halfSize half-extent of the grid in world units
|
||||
/// @param divisions number of cells per side
|
||||
static osg::ref_ptr<osg::Geode> createGrid(float halfSize = 10.f,
|
||||
int divisions = 20);
|
||||
|
||||
/// Create a simple 3-axis gizmo (red=X, green=Y, blue=Z).
|
||||
static osg::ref_ptr<osg::Geode> createAxes(float length = 1.f);
|
||||
|
||||
/// Build a LightSource node suitable for a warm key light.
|
||||
static osg::ref_ptr<osg::LightSource> createSunLight(int lightNum = 0);
|
||||
|
||||
/// Build an ambient fill light.
|
||||
static osg::ref_ptr<osg::LightSource> createAmbientLight(int lightNum = 1);
|
||||
};
|
||||
51
include/ShaderManager.h
Normal file
51
include/ShaderManager.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <osg/ref_ptr>
|
||||
#include <osg/Program>
|
||||
#include <osg/Node>
|
||||
#include <osg/StateSet>
|
||||
|
||||
/**
|
||||
* ShaderManager
|
||||
* -------------
|
||||
* Loads, caches, and applies GLSL shader programs to OSG nodes.
|
||||
*
|
||||
* Supported modes (toggled at runtime via applyTo):
|
||||
* "flat" – unlit, texture/vertex colour only
|
||||
* "cel" – quantised diffuse bands
|
||||
* "toon" – cel + hard specular + rim light + outline shell
|
||||
*
|
||||
* Shaders are loaded from `shaderDir` (default: assets/shaders/).
|
||||
* Editing the .glsl files and calling reload() hot-reloads them.
|
||||
*/
|
||||
class ShaderManager {
|
||||
public:
|
||||
explicit ShaderManager(const std::string& shaderDir = "assets/shaders");
|
||||
|
||||
/// Apply a named shader mode to `node`'s StateSet.
|
||||
/// Also sets sensible default uniforms.
|
||||
void applyTo(osg::Node* node, const std::string& mode);
|
||||
|
||||
/// Re-read all .glsl files from disk (call after editing shaders).
|
||||
void reload();
|
||||
|
||||
/// Update the light direction uniform on an already-shaded node
|
||||
/// (call when the light moves). `dirVS` should be in view space.
|
||||
static void setLightDir(osg::StateSet* ss, const osg::Vec3f& dirVS);
|
||||
|
||||
private:
|
||||
osg::ref_ptr<osg::Program> buildProgram(const std::string& vertFile,
|
||||
const std::string& fragFile);
|
||||
|
||||
void setCommonUniforms(osg::StateSet* ss);
|
||||
void setCelUniforms (osg::StateSet* ss);
|
||||
void setToonUniforms (osg::StateSet* ss);
|
||||
|
||||
std::string m_shaderDir;
|
||||
|
||||
// Cache: mode name → compiled program
|
||||
std::unordered_map<std::string, osg::ref_ptr<osg::Program>> m_programs;
|
||||
};
|
||||
Reference in New Issue
Block a user