DIRECTORY Ascii, Args, Buttons, Commander, Controls, Convert, CtBasic, CtFile, CtMap, Draw2d, FileNames, FS, G2dBasic, G3dBasic, G3dFunction, G3dIO, G3dNormalCoding, G3dTool, G3dVector, Icons, Imager, ImagerColor, ImagerFont, ImagerSample, IO, MessageWindow, Process, Random, Real, RealFns, Rope, TiogaAccess, VFonts, ViewerClasses, ViewerOps, ViewerSpecs; G3dLightingCmdsImpl: CEDAR PROGRAM IMPORTS Args, Buttons, Controls, Convert, CtBasic, CtFile, CtMap, FileNames, FS, G3dIO, G3dNormalCoding, G3dTool, G3dVector, ImagerColor, ImagerSample, Icons, IO, MessageWindow, Process, Random, Real, RealFns, Rope, TiogaAccess, VFonts, ViewerOps, ViewerSpecs ~ BEGIN CommandProc: TYPE ~ Commander.CommandProc; ButtonList: TYPE ~ Controls.ButtonList; Button: TYPE ~ Controls.Button; ClickProc: TYPE ~ Controls.ClickProc; Control: TYPE ~ Controls.Control; ControlList: TYPE ~ Controls.ControlList; DestroyProc: TYPE ~ Controls.DestroyProc; Mouse: TYPE ~ Controls.Mouse; MouseProc: TYPE ~ Controls.MouseProc; OuterData: TYPE ~ Controls.OuterData; Typescript: TYPE ~ Controls.Typescript; Viewer: TYPE ~ Controls.Viewer; SampleMap: TYPE ~ CtBasic.SampleMap; SampleMaps: TYPE ~ CtBasic.SampleMaps; Cmap: TYPE ~ CtMap.Cmap; SRGB: TYPE ~ CtMap.RGB; -- scaled RGB: Cardinals [0-255] (rgb) DrawProc: TYPE ~ G3dTool.DrawProc; Pair: TYPE ~ G2dBasic.Pair; Triple: TYPE ~ G3dBasic.Triple; URGB: TYPE ~ ImagerColor.RGB; -- unscaled RGB: Reals [0-1] (RGB) Font: TYPE ~ ImagerFont.Font; Box: TYPE ~ ImagerSample.Box; STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; Writer: TYPE ~ TiogaAccess.Writer; Color: TYPE ~ RECORD [rgb: URGB]; ColorArray: TYPE ~ ARRAY [0..255] OF Color; RealArray: TYPE ~ ARRAY [0..255] OF REAL; LightAccelerators: TYPE ~ RECORD [ cached, cachep: REF RealArray ¬ NIL, cacheD, cacheS: REF ColorArray ¬ NIL]; Light: TYPE ~ RECORD [ name: ROPE ¬ NIL, direction: Triple ¬ [0.0, 0.0, 1.0], spread: REAL ¬ 1.0, color: Color ¬ [rgb: [1.0, 1.0, 1.0]], accelerators: REF LightAccelerators ¬ NIL]; MaterialProps: TYPE ~ RECORD [ kd: REAL ¬ 1.0, ks: REAL ¬ 1.0, kr: REAL ¬ 1.0, ke: REAL ¬ 20.0, kt: REAL ¬ 1.0, km: REAL ¬ 1.0, kc: REAL ¬ 1.0]; MaterialAccelerators: TYPE ~ RECORD [cacheMw: Color]; Material: TYPE ~ RECORD [ name: ROPE ¬ NIL, materialProps: MaterialProps ¬ [], color: Color ¬ [rgb: [1.0, 1.0, 1.0]], accelerators: REF MaterialAccelerators ¬ NIL]; MaterialSequence: TYPE ~ REF MaterialSequenceRep; MaterialSequenceRep: TYPE ~ RECORD [ length: CARDINAL ¬ 0, element: SEQUENCE maxLength: CARDINAL OF Material]; LightSequence: TYPE ~ REF LightSequenceRep; LightSequenceRep: TYPE ~ RECORD [ length: CARDINAL ¬ 0, element: SEQUENCE maxLength: CARDINAL OF Light]; ToolState: TYPE ~ REF ToolStateRep; ToolStateRep: TYPE ~ RECORD [ redrawSphere: BOOLEAN ¬ FALSE, use24bits: BOOLEAN ¬ FALSE, radius: INTEGER ¬ 203, imageFileName: ROPE ¬ NIL, nextPressSpecial: BOOLEAN ¬ TRUE, pointDirection: BOOLEAN ¬ TRUE, pointHighlight: BOOLEAN ¬ FALSE, placeInvertZ: BOOLEAN ¬ FALSE, lightFocus: BOOLEAN ¬ TRUE, rs: Random.RandomStream, maps: SampleMaps ¬ NIL]; LightingTool: TYPE ~ REF LightingToolRep; LightingToolRep: TYPE ~ RECORD [ lights: LightSequence, activeLight: INT ¬ 0, materials: MaterialSequence, activeMaterial: INT ¬ 0, thetaControl: Control ¬ NIL, psiControl: Control ¬ NIL, spreadControl: Control ¬ NIL, falloffControl: Control ¬ NIL, lightColorSlider1: Control ¬ NIL, lightColorSlider2: Control ¬ NIL, lightColorSlider3: Control ¬ NIL, lightSpectrum: Control ¬ NIL, kdControl: Control ¬ NIL, ksControl: Control ¬ NIL, krControl: Control ¬ NIL, keControl: Control ¬ NIL, ktControl: Control ¬ NIL, kmControl: Control ¬ NIL, kcControl: Control ¬ NIL, materialColorSlider1: Control ¬ NIL, materialColorSlider2: Control ¬ NIL, materialColorSlider3: Control ¬ NIL, materialSpectrum: Control ¬ NIL, activeLightButton: Button, activeMaterialButton: Button, state: ToolState, outer: Viewer ¬ NIL, -- the parent viewer outerData: OuterData ¬ NIL, -- controls information ts: Typescript ¬ NIL, -- for user io lightColorPatch: Box, -- place for color sample materialColorPatch: Box, -- place for color sample myButtons: ButtonList ¬ NIL, -- my buttons graphics: Viewer ¬ NIL]; -- for drawing the sphere fgColor: INT = 0; bgColor: INT = 255; lightColor: INT = 1; materialColor: INT = 2; pi: REAL = 3.1415926535; twoPi: REAL = 6.283185; piOver2: REAL = 1.570796; piOver4: REAL = 0.7853982; program: ROPE ¬ "3dLighting Designer"; version: ROPE ¬ "v1.0"; NotANormal: ERROR = CODE; LightingCmd: CommandProc ~ { fileName: ROPE ¬ NIL; fileNameA: Args.Arg; [fileNameA] ¬ Args.ArgsGet[cmd, "%[s"]; IF fileNameA.ok THEN fileName ¬ fileNameA.rope; }; CreateMyTool: PROC [fileName: ROPE] ~ { lt: LightingTool ¬ NEW[LightingToolRep ¬ []]; lt.state ¬ NEW[ToolStateRep ¬ []]; lt.state.imageFileName ¬ fileName; MakeControls[lt]; lt.outer ¬ Controls.OuterViewer[ name: "3dLighting Designer", column: color, buttons: lt.myButtons, graphicsHeight: 300, -- height of color display's sphere mouseProc: LightingMouse, -- select a point on the sphere drawProc: LightingDraw, -- draw the sphere destroyProc: LightingDestroy, typescriptHeight: 36, clientData: lt, noOpen: TRUE].parent; InitializeMaterials[lt]; InitializeLights[lt]; lt.lightColorPatch ¬ [[90, 425], [180, 460]]; lt.materialColorPatch ¬ [[280, 425], [365, 460]]; lt.outer.icon ¬ lightingIcon; DrawInitialControls[lt]; lt.state.redrawSphere ¬ TRUE; lt.state.use24bits ¬ FALSE; lt.outerData ¬ NARROW[lt.outer.data]; lt.graphics ¬ lt.outerData.graphics; lt.ts ¬ lt.outerData.typescript; ViewerOps.OpenIcon[lt.outer]; lt.state.rs ¬ Random.Create[range: 500, seed: 1989]; UpdateActiveLight[lt]; UpdateActiveMaterial[lt]; UpdateLightColorSliders[lt]; UpdateMaterialColorSliders[lt]; BuildColormap[lt]; }; MakeControls: PROC [lt: LightingTool] ~ { myFont: Font ¬ VFonts.DefaultFont[]; lt.lightColorSlider1 ¬ Controls.NewControl[name: "H:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: LightColorSliders, x: 520, y: 375, w: 105, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.lightColorSlider2 ¬ Controls.NewControl[name: "S:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: LightColorSliders, x: 520, y: 360, w: 105, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.lightColorSlider3 ¬ Controls.NewControl[name: "L:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: LightColorSliders, x: 520, y: 345, w: 105, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.lightSpectrum ¬ Controls.NewControl[type: function, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: LightSpectrumFunction, x: 520, y: 305, w: 105, h: 35, textLocation: [left, down], font: myFont]; lt.psiControl ¬ Controls.NewControl[name: "Psi", type: vSlider, clientData: lt, min: -pi/2.0, max: pi/2.0, init: 0.0, proc: PsiControl, x: 540, y: 225, w: 30, h: 45, font: myFont]; lt.thetaControl ¬ Controls.NewControl[name: "Theta", type: dial, clientData: lt, min: pi, max: -pi, init: 0.0, proc: ThetaControl, x: 585, y: 225, w: 45, h: 60, font: myFont]; lt.spreadControl ¬ Controls.NewControl[name: "S:", type: hSlider, clientData: lt, min: 0., max: 80.0, init: 1.0, proc: SpreadControl, x: 475, y: 225, w: 50, h: 13, textLocation: [left, down, TRUE], font: myFont, taper: exp]; lt.materialColorSlider1 ¬ Controls.NewControl[name: "H:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialColorSliders, x: 520, y: 190, w: 105, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.materialColorSlider2 ¬ Controls.NewControl[name: "S:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialColorSliders, x: 520, y: 175, w: 105, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.materialColorSlider3 ¬ Controls.NewControl[name: "L:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialColorSliders, x: 520, y: 160, w: 105, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.materialSpectrum ¬ Controls.NewControl[ type: function, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialSpectrumFunction, x: 520, y: 120, w: 105, h: 35, textLocation: [left, down], font: myFont]; lt.kdControl ¬ Controls.NewControl[name: "kd:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialControls, x: 545, y: 100, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.ksControl ¬ Controls.NewControl[name: "ks:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialControls, x: 545, y: 85, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.keControl ¬ Controls.NewControl[name: "ke:", type: hSlider, clientData: lt, min: 0.0, max: 80.0, init: 20.0, proc: MaterialControls, x: 545, y: 70, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont, taper: exp]; lt.kmControl ¬ Controls.NewControl[name: "km:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialControls, x: 545, y: 55, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.krControl ¬ Controls.NewControl[name: "kr:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialControls, x: 545, y: 25, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.ktControl ¬ Controls.NewControl[name: "kt:", type: hSlider, clientData: lt, min: 0.0, max: 1.0, init: 1.0, proc: MaterialControls, x: 545, y: 10, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont]; lt.myButtons ¬ CONS[Controls.ClickButton["Debug", DebugOps, lt], lt.myButtons]; lt.myButtons ¬ CONS[Controls.ClickButton["Sphere", SphereOps, lt], lt.myButtons]; lt.myButtons ¬ CONS[Controls.ClickButton["IO", IOOps, lt], lt.myButtons]; lt.myButtons ¬ CONS[Controls.ClickButton["Material Ops", MaterialOps, lt], lt.myButtons]; lt.myButtons ¬ CONS[Controls.ClickButton["Lighting Ops", LightingOps, lt], lt.myButtons]; lt.myButtons ¬ CONS[Controls.ClickButton["Place Light", PlacingOps, lt], lt.myButtons]; lt.activeLightButton ¬ Controls.ClickButton[name: "Default", proc: SelectLightOps, clientData: lt, x: 435, y: 255]; lt.activeMaterialButton ¬ Controls.ClickButton[name: "Default", proc: SelectMaterialOps, clientData: lt, x: 435, y: 75]; }; DrawInitialControls: PROC [lt: LightingTool] ~ { DrawLightingControls[lt]; DrawMaterialControls[lt]; }; DrawLightingControls: PROC [lt: LightingTool] ~ { DrawControl[lt, lt.lightColorSlider1]; DrawControl[lt, lt.lightColorSlider2]; DrawControl[lt, lt.lightColorSlider3]; DrawControl[lt, lt.lightSpectrum]; DrawControl[lt, lt.psiControl]; DrawControl[lt, lt.thetaControl]; DrawControl[lt, lt.spreadControl]; ViewerOps.PaintViewer[lt.outer, client]; }; EraseLightingControls: PROC [lt: LightingTool] ~ { EraseControl[lt, lt.lightColorSlider1]; EraseControl[lt, lt.lightColorSlider2]; EraseControl[lt, lt.lightColorSlider3]; EraseControl[lt, lt.lightSpectrum]; EraseControl[lt, lt.psiControl]; EraseControl[lt, lt.thetaControl]; EraseControl[lt, lt.spreadControl]; ViewerOps.PaintViewer[lt.outer, client]; }; DrawMaterialControls: PROC [lt: LightingTool] ~ { DrawControl[lt, lt.materialColorSlider1]; DrawControl[lt, lt.materialColorSlider2]; DrawControl[lt, lt.materialColorSlider3]; DrawControl[lt, lt.materialSpectrum]; DrawControl[lt, lt.kdControl]; DrawControl[lt, lt.ksControl]; DrawControl[lt, lt.krControl]; DrawControl[lt, lt.keControl]; DrawControl[lt, lt.ktControl]; DrawControl[lt, lt.kmControl]; ViewerOps.PaintViewer[lt.outer, client]; }; EraseMaterialControls: PROC [lt: LightingTool] ~ { EraseControl[lt, lt.materialColorSlider1]; EraseControl[lt, lt.materialColorSlider2]; EraseControl[lt, lt.materialColorSlider3]; EraseControl[lt, lt.materialSpectrum]; EraseControl[lt, lt.kdControl]; EraseControl[lt, lt.ksControl]; EraseControl[lt, lt.krControl]; EraseControl[lt, lt.keControl]; EraseControl[lt, lt.ktControl]; EraseControl[lt, lt.kmControl]; ViewerOps.PaintViewer[lt.outer, client]; }; DrawControl: PROC [lt: LightingTool, control: Control] ~ { Controls.ControlViewer[lt.outer, control, NIL, lt.outerData]; }; EraseControl: PROC [lt: LightingTool, control: Control] ~ { ViewerOps.DestroyViewer[control.viewer, FALSE]; ViewerOps.DestroyViewer[control.title, FALSE]; ViewerOps.DestroyViewer[control.status, FALSE]; }; Redraw: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; ViewerOps.PaintViewer[lt.graphics, client, FALSE, $Redraw]; }; LightingOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["Light Ops"], LIST[ -- 1 -- ["Add Light", "Add a new light source"], -- 2 -- ["Delete Light", "Delete light source"], -- 3 -- ["Activate Light", "Make a light source active"] ]]; SELECT choice FROM 1 => AddLight[lt]; 2 => DeleteLight[lt]; 3 => ActivateLight[lt]; ENDCASE; SELECT choice FROM 1 => LightChanged[lt]; 2 => LightChanged[lt]; ENDCASE; }; MaterialOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["Material Ops"], LIST[ -- 1 -- ["Add Material", "Add a new material"], -- 2 -- ["Delete Material", "Delete material"], -- 3 -- ["Activate Material", "Make a material active"]]]; SELECT choice FROM 1 => AddMaterial[lt]; 2 => DeleteMaterial[lt]; 3 => ActivateMaterial[lt]; ENDCASE; SELECT choice FROM 1 => MaterialChanged[lt]; 2 => MaterialChanged[lt]; 3 => MaterialChanged[lt]; ENDCASE; }; PlacingOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["Place Active Light"], LIST[ -- 1 -- ["Place Direction", "Point to light direction"], -- 2 -- ["Place Highlight", "Point to highlight direction"], IF lt.state.placeInvertZ THEN -- 3 -- ["Point Away", "Point light away from you"] ELSE -- 3 -- ["Point Towards", "Point light towards you"] ]]; SELECT choice FROM 1 => DirectionPoint[lt]; 2 => HighlightPoint[lt]; 3 => lt.state.placeInvertZ ¬ NOT lt.state.placeInvertZ; ENDCASE; SELECT choice FROM 1 => LightChanged[lt]; 2 => LightChanged[lt]; 3 => LightChanged[lt]; ENDCASE; }; SphereOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["HiRes Sphere"], LIST[ -- 1 -- ["Enter radius", "Type in desired radius"], -- 2 -- ["Full size", "Full size"], -- 3 -- ["150", "radius 150 pixels"], -- 4 -- ["100", "radius 100 pixels"], -- 5 -- ["50", "radius 50 pixels"], -- 6 -- ["25", "radius 25 pixels"] ]]; IF choice > 0 THEN { lt.state.redrawSphere ¬ lt.state.use24bits ¬ TRUE; SELECT choice FROM 1 => { s: IO.STREAM ¬ IO.RIS[Controls.TypescriptRead[lt.ts,"enter radius: "]]; lt.state.radius ¬ Convert.IntFromRope[IO.GetTokenRope[s ! IO.EndOfStream => GOTO Eof].token]; IF lt.state.radius <= 1 THEN lt.state.radius ¬ 2; IF lt.state.radius > 200 THEN lt.state.radius ¬ 200; }; 2 => lt.state.radius ¬ 203; 3 => lt.state.radius ¬ 150; 4 => lt.state.radius ¬ 100; 5 => lt.state.radius ¬ 50; 6 => lt.state.radius ¬ 25; ENDCASE; CtMap.Mono[]; ViewerOps.PaintViewer[lt.outer, client]; lt.state.use24bits ¬ FALSE; lt.state.redrawSphere ¬ TRUE; }; EXITS Eof => NULL; }; IOOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["Input/Output"], LIST[ -- 1 -- ["Write Lights", "Save light descriptions"], -- 2 -- ["Read Lights", "Replace lights with file"], -- 3 -- ["Merge Lights", "Add file lights to current"], -- 4 -- ["Write Material", "Save material descriptions"], -- 5 -- ["Read Material", "Replace materials with file"], -- 6 -- ["Merge Material", "Add file materials to current"], -- 7 -- ["Write Lights and Materials", "Save lights and materials"], -- 8 -- ["Read Lights and Materials", "Replace lights and materials"], -- 9 -- ["Merge Lights and Materials", "Add lights and materials"], -- 10 -- ["Read Image File", "Read a new image file"] ]]; SELECT choice FROM 1 => WriteLights[lt]; 2 => ReadLights[lt, FALSE]; 3 => ReadLights[lt, TRUE]; 4 => WriteMaterials[lt]; 5 => ReadMaterials[lt, FALSE]; 6 => ReadMaterials[lt, TRUE]; 7 => WriteLightsAndMaterials[lt]; 8 => ReadLightsAndMaterials[lt, FALSE]; 9 => ReadLightsAndMaterials[lt, TRUE]; 10 => UsersFile[lt]; ENDCASE; SELECT choice FROM 2 => LightChanged[lt]; 3 => LightChanged[lt]; 5 => LightChanged[lt]; 6 => LightChanged[lt]; 8 => LightChanged[lt]; 9 => LightChanged[lt]; 10 => LightChanged[lt]; ENDCASE; }; SelectLightOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ ChooseLight[lt.lights, "Lights", FALSE]; IF choice > 0 THEN { Buttons.ReLabel[parent, lt.lights[choice-1].name]; }; }; SelectMaterialOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ ChooseMaterial[lt.materials, "Materials"]; IF choice > 0 THEN { Buttons.ReLabel[parent, lt.materials[choice-1].name]; }; }; DebugOps: ClickProc ~ { lt: LightingTool ¬ NARROW[clientData]; choice: INT ¬ Controls.PopUpRequest[["Debug options"], LIST[ -- 1 -- ["Redraw sphere", "Redraw sphere"], -- 2 -- ["Redraw sphere", "Redraw 24bit sphere"] ]]; SELECT choice FROM 1 => lt.state.redrawSphere ¬ NOT TRUE; 2 => lt.state.redrawSphere ¬ lt.state.use24bits ¬ TRUE; ENDCASE; SELECT choice FROM 1 => LightChanged[lt]; 2 => { ViewerOps.PaintViewer[lt.outer, client]; CtMap.Mono[]; }; ENDCASE; }; LightSpectrumFunction: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; }; MaterialSpectrumFunction: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; }; PsiControl: Controls.ControlProc ~ { theta, psi: REAL; lt: LightingTool ¬ NARROW[control.clientData]; IF GotAnActiveLight[lt] THEN { [theta, psi] ¬ GetActiveAngles[lt]; psi ¬ NARROW[control.value]; SetActiveAngles[lt, theta, psi]; LightChanged[lt]; }; }; ThetaControl: Controls.ControlProc ~ { theta, psi: REAL; lt: LightingTool ¬ NARROW[control.clientData]; IF GotAnActiveLight[lt] THEN { [theta, psi] ¬ GetActiveAngles[lt]; theta ¬ NARROW[control.value]; SetActiveAngles[lt, theta, psi]; LightChanged[lt]; }; }; SpreadControl: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; IF GotAnActiveLight[lt] THEN { lt.lights[lt.activeLight].spread ¬ control.value; LightChanged[lt]; }; }; SurfaceControls: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; }; LightColorSliders: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; lt.lights[lt.activeLight].color.rgb ¬ GetLightSlidersRGB[lt]; LightChanged[lt]; }; MaterialColorSliders: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; lt.materials[lt.activeMaterial].color.rgb ¬ GetMaterialSlidersRGB[lt]; MaterialChanged[lt]; }; MaterialControls: Controls.ControlProc ~ { lt: LightingTool ¬ NARROW[control.clientData]; mp: MaterialProps ¬ lt.materials[lt.activeMaterial].materialProps; IF Rope.Equal[control.name, lt.kdControl.name] THEN mp.kd ¬ control.value; IF Rope.Equal[control.name, lt.ksControl.name] THEN mp.ks ¬ control.value; IF Rope.Equal[control.name, lt.krControl.name] THEN mp.kr ¬ control.value; IF Rope.Equal[control.name, lt.keControl.name] THEN mp.ke ¬ control.value; IF Rope.Equal[control.name, lt.ktControl.name] THEN mp.kt ¬ control.value; IF Rope.Equal[control.name, lt.kmControl.name] THEN mp.km ¬ control.value; lt.materials[lt.activeMaterial].materialProps ¬ mp; MaterialChanged[lt]; }; UpdateLightAngles: PROC [lt: LightingTool] ~ { IF GotAnActiveLight[lt] THEN { theta, psi: REAL; [theta, psi] ¬ GetActiveAngles[lt]; Controls.SetSliderDialValue[lt.thetaControl, theta]; Controls.SetSliderDialValue[lt.psiControl, psi]; }; }; UpdateLightColorSliders: PROC [lt: LightingTool] ~ { }; UpdateMaterialColorSliders: PROC [lt: LightingTool] ~ { }; UpdateMaterialSliders: PROC [lt: LightingTool] ~ { mp: MaterialProps ¬ lt.materials[lt.activeMaterial].materialProps; Controls.SetSliderDialValue[lt.kdControl, mp.kd, TRUE]; Controls.SetSliderDialValue[lt.ksControl, mp.ks, TRUE]; Controls.SetSliderDialValue[lt.krControl, mp.kr, TRUE]; Controls.SetSliderDialValue[lt.keControl, mp.ke, TRUE]; Controls.SetSliderDialValue[lt.ktControl, mp.kt, TRUE]; Controls.SetSliderDialValue[lt.kmControl, mp.km, TRUE]; }; LightingDraw: Draw2d.DrawProc ~ { lt: LightingTool ~ NARROW[clientData]; DrawSphere: PROC ~ { outer: Viewer ¬ lt.outer; graphics: Viewer ¬ lt.graphics; ditherMode: BOOLEAN ¬ TRUE; SELECT whatChanged FROM NIL, $Redraw => { Inner: PROC [rgMap, bMap: SampleMap] ~ { box: ImagerSample.Box ¬ ImagerSample.GetBox[rgMap]; size: ImagerSample.Vec ¬ ImagerSample.GetSize[rgMap]; rgScanLine: ImagerSample.SampleBuffer¬ImagerSample.ObtainScratchSamples[size.f]; bScanLine: ImagerSample.SampleBuffer¬ImagerSample.ObtainScratchSamples[size.f]; diameter: INTEGER ¬ MIN[box.max.s-box.min.s, box.max.f-box.min.f]; radius: INTEGER ¬ Real.Floor[diameter/2.0]; iradius: REAL ¬ 1.0/radius; boxLeft: INTEGER ¬ box.min.f + 10; boxTop: INTEGER ¬ box.min.s + Real.Floor[((box.max.s-box.min.s)-diameter)/2.0]; center: Pair ¬ [boxLeft+radius, boxTop+radius]; FOR y: INTEGER IN [boxTop..boxTop+diameter) DO Process.CheckForAbort[]; ImagerSample.GetSamples[rgMap, [y, box.min.f],, rgScanLine, 0, size.f]; ImagerSample.GetSamples[bMap, [y, box.min.f],, bScanLine, 0, size.f]; FOR x: NAT IN [boxLeft..boxLeft+diameter) DO dx, dy: REAL; pval: INT; IF x=275 AND y=88 THEN { dx ¬ 5.0; }; dy ¬ (y-center.y) * iradius; dx ¬ (x-center.x) * iradius; pval ¬ G3dNormalCoding.EncodeNormal[dx, dy, ditherMode]; pval ¬ MAX[0, pval]; rgScanLine[x] ¬ pval+256*pval; bScanLine[x] ¬ pval; ENDLOOP; ImagerSample.PutSamples[rgMap, [y, box.min.f],, rgScanLine, 0, size.f]; ImagerSample.PutSamples[bMap, [y, box.min.f],, bScanLine, 0, size.f]; ENDLOOP; ImagerSample.ReleaseScratchSamples[rgScanLine]; ImagerSample.ReleaseScratchSamples[bScanLine]; }; maps: SampleMaps; -- ¬ CtBasic.GetColorDisplayMaps[]; x: NAT ¬ lt.outer.cx+lt.graphics.cx; y: NAT ¬ (ViewerSpecs.colorScreenHeight-1)-(lt.outer.wy+lt.graphics.cy+lt.graphics.ch); w: NAT ¬ lt.graphics.cw; h: NAT ¬ lt.graphics.ch; CtBasic.ReIndexMaps[maps: maps, box: [[x, y], [x+w, y+h]]]; Inner[maps[0].map, maps[1].map]; }; ENDCASE; }; Draw24bitSphere: PROC ~ { outer: Viewer ¬ lt.outer; graphics: Viewer ¬ lt.graphics; ditherMode: BOOLEAN ¬ TRUE; SELECT whatChanged FROM NIL, $Redraw => { Inner: PROC [rgMap, bMap: SampleMap] ~ { box: ImagerSample.Box ¬ ImagerSample.GetBox[rgMap]; size: ImagerSample.Vec ¬ ImagerSample.GetSize[rgMap]; rgScanLine: ImagerSample.SampleBuffer¬ImagerSample.ObtainScratchSamples[size.f]; bScanLine: ImagerSample.SampleBuffer¬ImagerSample.ObtainScratchSamples[size.f]; center: Pair ¬ [214.0, 276.0]; diameter: INTEGER ¬ 2 * lt.state.radius; boxTop: INTEGER ¬ Real.Round[center.y - lt.state.radius]; boxLeft: INTEGER ¬ Real.Round[center.x - lt.state.radius]; iradius: REAL ¬ 1.0/lt.state.radius; FOR y: INTEGER IN [boxTop..boxTop+diameter) DO Process.CheckForAbort[]; ImagerSample.GetSamples[rgMap, [y, box.min.f],, rgScanLine, 0, size.f]; ImagerSample.GetSamples[bMap, [y, box.min.f],, bScanLine, 0, size.f]; FOR x: NAT IN [boxLeft..boxLeft+diameter) DO dy: REAL ¬ (y-center.y) * iradius; dx: REAL ¬ (x-center.x) * iradius; dz: REAL ¬ 1.0 - ((dx*dx)+(dy*dy)); IF dz >= 0.0 THEN { srgb: SRGB; dz ¬ -RealFns.SqRt[dz]; srgb ¬ ShadeNormal[lt, [dx, dy, dz]]; rgScanLine[x] ¬ (srgb.r * 256) + srgb.g; bScanLine[x] ¬ srgb.b; } ELSE { rgScanLine[x] ¬ bScanLine[x] ¬ 0; }; ENDLOOP; ImagerSample.PutSamples[rgMap, [y, box.min.f],, rgScanLine, 0, size.f]; ImagerSample.PutSamples[bMap, [y, box.min.f],, bScanLine, 0, size.f]; ENDLOOP; ImagerSample.ReleaseScratchSamples[rgScanLine]; ImagerSample.ReleaseScratchSamples[bScanLine]; }; maps: SampleMaps; -- ¬ CtBasic.GetColorDisplayMaps[]; x: NAT ¬ lt.outer.cx+lt.graphics.cx; y: NAT ¬ (ViewerSpecs.colorScreenHeight-1)-(lt.outer.wy+lt.graphics.cy+lt.graphics.ch); w: NAT ¬ lt.graphics.cw; h: NAT ¬ lt.graphics.ch; CtBasic.ReIndexMaps[maps: maps, box: [[x, y], [x+w, y+h]]]; Inner[maps[0].map, maps[1].map]; }; ENDCASE; }; BuildColorPatch: PROC ~ { Inner: PROC [b: Box, c: INT, srgb: SRGB, showFlag: BOOLEAN] ~ { IF showFlag THEN { CtBasic.PutRGBBox[maps, b.min.f, b.min.s, b.max.f, b.max.s, [0, 0, 0]]; IF lt.state.use24bits THEN CtBasic.PutRGBBox[maps, b.min.f+1, b.min.s+1, b.max.f-1, b.max.s-1, srgb] ELSE CtBasic.PutRGBBox[maps, b.min.f+1,b.min.s+1,b.max.f-1,b.max.s-1, [c,c,c]]; } ELSE CtBasic.PutRGBBox[maps,b.min.f,b.min.s,b.max.f,b.max.s,[bgColor,bgColor,bgColor]]; }; maps: SampleMaps; -- ¬ CtBasic.GetColorDisplayMaps[]; Inner[ lt.lightColorPatch, lightColor, IF GotAnActiveLight[lt] THEN RGBuTos[lt.lights[lt.activeLight].color.rgb] ELSE [255, 255, 255], lt.lights # NIL AND lt.lights.length > 0]; Inner[ lt.materialColorPatch, materialColor, IF GotAnActiveMaterial[lt] THEN RGBuTos[lt.materials[lt.activeMaterial].color.rgb] ELSE [255, 255, 255], lt.materials # NIL AND lt.materials.length > 0]; }; DrawFile: PROC ~ { IF lt.state.maps = NIL THEN ReadFile[lt, lt.state.imageFileName]; }; }; UsersFile: PROC [lt: LightingTool] ~ { Inner: PROC ~ { ReadFile[lt, fileName]; }; fileName: ROPE ¬ Controls.TypescriptReadFileName[lt.ts]; }; LightingMouse: Controls.MouseProc ~ { lt: LightingTool ~ NARROW[clientData]; IF mouse.state # down AND mouse.state # held THEN RETURN; IF lt.state.nextPressSpecial THEN { IF lt.state.pointDirection THEN NewLightDirection[lt, mouse]; IF lt.state.pointHighlight THEN NewLightHighlight[lt, mouse]; UpdateLightAngles[lt]; }; }; RGBuTos: PROC [urgb: URGB] RETURNS [srgb: SRGB] ~ { srgb.r ¬ Real.Floor[MIN[255.0, MAX[0.0, 255.0 * urgb.R]]]; srgb.g ¬ Real.Floor[MIN[255.0, MAX[0.0, 255.0 * urgb.G]]]; srgb.b ¬ Real.Floor[MIN[255.0, MAX[0.0, 255.0 * urgb.B]]]; }; RGBsTou: PROC [srgb: SRGB] RETURNS [u: URGB] ~ {u¬[srgb.r/255.0,srgb.g/255.0,srgb.b/255.0]}; LightingDestroy: Controls.DestroyProc ~ {CtMap.Mono[]}; DirectionPoint: PROC [lt: LightingTool] ~ { lt.state.nextPressSpecial ¬ TRUE; lt.state.pointDirection ¬ TRUE; lt.state.pointHighlight ¬ FALSE; }; HighlightPoint: PROC [lt: LightingTool] ~ { lt.state.nextPressSpecial ¬ TRUE; lt.state.pointHighlight ¬ TRUE; lt.state.pointDirection ¬ FALSE; }; NewLightDirection: PROC [lt: LightingTool, mouse: Mouse] ~ { norm: Triple ¬ GetNormalFromPixel[lt, mouse ! NotANormal => GOTO NoNormal]; IF lt.lights # NIL AND lt.lights.length > 0 THEN { norm ¬ G3dVector.Negate[norm]; IF lt.state.placeInvertZ THEN norm.z ¬ -norm.z; lt.lights[lt.activeLight].direction ¬ norm; LightChanged[lt]; }; EXITS NoNormal => NULL; }; NewLightHighlight: PROC [lt: LightingTool, mouse: Mouse] ~ { norm: Triple ¬ GetNormalFromPixel[lt, mouse ! NotANormal => GOTO NoNormal]; dir: Triple; IF lt.lights # NIL AND lt.lights.length > 0 THEN { norm ¬ G3dVector.Negate[norm]; IF lt.state.placeInvertZ THEN norm.z ¬ -norm.z; dir ¬ G3dVector.Sub[[0, 0, 1.0], G3dVector.Mul[norm, 2.0]]; lt.lights[lt.activeLight].direction ¬ G3dVector.Mul[G3dVector.Unit[dir], -1.0]; LightChanged[lt]; }; EXITS NoNormal => NULL; }; LightChanged: PROC [lt: LightingTool] ~ { BuildColormap[lt]; }; MaterialChanged: PROC [lt: LightingTool] ~ {BuildColormap[lt]}; BuildColormap: PROC [lt: LightingTool] ~ { cm: Cmap ¬ CtMap.NewCmap[]; IF lt.state.redrawSphere THEN ViewerOps.PaintViewer[lt.outer, client]; IF lt.lights # NIL AND lt.materials # NIL AND lt.lights.length > 0 AND lt.materials.length > 0 THEN { FOR entry: INT IN [0 .. 255] DO IF G3dNormalCoding.IsValidNormalIndex[entry] THEN CtMap.SetEntry[cm, entry, ShadeNormal[lt, G3dNormalCoding.DecodeNormal[entry]]] ELSE CtMap.SetEntry[cm, entry, [0, 0, 0]]; ENDLOOP; } ELSE { FOR entry: INT IN [0 .. 255] DO CtMap.SetEntry[cm, entry, [128, 128, 128]]; ENDLOOP; }; CtMap.SetEntry[cm, fgColor, [0, 0, 0]]; CtMap.SetEntry[cm, lightColor, RGBuTos[GetLightSlidersRGB[lt]]]; CtMap.SetEntry[cm, materialColor, RGBuTos[GetMaterialSlidersRGB[lt]]]; CtMap.SetEntry[cm, bgColor, [255, 255, 255]]; CtMap.WriteAndRelease[cm]; }; NewBuildColormap: PROC [lt: LightingTool] ~ { cm: Cmap ¬ CtMap.NewCmap[]; IF lt.state.redrawSphere THEN ViewerOps.PaintViewer[lt.outer, client]; IF lt.lights # NIL AND lt.materials # NIL AND lt.lights.length > 0 AND lt.materials.length > 0 THEN { FOR entry: INT IN [0 .. 255] DO CtMap.SetEntry[cm, entry, ShadeIndex[lt, entry]]; ENDLOOP; } ELSE { FOR entry: INT IN [0 .. 255] DO CtMap.SetEntry[cm, entry, [128, 128, 128]]; ENDLOOP; }; CtMap.SetEntry[cm, fgColor, [0, 0, 0]]; CtMap.SetEntry[cm, lightColor, RGBuTos[GetLightSlidersRGB[lt]]]; CtMap.SetEntry[cm, materialColor, RGBuTos[GetMaterialSlidersRGB[lt]]]; CtMap.SetEntry[cm, bgColor, [255, 255, 255]]; CtMap.WriteAndRelease[cm]; }; GetLightSlidersRGB: PROC [lt: LightingTool] RETURNS [rgb: URGB] ~ { rgb ¬ ImagerColor.RGBFromColor[ImagerColor.ColorFromHSV[lt.lightColorSlider1.value, lt.lightColorSlider2.value, lt.lightColorSlider3.value]]; }; GetMaterialSlidersRGB: PROC [lt: LightingTool] RETURNS [rgb: URGB] ~ { rgb ¬ ImagerColor.RGBFromColor[ImagerColor.ColorFromHSV[lt.materialColorSlider1.value, lt.materialColorSlider2.value, lt.materialColorSlider3.value]]; }; ShadeNormal: PROC [lt: LightingTool, norm: Triple] RETURNS [srgb: SRGB] ~ { diffuse, specular, total: URGB ¬ [0.0, 0.0, 0.0]; kd: REAL ¬ lt.materials[lt.activeMaterial].materialProps.kd; ks: REAL ¬ lt.materials[lt.activeMaterial].materialProps.ks; ke: REAL ¬ lt.materials[lt.activeMaterial].materialProps.ke; km: REAL ¬ lt.materials[lt.activeMaterial].materialProps.km; kc: REAL ¬ lt.materials[lt.activeMaterial].materialProps.kc; ref: Triple; white: URGB ¬ [1.0, 1.0, 1.0]; matclr: URGB ¬ lt.materials[lt.activeMaterial].color.rgb; refdot: REAL; IF lt.lights # NIL THEN { FOR n: NAT IN [0..lt.lights.length) DO l: Light ¬ lt.lights[n]; lightclr: URGB ¬ l.color.rgb; IF Rope.Equal[s1: l.name, s2: "Ambient", case: FALSE] THEN { diffuse.R ¬ diffuse.R + lightclr.R; diffuse.G ¬ diffuse.G + lightclr.G; diffuse.B ¬ diffuse.B + lightclr.B; } ELSE { direction: Triple ¬ [l.direction.x, l.direction.y, l.direction.z]; di: REAL ¬ G3dVector.Dot[norm, G3dVector.Negate[direction]]; IF di > 0.0 THEN { diffuse ¬ AddColors[diffuse, MulColor[lightclr, MyPower[di, l.spread]]]; ref ¬ G3dVector.Add[direction, G3dVector.Mul[norm, 2.0*di]]; refdot ¬ -ref.z; IF refdot > 0.0 THEN { specscl: REAL ¬ MyPower[refdot, l.spread * ke]; difclr: URGB ¬ SubColors[matclr, white]; modclr: URGB ¬ MulColor[difclr, km*MyPower[di, kc]]; comclr: URGB ¬ AddColors[modclr, white]; hiclr: URGB ¬ ScaleColor[comclr, lightclr]; specular ¬ AddColors[specular, MulColor[hiclr, specscl]]; }; }; }; ENDLOOP; }; diffuse ¬ ScaleColor[diffuse, matclr]; diffuse ¬ MulColor[diffuse, kd]; specular ¬ MulColor[specular, ks]; total ¬ AddColors[diffuse, specular]; srgb ¬ RGBuTos[total]; }; ScaleColor: PROC [clr, scl: URGB] RETURNS [new: URGB] ~ { new.R ¬ clr.R * scl.R; new.G ¬ clr.G * scl.G; new.B ¬ clr.B * scl.B; }; MulColor: PROC [clr: URGB, mul: REAL] RETURNS [new: URGB] ~ { new.R ¬ clr.R * mul; new.G ¬ clr.G * mul; new.B ¬ clr.B * mul; }; AddColors: PROC [a, b: URGB] RETURNS [new: URGB] ~ { new.R ¬ a.R + b.R; new.G ¬ a.G + b.G; new.B ¬ a.B + b.B; }; SubColors: PROC [a, b: URGB] RETURNS [new: URGB] ~ { new.R ¬ a.R - b.R; new.G ¬ a.G - b.G; new.B ¬ a.B - b.B; }; ShadeIndex: PROC [lt: LightingTool, index: INT] RETURNS [SRGB] ~ { kd: REAL ¬ lt.materials[lt.activeMaterial].materialProps.kd; ks: REAL ¬ lt.materials[lt.activeMaterial].materialProps.ks; matclr: URGB ¬ lt.materials[lt.activeMaterial].color.rgb; total, diffuse, specular: URGB ¬ [0, 0, 0]; IF NOT G3dNormalCoding.IsValidNormalIndex[index] THEN RETURN [[0,0,0]]; IF lt.lights = NIL THEN RETURN [[0,0,0]]; FOR n: NAT IN [0..lt.lights.length) DO l: Light ¬ lt.lights[n]; IF Rope.Equal[s1: l.name, s2: "Ambient", case: FALSE] THEN diffuse ¬ AddColors[diffuse, l.color.rgb] ELSE { diffuse ¬ AddColors[diffuse, l.accelerators.cacheD[index].rgb]; specular ¬ AddColors[specular, l.accelerators.cacheS[index].rgb]; }; ENDLOOP; diffuse ¬ ScaleColor[MulColor[diffuse, kd], matclr]; specular ¬ MulColor[specular, ks]; total ¬ AddColors[diffuse, specular]; RETURN [RGBuTos[total]]; }; GetPixelRGB: PROC [lt: LightingTool, mouse: Mouse] RETURNS [srgb: SRGB] ~ { }; GetNormalFromPixel: PROC [lt: LightingTool, mouse: Mouse] RETURNS [norm: Triple] ~ { Inner: PROC ~ { pval: ARRAY [0..25) OF INT; maps: SampleMaps -- ¬ CtBasic.GetColorDisplayMaps[] -- ; index: INT ¬ 0; badnorm: BOOLEAN ¬ FALSE; FOR py: INT IN [mouse.pos.y-2 .. mouse.pos.y+2] DO FOR px: INT IN [mouse.pos.x-2 .. mouse.pos.x+2] DO IF px>=0 AND py>=0 AND px<640 AND py<480 THEN { srgb: SRGB ¬ CtBasic.GetRGBPixel[maps, px, 479-py]; IF NOT IsValidNormalPixel[srgb] THEN badnorm ¬ TRUE; pval[index] ¬ srgb.r; } ELSE pval[index] ¬ 0; index ¬ index+1; ENDLOOP; ENDLOOP; IF badnorm THEN GOTO RaiseError; norm ¬ [0.0, 0.0, 0.0]; FOR index: INT IN [0..25) DO tnorm: Triple ¬ PvalToNorm[pval[index]]; norm.x ¬ norm.x + tnorm.x; norm.y ¬ norm.y + tnorm.y; norm.z ¬ norm.z + tnorm.z; ENDLOOP; norm ¬ G3dVector.Unit[norm]; EXITS RaiseError => ERROR NotANormal; }; }; PvalToNorm: PROC [pval: INT] RETURNS [t: Triple] ~{t¬G3dNormalCoding.DecodeNormal[pval]}; RebuildLight: PROC [l: Light, m: Material] RETURNS [Light] ~ { di, pi: REAL; lDi, lSi: URGB; FOR i: INT IN [0 .. 255] DO IF G3dNormalCoding.IsValidNormalIndex[i] THEN { norm: Triple ¬ G3dNormalCoding.DecodeNormal[i]; di ¬ G3dVector.Dot[l.direction, norm]; IF di > 0.0 THEN { lSi2, lSi3: URGB; lDi ¬ MulColor[l.color.rgb, MyPower[di, l.spread]]; pi ¬ l.direction.z + (2.0 * norm.z * di); -- assume eye = [0, 0, 1] lSi2 ¬ MulColor[m.color.rgb, di * m.materialProps.km]; lSi3 ¬ ScaleColor[AddColors[[1.0, 1.0, 1.0], lSi2], l.color.rgb]; lSi ¬ MulColor[lSi3, MyPower[pi, m.materialProps.ke * l.spread]]; } ELSE { di ¬ pi ¬ 0.0; lDi ¬ lSi ¬ [0.0, 0.0, 0.0]; }; l.accelerators.cached[i] ¬ di; l.accelerators.cacheD[i] ¬ [rgb: lDi]; l.accelerators.cachep[i] ¬ pi; l.accelerators.cacheS[i] ¬ [rgb: lSi]; }; ENDLOOP; RETURN[l]; }; IsValidNormalPixel: PROC [srgb: SRGB] RETURNS [BOOL] ~ { RETURN[G3dNormalCoding.IsValidNormalIndex[srgb.r]]; }; SuperEllipse: PROC [x, e: REAL] RETURNS [REAL] ~ { IF e <= 0.0 THEN RETURN[1.0]; RETURN[RealFns.Exp[0.5*e*RealFns.Ln[1.0-RealFns.Power[x, 2.0/e]]]]; }; Perlin: PROC [u, v: REAL] RETURNS [REAL] ~ { IF u <= 0.0 OR v <= 0.0 THEN RETURN[0.0]; RETURN[RealFns.Power[0.5, RealFns.Log[0.5, u]*RealFns.Log[0.5, v]]]; }; NuclearDecay: PROC [x0, k, t: REAL] RETURNS [r: REAL] ~ { -- xt = x0e-kt RETURN[x0*RealFns.Exp[-k*t]]; }; MyPower: PROC [a, b: REAL] RETURNS [c: REAL] ~ { d: REAL; IF a < 1e-11 THEN RETURN [0.0]; IF b = 1.0 THEN RETURN [a]; d ¬ b * RealFns.Ln[a]; c ¬ IF d < -20.0 THEN 0.0 ELSE RealFns.Exp[d]; -- less than about .000000002 = 2.0e-9 }; AddLight: PROC [lt: LightingTool] ~ { s: IO.STREAM ¬ IO.RIS[Controls.TypescriptRead[lt.ts, "new light name: "]]; name: ROPE ¬ IO.GetTokenRope[s ! IO.EndOfStream => GOTO Eof].token; lt.lights ¬ AddToLightSequence[lt.lights, [name, lt.lights[lt.activeLight].direction, lt.lights[lt.activeLight].spread, lt.lights[lt.activeLight].color]]; lt.activeLight ¬ lt.lights.length - 1; IF lt.lights.length = 1 THEN DrawLightingControls[lt]; EXITS Eof => NULL; }; DeleteLight: PROC [lt: LightingTool] ~ { lnum: INT; IF lt.lights = NIL OR lt.lights.length = 0 THEN Blink["No lights."] ELSE { lnum ¬ ChooseLight[lt.lights, "Delete Light", TRUE]-1; IF lnum = 0 THEN { IF Confirm["delete all lights"] THEN { lt.lights ¬ NIL; EraseLightingControls[lt]; }; } ELSE { lnum ¬ lnum-1; Controls.TypescriptWrite[lt.ts, IO.PutFR1["Deleting light %g\n", IO.rope[lt.lights[lnum].name]]]; RemoveFromLightSequence[lnum, lt.lights]; IF lt.activeLight >= lnum THEN lt.activeLight ¬ MAX[0, lt.activeLight-1]; Controls.TypescriptWrite[lt.ts, IO.PutFR1["New active light %g\n", IO.rope[lt.lights[lt.activeLight].name]]]; IF lt.lights.length = 0 THEN EraseLightingControls[lt]; LightChanged[lt]; }; }; }; ActivateLight: PROC [lt: LightingTool] ~ { lnum: INT; IF lt.lights = NIL OR lt.lights.length = 0 THEN Blink["No lights."] ELSE { lnum ¬ ChooseLight[lt.lights, "Activate Light", FALSE]-1; IF lnum >= 0 THEN { lt.activeLight ¬ lnum; Controls.TypescriptWrite[lt.ts, IO.PutFR1["Light %g active\n", IO.rope[lt.lights[lnum].name]]]; }; }; UpdateActiveLight[lt]; }; NormalizeLights: PROC [lt: LightingTool] ~ { lights: LightSequence ¬ lt.lights; clr: URGB ¬ [0, 0, 0]; IF lights # NIL THEN FOR n: NAT IN [0..lights.length) DO clr.R ¬ clr.R + lights[n].color.rgb.R; clr.G ¬ clr.G + lights[n].color.rgb.G; clr.B ¬ clr.B + lights[n].color.rgb.B; ENDLOOP; clr.R ¬ IF clr.R # 0.0 THEN 1.0/clr.R ELSE 1.0; clr.G ¬ IF clr.G # 0.0 THEN 1.0/clr.G ELSE 1.0; clr.B ¬ IF clr.B # 0.0 THEN 1.0/clr.B ELSE 1.0; FOR n: NAT IN [0..lights.length) DO lights[n].color.rgb.R ¬ lights[n].color.rgb.R * clr.R; lights[n].color.rgb.G ¬ lights[n].color.rgb.G * clr.G; lights[n].color.rgb.B ¬ lights[n].color.rgb.B * clr.B; ENDLOOP; UpdateActiveLight[lt]; }; AddMaterial: PROC [lt: LightingTool] ~ { s: IO.STREAM ¬ IO.RIS[Controls.TypescriptRead[lt.ts, "new Material name: "]]; name: ROPE ¬ IO.GetTokenRope[s ! IO.EndOfStream => GOTO Eof].token; lt.materials ¬ AddToMaterialSequence[lt.materials, [ name: name, materialProps: lt.materials[lt.activeMaterial].materialProps, color: lt.materials[lt.activeMaterial].color]]; lt.activeMaterial ¬ lt.materials.length - 1; IF lt.materials.length = 1 THEN DrawMaterialControls[lt]; EXITS Eof => NULL; }; DeleteMaterial: PROC [lt: LightingTool] ~ { lnum: INT; IF lt.materials = NIL OR lt.materials.length = 0 THEN Blink["No materials."] ELSE { lnum ¬ ChooseMaterial[lt.materials, "Delete Material"]-1; IF lnum = 0 THEN { IF Confirm["delete all materials"] THEN { lt.materials ¬ NIL; EraseMaterialControls[lt]; }; } ELSE { lnum ¬ lnum-1; Controls.TypescriptWrite[lt.ts, IO.PutFR1["Deleting material %g\n", IO.rope[lt.materials[lnum].name]]]; RemoveFromMaterialSequence[lnum, lt.materials]; IF lt.activeMaterial >= lnum THEN lt.activeMaterial ¬ MAX[0, lt.activeMaterial-1]; Controls.TypescriptWrite[lt.ts, IO.PutFR1["New active material %g\n", IO.rope[lt.materials[lt.activeMaterial].name]]]; IF lt.materials.length = 0 THEN EraseMaterialControls[lt]; MaterialChanged[lt]; }; }; }; ActivateMaterial: PROC [lt: LightingTool] ~ { lnum: INT; IF lt.materials = NIL OR lt.materials.length = 0 THEN Blink["No materials."] ELSE { lnum ¬ ChooseMaterial[lt.materials, "Activate Material"]-1; IF lnum >= 0 THEN { lt.activeMaterial ¬ lnum; Controls.TypescriptWrite[lt.ts, IO.PutFR1["Material %g active\n", IO.rope[lt.materials[lnum].name]]]; }; }; UpdateActiveMaterial[lt]; }; ChooseLight: PROC [lights: LightSequence, header: ROPE, all: BOOL] RETURNS [choice: INT] ~ { IF lights # NIL AND lights.length > 0 THEN { popUps: LIST OF Controls.Request ¬ NIL; FOR n: NAT IN [1..lights.length] DO popUps ¬ CONS[[lights[lights.length-n].name], popUps]; ENDLOOP; IF all THEN popUps ¬ CONS[["All Lights"], popUps]; choice ¬ Controls.PopUpRequest[[header], popUps]; }; }; GetActiveAngles: PROC [lt: LightingTool] RETURNS [theta, psi: REAL] ~ { l: Light ¬ lt.lights[lt.activeLight]; d: REAL ¬ RealFns.SqRt[(l.direction.x * l.direction.x) + (l.direction.z * l.direction.z)]; theta ¬ RealFns.ArcTan[l.direction.x, l.direction.z]; psi ¬ RealFns.ArcTan[l.direction.y, d]; }; SetActiveAngles: PROC [lt: LightingTool, theta, psi: REAL] ~ { d: REAL ¬ RealFns.Cos[psi]; lt.lights[lt.activeLight].direction.y ¬ RealFns.Sin[psi]; lt.lights[lt.activeLight].direction.x ¬ d * RealFns.Sin[theta]; lt.lights[lt.activeLight].direction.z ¬ d * RealFns.Cos[theta]; }; UpdateActiveLight: PROC [lt: LightingTool] ~ { l: Light ¬ lt.lights[lt.activeLight]; UpdateLightAngles[lt]; UpdateLightColorSliders[lt]; }; GotAnActiveLight: PROC [lt: LightingTool] RETURNS [BOOLEAN] ~ { RETURN[lt.lights # NIL AND lt.activeLight >= 0 AND lt.activeLight <= INTEGER[lt.lights.length]]; }; ChooseMaterial: PROC [materials: MaterialSequence, header: ROPE] RETURNS [choice: INT] ~ { IF materials # NIL AND materials.length > 0 THEN { popUps: LIST OF Controls.Request ¬ NIL; FOR n: NAT IN [1..materials.length] DO popUps ¬ CONS[[materials[materials.length-n].name], popUps]; ENDLOOP; choice ¬ Controls.PopUpRequest[[header], popUps]; }; }; UpdateActiveMaterial: PROC [lt: LightingTool] ~ { m: Material ¬ lt.materials[lt.activeMaterial]; UpdateMaterialSliders[lt]; UpdateMaterialColorSliders[lt]; }; GotAnActiveMaterial: PROC [lt: LightingTool] RETURNS [BOOLEAN] ~ { RETURN[lt.materials # NIL AND lt.activeMaterial >= 0 AND lt.activeMaterial <= INTEGER[lt.materials.length]]; }; ReadFile: PROC [lt: LightingTool, name: ROPE ¬ NIL] ~ { r: ROPE ¬ FileNames.ResolveRelativePath[IF name = NIL THEN defaultSphereFile ELSE name]; map: SampleMap ¬ CtFile.Read8BitFile[r, 0, 0, 405, 405][0].map; lt.state.maps ¬ CtBasic.CreateMaps[24, 0, 0, 405, 405, FALSE]; lt.state.maps[1].map ¬ map; CtBasic.ReIndexMaps[lt.state.maps, [73, 12]]; }; GetFileName: PROC [lt: LightingTool, prompt: ROPE] RETURNS [ROPE] ~ { msg: ROPE ¬ IO.PutFR["%g: (dir = %g) ", IO.rope[prompt], IO.rope[lt.outerData.directory]]; r: ROPE ¬ Controls.TypescriptRead[lt.ts, msg]; IF r # NIL THEN { c: CHAR ¬ Rope.Fetch[r, 0]; name: ROPE; IF c # '[ AND c # '/ THEN r ¬ Rope.Concat[lt.outerData.directory, r]; name ¬ FileNames.ResolveRelativePath[r]; RETURN[name]; }; RETURN[NIL]; }; WriteLightsAndMaterials: PROC [lt: LightingTool] ~ { fileName: ROPE ¬ GetFileName[lt, "Output file"]; w: Writer ¬ G3dIO.WriteCreate[Rope.Cat["Lights and materials file from ", program, " ", version]]; LightsToWriter[lt.lights, w]; G3dIO.WriteNode[w, "", "", TRUE]; MaterialsToWriter[lt.materials, w]; TiogaAccess.WriteFile[w, fileName! FS.Error => {Blink[error.explanation]; CONTINUE}] }; ReadLightsAndMaterials: PROC [lt: LightingTool, merge: BOOLEAN] ~ { name: ROPE ¬ GetFileName[lt, "Input file"]; in: STREAM ¬ FS.StreamOpen[name]; IF in = NIL THEN RETURN; LightsFromStream[lt, in, merge]; MaterialsFromStream[lt, in, merge]; IO.Close[in]; }; WriteLights: PROC [lt: LightingTool] ~ { fileName: ROPE ¬ GetFileName[lt, "Output light file"]; w: Writer ¬ G3dIO.WriteCreate[Rope.Cat["Lights file from ", program, " ", version]]; LightsToWriter[lt.lights, w]; TiogaAccess.WriteFile[w, fileName! FS.Error => {Blink[error.explanation]; CONTINUE}] }; LightsToWriter: PROC [lights: LightSequence, w: Writer] ~ { IF lights = NIL THEN RETURN; G3dIO.WriteNode[w, "Lights", "i", TRUE]; TiogaAccess.Nest[w, 1]; FOR n: NAT IN [0..lights.length) DO l: Light ¬ lights[n]; d: Triple ¬ l.direction; c: URGB ¬ l.color.rgb; IF NOT Rope.Equal[l.name, "Ambient"] THEN { msg1: ROPE ¬ IO.PutFLR[" -name %g -position %g %g %g", LIST[IO.rope[l.name], IO.real[d.x], IO.real[d.y], IO.real[d.z]]]; msg2: ROPE ¬ IO.PutFR[" -color %g %g %g", IO.real[c.R], IO.real[c.G], IO.real[c.B]]; msg3: ROPE ¬ Rope.Concat[msg1, msg2]; G3dIO.WritePartialNode[w, "AddLight", "b"]; G3dIO.WriteNode[w, msg3]; }; ENDLOOP; FOR n: NAT IN [0..lights.length) DO l: Light ¬ lights[n]; d: Triple ¬ l.direction; c: URGB ¬ l.color.rgb; IF Rope.Equal[l.name, "Ambient"] THEN { msg: ROPE ¬ IO.PutFR[" %g %g %g", IO.real[c.R], IO.real[c.G], IO.real[c.B]]; G3dIO.WriteNode[w, "", "", TRUE]; G3dIO.WritePartialNode[w, "AmbientLight", "b"]; G3dIO.WriteNode[w, msg]; }; ENDLOOP; TiogaAccess.Nest[w, -1]; }; ReadLights: PROC [lt: LightingTool, merge: BOOLEAN] ~ { newone: BOOL ¬ FALSE; origNum: INT ¬ IF lt.lights # NIL THEN lt.lights.length ELSE 0; name: ROPE ¬ GetFileName[lt, "Input light file"]; in: STREAM ¬ FS.StreamOpen[name]; IF in = NIL THEN { Blink[IO.PutFR1["Can't open file %g", IO.rope[name]]]; RETURN; }; LightsFromStream[lt, in, merge]; IF origNum=0 AND lt.lights#NIL AND lt.lights.length>0 THEN DrawLightingControls[lt]; UpdateLightAngles[lt]; UpdateLightColorSliders[lt]; }; LightsFromStream: PROC [lt: LightingTool, stream: STREAM, merge: BOOLEAN] ~ { IF NOT merge THEN lt.lights ¬ NIL; { DO ENABLE IO.EndOfStream => EXIT; line: ROPE ¬ IO.GetLineRope[stream]; firstLetter: INT ¬ Rope.SkipOver[line, 0, " \t"]; firstWord: ROPE ¬ Rope.Substr[line, firstLetter, Rope.SkipTo[line, firstLetter, " \t"]-firstLetter]; IF Rope.Equal[firstWord, "AmbientLight", FALSE] THEN { light: Light ¬ []; cmdA, rA, gA, bA: Args.Arg; [cmdA, rA, gA, bA] ¬ Args.ArgsGetFromRope[line, "%srrr"]; light.name ¬ "Ambient"; light.color.rgb ¬ [rA.real, gA.real, bA.real]; lt.lights ¬ AddToLightSequence[lt.lights, light]; }; IF Rope.Equal[firstWord, "AddLight", FALSE] THEN { light: Light ¬ []; cmdA, xA, yA, zA, nameA, rA, gA, bA: Args.Arg; [cmdA, nameA, xA, yA, zA, rA, gA, bA] ¬ Args.ArgsGetFromRope[line, "%s-name%s-position%rrr-color%rrr"]; IF nameA.ok THEN light.name ¬ nameA.rope ELSE { index: INT ¬ IF lt.lights # NIL THEN lt.lights.length ELSE 0; light.name ¬ IO.PutFR1["Light%g", IO.int[index]]; }; IF xA.ok THEN light.direction ¬ [xA.real, yA.real, zA.real]; IF rA.ok THEN light.color.rgb ¬ [rA.real, gA.real, bA.real]; lt.lights ¬ AddToLightSequence[lt.lights, light]; }; ENDLOOP; }; }; WriteMaterials: PROC [lt: LightingTool] ~ { fileName: ROPE ¬ GetFileName[lt, "Output light file"]; w: Writer ¬ G3dIO.WriteCreate[Rope.Cat["Materials file from ", program, " ", version]]; MaterialsToWriter[lt.materials, w]; TiogaAccess.WriteFile[w, fileName! FS.Error => {Blink[error.explanation]; CONTINUE}] }; MaterialsToWriter: PROC [materials: MaterialSequence, w: Writer] ~ { IF materials = NIL THEN RETURN; G3dIO.WriteNode[w, "Materials", "i", TRUE]; TiogaAccess.Nest[w, 1]; FOR n: NAT IN [0..materials.length) DO m: Material ¬ materials[n]; mp: MaterialProps ¬ m.materialProps; G3dIO.WriteNode[w, IO.PutFR1["%g Material", IO.rope[m.name]], "i", TRUE]; TiogaAccess.Nest[w, 1]; G3dIO.WriteKey[w, "Diffuse", IO.real[mp.kd]]; G3dIO.WriteKey[w, "Specular", IO.real[mp.ks]]; G3dIO.WriteKey[w, "Shininess", IO.real[mp.ke]]; G3dIO.WriteKey[w, "Metallicity", IO.real[mp.km]]; G3dIO.WriteKey[w, "Transmittance", IO.real[mp.kt]]; G3dIO.WriteKey[w, "Transmittance", IO.real[m.color.rgb.G], IO.real[m.color.rgb.R], IO.real[m.color.rgb.B]]; G3dIO.WriteKey[w, "SaveMaterial", IO.rope[m.name]]; TiogaAccess.Nest[w, -1]; ENDLOOP; TiogaAccess.Nest[w, -1]; }; ReadMaterials: PROC [lt: LightingTool, merge: BOOLEAN] ~ { origNum: INT ¬ IF lt.materials # NIL THEN lt.materials.length ELSE 0; name: ROPE ¬ GetFileName[lt, "Input material file"]; in: STREAM ¬ FS.StreamOpen[name]; IF in = NIL THEN { Blink[IO.PutFR1["Can't open file %g", IO.rope[name]]]; RETURN; }; MaterialsFromStream[lt, in, merge]; IF origNum=0 AND lt.materials#NIL AND lt.materials.length>0 THEN DrawMaterialControls[lt]; UpdateMaterialColorSliders[lt]; UpdateMaterialSliders[lt]; }; Blink: PROC [message: ROPE] ~ { MessageWindow.Append[Rope.Concat["\t\t", message], TRUE]; MessageWindow.Blink[]; }; MaterialsFromStream: PROC [lt: LightingTool, in: STREAM, merge: BOOLEAN] ~ { IF NOT merge THEN lt.materials ¬ NIL; { material: Material ¬ []; cmdA, rA, gA, bA, vA: Args.Arg; DO ENABLE IO.EndOfStream => EXIT; Re: PROC [r: ROPE] RETURNS [BOOL] ~ { RETURN[Rope.Equal[firstWord, r, FALSE]]; }; GetOneReal: PROC RETURNS [REAL] ~ { cmdA, vA: Args.Arg; [cmdA, vA] ¬ Args.ArgsGetFromRope[line, "%sr"]; RETURN[vA.real]; }; line: ROPE ¬ IO.GetLineRope[in]; firstLetter: INT ¬ Rope.SkipOver[line, 0, " \t"]; firstWord: ROPE ¬ Rope.Substr[line, firstLetter, Rope.SkipTo[line, firstLetter, " \t"]-firstLetter]; SELECT TRUE FROM Re["Diffuse"] => material.materialProps.kd ¬ GetOneReal[]; Re["Specular"] => material.materialProps.ks ¬ GetOneReal[]; Re["Shininess"] => material.materialProps.ke ¬ GetOneReal[]; Re["Metallicity"] => material.materialProps.km ¬ GetOneReal[]; Re["Transmittance"] => material.materialProps.kt ¬ GetOneReal[]; Re["Color"] => { [cmdA, rA, gA, bA] ¬ Args.ArgsGetFromRope[line, "%srrr"]; material.color.rgb ¬ [rA.real, gA.real, bA.real]; }; Re["SaveMaterial"] => { [cmdA, vA] ¬ Args.ArgsGetFromRope[line, "%ss"]; material.name ¬ vA.rope; lt.materials ¬ AddToMaterialSequence[lt.materials, material]; }; ENDCASE; ENDLOOP; }; }; AddToLightSequence: PROC [ls: LightSequence, light: Light] RETURNS [LightSequence] ~ { light.accelerators ¬ NEW[LightAccelerators]; light.accelerators.cached ¬ NEW[RealArray]; light.accelerators.cacheD ¬ NEW[ColorArray]; light.accelerators.cachep ¬ NEW[RealArray]; light.accelerators.cacheS ¬ NEW[ColorArray]; IF ls = NIL THEN ls ¬ NEW[LightSequenceRep[1]]; IF ls.length = ls.maxLength THEN ls ¬ LengthenLightSequence[ls]; light.direction ¬ G3dVector.Unit[light.direction]; ls[ls.length] ¬ light; ls.length ¬ ls.length+1; RETURN[ls]; }; LengthenLightSequence: PROC [ls: LightSequence, amount: REAL ¬ 1.3] RETURNS [new: LightSequence] ~ { newLength: NAT ¬ MAX[Real.Ceiling[amount*ls.maxLength], 3]; new ¬ NEW[LightSequenceRep[newLength]]; FOR i: NAT IN [0..ls.length) DO new[i] ¬ ls[i]; ENDLOOP; new.length ¬ ls.length; }; RemoveFromLightSequence: PROC [lnum: INT, ls: LightSequence] ~ { FOR nn: INT IN [lnum..ls.length-1) DO ls[nn] ¬ ls[nn+1]; ENDLOOP; ls.length ¬ ls.length-1; }; AddToMaterialSequence: PROC [ms: MaterialSequence, material: Material] RETURNS [MaterialSequence] ~ { material.accelerators ¬ NEW [MaterialAccelerators]; IF ms = NIL THEN ms ¬ NEW[MaterialSequenceRep[1]]; IF ms.length = ms.maxLength THEN ms ¬ LengthenMaterialSequence[ms]; ms[ms.length] ¬ material; ms.length ¬ ms.length+1; RETURN[ms]; }; LengthenMaterialSequence: PROC [ms: MaterialSequence, amount: REAL ¬ 1.3] RETURNS [new: MaterialSequence] ~ { newLength: NAT ¬ MAX[Real.Ceiling[amount*ms.maxLength], 3]; new ¬ NEW[MaterialSequenceRep[newLength]]; FOR i: NAT IN [0..ms.length) DO new[i] ¬ ms[i]; ENDLOOP; new.length ¬ ms.length; }; RemoveFromMaterialSequence: PROC [lnum: INT, ms: MaterialSequence] ~ { FOR nn: INT IN [lnum..ms.length-1) DO ms[nn] ¬ ms[nn+1]; ENDLOOP; ms.length ¬ ms.length-1; }; Confirm: PROC [op: ROPE] RETURNS [b: BOOL] ~ { choice: INT ¬ Controls.PopUpRequest[[Rope.Concat["Really ", Rope.Concat[op, "?"]]], LIST[ -- 1 -- [Rope.Concat["Yes, ", op], IO.PutFR1["Confirm %g", IO.rope[op]]], -- 2 -- [Rope.Concat["No, don't ", op], IO.PutFR1["Cancel %g", IO.rope[op]]] ]]; RETURN[choice=1]; }; InitializeMaterials: PROC [lt: LightingTool] ~ { lt.materials ¬ AddToMaterialSequence[lt.materials, ["Default", [1.0, 1.0, 1.0, 20.0, 1.0, 1.0, 1.0], [[1., 1., 1.]]]]; }; InitializeLights: PROC [lt: LightingTool] ~ { lt.lights ¬ AddToLightSequence[lt.lights, ["Default", [1.0, 1.0, 1.0], 1.0, [[1., 1., 0.]]]]; lt.lights ¬ AddToLightSequence[lt.lights, ["Ambient", [1.0, 1.0, 1.0], 1.0, [[.1, .1, .1]]]]; }; defaultSphereFile: ROPE ¬ FileNames.StripVersionNumber[FS.FileInfo["3dNormalSphere.ais"].fullFName]; usage: ROPE ~ "lighting - interactive light design\nlighting [AIS file name]"; lightingIcon: Icons.IconFlavor ¬ Icons.NewIconFromFile["G3dLighting.icons", 0]; G3dTool.Register["Lighting", LightingCmd, usage]; END. !t G3dLightingCmdsImpl.mesa Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved. Glassner, July 24, 1989 12:29:13 pm PDT Bloomenthal, October 20, 1992 3:22 pm PDT Imported Types Local Types -- debug flags -- image file -- normal flags -- light source data -- material data -- lighting controls -- surface controls special buttons -- tool state -- viewer stuff Constants Errors Commands state: ColorDisplayManager.State ¬ ColorDisplayManager.NextState[]; IF state.level # viewers OR state.type # $FullColor THEN IO.PutF[cmd.out, "Before running 3dlighting, please viewer-enable color display in 24 bpp mode\n"] ELSE CreateMyTool[fileName]; Tool Creation / Initialization CtBasic.CloseColorViewers[]; myFont: Font _ VFonts.EstablishFont[family: "Tioga", size: 4]; lt.kcControl _ Controls.NewControl[name: "kc:", type: hSlider, clientData: lt, min: 0.0, max: 80.0, init: 1.0, proc: MaterialControls, x: 545, y: 40, w: 85, h: 13, textLocation: [left, down, TRUE], font: myFont, taper: exp]; lt.myButtons _ CONS[lt.activeLightButton, lt.myButtons]; lt.myButtons _ CONS[lt.activeMaterialButton, lt.myButtons]; Control Draw/Erase Ops DrawControl[lt, lt.kcControl]; EraseControl[lt, lt.kcControl]; Button Ops Function Click-Response Procedures Control Click-Response Procedures IF Rope.Equal[control.name, lt.kcControl.name] THEN mp.kc _ control.value; Control Update Procedures hsl: HSL ¬ ImagerColorFns.HSLFromRGB[lt.lights[lt.activeLight].color.rgb]; Controls.SetSliderDialValue[lt.lightColorSlider1, hsl.H]; Controls.SetSliderDialValue[lt.lightColorSlider2, hsl.S]; Controls.SetSliderDialValue[lt.lightColorSlider3, hsl.L]; CtMap.WriteEntry[1, RGBuTos[GetLightSlidersRGB[lt]]]; hsl: HSL ¬ ImagerColorFns.HSLFromRGB[lt.materials[lt.activeMaterial].color.rgb]; Controls.SetSliderDialValue[lt.materialColorSlider1, hsl.H]; Controls.SetSliderDialValue[lt.materialColorSlider2, hsl.S]; Controls.SetSliderDialValue[lt.materialColorSlider3, hsl.L]; CtMap.WriteEntry[1, RGBuTos[GetMaterialSlidersRGB[lt]]]; Controls.SetSliderDialValue[lt.kcControl, mp.kc, TRUE]; Drawing Procedures dy: REAL _ (y-center.y) * iradius; dx: REAL _ (x-center.x) * iradius; pval: INT _ EncodeNormal[lt, dx, dy, ditherMode]; CtBasic.ShowMaps[lt.state.maps]; IF ColorDisplayManager.NextState[NIL].level = viewers THEN { [] ¬ Terminal.ModifyColorFrame[Terminal.Current[], BuildColorPatch]; IF lt.state.use24bits THEN [] ¬ Terminal.ModifyColorFrame[Terminal.Current[], Draw24bitSphere] ELSE [] ¬ Terminal.ModifyColorFrame[Terminal.Current[], DrawFile]; [] _ Terminal.ModifyColorFrame[Terminal.Current[], DrawSphere]; lt.state.redrawSphere ¬ FALSE; lt.state.use24bits ¬ FALSE; lt.state.radius ¬ 203; }; r: ROPE _ FileNames.ResolveRelativePath[fileName]; map: SampleMap _ CtFile.Read8BitFile[r, 0, 0, 405, 405][0].map; lt.state.maps _ CtBasic.CreateMaps[24, 0, 0, 405, 405, FALSE]; lt.state.maps[1].map _ map; lt.state.maps[0].map _ CtBasic.RGFromRedAndGrn[map, map]; CtBasic.ReIndexMaps[lt.state.maps, [73, 12]]; CtBasic.ShowMaps[lt.state.maps]; [] ¬ Terminal.ModifyColorFrame[Terminal.Current[], Inner]; Mouse Procedures Controls.TypescriptWrite[lt.outerData.typescript, IO.PutFR["mouse at (%g, %g)", IO.int[graphicsData.mouse.pos.x], IO.int[graphicsData.mouse.pos.y]]]; SELECT TRUE FROM graphicsData.mouse.state = none => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" state: none"]]; graphicsData.mouse.state = down => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" state: down"]]; graphicsData.mouse.state = held => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" state: held"]]; graphicsData.mouse.state = up => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" state: up"]]; ENDCASE; SELECT TRUE FROM graphicsData.mouse.button = none => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" button: none\n"]]; graphicsData.mouse.button = left => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" button: left\n"]]; graphicsData.mouse.button = middle => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" button: middle\n"]]; graphicsData.mouse.button = right => Controls.TypescriptWrite[lt.outerData.typescript,IO.PutFR[" button: right\n"]]; ENDCASE; IF lt.state.nextPressSpecial THEN [] ¬ Terminal.SetColorCursorPresentation[Terminal.Current[], $onesAreBlack]; Utilities Miscellaneous Procedures Light Placement Commands [] ¬ Terminal.SetColorCursorPresentation[Terminal.Current[], $onesAreWhite]; [] ¬ Terminal.SetColorCursorPresentation[Terminal.Current[], $onesAreWhite]; Mouse Light Placement Procedures we assume that all three colors contain the same value -- we assume that all three colors contain the same value Shading Procedures lt.lights[lt.activeLight] _ RebuildLight[lt.lights[lt.activeLight], lt.materials[lt.activeMaterial]]; rgb.R _ NARROW[lt.lightColorSlider1.value]; rgb.G _ NARROW[lt.lightColorSlider2.value]; rgb.B _ NARROW[lt.lightColorSlider3.value]; rgb.R _ NARROW[lt.materialColorSlider1.value]; rgb.G _ NARROW[lt.materialColorSlider2.value]; rgb.B _ NARROW[lt.materialColorSlider3.value]; Shade with a model described by the following equations: (1) E = kd Mc (La + S [Difi]) + ks S [Speci] (2a) di = N . Si ; qi = Acos(di) (2b) did = cos(qd) ; qid = q/spreadi (2c) dis = cos(qs) ; qis = q/kc (3) Difi = Li did (4) Speci = (E . Ri)ke Hi (4a) Ri = 2N - Si (5a) Hi = km Fi + (1-km) Li (5b) Fi = dis Mc Li + (1-dis) Li Equations (5) may be rewritten as (5') Hi = Li (1 - km dis) + (km dis) (Mc Li) where 1 E is the emitted light kd is the material diffuse reflection coefficient Mc is the material color La is the ambient light color Difi is the diffuse contribution of light source i ks is the material specular reflection coefficient Speci is the specular contribution of light source i 2 di is the cosine of the incident angle with light source i N is the surface normal at the shading point Si is the vector from the shading point to light source i qi is the angle between the normal and light source i qid is the spread diffuse angle between the normal and light source i spreadi is the material diffuse spread factor did is the cosine of qid qis is the spread specular angle between the normal and light source i kc is the material specular spread factor dis is the cosine of qis 3 Li is the color of light source i 4 Ri is the reflected direction of the light 5 Hi is the color of the highlight of light source i km is the material metallic coefficient Fi is the "faked" Fresnel color-shifted highlight Note that the color shifting computed in Speci is within the summation, since it is dependent on the color and angle of incidence of each light source -- since E = (0, 0, -1), the result of (E . Ri) is simply -(Ri)z g: REAL _ km * dis; hiclr _ AddColors[ MulColor[lightclr, 1.0-g], MulColor[ScaleColor[lightclr, matclr], g]]; -- compute (kd Mc diffuse) -- compute (ks specular) -- add together the two components Normal Extraction maps: SampleMaps ¬ CtBasic.GetColorDisplayMaps[]; srgb ¬ CtBasic.GetRGBPixel[maps, mouse.pos.x, 479-mouse.pos.y]; [] ¬ Terminal.ModifyColorFrame[Terminal.Current[], Inner]; Accelerators IF light.accelerators.cached = NIL THEN light.accelerators.cached _ NEW[RealArray]; IF light.accelerators.cacheD = NIL THEN light.accelerators.cacheD _ NEW[ColorArray]; IF light.accelerators.cachep = NIL THEN light.accelerators.cachep _ NEW[RealArray]; IF light.accelerators.cacheS = NIL THEN light.accelerators.cacheS _ NEW[ColorArray]; Utilties Lighting Commands Material Commands Light Procs Material Procs File I/O Read24File: PROC [lt: LightingTool, name: ROPE _ NIL] ~ { IF name = NIL THEN name _ defaultSphereFile; name _ FileNames.ResolveRelativePath[name]; lt.state.maps _ CtFile.Read24BitFile[name, name, name]; CtBasic.ReIndexMaps[lt.state.maps, [73, 12]]; }; map: SampleMap _ CtFile.Read8BitFile[r][0].map; lt.state.maps[0].map ¬ CtBasic.RGFromRedAndGrn[map, map]; Light Sequence Procedures Material Sequence Procedures Utility -- pass in suffix which will be appended (after a dot) if the filename has no dot AppendSuffixIfNeeded: PROC [file, suffix: ROPE] RETURNS [ROPE] ~ { lastDot: INT _ Rope.FindBackward[s1: file, s2: ".", case: FALSE]; IF lastDot > 0 THEN { nextArrow: INT _ Rope.Find[s1: file, s2: ">", pos1: lastDot, case: FALSE]; nextSlash: INT _ Rope.Find[s1: file, s2: "/", pos1: lastDot, case: FALSE]; IF nextArrow < 0 AND nextSlash < 0 THEN RETURN [file]; }; RETURN[Rope.Cat[file, Rope.Cat[".", suffix]]]; }; Intialize Start Code Ê8®–"cedarcode" style•NewlineDelimiter ™™Jšœ Ïeœ6™BJšœ'™'™)J˜——šÏk œ`žœ…žœr˜äJ˜—šÑblnœžœž˜"šžœ˜žœb˜ƒJ˜——Jšœž˜headšÏl™Jšœžœ˜,Jšœžœ˜*Jšœ žœ˜#Jšœžœ˜(Jšœ žœ˜%Jšœžœ˜,Jšœžœ˜,Jšœ žœ˜!Jšœžœ˜(Jšœžœ˜(Jšœžœ˜*Jšœ žœ˜#Jšœžœ˜'Jšœžœ˜)Jšœ žœ˜Jšžœžœ žœÏc&˜FJšœ žœ˜%Jšœ žœ˜ Jšœ žœ˜#Jšžœžœžœ¡"˜FJšœ žœ˜#Jšœ žœ˜"Jšžœžœžœžœ˜Jšžœžœžœ˜Jšœ žœ˜&—š  ™ šœ žœžœžœ˜&J˜—Jšœžœžœ žœ˜.Jš œžœžœ žœžœ˜,J˜šœžœžœ˜#Jšœžœ žœ˜(Jšœžœžœ˜*J˜—šœ žœžœ˜Jšœ žœžœ˜J˜*Jšœžœ˜J˜-Jšœžœžœ˜0J˜—šœžœžœ˜ Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜J˜—šœžœžœ˜5J˜—šœžœžœ˜Jšœ žœžœ˜J˜&J˜-Jšœžœžœ˜3J˜—Jšœžœžœ˜3šœžœžœ˜%Jšœžœ˜Jšœžœ žœžœ ˜9J˜—Jšœžœžœ˜-šœžœžœ˜"Jšœžœ˜Jšœžœ žœžœ˜6J˜—Jšœžœžœ˜'šœžœžœ˜ šœ ¡™Jšœžœžœ˜"Jšœžœžœ˜!Jšœžœ˜—™ Jšœžœžœ˜—šœ™Jšœžœžœ˜%Jšœžœžœ˜#Jšœžœžœ˜$Jšœžœžœ˜#Jšœžœžœ˜ J˜ Jšœžœ˜——J˜Jšœžœžœ˜+šœžœžœ˜!š¡™Jšœ˜Jšœžœ˜—š¡™J˜"Jšœžœ˜—š¡™Jšœžœ˜!Jšœžœ˜Jšœžœ˜!Jšœžœ˜"Jšœ žœ˜$Jšœ žœ˜$Jšœ žœ˜$Jšœžœ˜!—š¡™Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœ#žœ˜'Jšœ#žœ˜'Jšœ#žœ˜'Jšœžœ˜#—™J˜J˜—š¡ ™ J˜—š¡™Jšœžœ¡˜1Jšœžœ¡˜8Jšœžœ¡˜,Jšœ¡˜7Jšœ ¡˜9Jšœžœ¡ ˜/Jšœžœ¡˜9———š  ™ Jšœžœ˜Jšœžœ˜Jšœ žœ˜Jšœžœ˜Jšœž œ˜Jšœžœ ˜Jšœžœ ˜Jšœžœ ˜Jšœžœ˜(Jšœžœ ˜—š ™JšÏn œžœžœ˜—š ™š¢ œ˜J™CJšœ žœžœ˜J˜J˜'Jšžœžœ˜/šžœžœ™3Jšžœžœ`™gJšžœ™—J˜——š ™š¢ œžœ žœ˜'Jšœžœ˜-J™Jšœ žœ˜"J˜"J˜˜ Jšœ˜J˜Jšœ˜Jšœ¡#˜=Jšœ¡˜™>J˜$JšœÈžœ˜ÝJšœÈžœ˜ÝJšœÈžœ˜ÝJ˜ÌJ˜´J˜¯Jšœ¿žœ˜àJšœÎžœ˜ãJšœÎžœ˜ãJšœÎžœ˜ãJ˜ÓJšœ¿žœ˜ÔJšœ¾žœ˜ÓJšœÀžœ˜áJšœ¾žœ˜ÓJšœ¿žœ™àJšœ¾žœ˜ÓJšœ¾žœ˜ÓJšœžœ<˜OJšœžœ>˜QJšœžœ6˜IJšœžœF˜YJšœžœF˜YJšœžœD˜WJ˜sJ˜xJšœ8™8Jšœ;™;J˜——š ™š¢œžœ˜0J˜J˜J˜J˜—š¢œžœ˜1J˜&J˜&J˜&Jšœ"˜"Jšœ˜Jšœ!˜!Jšœ"˜"J˜(J˜J˜—š¢œžœ˜2J˜'J˜'J˜'Jšœ#˜#Jšœ ˜ Jšœ"˜"Jšœ#˜#J˜(J˜J˜—š¢œžœ˜1Jšœ)˜)Jšœ)˜)Jšœ)˜)Jšœ%˜%Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ™J˜(J˜J˜—š¢œžœ˜2Jšœ*˜*Jšœ*˜*Jšœ*˜*Jšœ&˜&Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ™J˜(J˜J˜—š¢ œžœ)˜:Jšœ*žœ˜=J˜J˜—š¢ œžœ)˜;Jšœ(žœ˜/Jšœ'žœ˜.Jšœ(žœ˜/J˜——š  ™ š¢œ˜Jšœžœ ˜&Jšœ+žœ ˜;J˜J˜—š¢ œ˜Jšœžœ ˜&šœžœ(žœ˜8Jš¡œ)˜0Jš¡œ)˜0Jš¡œ1˜8Jšœ˜—šžœž˜J˜J˜J˜Jšžœ˜—šžœž˜J˜J˜Jšžœ˜—J˜J˜—š¢ œ˜Jšœžœ ˜&šœžœ+žœ˜;Jš¡œ(˜/Jš¡œ(˜/Jš¡œ3˜:—šžœž˜J˜J˜J˜Jšžœ˜—šžœž˜J˜J˜J˜Jšžœ˜—J˜J˜—š¢ œ˜Jšœžœ ˜&šœžœ1žœ˜AJš¡œ1˜8Jš¡œ5˜<šžœ˜Jšžœ¡œ,˜8Jšžœ¡œ-˜9—Jšœ˜—šžœž˜J˜J˜Jšœžœ˜8Jšžœ˜—šžœž˜J˜J˜J˜Jšžœ˜—J˜J˜—š¢ œ˜Jšœžœ ˜&šœžœ+žœ˜;Jš¡œ,˜3Jš¡œ˜#Jš¡œ˜%Jš¡œ˜%Jš¡œ˜#Jš¡œ˜"Jšœ˜—šžœ žœ˜Jšœ-žœ˜2šžœž˜˜Jš œžœžœžœžœ2˜GJšœ&žœžœžœ ˜]Jšžœžœ˜1Jšžœžœ˜4J˜—J˜J˜J˜J˜J˜Jšžœ˜—Jšœ ˜ Jšœ)˜)Jšœžœ˜Jšœžœ˜Jšœ˜—Jšžœžœ˜J˜J˜—š¢œ˜Jšœžœ ˜&šœžœ+žœ˜;Jš¡œ-˜4Jš¡œ-˜4Jš¡œ0˜7Jš¡œ2˜9Jš¡œ2˜9Jš¡œ5˜™C—Jšœ?™?Jšœžœ™Jšœžœ™J™J™—J˜—š¢ œžœ˜&š¢œžœ˜J˜Jšœžœ+™2Jšœ?™?Jšœ7žœ™>Jšœ™Jšœ9™9Jšœ-™-Jšœ ™ J˜—Jšœ žœ*˜8J™:J˜——š ™š¢ œ˜%Jšœžœ ˜&Jšžœžœžœžœ˜9šœ2žœ™OJšžœ žœ!™Ešžœžœž™JšœTžœ™oJšœTžœ™oJšœTžœ™oJšœRžœ™kJšžœ™—šžœžœž™JšœUžœ™sJšœUžœ™sJšœWžœ™wJšœVžœ™uJšžœ™——šžœžœ˜#Jšžœžœ˜=Jšžœžœ˜=J˜J˜—šžœžœ™"J™L—J˜——š  ™ š ¢œžœžœžœžœ˜3Jšœžœžœ˜:Jšœžœžœ˜:Jšœžœžœ˜:J˜J˜—Jš¢œÏsž¦œ¦žœ¦ž¦œ¦žœ¦œ¦œ-˜\—š ™Jš¢œ(˜7—š ™š¢œžœ˜+Jšœžœ˜!Jšœžœ˜ Jšœžœ˜!J™LJ˜J˜—š¢œžœ˜+Jšœžœ˜!Jšœžœ˜Jšœžœ˜!J™LJ˜——š  ™ š¢œžœ%˜J™/Jšœ§œ8™Jšœžœ˜ Jšœ žœ˜Jšžœžœžœžœ ™SJšžœžœžœžœ ™TJšžœžœžœžœ ™SJšžœžœžœžœ ™Tšžœžœžœ žœ˜šžœ'žœ˜/J˜/J˜&šžœ ˜ šžœ˜Jšœ žœ˜J˜3Jšœ*¡˜CJ˜6J˜AJ˜AJ˜—šžœ˜J˜J˜J˜——J˜J˜&J˜J˜&J˜—Jšžœ˜—Jšžœ˜ J˜——š ™š ¢œžœžœžœžœ˜8Jšžœ-˜3J˜J˜—š ¢ œžœžœžœžœ˜2Jšžœ žœžœ˜Jšžœ=˜CJ˜J˜—š ¢œžœžœžœžœ˜,Jšžœ žœ žœžœ˜)Jšžœ>˜DJ˜J˜—š¢ œžœ žœžœžœ¡ÑcdtÐct›00®Ñctu˜HJšžœ˜Jšœ˜J˜—š ¢œžœžœžœžœ˜0Jšœžœ˜Jšžœ žœžœ˜Jšžœ žœžœ˜J˜Jšœžœ žœžœ¡&˜UJ˜——š ™š£œžœ˜%Jš œžœžœžœžœ5˜JJš œžœžœžœžœ ˜C˜*J˜,J˜D—J˜&Jšžœžœ˜6Jšžœžœ˜J˜J˜—š¢£œžœ˜(Jšœžœ˜ šžœ žœžœ˜*Jšžœ˜šžœ˜Jšœ.žœ˜6šžœ ˜ šžœ˜šžœžœ˜&Jšœ žœ˜J˜J˜——J˜šžœ˜J˜˜ Jšžœžœ˜A—J˜)Jšžœžœžœ˜I˜ Jšžœ!žœ(˜M—Jšžœžœ˜7J˜—J˜——J˜—J˜J˜—š¢£œžœ˜*Jšœžœ˜ šžœ žœžœ˜+Jšžœ˜šžœ˜Jšœ0žœ˜9šžœ žœ˜J˜˜ Jšžœžœ˜?——J˜—J˜J˜—J˜J˜—š¢œžœ˜,J˜"Jšœžœ ˜šžœ žœž˜šžœžœžœž˜#J˜&J˜&J˜&Jšžœ˜—Jšœžœ žœ žœ˜/Jšœžœ žœ žœ˜/Jšœžœ žœ žœ˜/šžœžœžœž˜#J˜6J˜6J˜6Jšžœ˜——J˜J˜——š ™š£ œžœ˜(Jš œžœžœžœžœ8˜MJš œžœžœžœžœ ˜C˜4J˜ J˜>J˜/—J˜,Jšžœžœ˜9Jšžœžœ˜J˜J˜—š¢£œžœ˜+Jšœžœ˜ šžœžœžœ˜1Jšžœ˜šžœ˜J˜9˜ ˜šžœ!žœ˜)Jšœžœ˜J˜J˜—J˜—˜J˜˜ Jšžœ"žœ!˜G—J˜/Jšžœžœžœ˜R˜ Jšžœ$žœ.˜V—Jšžœžœ˜:J˜—J˜——J˜—J˜J˜—š¢£œžœ˜-Jšœžœ˜ šžœžœžœ˜0Jšžœ˜šžœ˜J˜;šžœ žœ˜J˜˜ Jšžœ žœ!˜E——J˜—J˜J˜—J˜——š  ™ š ¢ œžœ!žœžœžœ žœ˜\šžœ žœžœžœ˜,Jšœžœžœžœ˜'šžœžœžœž˜#Jšœ žœ)˜6Jšžœ˜—Jšžœžœ žœ˜2J˜1J˜—J˜J˜—š¢œžœžœžœ˜GJ˜%JšœžœS˜ZJ˜5J˜'J˜J˜—š¢œžœ žœ˜>Jšœžœ˜J˜9J˜?J˜?J˜J˜—š¢œžœ˜/J˜%J˜J˜J˜J˜—š¢œžœžœžœ˜@Jš žœ žœžœžœžœ˜`J˜——š ™š ¢œžœ'žœžœ žœ˜Zšžœ žœžœžœ˜2Jšœžœžœžœ˜'šžœžœžœž˜&Jšœ žœ/˜J˜J™9J˜-J˜—J˜š ¢ œžœžœžœžœ˜EJš œžœžœžœžœ˜ZJšœžœ'˜.šžœžœžœ˜Jšœžœ˜Jšœžœ˜ Jšžœžœžœ,˜EJ˜(Jšžœ˜ J˜—Jšžœžœ˜ J˜J˜—š¢œžœ˜4Jšœ žœ"˜0˜ J˜V—Jšœ˜Jšœžœ˜!Jšœ#˜#Jšœ#žœ%žœ˜TJ˜J˜—š¢œžœžœ˜CJšœžœ!˜+Jšœžœžœ˜!Jšžœžœžœžœ˜Jšœ ˜ Jšœ#˜#Jšžœ ˜ J˜—J˜š¢ œžœ˜(Jšœ žœ(˜6J˜TJšœ˜Jšœ#žœ%žœ˜TJ˜J˜—š¢œžœ'˜;Jšžœ žœžœžœ˜J˜(Jšœ˜šžœžœžœž˜#J˜J˜Jšœžœ˜šžœžœžœ˜,šœžœžœ'˜6Jš žœžœžœ žœ žœ ˜A—Jš œžœžœžœ žœ žœ ˜TJšœžœ˜%J˜+J˜J˜—Jšžœ˜—šžœžœžœž˜#J˜J˜Jšœžœ˜šžœžœ˜'Jš œžœžœžœ žœ žœ ˜LJšœžœ˜!J˜/J˜J˜—Jšžœ˜—Jšœ˜J˜J˜—š¢ œžœžœ˜7Jšœžœžœ˜Jš œ žœžœ žœžœžœ˜?Jšœžœ'˜1Jšœžœžœ˜!šžœžœžœ˜Jšœžœžœ˜6Jšžœ˜J˜—J˜ Jš žœ žœ žœžœžœ˜TJ˜J˜J˜J˜—š¢œžœžœ žœ˜MJšžœžœžœ žœ˜"šœ˜šž˜šž˜Jšžœžœ˜—Jšœžœžœ˜$Jšœ žœ!˜1Jšœ žœU˜dšžœ'žœžœ˜6J˜J˜J˜9J˜J˜.J˜1J˜—šžœ#žœžœ˜2J˜J˜.J˜gšžœ ˜ Jšžœ˜šžœ˜Jš œžœžœ žœžœžœ˜=Jšœ žœžœ ˜1J˜——Jšžœžœ/˜J˜@˜J˜9J˜1J˜—˜J˜/J˜J˜=J˜—Jšžœ˜—Jšžœ˜—J˜—J˜——š ™š¢œžœ"˜:Jšžœ˜J˜J˜,J˜+J˜,J˜+J˜,Jšžœžœžœžœ˜/Jšžœžœ ˜@J˜2J˜J˜Jšžœ˜ J˜J˜—š¢œžœžœ˜CJšžœ˜ Jšœ žœžœ'˜;Jšœžœ˜'Jš žœžœžœžœžœ˜8J˜J˜J˜—š¢œžœžœ˜@šžœžœžœž˜%J˜Jšžœ˜—J˜J˜——š ™š¢œžœ+˜FJšžœ˜J˜J˜3Jšžœžœžœžœ˜2Jšžœžœ#˜CJ˜J˜Jšžœ˜ J˜J˜—š¢œžœ žœ˜IJšžœ˜#Jšœ žœžœ'˜;Jšœžœ!˜*Jš žœžœžœžœžœ˜8J˜J˜J˜J˜—š¢œžœžœ˜Fšžœžœžœž˜%J˜Jšžœ˜—J˜J˜——š ™š ¢œžœžœžœžœ˜.šœžœIžœ˜YJš¡œžœžœ ˜IJš¡œ!žœžœ ˜LJ˜—Jšžœ ˜J˜J˜—Jš¡Q™Qš ¢œžœžœžœžœ™BJšœ žœ.žœ™Ašžœ žœ™Jšœ žœ5žœ™JJšœ žœ5žœ™JJšžœžœžœžœ™6J™—Jšžœ(™.J™——š  ™ š¢œžœ˜0˜3J˜C—J˜J˜—š¢œžœ˜-J˜]J˜]J˜——š  ™ Jšœžœ žœ+˜dJ˜šœžœC˜NJ˜—˜OJ˜—˜1J˜——Jšžœ˜J™J˜J˜J˜—…—Ç8!Z