mirror of
https://github.com/Nexoniarz/Fry3DX.git
synced 2026-11-03 20:44:26 +00:00
Materials shown in the view: each object's metal and rough reach the shader (Solid and Rendered); a Material picker with sixteen to start from (gold, chrome, wood, ceramic...); o.material and fry.materials() in scripts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
87436019b3
commit
e93ae44684
9 changed files with 101 additions and 5 deletions
|
|
@ -53,7 +53,8 @@ struct DrawData { // DRAW_FIELDS in model.glsl
|
||||||
glm::vec4 cube[6];
|
glm::vec4 cube[6];
|
||||||
glm::vec4 params; // x = sun visibility, y = lit by probes, z = liquid cut, w = LiquidStyle
|
glm::vec4 params; // x = sun visibility, y = lit by probes, z = liquid cut, w = LiquidStyle
|
||||||
glm::vec4 plane; // liquid surface
|
glm::vec4 plane; // liquid surface
|
||||||
glm::vec4 probe; // the nearest reflection probe: x = its texture (0 = none), y = its top level; z = time (s)
|
glm::vec4 probe; // the nearest reflection probe: x = its texture (0 = none), y = its top level; z = time (s);
|
||||||
|
// w = its own surface without a material map: 0 none, else 1 + metal (0..255) + rough (0..255) * 256
|
||||||
};
|
};
|
||||||
struct FrameData { // FRAME_FIELDS in model.glsl, then (Vulkan) four buffer addresses
|
struct FrameData { // FRAME_FIELDS in model.glsl, then (Vulkan) four buffer addresses
|
||||||
glm::mat4 cascades[4];
|
glm::mat4 cascades[4];
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ void Renderer::drawStreak(const glm::vec3& from, const glm::vec3& to, float widt
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::drawModel(ModelId model, const glm::mat4& transform, const glm::vec4& tint, const std::vector<glm::mat4>* skin,
|
void Renderer::drawModel(ModelId model, const glm::mat4& transform, const glm::vec4& tint, const std::vector<glm::mat4>* skin,
|
||||||
const ModelLighting* lighting, bool castShadow, const glm::vec4* liquidPlane, LiquidStyle liquid) {
|
const ModelLighting* lighting, bool castShadow, const glm::vec4* liquidPlane, LiquidStyle liquid, const Surface* surface) {
|
||||||
if (!model.valid() || packet_.draws.size() >= kMaxDraws) return;
|
if (!model.valid() || packet_.draws.size() >= kMaxDraws) return;
|
||||||
ModelDraw d{model.index, transform, tint};
|
ModelDraw d{model.index, transform, tint};
|
||||||
d.castShadow = castShadow || shadowOnly_;
|
d.castShadow = castShadow || shadowOnly_;
|
||||||
|
|
@ -261,6 +261,8 @@ void Renderer::drawModel(ModelId model, const glm::mat4& transform, const glm::v
|
||||||
}
|
}
|
||||||
static const auto t0 = std::chrono::steady_clock::now(); // water's waves move with this
|
static const auto t0 = std::chrono::steady_clock::now(); // water's waves move with this
|
||||||
dd.probe.z = hasClock_ ? float(std::fmod(clock_, 3600.0)) : std::fmod(std::chrono::duration<float>(std::chrono::steady_clock::now() - t0).count(), 3600.0f);
|
dd.probe.z = hasClock_ ? float(std::fmod(clock_, 3600.0)) : std::fmod(std::chrono::duration<float>(std::chrono::steady_clock::now() - t0).count(), 3600.0f);
|
||||||
|
if (surface)
|
||||||
|
dd.probe.w = 1.0f + std::round(std::clamp(surface->metal, 0.0f, 1.0f) * 255.0f) + std::round(std::clamp(surface->rough, 0.0f, 1.0f) * 255.0f) * 256.0f;
|
||||||
packet_.draws.push_back(dd);
|
packet_.draws.push_back(dd);
|
||||||
if (skin && mdl.skinned && packet_.joints.size() + skin->size() <= kMaxJointsPerFrame) {
|
if (skin && mdl.skinned && packet_.joints.size() + skin->size() <= kMaxJointsPerFrame) {
|
||||||
d.firstJoint = uint32_t(packet_.joints.size());
|
d.firstJoint = uint32_t(packet_.joints.size());
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,11 @@ struct Camera {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Baked light for a moving model (from the map's probes): ambient cube + sun visibility.
|
// Baked light for a moving model (from the map's probes): ambient cube + sun visibility.
|
||||||
|
// A whole model's surface where it has no material map: how metal (0..1) and how rough (0..1).
|
||||||
|
struct Surface {
|
||||||
|
float metal = 0.0f, rough = 0.85f;
|
||||||
|
};
|
||||||
|
|
||||||
struct ModelLighting {
|
struct ModelLighting {
|
||||||
glm::vec3 cube[6]; // light arriving from +X -X +Y -Y +Z -Z
|
glm::vec3 cube[6]; // light arriving from +X -X +Y -Y +Z -Z
|
||||||
float sunVisibility = 1.0f;
|
float sunVisibility = 1.0f;
|
||||||
|
|
@ -98,7 +103,7 @@ public:
|
||||||
// lighting: probe light for moving things (nullptr = lightmap or simple sun shading).
|
// lighting: probe light for moving things (nullptr = lightmap or simple sun shading).
|
||||||
void drawModel(ModelId model, const glm::mat4& transform, const glm::vec4& tint = glm::vec4(1.0f),
|
void drawModel(ModelId model, const glm::mat4& transform, const glm::vec4& tint = glm::vec4(1.0f),
|
||||||
const std::vector<glm::mat4>* skin = nullptr, const ModelLighting* lighting = nullptr, bool castShadow = true,
|
const std::vector<glm::mat4>* skin = nullptr, const ModelLighting* lighting = nullptr, bool castShadow = true,
|
||||||
const glm::vec4* liquidPlane = nullptr, LiquidStyle liquid = LiquidStyle::None);
|
const glm::vec4* liquidPlane = nullptr, LiquidStyle liquid = LiquidStyle::None, const Surface* surface = nullptr);
|
||||||
// liquidPlane (world space: xyz normal, w distance): draws a closed mesh as liquid filling it
|
// liquidPlane (world space: xyz normal, w distance): draws a closed mesh as liquid filling it
|
||||||
// up to the plane — above it is cut away, and its back faces show as the flat surface.
|
// up to the plane — above it is cut away, and its back faces show as the flat surface.
|
||||||
// Alpha below 1 (tint × material) makes a submesh translucent: sorted, blended, after opaque.
|
// Alpha below 1 (tint × material) makes a submesh translucent: sorted, blended, after opaque.
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,10 @@ void main() {
|
||||||
n = normalize(tbn * vec3(xy, sqrt(max(1.0 - dot(xy, xy), 0.0))));
|
n = normalize(tbn * vec3(xy, sqrt(max(1.0 - dot(xy, xy), 0.0))));
|
||||||
metal = m.z;
|
metal = m.z;
|
||||||
gloss = m.w;
|
gloss = m.w;
|
||||||
|
} else if (DRAW.probe.w > 0.5) { // the model's own surface (Renderer::Surface)
|
||||||
|
uint s = uint(DRAW.probe.w - 0.5);
|
||||||
|
metal = float(s & 255u) / 255.0;
|
||||||
|
gloss = 1.0 - float((s >> 8) & 255u) / 255.0;
|
||||||
}
|
}
|
||||||
// Weather: how open to the sky this spot is (the lightmap's sky map; moving things: their
|
// Weather: how open to the sky this spot is (the lightmap's sky map; moving things: their
|
||||||
// sun visibility), then rain wetting it (darker, glossy) and snow lying on what faces up.
|
// sun visibility), then rain wetting it (darker, glossy) and snow lying on what faces up.
|
||||||
|
|
|
||||||
30
src/app.cpp
30
src/app.cpp
|
|
@ -4576,7 +4576,8 @@ private:
|
||||||
for (const Object& o : scene_.objects) {
|
for (const Object& o : scene_.objects) {
|
||||||
if (!o.visible) continue;
|
if (!o.visible) continue;
|
||||||
const glm::vec4 tint = painted(o) ? glm::vec4(1.0f) : glm::vec4(glm::pow(o.color, glm::vec3(2.2f)), 1.0f); // (colours are picked as seen: sRGB)
|
const glm::vec4 tint = painted(o) ? glm::vec4(1.0f) : glm::vec4(glm::pow(o.color, glm::vec3(2.2f)), 1.0f); // (colours are picked as seen: sRGB)
|
||||||
r.drawModel(modelOf(o), toUnits * objectMatrix(o), tint, nullptr, &light, rendered);
|
const keel::Surface surface{o.metal, o.rough};
|
||||||
|
r.drawModel(modelOf(o), toUnits * objectMatrix(o), tint, nullptr, &light, rendered, nullptr, keel::LiquidStyle::None, &surface);
|
||||||
}
|
}
|
||||||
// Rendered: a ground to stand on and catch the shadows (not part of what you make).
|
// Rendered: a ground to stand on and catch the shadows (not part of what you make).
|
||||||
if (rendered && look.ground) {
|
if (rendered && look.ground) {
|
||||||
|
|
@ -5710,6 +5711,7 @@ private:
|
||||||
if (ImGui::Button("Save a picture (F12)", ImVec2(w, 0))) dialog(5);
|
if (ImGui::Button("Save a picture (F12)", ImVec2(w, 0))) dialog(5);
|
||||||
if (Object* o = selected_.size() == 1 ? find(active_) : nullptr) {
|
if (Object* o = selected_.size() == 1 ? find(active_) : nullptr) {
|
||||||
ImGui::SeparatorText(o->name.c_str());
|
ImGui::SeparatorText(o->name.c_str());
|
||||||
|
materialPicker(*o);
|
||||||
undoable([&] { return ImGui::ColorEdit3("Colour", &o->color.x); });
|
undoable([&] { return ImGui::ColorEdit3("Colour", &o->color.x); });
|
||||||
undoable([&] { return ImGui::SliderFloat("Metal", &o->metal, 0, 1); });
|
undoable([&] { return ImGui::SliderFloat("Metal", &o->metal, 0, 1); });
|
||||||
undoable([&] { return ImGui::SliderFloat("Rough", &o->rough, 0, 1); });
|
undoable([&] { return ImGui::SliderFloat("Rough", &o->rough, 0, 1); });
|
||||||
|
|
@ -5916,6 +5918,7 @@ private:
|
||||||
const glm::vec3 lo = boundsMin(shownMesh(*o)) * glm::abs(o->scale), hi = boundsMax(shownMesh(*o)) * glm::abs(o->scale);
|
const glm::vec3 lo = boundsMin(shownMesh(*o)) * glm::abs(o->scale), hi = boundsMax(shownMesh(*o)) * glm::abs(o->scale);
|
||||||
ImGui::TextDisabled("%.3f x %.3f x %.3f m", hi.x - lo.x, hi.y - lo.y, hi.z - lo.z);
|
ImGui::TextDisabled("%.3f x %.3f x %.3f m", hi.x - lo.x, hi.y - lo.y, hi.z - lo.z);
|
||||||
ImGui::SeparatorText("Look");
|
ImGui::SeparatorText("Look");
|
||||||
|
materialPicker(*o);
|
||||||
undoable([&] { return ImGui::ColorEdit3("Colour", &o->color.x); });
|
undoable([&] { return ImGui::ColorEdit3("Colour", &o->color.x); });
|
||||||
undoable([&] { return ImGui::SliderFloat("Metal", &o->metal, 0, 1); });
|
undoable([&] { return ImGui::SliderFloat("Metal", &o->metal, 0, 1); });
|
||||||
undoable([&] { return ImGui::SliderFloat("Rough", &o->rough, 0, 1); });
|
undoable([&] { return ImGui::SliderFloat("Rough", &o->rough, 0, 1); });
|
||||||
|
|
@ -5970,6 +5973,31 @@ private:
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A material to start from: sets colour, metal and rough in one go (each a little ball of it).
|
||||||
|
void materialPicker(Object& o) {
|
||||||
|
const MaterialPreset* now = nullptr;
|
||||||
|
for (const MaterialPreset& m : materialPresets())
|
||||||
|
if (std::abs(m.metal - o.metal) < 0.01f && std::abs(m.rough - o.rough) < 0.01f && (m.keepColor || glm::all(glm::lessThan(glm::abs(m.color - o.color), glm::vec3(0.01f))))) now = &m;
|
||||||
|
ImGui::SetNextItemWidth(ImGui::CalcItemWidth());
|
||||||
|
if (!ImGui::BeginCombo("Material", now ? now->name : "(its own)", ImGuiComboFlags_HeightLarge)) return;
|
||||||
|
for (const MaterialPreset& m : materialPresets()) {
|
||||||
|
const ImVec2 p = ImGui::GetCursorScreenPos();
|
||||||
|
const float h = ImGui::GetTextLineHeight();
|
||||||
|
ImGui::PushID(m.id);
|
||||||
|
if (ImGui::Selectable("##m", now == &m, 0, ImVec2(0, h))) pushUndo(), applyMaterial(o, m), touched(o.id);
|
||||||
|
ImGui::PopID();
|
||||||
|
// A ball: its colour, darker round the edge, a highlight as sharp as it's smooth.
|
||||||
|
const glm::vec3 c = m.keepColor ? o.color : m.color;
|
||||||
|
const ImVec2 mid(p.x + h * 0.5f, p.y + h * 0.5f);
|
||||||
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||||
|
dl->AddCircleFilled(mid, h * 0.45f, ImGui::ColorConvertFloat4ToU32(ImVec4(c.r * 0.55f, c.g * 0.55f, c.b * 0.55f, 1)));
|
||||||
|
dl->AddCircleFilled(ImVec2(mid.x - h * 0.05f, mid.y - h * 0.05f), h * 0.36f, ImGui::ColorConvertFloat4ToU32(ImVec4(c.r, c.g, c.b, 1)));
|
||||||
|
dl->AddCircleFilled(ImVec2(mid.x - h * 0.14f, mid.y - h * 0.14f), h * (0.06f + 0.1f * m.rough), IM_COL32(255, 255, 255, int(230 * (1.0f - m.rough * 0.8f))));
|
||||||
|
dl->AddText(ImVec2(p.x + h * 1.4f, p.y), ImGui::GetColorU32(ImGuiCol_Text), m.name);
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
void editProperties(Object& o, float w) {
|
void editProperties(Object& o, float w) {
|
||||||
ImGui::Text("Editing the shape of %s", o.name.c_str());
|
ImGui::Text("Editing the shape of %s", o.name.c_str());
|
||||||
ImGui::TextDisabled("%zu points, %zu faces", o.mesh.verts.size(), o.mesh.faces.size());
|
ImGui::TextDisabled("%zu points, %zu faces", o.mesh.verts.size(), o.mesh.faces.size());
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,31 @@ glm::mat4 objectMatrix(const Object& o) {
|
||||||
return glm::translate(glm::mat4(1.0f), o.position) * glm::mat4_cast(o.rotation) * glm::scale(glm::mat4(1.0f), o.scale);
|
return glm::translate(glm::mat4(1.0f), o.position) * glm::mat4_cast(o.rotation) * glm::scale(glm::mat4(1.0f), o.scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<MaterialPreset>& materialPresets() {
|
||||||
|
static const std::vector<MaterialPreset> list = {
|
||||||
|
{"matte", "Matte (its colour)", {}, 0.0f, 0.9f, true}, {"satin", "Satin (its colour)", {}, 0.0f, 0.45f, true},
|
||||||
|
{"glossy", "Glossy (its colour)", {}, 0.0f, 0.12f, true}, {"plastic", "White plastic", {0.9f, 0.9f, 0.88f}, 0.0f, 0.35f},
|
||||||
|
{"rubber", "Black rubber", {0.08f, 0.08f, 0.08f}, 0.0f, 0.95f}, {"wood", "Wood", {0.62f, 0.42f, 0.26f}, 0.0f, 0.7f},
|
||||||
|
{"clay", "Clay", {0.72f, 0.4f, 0.28f}, 0.0f, 0.95f}, {"ceramic", "Ceramic", {0.95f, 0.95f, 0.93f}, 0.0f, 0.08f},
|
||||||
|
{"concrete", "Concrete", {0.6f, 0.6f, 0.58f}, 0.0f, 1.0f}, {"steel", "Brushed steel", {0.75f, 0.76f, 0.77f}, 1.0f, 0.4f},
|
||||||
|
{"chrome", "Chrome", {0.95f, 0.95f, 0.96f}, 1.0f, 0.04f}, {"aluminium", "Aluminium", {0.91f, 0.92f, 0.92f}, 1.0f, 0.3f},
|
||||||
|
{"gold", "Gold", {1.0f, 0.78f, 0.34f}, 1.0f, 0.18f}, {"copper", "Copper", {0.96f, 0.64f, 0.54f}, 1.0f, 0.25f},
|
||||||
|
{"brass", "Brass", {0.89f, 0.75f, 0.42f}, 1.0f, 0.3f}, {"iron", "Cast iron", {0.3f, 0.3f, 0.31f}, 1.0f, 0.75f},
|
||||||
|
};
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MaterialPreset* findMaterial(std::string_view id) {
|
||||||
|
for (const MaterialPreset& m : materialPresets())
|
||||||
|
if (id == m.id) return &m;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyMaterial(Object& o, const MaterialPreset& m) {
|
||||||
|
if (!m.keepColor) o.color = m.color;
|
||||||
|
o.metal = m.metal, o.rough = m.rough;
|
||||||
|
}
|
||||||
|
|
||||||
Mesh finishedMesh(const Object& o, bool mirrorOnly) {
|
Mesh finishedMesh(const Object& o, bool mirrorOnly) {
|
||||||
Mesh m = o.mesh;
|
Mesh m = o.mesh;
|
||||||
for (int axis = 0; axis < 3; ++axis) {
|
for (int axis = 0; axis < 3; ++axis) {
|
||||||
|
|
|
||||||
14
src/scene.h
14
src/scene.h
|
|
@ -11,6 +11,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace fry {
|
namespace fry {
|
||||||
|
|
@ -128,6 +129,19 @@ struct Scene {
|
||||||
uint32_t nextId = 1;
|
uint32_t nextId = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Materials to start from: a colour (unless `keepColor`: only how metal and rough it is) and
|
||||||
|
// how metal and rough. Shown in the view (Solid and Rendered) and exported with the object.
|
||||||
|
struct MaterialPreset {
|
||||||
|
const char* id; // as scripts name it
|
||||||
|
const char* name; // as shown
|
||||||
|
glm::vec3 color; // sRGB
|
||||||
|
float metal, rough;
|
||||||
|
bool keepColor = false;
|
||||||
|
};
|
||||||
|
const std::vector<MaterialPreset>& materialPresets();
|
||||||
|
const MaterialPreset* findMaterial(std::string_view id);
|
||||||
|
void applyMaterial(Object& o, const MaterialPreset& m);
|
||||||
|
|
||||||
glm::mat4 objectMatrix(const Object& o);
|
glm::mat4 objectMatrix(const Object& o);
|
||||||
// A turn as degrees about X, then Y, then Z (what the Properties panel shows), and back.
|
// A turn as degrees about X, then Y, then Z (what the Properties panel shows), and back.
|
||||||
glm::vec3 toEuler(const glm::quat& q);
|
glm::vec3 toEuler(const glm::quat& q);
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,10 @@ bool setProperty(lua_State* L, Object& o, const std::string& k, int v) {
|
||||||
else if (k == "mirror") {
|
else if (k == "mirror") {
|
||||||
const std::string s = lower(luaL_checkstring(L, v));
|
const std::string s = lower(luaL_checkstring(L, v));
|
||||||
o.mirror = (s.find('x') != std::string::npos ? 1 : 0) | (s.find('y') != std::string::npos ? 2 : 0) | (s.find('z') != std::string::npos ? 4 : 0);
|
o.mirror = (s.find('x') != std::string::npos ? 1 : 0) | (s.find('y') != std::string::npos ? 2 : 0) | (s.find('z') != std::string::npos ? 4 : 0);
|
||||||
|
} else if (k == "material") {
|
||||||
|
const MaterialPreset* m = findMaterial(lower(luaL_checkstring(L, v)));
|
||||||
|
if (!m) luaL_error(L, "there's no material called %s (fry.materials() lists them)", lua_tostring(L, v));
|
||||||
|
else applyMaterial(o, *m);
|
||||||
} else if (k == "round") o.round = std::max(0.0f, float(luaL_checknumber(L, v)));
|
} else if (k == "round") o.round = std::max(0.0f, float(luaL_checknumber(L, v)));
|
||||||
else if (k == "round_steps") o.roundSteps = std::clamp(int(luaL_checkinteger(L, v)), 1, 12);
|
else if (k == "round_steps") o.roundSteps = std::clamp(int(luaL_checkinteger(L, v)), 1, 12);
|
||||||
else if (k == "smooth_levels") o.smoothLevels = std::clamp(int(luaL_checkinteger(L, v)), 0, 4);
|
else if (k == "smooth_levels") o.smoothLevels = std::clamp(int(luaL_checkinteger(L, v)), 0, 4);
|
||||||
|
|
@ -640,6 +644,12 @@ int fryShapes(lua_State* L) {
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
int fryMaterials(lua_State* L) { // fry.materials(): every material's name, for o.material = ...
|
||||||
|
lua_newtable(L);
|
||||||
|
int i = 0;
|
||||||
|
for (const MaterialPreset& m : materialPresets()) lua_pushstring(L, m.id), lua_rawseti(L, -2, ++i);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
int fryLook(lua_State* L) { // fry.look{sun_turn = 40, sky = false, ...}; returns the look as it is
|
int fryLook(lua_State* L) { // fry.look{sun_turn = 40, sky = false, ...}; returns the look as it is
|
||||||
Look& k = scene(L).look;
|
Look& k = scene(L).look;
|
||||||
struct Num {
|
struct Num {
|
||||||
|
|
@ -696,7 +706,7 @@ int fryHelp(lua_State* L) {
|
||||||
|
|
||||||
const luaL_Reg kFry[] = {
|
const luaL_Reg kFry[] = {
|
||||||
{"add", fryAdd}, {"mesh", fryMesh}, {"lathe", fryLathe}, {"extrude", fryExtrude}, {"objects", fryObjects}, {"find", fryFind},
|
{"add", fryAdd}, {"mesh", fryMesh}, {"lathe", fryLathe}, {"extrude", fryExtrude}, {"objects", fryObjects}, {"find", fryFind},
|
||||||
{"selected", frySelected}, {"select", frySelect}, {"clear", fryClear}, {"group", fryGroup}, {"shapes", fryShapes}, {"look", fryLook},
|
{"selected", frySelected}, {"select", frySelect}, {"clear", fryClear}, {"group", fryGroup}, {"shapes", fryShapes}, {"look", fryLook}, {"materials", fryMaterials},
|
||||||
{"vec", fryVec}, {"say", fryPrint}, {"frame", fryFrame}, {"help", fryHelp}, {nullptr, nullptr},
|
{"vec", fryVec}, {"say", fryPrint}, {"frame", fryFrame}, {"help", fryHelp}, {nullptr, nullptr},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -781,6 +791,7 @@ const char* scriptHelp() {
|
||||||
|
|
||||||
fry.add(shape, {setting = value, property = value, ...}) a library shape: fry.add("cube", {width = 2, color = "#ff8000"})
|
fry.add(shape, {setting = value, property = value, ...}) a library shape: fry.add("cube", {width = 2, color = "#ff8000"})
|
||||||
fry.shapes() every shape: {id, name, settings}
|
fry.shapes() every shape: {id, name, settings}
|
||||||
|
fry.materials() every material's name, for o.material
|
||||||
fry.mesh(points, faces, name) your own: points {{x,y,z}, ...}, faces {{1,2,3,4}, ...} (counter-clockwise from outside)
|
fry.mesh(points, faces, name) your own: points {{x,y,z}, ...}, faces {{1,2,3,4}, ...} (counter-clockwise from outside)
|
||||||
fry.lathe(profile, sides, name) a profile of {out, up} points turned round the Z axis (bottles, vases, bowls)
|
fry.lathe(profile, sides, name) a profile of {out, up} points turned round the Z axis (bottles, vases, bowls)
|
||||||
fry.extrude(outline, height, name) an outline of {x, y} points on the ground, made that tall
|
fry.extrude(outline, height, name) an outline of {x, y} points on the ground, made that tall
|
||||||
|
|
@ -792,6 +803,7 @@ fry.frame() fry.say(text) print(...) fry.help()
|
||||||
|
|
||||||
An object (o):
|
An object (o):
|
||||||
o.name o.position o.turn o.size o.color o.metal o.rough o.smooth o.visible o.locked
|
o.name o.position o.turn o.size o.color o.metal o.rough o.smooth o.visible o.locked
|
||||||
|
o.material = "gold" sets colour, metal and rough ("matte", "satin", "glossy" keep the colour)
|
||||||
o.mirror ("x", "xy"...) o.round o.round_steps o.smooth_levels (finishing touches, not baked)
|
o.mirror ("x", "xy"...) o.round o.round_steps o.smooth_levels (finishing touches, not baked)
|
||||||
o.width, o.sides ... a library shape's own settings, changeable until its shape is changed by hand
|
o.width, o.sides ... a library shape's own settings, changeable until its shape is changed by hand
|
||||||
o.id o.shape o.point_count o.face_count (to read)
|
o.id o.shape o.point_count o.face_count (to read)
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ int main() {
|
||||||
CHECK(!r.ok, "no io");
|
CHECK(!r.ok, "no io");
|
||||||
r = runScript(host, "local o = fry.add('cube')\nlocal n = #o:points()\nlocal p = o:points()\nfor i = 1, n do p[i].z = p[i].z * 2 end\no:setpoints(p)\nlocal lo, hi = o:bounds()\nprint(hi.z - lo.z)");
|
r = runScript(host, "local o = fry.add('cube')\nlocal n = #o:points()\nlocal p = o:points()\nfor i = 1, n do p[i].z = p[i].z * 2 end\no:setpoints(p)\nlocal lo, hi = o:bounds()\nprint(hi.z - lo.z)");
|
||||||
CHECK(r.ok && out.find("\n2.0\n") != std::string::npos, "points round-trip (%s)", r.error.c_str());
|
CHECK(r.ok && out.find("\n2.0\n") != std::string::npos, "points round-trip (%s)", r.error.c_str());
|
||||||
|
r = runScript(host, "local o = fry.add('cube')\no.material = 'Gold'\nlocal m = fry.add('cube', {color = '#102030', material = 'glossy'})\nprint(#fry.materials())");
|
||||||
|
CHECK(r.ok && scene.objects.back().metal == 0.0f && scene.objects.back().rough < 0.2f && scene.objects.back().color.r < 0.1f, "a material keeping its colour (%s)", r.error.c_str());
|
||||||
|
CHECK(scene.objects[scene.objects.size() - 2].metal == 1.0f && scene.objects[scene.objects.size() - 2].color.g > 0.7f, "gold");
|
||||||
|
r = runScript(host, "fry.add('cube').material = 'cheese'");
|
||||||
|
CHECK(!r.ok && r.error.find("no material called cheese") != std::string::npos, "an unknown material is an error");
|
||||||
r = runScript(host, "fry.clear()");
|
r = runScript(host, "fry.clear()");
|
||||||
CHECK(r.ok && scene.objects.empty() && active == 0 && selected.empty(), "clear");
|
CHECK(r.ok && scene.objects.empty() && active == 0 && selected.empty(), "clear");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue