Unravel Engine C++ Reference
Loading...
Searching...
No Matches
mcp_tools_scene.cpp
Go to the documentation of this file.
1#include "mcp_tools_common.h"
2
9#include <engine/ecs/ecs.h>
10#include <engine/ecs/prefab.h>
11#include <engine/ecs/scene.h>
16#include <hpp/utility.hpp>
17#include <math/math.h>
18
19namespace unravel::mcp
20{
21namespace
22{
23
24auto entity_to_json(entt::handle entity, int depth, int max_depth) -> std::string
25{
26 return entity_to_summary_json(entity, depth, max_depth);
27}
28
29auto parse_light_type(const std::string& value, light_type& out) -> bool
30{
31 if(value == "directional" || value == "Directional")
32 {
34 return true;
35 }
36 if(value == "point" || value == "Point")
37 {
39 return true;
40 }
41 if(value == "spot" || value == "Spot")
42 {
44 return true;
45 }
46 return false;
47}
48
49void maybe_set_parent(rtti::context& ctx, entt::handle entity, entt::handle parent)
50{
51 if(!entity || !parent)
52 {
53 return;
54 }
55 auto& em = ctx.get_cached<editing_manager>();
56 auto old_parent = entity.get<transform_component>().get_parent();
57 em.do_action<transform_set_parent_action_t>("MCP Set Parent", entity, old_parent, parent);
58}
59
60void maybe_set_position(rtti::context& ctx, entt::handle entity, const math::vec3& position)
61{
62 if(!entity)
63 {
64 return;
65 }
66 // Create tools place entities in WORLD space (matches prefab spawn / focus tools).
67 auto& transform = entity.get<transform_component>();
68 auto old_pos = transform.get_position_global();
69 auto& em = ctx.get_cached<editing_manager>();
70 em.do_action<transform_move_global_action_t>("MCP Set Position", entity, old_pos, position);
71}
72
73void apply_transform_fields(rtti::context& ctx,
74 entt::handle entity,
75 const simdjson::dom::object& args,
76 bool is_local)
77{
78 auto& transform = entity.get<transform_component>();
79 auto& em = ctx.get_cached<editing_manager>();
80
81 math::vec3 position{};
82 if(read_vec3(args, "position", position))
83 {
84 if(is_local)
85 {
86 em.do_action<transform_move_action_t>("MCP Set Position Local",
87 entity,
88 transform.get_position_local(),
89 position);
90 }
91 else
92 {
93 em.do_action<transform_move_global_action_t>("MCP Set Position World",
94 entity,
95 transform.get_position_global(),
96 position);
97 }
98 }
99
100 math::vec3 rotation{};
101 if(read_vec3(args, "rotation_euler", rotation))
102 {
103 if(is_local)
104 {
105 const auto old_euler = transform.get_rotation_euler_local();
106 const auto new_euler = rotation;
107 em.do_action(
108 "MCP Set Rotation Local",
109 [entity, new_euler]()
110 {
111 if(auto* t = entity.try_get<transform_component>())
112 {
113 t->set_rotation_euler_local(new_euler);
114 }
115 },
116 [entity, old_euler]()
117 {
118 if(auto* t = entity.try_get<transform_component>())
119 {
120 t->set_rotation_euler_local(old_euler);
121 }
122 });
123 }
124 else
125 {
126 const auto old_euler = transform.get_rotation_euler_global();
127 const auto new_euler = rotation;
128 em.do_action(
129 "MCP Set Rotation World",
130 [entity, new_euler]()
131 {
132 if(auto* t = entity.try_get<transform_component>())
133 {
134 t->set_rotation_euler_global(new_euler);
135 }
136 },
137 [entity, old_euler]()
138 {
139 if(auto* t = entity.try_get<transform_component>())
140 {
141 t->set_rotation_euler_global(old_euler);
142 }
143 });
144 }
145 }
146
147 math::vec3 scale{};
148 if(read_vec3(args, "scale", scale))
149 {
150 if(is_local)
151 {
152 em.do_action<transform_scale_action_t>("MCP Set Scale Local",
153 entity,
154 transform.get_scale_local(),
155 scale);
156 }
157 else
158 {
159 const auto old_scale = transform.get_scale_global();
160 const auto new_scale = scale;
161 em.do_action(
162 "MCP Set Scale World",
163 [entity, new_scale]()
164 {
165 if(auto* t = entity.try_get<transform_component>())
166 {
167 t->set_scale_global(new_scale);
168 }
169 },
170 [entity, old_scale]()
171 {
172 if(auto* t = entity.try_get<transform_component>())
173 {
174 t->set_scale_global(old_scale);
175 }
176 });
177 }
178 }
179}
180
181auto normalize_scene_key(std::string key) -> std::string
182{
183 if(key.empty())
184 {
185 return key;
186 }
187 fs::error_code ec;
188 const fs::path as_path(key);
189 if(as_path.is_absolute() && fs::exists(as_path, ec))
190 {
191 key = fs::convert_to_protocol(as_path).generic_string();
192 }
193 if(key.size() < 5 || key.substr(key.size() - 5) != ".spfb")
194 {
195 key += ".spfb";
196 }
197 return key;
198}
199
200} // namespace
201
202void register_scene_tools(mcp_tool_registry& registry)
203{
204 registry.add(
205 {.name="scene_get_info",
206 .description="Get active scene info: tag/source, entity count, and play mode phase.",
207 .input_schema_json=empty_object_schema(),
208 .handler=[](rtti::context& ctx, const simdjson::dom::object&) -> tool_result
209 {
210 auto& em = ctx.get_cached<editing_manager>();
211 auto* scn = em.get_active_scene(ctx);
212 std::string phase = "inactive";
213 if(ctx.has<play_mode>())
214 {
215 auto& play = ctx.get_cached<play_mode>();
216 if(play.is_splash())
217 {
218 phase = "splash";
219 }
220 else if(play.is_simulation_running())
221 {
222 phase = "running";
223 }
224 else if(play.is_active())
225 {
226 phase = "active";
227 }
228 }
229
230 if(!scn || !scn->registry)
231 {
232 return {R"({"has_scene":false,"play_phase":")" + phase + "\"}", false};
233 }
234
235 const auto entity_count = scn->registry->storage<entt::entity>().size();
236 const auto source = scn->source ? scn->source.id() : std::string{};
237 return {.text=fmt::format(R"({{"has_scene":true,"tag":{},"source":{},"entity_count":{},"play_phase":{}}})",
238 make_json_string(scn->tag),
239 make_json_string(source),
240 entity_count,
242 .is_error=false};
243 },
244 .mutates_scene=false});
245
246 registry.add(
247 {.name="scene_list_entities_batch",
248 .description=
249 "List entities in the active scene hierarchy. Optional parent_id and max_depth (default 2). "
250 "Axes: X-right, Y-up, Z-forward. "
251 "Transform fields: position/rotation_euler/scale are WORLD (global); "
252 "position_local/rotation_euler_local/scale_local are LOCAL (parent-relative).",
253 .input_schema_json=R"({"type":"object","properties":{"parent_id":{"type":"string"},"max_depth":{"type":"integer","minimum":0}}})",
254 .handler=[](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
255 {
256 auto& em = ctx.get_cached<editing_manager>();
257 auto* scn = em.get_active_scene(ctx);
258 if(!scn || !scn->registry)
259 {
260 return {.text="No active scene", .is_error=true};
261 }
262
263 int64_t max_depth = 2;
264 if(args["max_depth"].get(max_depth))
265 {
266 max_depth = 2;
267 }
268 if(max_depth < 0)
269 {
270 max_depth = 0;
271 }
272
273 std::string parent_id;
274 read_string(args, "parent_id", parent_id);
275
276 std::string json = "[";
277 bool first = true;
278 auto append = [&](entt::handle entity)
279 {
280 if(!first)
281 {
282 json += ",";
283 }
284 first = false;
285 json += entity_to_json(entity, 0, static_cast<int>(max_depth));
286 };
287
288 if(!parent_id.empty())
289 {
290 auto parent = find_entity(*scn, parent_id);
291 if(!parent)
292 {
293 return {.text="Entity not found: " + parent_id, .is_error=true};
294 }
295 if(auto* transform = parent.try_get<transform_component>())
296 {
297 for(auto child : transform->get_children())
298 {
299 append(child);
300 }
301 }
302 }
303 else
304 {
305 scn->registry->view<root_component, transform_component>().each(
306 [&](auto e, auto&&, auto&& transform)
307 {
308 append(transform.get_owner());
309 });
310 }
311
312 json += "]";
313 return {.text=json, .is_error=false};
314 },
315 .mutates_scene=false});
316
317 registry.add(
318 {.name="scene_create_light",
319 .description=
320 "Create a light entity. Args: light_type (directional|point|spot), name, optional parent_id/position. "
321 "position is WORLD space.",
322 .input_schema_json=R"json({"type":"object","properties":{"light_type":{"type":"string"},"name":{"type":"string"},"parent_id":{"type":"string"},"position":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3,"description":"WORLD space [x,y,z] (X-right, Y-up, Z-forward)"}},"required":["light_type","name"]})json",
323 .handler=[](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
324 {
325 scene* scn = nullptr;
326 std::string error;
327 if(!require_edit_scene(ctx, scn, error))
328 {
329 return {.text=error, .is_error=true};
330 }
331
332 std::string type_name;
333 std::string name;
334 if(!read_string(args, "light_type", type_name) || !read_string(args, "name", name))
335 {
336 return {.text="Missing light_type or name", .is_error=true};
337 }
339 if(!parse_light_type(type_name, type))
340 {
341 return {.text="Invalid light_type", .is_error=true};
342 }
343
344 entt::handle parent{};
345 std::string parent_id;
346 if(read_string(args, "parent_id", parent_id) && !parent_id.empty())
347 {
348 parent = find_entity(*scn, parent_id);
349 if(!parent)
350 {
351 return {.text="Parent not found: " + parent_id, .is_error=true};
352 }
353 }
354
355 math::vec3 position{};
356 const bool has_position = read_vec3(args, "position", position);
357
358 entt::handle created{};
359 auto& em = ctx.get_cached<editing_manager>();
360 em.do_action<create_entities_action_t>("MCP Create Light",
361 [&]()
362 {
363 created = defaults::create_light_entity(ctx, *scn, type, name);
364 return created;
365 });
366 if(!created)
367 {
368 return {.text="Failed to create light", .is_error=true};
369 }
370 maybe_set_parent(ctx, created, parent);
371 if(has_position)
372 {
373 maybe_set_position(ctx, created, position);
374 }
375 return {.text=entity_to_json(created, 0, 0), .is_error=false};
376 },
377 .mutates_scene=true});
378
379 registry.add(
380 {.name="scene_create_camera",
381 .description=
382 "Create a camera entity. Args: name, optional parent_id/position. position is WORLD space.",
383 .input_schema_json=R"json({"type":"object","properties":{"name":{"type":"string"},"parent_id":{"type":"string"},"position":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3,"description":"WORLD space [x,y,z] (X-right, Y-up, Z-forward)"}},"required":["name"]})json",
384 .handler=[](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
385 {
386 scene* scn = nullptr;
387 std::string error;
388 if(!require_edit_scene(ctx, scn, error))
389 {
390 return {.text=error, .is_error=true};
391 }
392
393 std::string name;
394 if(!read_string(args, "name", name))
395 {
396 return {.text="Missing name", .is_error=true};
397 }
398
399 entt::handle parent{};
400 std::string parent_id;
401 if(read_string(args, "parent_id", parent_id) && !parent_id.empty())
402 {
403 parent = find_entity(*scn, parent_id);
404 if(!parent)
405 {
406 return {.text="Parent not found: " + parent_id, .is_error=true};
407 }
408 }
409
410 math::vec3 position{};
411 const bool has_position = read_vec3(args, "position", position);
412
413 entt::handle created{};
414 auto& em = ctx.get_cached<editing_manager>();
415 em.do_action<create_entities_action_t>("MCP Create Camera",
416 [&]()
417 {
418 created = defaults::create_camera_entity(ctx, *scn, name);
419 return created;
420 });
421 if(!created)
422 {
423 return {.text="Failed to create camera", .is_error=true};
424 }
425 maybe_set_parent(ctx, created, parent);
426 if(has_position)
427 {
428 maybe_set_position(ctx, created, position);
429 }
430 return {.text=entity_to_json(created, 0, 0), .is_error=false};
431 },
432 .mutates_scene=true});
433
434 registry.add(
435 {.name="scene_delete_entities_batch",
436 .description="Delete one or more entities by id.",
437 .input_schema_json=R"({"type":"object","properties":{"entity_ids":{"type":"array","items":{"type":"string"}}},"required":["entity_ids"]})",
438 .handler=[](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
439 {
440 scene* scn = nullptr;
441 std::string error;
442 if(!require_edit_scene(ctx, scn, error))
443 {
444 return {.text=error, .is_error=true};
445 }
446
447 simdjson::dom::array ids;
448 if(args["entity_ids"].get(ids))
449 {
450 return {.text="Missing entity_ids", .is_error=true};
451 }
452
453 std::vector<entt::handle> entities;
454 for(auto el : ids)
455 {
456 std::string_view id_view;
457 if(el.get(id_view))
458 {
459 return {.text="entity_ids must be strings", .is_error=true};
460 }
461 auto entity = find_entity(*scn, std::string(id_view));
462 if(!entity)
463 {
464 return {.text="Entity not found: " + std::string(id_view), .is_error=true};
465 }
466 entities.push_back(entity);
467 }
468
469 ctx.get_cached<editing_manager>().do_action<delete_entities_action_t>("MCP Delete Entities", entities);
470 return {.text=fmt::format(R"({{"deleted":{}}})", entities.size()), .is_error=false};
471 },
472 .mutates_scene=true});
473
474 registry.add(
475 {.name="scene_list_component_types",
476 .description="List addable component pretty names for scene_add_components_batch.",
477 .input_schema_json=empty_object_schema(),
478 .handler=[](rtti::context&, const simdjson::dom::object&) -> tool_result
479 {
480 std::string json = "[";
481 bool first = true;
482 hpp::for_each_tuple_type<all_addable_components>(
483 [&](auto index)
484 {
485 using ctype = std::tuple_element_t<decltype(index)::value, all_addable_components>;
486 auto type = entt::resolve<ctype>();
487 if(!first)
488 {
489 json += ",";
490 }
491 first = false;
492 json += make_json_string(std::string(entt::get_pretty_name(type)));
493 });
494 json += "]";
495 return {.text=json, .is_error=false};
496 },
497 .mutates_scene=false});
498
499 registry.add(
500 {.name = "scene_list_presets",
501 .description =
502 "List defaults::scene_preset values usable with scene_new_from_preset / project_open "
503 "(low, medium, high, showcase).",
504 .input_schema_json = empty_object_schema(),
505 .handler =
506 [](rtti::context&, const simdjson::dom::object&) -> tool_result
507 {
508 return {.text = R"(["low","medium","high","showcase"])", .is_error = false};
509 },
510 .mutates_scene = false});
511
512 registry.add(
513 {.name = "scene_save",
514 .description =
515 "Save the active edit scene to a .spfb via asset_writer::atomic_save_to_file. "
516 "Omit key/path to overwrite scene.source; provide key or absolute path for save-as "
517 "(sets scene.source). Requires an open project. Refuses play mode and prefab mode.",
518 .input_schema_json =
519 R"json({"type":"object","properties":{"key":{"type":"string","description":"Asset key e.g. app:/data/Village.spfb"},"path":{"type":"string","description":"Absolute filesystem path to a .spfb"}}})json",
520 .handler =
521 [](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
522 {
523 std::string error;
525 {
526 return {.text = error, .is_error = true};
527 }
528
529 auto& em = ctx.get_cached<editing_manager>();
530 if(em.is_prefab_mode())
531 {
532 return {.text = "Cannot scene_save while in prefab mode", .is_error = true};
533 }
534
535 auto& ec = ctx.get_cached<ecs>();
536 auto& scene = ec.get_scene();
537
538 std::string key;
539 std::string path;
540 read_string(args, "key", key);
541 read_string(args, "path", path);
542 if(key.empty() && !path.empty())
543 {
544 key = path;
545 }
546
547 if(key.empty())
548 {
549 if(!scene.source)
550 {
551 return {.text = "Scene has no source; provide key or path for save-as", .is_error = true};
552 }
553 key = scene.source.id();
554 }
555 else
556 {
557 key = normalize_scene_key(key);
558 }
559
560 const auto absolute = fs::absolute(fs::resolve_protocol(key));
561 if(!editor_actions::save_scene_to_path(ctx, absolute, true, false))
562 {
563 return {.text = "Failed to save scene: " + key, .is_error = true};
564 }
565
566 return {.text = fmt::format(R"({{"ok":true,"key":{},"path":{}}})",
567 make_json_string(scene.source ? scene.source.id() : key),
568 make_json_string(absolute.generic_string())),
569 .is_error = false};
570 },
571 .mutates_scene = true});
572
573 registry.add(
574 {.name = "scene_open",
575 .description =
576 "Open a scene asset (.spfb) by key or absolute path. No ImGui save prompt; pass "
577 "force:true (default) to discard unsaved changes. Requires an open project.",
578 .input_schema_json =
579 R"json({"type":"object","properties":{"key":{"type":"string","description":"Asset key e.g. app:/data/MyScene.spfb"},"path":{"type":"string","description":"Absolute filesystem path to a .spfb"},"force":{"type":"boolean","default":true}},"required":[]})json",
580 .handler =
581 [](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
582 {
583 std::string error;
585 {
586 return {.text = error, .is_error = true};
587 }
588
589 std::string key;
590 std::string path;
591 read_string(args, "key", key);
592 read_string(args, "path", path);
593 if(key.empty() && !path.empty())
594 {
595 key = path;
596 }
597 if(key.empty())
598 {
599 return {.text = "Provide key or path", .is_error = true};
600 }
601 key = normalize_scene_key(key);
602
603 bool force = true;
604 read_bool(args, "force", force);
605
606 auto& em = ctx.get_cached<editing_manager>();
607 if(em.has_unsaved_changes() && !force)
608 {
609 return {.text = "Unsaved scene changes; pass force:true to discard", .is_error = true};
610 }
611 if(force)
612 {
613 em.clear_unsaved_changes();
614 }
615
616 auto& am = ctx.get_cached<asset_manager>();
617 auto asset = am.get_asset<scene_prefab>(key);
618 if(!asset)
619 {
620 return {.text = "Scene asset not found: " + key, .is_error = true};
621 }
622
624 {
625 return {.text = error, .is_error = true};
626 }
627
628 auto* scn = em.get_active_scene(ctx);
629 size_t entity_count = 0;
630 if(scn && scn->registry)
631 {
632 for(auto entity : scn->registry->view<transform_component>())
633 {
634 (void)entity;
635 ++entity_count;
636 }
637 }
638 return {.text = fmt::format(R"({{"ok":true,"key":{},"entity_count":{}}})",
639 make_json_string(asset.id()),
640 entity_count),
641 .is_error = false};
642 },
643 .mutates_scene = true});
644
645 registry.add(
646 {.name = "scene_new_from_preset",
647 .description =
648 "Create a new unsaved scene from defaults::scene_preset (camera, skylight, probe, "
649 "volume). No ImGui modal. Presets: low|medium|high|showcase (default medium). "
650 "Requires an open project. force:true (default) discards unsaved changes.",
651 .input_schema_json =
652 R"({"type":"object","properties":{"preset":{"type":"string","enum":["low","medium","high","showcase"]},"force":{"type":"boolean","default":true}}})",
653 .handler =
654 [](rtti::context& ctx, const simdjson::dom::object& args) -> tool_result
655 {
656 std::string error;
658 {
659 return {.text = error, .is_error = true};
660 }
661
662 std::string preset_str = "medium";
663 read_string(args, "preset", preset_str);
665 if(!defaults::parse_scene_preset(preset_str, preset))
666 {
667 return {.text = "Invalid preset (use low|medium|high|showcase)", .is_error = true};
668 }
669
670 bool force = true;
671 read_bool(args, "force", force);
672
673 auto& em = ctx.get_cached<editing_manager>();
674 if(em.has_unsaved_changes() && !force)
675 {
676 return {.text = "Unsaved scene changes; pass force:true to discard", .is_error = true};
677 }
678 if(force)
679 {
680 em.clear_unsaved_changes();
681 }
682
684
685 auto* scn = em.get_active_scene(ctx);
686 size_t entity_count = 0;
687 if(scn && scn->registry)
688 {
689 for(auto entity : scn->registry->view<transform_component>())
690 {
691 (void)entity;
692 ++entity_count;
693 }
694 }
695 return {.text = fmt::format(R"({{"ok":true,"preset":{},"entity_count":{}}})",
697 entity_count),
698 .is_error = false};
699 },
700 .mutates_scene = true});
701}
702
703} // namespace unravel::mcp
defaults::scene_preset preset
math::vec3 position
Definition defaults.cpp:52
uint16_t index
std::string name
Definition hub.cpp:33
std::string error
Definition mcp_async.cpp:33
std::string parent_id
bool is_local
texture_job_type type
phase_t phase
const aiScene * scene
auto get_pretty_name(const meta_type &t) -> std::string
path resolve_protocol(const path &_path)
Given the specified path/filename, resolve the final full filename. This will be based on either the ...
path convert_to_protocol(const path &_path)
Oposite of the resolve_protocol this function tries to convert to protocol path from an absolute one.
bgfx::Transform transform
Definition graphics.h:42
auto require_not_play_mode(rtti::context &ctx, std::string &error) -> bool
auto read_vec3(const simdjson::dom::object &args, const char *key, math::vec3 &out) -> bool
auto empty_object_schema() -> std::string
auto find_entity(scene &scn, const std::string &id) -> entt::handle
auto require_edit_scene(rtti::context &ctx, scene *&out_scene, std::string &error) -> bool
auto read_bool(const simdjson::dom::object &args, const char *key, bool &out) -> bool
auto make_json_string(const std::string &value) -> std::string
void register_scene_tools(mcp_tool_registry &registry)
auto read_string(const simdjson::dom::object &args, const char *key, std::string &out) -> bool
auto require_open_project(rtti::context &ctx, std::string &error) -> bool
std::tuple< test_component, model_component, animation_component, camera_component, volume_component, auto_exposure_component, tonemapping_component, assao_component, bloom_component, fxaa_component, taa_component, ssr_component, ssil_component, light_component, skylight_component, reflection_probe_component, physics_component, character_controller_component, audio_source_component, audio_listener_component, text_component, particle_emitter_component, ui_document_component > all_addable_components
light_type
Enum representing the type of light.
Definition light.h:14
auto entity_to_summary_json(entt::handle entity, int depth, int max_depth) -> std::string
JSON summary: transform, component pretty-names, optional children.
octet_iterator append(utfchar32_t cp, octet_iterator result)
The library API - functions intended to be called by the users.
Definition checked.h:74
std::vector< float > scale
std::vector< math::quat > rotation
entt::handle entity
auto get_cached() -> T &
Definition context.hpp:49
auto has() const -> bool
Definition context.hpp:28
static auto parse_scene_preset(hpp::string_view value, scene_preset &out) -> bool
Parse a preset name (low|medium|high|showcase; aliases: standard/default -> medium)....
scene_preset
Quality presets for new scene creation (low = less expensive, high = more expensive).
Definition defaults.h:215
static auto scene_preset_to_string(scene_preset preset) -> const char *
Stable string for logging / MCP / UI ("low", "medium", "high", "showcase").
static auto create_light_entity(rtti::context &ctx, scene &scn, light_type type, const std::string &name) -> entt::handle
Creates a light entity.
Definition defaults.cpp:921
static auto create_camera_entity(rtti::context &ctx, scene &scn, const std::string &name) -> entt::handle
Creates a camera entity.
Definition defaults.cpp:999
static auto load_scene_from_asset(rtti::context &ctx, const asset_handle< scene_prefab > &asset, std::string *error=nullptr) -> bool
Non-modal scene load (shared by File menu + MCP). Clears edit state, loads asset, syncs prefabs,...
static auto save_scene_to_path(rtti::context &ctx, const fs::path &path, bool update_source=true, bool show_notification=true) -> bool
Atomic-save active scene to path/key. When update_source is true, sets scene.source and project opene...
static auto new_scene_from_preset(rtti::context &ctx, defaults::scene_preset preset) -> bool
Non-modal new scene from preset (shared by create-scene modal + MCP). Cancels any pending create-scen...
float size