Skip to content

Commit 564af93

Browse files
committed
spline editor wip
1 parent 345abba commit 564af93

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11

22
add_example(NAME "catmull_room_curve3" WEB)
33
add_example(NAME "cubic_bezier_curve" WEB)
4+
add_example(NAME "spline_editor")
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
2+
3+
#include "threepp/extras/curves/CatmullRomCurve3.hpp"
4+
#include <threepp/controls/OrbitControls.hpp>
5+
#include <threepp/controls/TransformControls.hpp>
6+
#include <threepp/threepp.hpp>
7+
8+
#include <map>
9+
10+
using namespace threepp;
11+
12+
int main() {
13+
14+
// Renderer & canvas
15+
Canvas canvas("Spline Editor", {{"aa", 4}});
16+
GLRenderer renderer(canvas.size());
17+
renderer.shadowMap().enabled = true;
18+
19+
// Scene
20+
auto scene = Scene::create();
21+
scene->background = Color().setHex(0xf0f0f0);
22+
23+
// Camera
24+
auto camera = PerspectiveCamera::create(70, canvas.aspect(), 1, 10000);
25+
camera->position.set(0, 250, 1000);
26+
scene->add(camera);
27+
28+
// Lights
29+
scene->add(AmbientLight::create(0xf0f0f0, 3.f));
30+
auto light = SpotLight::create(0xffffff, 4.5f);
31+
light->position.set(0, 1500, 200);
32+
light->angle = math::PI * 0.2f;
33+
light->decay = 0;
34+
light->castShadow = true;
35+
scene->add(light);
36+
37+
// Ground
38+
auto planeGeo = PlaneGeometry::create(2000, 2000);
39+
planeGeo->rotateX(-math::PI / 2);
40+
auto planeMat = ShadowMaterial::create();
41+
planeMat->opacity = 0.2f;
42+
auto plane = Mesh::create(planeGeo, planeMat);
43+
plane->position.y = -200;
44+
plane->receiveShadow = true;
45+
scene->add(plane);
46+
47+
// Grid helper
48+
auto grid = GridHelper::create(2000, 100);
49+
grid->position.y = -199;
50+
grid->material()->opacity = 0.25;
51+
grid->material()->transparent = true;
52+
scene->add(grid);
53+
54+
// Spline points
55+
std::vector<Vector3> positions = {
56+
{289.768f, 452.515f, 56.1f},
57+
{-53.563f, 171.497f, -14.495f},
58+
{-91.401f, 176.431f, -6.958f},
59+
{-383.785f, 491.137f, 47.869f}};
60+
61+
constexpr int ARC_SEGMENTS = 200;
62+
63+
// Spline meshes
64+
std::map<std::string, std::shared_ptr<Line>> splines;
65+
66+
auto createSplineLine = [&](const std::string& type, const Color& color) {
67+
auto curve = std::make_unique<CatmullRomCurve3>(positions);
68+
if (type == "centripetal") curve->curveType = CatmullRomCurve3::CurveType::catmullrom;
69+
if (type == "chordal") curve->curveType = CatmullRomCurve3::CurveType::chordal;
70+
71+
auto geom = BufferGeometry::create();
72+
geom->setAttribute("position", FloatBufferAttribute::create(std::vector<float>(ARC_SEGMENTS * 3), 3));
73+
auto line = Line::create(geom, LineBasicMaterial::create({{"color", color}}));
74+
line->castShadow = true;
75+
splines[type] = line;
76+
scene->add(line);
77+
return curve;
78+
};
79+
80+
auto uniformCurve = createSplineLine("uniform", Color::red);
81+
auto centripetalCurve = createSplineLine("centripetal", Color::green);
82+
auto chordalCurve = createSplineLine("chordal", Color::blue);
83+
84+
// Helper objects (control points)
85+
auto boxGeo = BoxGeometry::create(20, 20, 20);
86+
std::vector<Object3D*> splineHelpers;
87+
for (auto& pos : positions) {
88+
auto mat = MeshLambertMaterial::create({{"color", Color().randomize()}});
89+
auto obj = Mesh::create(boxGeo, mat);
90+
obj->position.copy(pos);
91+
obj->castShadow = true;
92+
obj->receiveShadow = true;
93+
scene->add(obj);
94+
splineHelpers.push_back(obj.get());
95+
}
96+
97+
// Orbit controls
98+
OrbitControls controls(*camera, canvas);
99+
controls.dampingFactor = 0.2f;
100+
101+
auto updateSplines([&] {
102+
// Update spline meshes whenever a control point moves
103+
auto updateSplineMesh = [&](const CatmullRomCurve3& curve, const std::shared_ptr<Line>& line) {
104+
auto posAttr = line->geometry()->getAttribute<float>("position");
105+
for (int i = 0; i < ARC_SEGMENTS; i++) {
106+
float t = static_cast<float>(i) / static_cast<float>(ARC_SEGMENTS - 1);
107+
Vector3 p;
108+
curve.getPoint(t, p);
109+
posAttr->setXYZ(i, p.x, p.y, p.z);
110+
}
111+
posAttr->needsUpdate();
112+
};
113+
114+
// Copy helper positions to curve
115+
for (int i = 0; i < splineHelpers.size(); i++) {
116+
positions[i] = splineHelpers[i]->position;
117+
}
118+
uniformCurve->points = positions;
119+
centripetalCurve->points = positions;
120+
chordalCurve->points = positions;
121+
122+
updateSplineMesh(*uniformCurve, splines["uniform"]);
123+
updateSplineMesh(*centripetalCurve, splines["centripetal"]);
124+
updateSplineMesh(*chordalCurve, splines["chordal"]);
125+
});
126+
127+
LambdaEventListener eventListener([&](const Event&) {
128+
updateSplines();
129+
});
130+
131+
132+
LambdaEventListener changeListener([&](const Event& event) {
133+
controls.enabled = !std::any_cast<bool>(event.target);
134+
});
135+
136+
// Transform controls
137+
TransformControls transformControl(*camera, canvas);
138+
transformControl.addEventListener("change", eventListener);
139+
transformControl.addEventListener("dragging-changed", changeListener);
140+
scene->add(transformControl);
141+
142+
143+
Vector2 mouse{-Infinity<float>, -Infinity<float>};
144+
MouseDownListener mouseListener([&](int button, const Vector2& pos) {
145+
if (button == 0) {
146+
147+
Raycaster raycaster;
148+
const auto size = canvas.size();
149+
mouse.x = (pos.x / static_cast<float>(size.width())) * 2 - 1;
150+
mouse.y = -(pos.y / static_cast<float>(size.height())) * 2 + 1;
151+
raycaster.setFromCamera(mouse, *camera);
152+
const auto intersects = raycaster.intersectObjects(splineHelpers, false);
153+
if (!intersects.empty()) {
154+
transformControl.attach(*intersects[0].object);
155+
}
156+
} else if (button == 1) {
157+
transformControl.detach();
158+
}
159+
});
160+
161+
canvas.addMouseListener(mouseListener);
162+
163+
updateSplines();
164+
canvas.animate([&] {
165+
controls.update();
166+
renderer.render(*scene, *camera);
167+
});
168+
}

0 commit comments

Comments
 (0)