Add lighting presets to 3D effects

Squashed from #3442 onto main.
This commit is contained in:
bflatastic
2026-10-22 00:01:37 +07:00
parent 4d1581f3da
commit 2cd7f156fb
39 changed files with 475 additions and 16 deletions
+2
View File
@@ -40,6 +40,7 @@ mod clip;
mod distort;
mod group;
mod lighting;
mod lighting_presets;
mod live;
mod marks;
pub mod pixel;
@@ -72,6 +73,7 @@ pub use group::{
OutlineHook, PATHFINDER_EFFECTS, evaluate_container, has_container_appearance, has_pathfinder, is_pathfinder, member_shapes, paints,
pathfinder_children,
};
pub use lighting_presets::{LIGHTING_PRESETS, LightingPreset, lighting_preset_params, matching_lighting_preset};
pub use live::{expand_live, expand_live_deep, expanded_live_group, text_outliner};
pub use marks::{CROP_MARKS, crop_marks_art, has_crop_marks};
pub use pixel::{PIXEL_EFFECTS, PixelFx, PixelSpace};
+112
View File
@@ -0,0 +1,112 @@
//! Original lighting rigs for the publicly documented 3D preset names.
//! Adobe publishes the names, not numeric settings. These values are our own; Standard
//! retains the existing default. A preset replaces the light rig and ambient only.
use serde_json::{Value, json};
use vectorcraft_three_d::{DEFAULT_LIGHT, Light};
#[derive(Clone, Copy, Debug)]
pub struct LightingPreset {
pub id: &'static str,
pub label: &'static str,
rotation: f64,
height: f64,
softness: f64,
ambient: f64,
}
pub const LIGHTING_PRESETS: [LightingPreset; 4] = [
LightingPreset { id: "standard", label: "Standard", rotation: DEFAULT_LIGHT.0, height: DEFAULT_LIGHT.1, softness: 0.0, ambient: 25.0 },
LightingPreset { id: "diffuse", label: "Diffuse", rotation: DEFAULT_LIGHT.0, height: DEFAULT_LIGHT.1, softness: 100.0, ambient: 35.0 },
LightingPreset { id: "topLeft", label: "Top Left", rotation: 135.0, height: 45.0, softness: 0.0, ambient: 25.0 },
LightingPreset { id: "right", label: "Right", rotation: 0.0, height: 30.0, softness: 0.0, ambient: 25.0 },
];
impl LightingPreset {
/// The same patch is used by commands and the dialog's preview transaction. Null
/// `lights` replaces an explicit multi-light rig with this single white light.
pub fn params(self) -> Value {
json!({
"lights": null, "lightColor": "#ffffff", "lightIntensity": 80.0,
"lightRotation": self.rotation, "lightHeight": self.height,
"lightSoftness": self.softness, "lightBehind": false, "ambient": self.ambient,
})
}
fn matches(self, light: Light, ambient: f64) -> bool {
let close = |a: f64, b: f64| (a - b).abs() < 1e-6;
!light.behind
&& light.color.iter().all(|c| close(*c, 1.0))
&& close(light.intensity, 0.8)
&& close(light.rotation, self.rotation)
&& close(light.height, self.height)
&& close(light.softness, self.softness / 100.0)
&& close(ambient, self.ambient / 100.0)
}
}
pub fn lighting_preset_params(id: &str) -> Option<Value> {
LIGHTING_PRESETS.iter().find(|p| p.id == id).map(|p| p.params())
}
/// Identify the effective rig, including an equivalent explicit single light. Names
/// are UI state derived from settings, so edits and older files cannot leave stale names.
pub fn matching_lighting_preset(params: &Value) -> Option<usize> {
let upgraded = crate::upgrade_params(crate::REVOLVE, params);
let o = crate::revolve_options(&upgraded);
let light = if let Some(lights) = &o.lights {
let mut entries = lights.iter();
let light = *entries.next()?;
if entries.next().is_some() {
return None;
}
light
} else {
Light {
rotation: o.light_rotation,
height: o.light_height,
behind: o.light_behind,
color: o.light_color,
intensity: o.light_intensity,
softness: o.light_softness,
}
};
LIGHTING_PRESETS.iter().position(|preset| preset.matches(light, o.ambient))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lighting_presets_are_valid_unique_and_standard_preserves_the_default() {
for (i, preset) in LIGHTING_PRESETS.iter().enumerate() {
let patch = preset.params();
crate::lighting::validate(&patch).unwrap();
assert_eq!(lighting_preset_params(preset.id), Some(patch.clone()));
assert_eq!(matching_lighting_preset(&patch), Some(i));
assert!(!patch.as_object().unwrap().contains_key("shade"));
assert!(!patch.as_object().unwrap().contains_key("rotationX"));
}
assert_eq!(matching_lighting_preset(&json!({})), Some(0));
assert!(lighting_preset_params("unknown").is_none());
assert_eq!(crate::revolve_options(&LIGHTING_PRESETS[0].params()).light_direction(), crate::revolve_options(&json!({})).light_direction());
}
#[test]
fn lighting_preset_matching_uses_effective_lights_and_detects_custom_edits() {
let mut p = LIGHTING_PRESETS[3].params();
p["lightColor"] = json!("#ff0000");
p["lights"] = json!([{"rotation":0,"height":30}]);
assert_eq!(matching_lighting_preset(&p), Some(3), "explicit light overrides stale legacy color");
p["lights"][0]["color"] = json!("#4080ff");
assert_eq!(matching_lighting_preset(&p), None);
p["lights"] = json!([{}, {}]);
assert_eq!(matching_lighting_preset(&p), None);
for (key, value) in [("ambient", json!(26)), ("lightIntensity", json!(79)), ("lightBehind", json!(true))] {
let mut p = LIGHTING_PRESETS[0].params();
p[key] = value;
assert_eq!(matching_lighting_preset(&p), None, "{key}");
}
}
}
+37
View File
@@ -55,6 +55,15 @@ pub fn specs() -> Vec<CommandSpec> {
has_doc,
set_params
),
cmd!(
"effect.lightingPreset",
"3D Lighting Preset",
[],
None,
"{index: int, preset: standard|diffuse|topLeft|right, item?: appearance item index|null (omitted: the active item, else the object's), ids?|id?: [..], target?: object|contents} replace an applied 3D effect's light rig and ambient with an original lighting preset; preserve shading, geometry and view; all targets must have a 3D effect at index; one undo step → {ids}",
has_doc,
lighting_preset
),
cmd!(
"effect.expandAppearance",
"Expand Appearance",
@@ -300,6 +309,30 @@ pub(crate) fn set_params_effects(s: &Session, p: &Value) -> Vec<String> {
ids.iter().filter_map(effect).collect()
}
fn lighting_preset(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "effect.lightingPreset";
let patch = str_param(p, "preset")
.and_then(effects::lighting_preset_params)
.ok_or_else(|| bad(C, "preset must be standard, diffuse, topLeft or right"))?;
let index = index_param(p, "index", C)?;
let item = item_target(s, p, C)?;
// Validate the whole selection before changing any object. An unrelated effect or
// a missing index must not leave just some objects with a new lighting rig.
for id in appearance_targets(s, p)? {
let n = s.doc()?.doc.node(id).ok_or(EngineError::NoNode(id))?;
let slot = item.effects_item(&n.appearance, C)?;
let effect = n.appearance.effects_at(slot).and_then(|fx| fx.get(index));
if !effect.is_some_and(|e| effects::is_three_d(&e.id)) {
return Err(bad(C, format!("object {} needs a 3D effect at index {index}", id.0)));
}
}
let mut options = p.clone();
let map = options.as_object_mut().ok_or_else(|| bad(C, "parameters must be an object"))?;
map.remove("preset");
map.insert("params".into(), patch);
set_params(s, &options)
}
fn set_params(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "effect.setParams";
let index = index_param(p, "index", C)?;
@@ -673,6 +706,10 @@ fn expand_appearance(s: &mut Session, p: &Value) -> Result<Value> {
Ok(out)
}
#[cfg(test)]
#[path = "tests_lighting_presets.rs"]
mod lighting_presets_tests;
#[cfg(test)]
mod tests {
use serde_json::json;
@@ -0,0 +1,93 @@
#![allow(clippy::unwrap_used, clippy::panic)]
use serde_json::{Value, json};
use vectorcraft_doc::NodeId;
use vectorcraft_render::effects::{LIGHTING_PRESETS, matching_lighting_preset};
use crate::Session;
fn setup(effect: &str, item: Value) -> (Session, NodeId) {
let mut s = Session::new();
s.execute("file.new", &json!({"width":400,"height":400})).unwrap();
let id = NodeId(s.execute("path.create", &json!({"d":"M100 100L200 100L200 200L100 200Z"})).unwrap()["id"].as_u64().unwrap());
s.execute("paint.setFill", &json!({"color":"#338ad6"})).unwrap();
s.execute(
"effect.apply",
&json!({"effect":effect,"item":item,"params":{
"rotationX":13,"rotationY":17,"rotationZ":9,"shade":false,"ambient":17,
"lights":[{"color":"#ff8040"},{"color":"#4080ff","rotation":-35}]
}}),
)
.unwrap();
(s, id)
}
#[test]
fn lighting_presets_preserve_source_and_view_and_round_trip_in_one_undo_step() {
for effect in ["threeD.revolve", "threeD.extrude", "threeD.inflate", "threeD.rotate"] {
for (i, preset) in LIGHTING_PRESETS.iter().enumerate() {
let (mut s, id) = setup(effect, Value::Null);
s.doc_mut().unwrap().mark_saved();
let before = s.doc().unwrap().doc.clone();
let history = s.doc().unwrap().history.undo.len();
let p = json!({"index":0,"item":null,"preset":preset.id});
s.check_params("effect.lightingPreset", &p).unwrap();
s.execute("effect.lightingPreset", &p).unwrap();
let after = s.doc().unwrap().doc.clone();
let actual = &after.node(id).unwrap().appearance.effects[0].params;
let mut expected = before.node(id).unwrap().appearance.effects[0].params.clone();
expected.as_object_mut().unwrap().extend(preset.params().as_object().unwrap().clone());
assert_eq!(*actual, expected);
assert_eq!(matching_lighting_preset(actual), Some(i));
assert_eq!(after.node(id).unwrap().path_data(), before.node(id).unwrap().path_data());
assert_eq!(actual["rotationX"], 13);
assert_eq!(actual["shade"], false);
assert_eq!(s.doc().unwrap().history.undo.len(), history + 1);
let reopened = vectorcraft_format::load(&vectorcraft_format::save(&after, false)).unwrap();
assert_eq!(reopened.node(id).unwrap().appearance.effects[0].params, *actual);
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().doc, before);
assert!(!s.doc().unwrap().is_dirty());
s.execute("edit.redo", &json!({})).unwrap();
assert_eq!(s.doc().unwrap().doc, after);
}
}
}
#[test]
fn lighting_presets_target_fill_effects_and_reject_invalid_requests_atomically() {
let (mut s, id) = setup("threeD.rotate", json!(0));
for p in [
json!({"index":0,"item":0,"preset":"unknown"}),
json!({"index":0,"item":0,"preset":12}),
json!({"index":1,"item":0,"preset":"right"}),
json!({"index":0,"item":null,"preset":"right"}),
json!({"index":0,"item":0,"preset":"right","params":{"ambient":200}}),
] {
s.doc_mut().unwrap().mark_saved();
let before = s.doc().unwrap().doc.clone();
let history = s.doc().unwrap().history.undo.len();
assert!(s.execute("effect.lightingPreset", &p).is_err(), "{p}");
assert_eq!(s.doc().unwrap().doc, before);
assert_eq!(s.doc().unwrap().history.undo.len(), history);
assert!(!s.doc().unwrap().is_dirty());
}
s.execute("effect.lightingPreset", &json!({"ids":[id.0],"index":0,"item":0,"preset":"topLeft"})).unwrap();
let n = s.doc().unwrap().doc.node(id).unwrap();
assert!(n.appearance.effects.is_empty());
assert_eq!(n.appearance.effects_at(Some(0)).unwrap()[0].params["lightRotation"], 135.0);
}
#[test]
fn lighting_presets_reject_a_mixed_selection_before_changing_any_effect() {
let (mut s, first) = setup("threeD.rotate", Value::Null);
let second = s.execute("shape.rectangle", &json!({"x":250,"y":100,"width":80,"height":80})).unwrap()["id"].clone();
s.execute("effect.apply", &json!({"effect":"path.offsetPath","item":null})).unwrap();
s.execute("select.set", &json!({"ids":[first.0,second]})).unwrap();
s.doc_mut().unwrap().mark_saved();
let before = s.doc().unwrap().doc.clone();
let history = s.doc().unwrap().history.undo.len();
assert!(s.execute("effect.lightingPreset", &json!({"index":0,"item":null,"preset":"right"})).is_err());
assert_eq!(s.doc().unwrap().doc, before);
assert_eq!(s.doc().unwrap().history.undo.len(), history);
assert!(!s.doc().unwrap().is_dirty());
}
+21
View File
@@ -293,6 +293,7 @@ fn lighting_controls(ui: &mut egui::Ui, d: &mut Dialog) {
}
});
ui.add_enabled_ui(d.bool("shade"), |ui| {
lighting_presets(ui, d);
light_list(ui, d);
let mut selected = selected_light(d);
let before = light_record(&selected);
@@ -310,6 +311,26 @@ fn lighting_controls(ui: &mut egui::Ui, d: &mut Dialog) {
});
}
fn lighting_presets(ui: &mut egui::Ui, d: &mut Dialog) {
let names = [tl!("Standard"), tl!("Diffuse"), tl!("Top Left"), tl!("Right")];
let current = vectorcraft_effects::matching_lighting_preset(&super::form::params(d));
ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 6.0;
label(ui, tl!("Presets"));
let shown = current.and_then(|i| names.get(i).copied()).unwrap_or(tl!("Custom"));
if let Some(preset) = widgets::dropdown(ui, "three-d-lighting-presets", shown, &names, VALUE_W + rail_width(ui) + 6.0)
.and_then(|i| vectorcraft_effects::LIGHTING_PRESETS.get(i))
&& let Some(patch) = preset.params().as_object()
{
d.fields.extend(patch.clone());
d.fields.insert("__light".into(), json!(0));
ui.memory_mut(|m| m.stop_text_input());
super::revolve_gizmo::clear(ui.ctx());
}
});
ui.add_space(4.0);
}
const LIGHT_KEYS: [(&str, &str); 6] = [
("color", "lightColor"),
("intensity", "lightIntensity"),
@@ -438,3 +438,63 @@ fn light_list_stops_at_ten_and_cancel_restores_the_source() {
dialogs::cancel(&mut h.app);
assert_eq!(h.app.session.doc().unwrap().doc, before);
}
#[test]
fn lighting_presets_preview_custom_edits_reset_and_commit_as_one_undo_step() {
for effect in [REVOLVE, EXTRUDE, INFLATE, vectorcraft_effects::ROTATE] {
let mut h = Harness::with("M100 100L200 100L200 200L100 200Z", effect, vec2(1280.0, 900.0));
let before = h.app.session.doc().unwrap().doc.clone();
h.settle();
h.click(h.rect("Lighting").center());
let view = h.app.ui.dialog.as_ref().unwrap().fields["rotationX"].clone();
h.click(h.rect("Standard").center());
h.click(h.rect("Right").center());
assert_eq!(h.value("lightRotation"), 0.0);
assert_eq!(h.app.ui.dialog.as_ref().unwrap().fields["rotationX"], view);
h.drag(h.rect("Height").center(), vec2(20.0, 0.0));
assert!(!h.rect("Custom").is_negative());
h.click(h.rect("Custom").center());
h.click(h.rect("Diffuse").center());
assert_eq!(h.value("lightSoftness"), 100.0);
assert_eq!(h.value("ambient"), 35.0);
h.click(h.rect("Add Light").center());
assert!(!h.rect("Custom").is_negative());
h.click(h.rect("Custom").center());
h.click(h.rect("Top Left").center());
let d = h.app.ui.dialog.as_ref().unwrap();
assert!(d.fields["lights"].is_null());
assert_eq!(d.fields["__light"], 0);
assert_eq!(d.f64("lightRotation", 0.0), 135.0);
h.click(h.response("revolve-reset-all").center());
assert!(!h.rect("Standard").is_negative());
h.click(h.rect("Standard").center());
h.click(h.rect("Right").center());
dialogs::confirm(&mut h.app).unwrap();
h.app.run("edit.undo", json!({})).unwrap();
assert_eq!(h.app.session.doc().unwrap().doc, before);
}
}
#[test]
fn lighting_presets_cancel_restores_an_existing_custom_light_rig() {
let mut h = Harness::new(vec2(1280.0, 900.0));
h.settle();
dialogs::confirm(&mut h.app).unwrap();
h.app
.run(
"effect.setParams",
json!({"index":0,"item":null,"params":{
"ambient":19,"lights":[{"color":"#ff8040"},{"color":"#4080ff"}]
}}),
)
.unwrap();
let before = h.app.session.doc().unwrap().doc.clone();
h.app.run("effect.dialog", json!({"effect":REVOLVE,"index":0,"item":null})).unwrap();
h.settle();
h.click(h.rect("Lighting").center());
h.click(h.rect("Custom").center());
h.click(h.rect("Right").center());
assert_ne!(h.app.session.doc().unwrap().doc, before);
dialogs::cancel(&mut h.app);
assert_eq!(h.app.session.doc().unwrap().doc, before);
}
+3
View File
@@ -5065,6 +5065,9 @@ theme Appearance Външен вид
Softness Мекота
Ambient Light Околна светлина
Light behind the object Светлина зад обекта
3D Lighting Preset Предварителна настройка за 3D осветление
Standard Стандартно
Diffuse Дифузно
# 3D light management.
Lights Светлини
Add Light Добавяне на светлина
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5165,6 +5165,9 @@ mark-style Japanese Japonské
Softness Měkkost
Ambient Light Okolní světlo
Light behind the object Světlo za objektem
3D Lighting Preset Předvolba 3D osvětlení
Standard Standardní
Diffuse Rozptýlené
# 3D light management.
Lights Světla
Add Light Přidat světlo
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4828,6 +4828,9 @@ mark-style Japanese Japansk
Softness Blødhed
Ambient Light Omgivelseslys
Light behind the object Lys bag objektet
3D Lighting Preset Forudindstilling for 3D-belysning
Standard Standard
Diffuse Diffust
# 3D light management.
Lights Lyskilder
Add Light Tilføj lyskilde
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4741,6 +4741,9 @@ effect Soft Weich
Softness Weichheit
Ambient Light Umgebungslicht
Light behind the object Licht hinter dem Objekt
3D Lighting Preset 3D-Beleuchtungsvorgabe
Standard Standard
Diffuse Diffus
# 3D light management.
Lights Lichter
Add Light Licht hinzufügen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4872,6 +4872,9 @@ theme Appearance Εμφάνιση
to set spacing and orientation για ορισμό απόστασης και προσανατολισμού
: :
Light behind the object Φως πίσω από το αντικείμενο
3D Lighting Preset Προεπιλογή φωτισμού 3D
Standard Τυπικός
Diffuse Διάχυτος
# 3D light management.
Lights Φώτα
Add Light Προσθήκη φωτός
Can't render this file because it contains an unexpected character in line 2031 and column 19.
+3
View File
@@ -5106,6 +5106,9 @@ mark-style Japanese Japonesas
Softness Suavidad
Ambient Light Luz ambiental
Light behind the object Luz detrás del objeto
3D Lighting Preset Ajuste preestablecido de iluminación 3D
Standard Estándar
Diffuse Difusa
# 3D light management.
Lights Luces
Add Light Añadir luz
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5054,6 +5054,9 @@ mark-style Japanese Japanilainen
Softness Pehmeys
Ambient Light Ympäristövalo
Light behind the object Valo objektin takana
3D Lighting Preset 3D-valaistuksen esiasetus
Standard Vakio
Diffuse Hajavalo
# 3D light management.
Lights Valot
Add Light Lisää valo
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5165,6 +5165,9 @@ theme Appearance Apparence
Softness Douceur
Ambient Light Lumière ambiante
Light behind the object Lumière derrière l'objet
3D Lighting Preset Préréglage d’éclairage 3D
Standard Standard
Diffuse Diffus
# 3D light management.
Lights Lumières
Add Light Ajouter une lumière
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5151,6 +5151,9 @@ imageTrace Outline Körvonal
Softness Lágyság
Ambient Light Környezeti fény
Light behind the object Fény az objektum mögött
3D Lighting Preset 3D világítási készlet
Standard Normál
Diffuse Szórt
# 3D light management.
Lights Fények
Add Light Fény hozzáadása
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4828,6 +4828,9 @@ mark-style Japanese Jepang
Softness Kelembutan
Ambient Light Cahaya ambient
Light behind the object Cahaya di belakang objek
3D Lighting Preset Preset Pencahayaan 3D
Standard Standar
Diffuse Difus
# 3D light management.
Lights Lampu
Add Light Tambah Lampu
Can't render this file because it contains an unexpected character in line 3801 and column 19.
+3
View File
@@ -4775,6 +4775,9 @@ lighting Light Luce
Softness Morbidezza
Ambient Light Luce ambientale
Light behind the object Luce dietro l'oggetto
3D Lighting Preset Predefinito illuminazione 3D
Standard Standard
Diffuse Diffusa
# 3D light management.
Lights Luci
Add Light Aggiungi luce
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4814,6 +4814,9 @@ lighting Light ライト
Softness 柔らかさ
Ambient Light 環境光
Light behind the object オブジェクトの背後のライト
3D Lighting Preset 3D ライティングプリセット
Standard 標準
Diffuse 拡散
# 3D light management.
Lights ライト一覧
Add Light ライトを追加
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4740,6 +4740,9 @@ imageTrace Outline კონტური
In the File ფაილში
Keys კლავიშები
Light behind the object სინათლე ობიექტის უკან
3D Lighting Preset 3D განათების წინასწარი პარამეტრი
Standard სტანდარტული
Diffuse დიფუზური
Line {line} სტრიქონი {line}
Line {line}: {name}: {status} სტრიქონი {line}: {name}: {status}
Needs Attention Only მხოლოდ საყურადღებო
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4738,6 +4738,9 @@ lighting Light 광원
Softness 부드러움
Ambient Light 주변광
Light behind the object 개체 뒤의 조명
3D Lighting Preset 3D 조명 사전 설정
Standard 표준
Diffuse 확산
# 3D light management.
Lights 광원 목록
Add Light 광원 추가
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4840,6 +4840,9 @@ mark-style Japanese Japans
Softness Zachtheid
Ambient Light Omgevingslicht
Light behind the object Licht achter het object
3D Lighting Preset Voorinstelling 3D-belichting
Standard Standaard
Diffuse Diffuus
# 3D light management.
Lights Lichtbronnen
Add Light Lichtbron toevoegen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5002,6 +5002,9 @@ mark-style Japanese Japońskie
Softness Miękkość
Ambient Light Światło otoczenia
Light behind the object Światło za obiektem
3D Lighting Preset Ustawienie oświetlenia 3D
Standard Standardowe
Diffuse Rozproszone
# 3D light management.
Lights Światła
Add Light Dodaj światło
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4082,6 +4082,9 @@ axis Both Ambos
Softness Suavidade
Ambient Light Luz ambiente
Light behind the object Luz atrás do objeto
3D Lighting Preset Predefinição de iluminação 3D
Standard Padrão
Diffuse Difusa
# 3D light management.
Lights Luzes
Add Light Adicionar luz
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3
View File
@@ -4771,6 +4771,9 @@ lighting Light Свет
Softness Мягкость
Ambient Light Окружающий свет
Light behind the object Свет позади объекта
3D Lighting Preset Предустановка 3D освещения
Standard Стандартное
Diffuse Рассеянное
# 3D light management.
Lights Источники света
Add Light Добавить свет
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -5070,6 +5070,9 @@ theme Appearance Utseende
Softness Mjukhet
Ambient Light Omgivande ljus
Light behind the object Ljus bakom objektet
3D Lighting Preset Förinställning för 3D-belysning
Standard Standard
Diffuse Diffust
# 3D light management.
Lights Ljuskällor
Add Light Lägg till ljus
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3
View File
@@ -4755,6 +4755,9 @@ lighting Light Світло
Softness М'якість
Ambient Light Навколишнє світло
Light behind the object Світло позаду об'єкта
3D Lighting Preset Попереднє налаштування 3D освітлення
Standard Стандартне
Diffuse Розсіяне
# 3D light management.
Lights Джерела світла
Add Light Додати світло
Can't render this file because it contains an unexpected character in line 726 and column 40.
+3
View File
@@ -4757,6 +4757,9 @@ theme Appearance Giao diện
Softness Độ mềm
Ambient Light Ánh sáng môi trường
Light behind the object Ánh sáng phía sau đối tượng
3D Lighting Preset Thiết lập sẵn ánh sáng 3D
Standard Tiêu chuẩn
Diffuse Khuếch tán
# 3D light management.
Lights Danh sách đèn
Add Light Thêm đèn
Can't render this file because it contains an unexpected character in line 4003 and column 10.
+3
View File
@@ -5030,6 +5030,9 @@ lighting Light 光源
Softness 柔和度
Ambient Light 环境光
Light behind the object 光源位于对象后方
3D Lighting Preset 3D 光照预设
Standard 标准
Diffuse 漫射
# 3D light management.
Lights 光源列表
Add Light 添加光源
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3
View File
@@ -4837,6 +4837,9 @@ lighting Light 光源
Softness 柔和度
Ambient Light 環境光
Light behind the object 光源位於物件後方
3D Lighting Preset 3D 光照預設
Standard 標準
Diffuse 漫射
# 3D light management.
Lights 光源清單
Add Light 新增光源
Can't render this file because it contains an unexpected character in line 6 and column 47.