Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
406 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
// This extension defines three "punctual" light types: directional, point and spot. | ||
// Punctual lights are defined as parameterized, infinitely small points | ||
// that emit light in well-defined directions and intensities. | ||
package lightspuntual | ||
|
||
import ( | ||
"encoding/json" | ||
"math" | ||
|
||
"github.com/qmuntal/gltf" | ||
) | ||
|
||
const ( | ||
// ExtensionName defines the KHR_lights_punctual unique key. | ||
ExtensionName = "KHR_lights_punctual" | ||
) | ||
|
||
func init() { | ||
gltf.RegisterExtension(ExtensionName, Unmarshal) | ||
} | ||
|
||
type envelop struct { | ||
Lights Lights | ||
Light *LightIndex | ||
} | ||
|
||
// Unmarshal decodes the json data into the correct type. | ||
func Unmarshal(data []byte) (interface{}, error) { | ||
var env envelop | ||
if err := json.Unmarshal([]byte(data), &env); err != nil { | ||
return nil, err | ||
} | ||
if env.Light != nil { | ||
return *env.Light, nil | ||
} | ||
return env.Lights, nil | ||
} | ||
|
||
const ( | ||
// TypeDirectional lights act as though they are infinitely far away and emit light in the direction of the local -z axis. | ||
TypeDirectional = "directional" | ||
// TypePoint lights emit light in all directions from their position in space. | ||
TypePoint = "point" | ||
// TypeSpot lights emit light in a cone in the direction of the local -z axis. | ||
TypeSpot = "spot" | ||
) | ||
|
||
// LightIndex is the id of the light referenced by this node. | ||
type LightIndex uint32 | ||
|
||
// Spot defines the spot cone. | ||
type Spot struct { | ||
InnerConeAngle float64 `json:"innerConeAngle,omitempty"` | ||
OuterConeAngle *float64 `json:"outerConeAngle,omitempty"` | ||
} | ||
|
||
// OuterConeAngleOrDefault returns the OuterConeAngle if it is not nil, else return the default one. | ||
func (s *Spot) OuterConeAngleOrDefault() float64 { | ||
if s.OuterConeAngle == nil { | ||
return math.Pi / 4 | ||
} | ||
return *s.OuterConeAngle | ||
} | ||
|
||
// UnmarshalJSON unmarshal the spot with the correct default values. | ||
func (s *Spot) UnmarshalJSON(data []byte) error { | ||
type alias Spot | ||
tmp := alias(Spot{OuterConeAngle: gltf.Float64(math.Pi / 4)}) | ||
err := json.Unmarshal(data, &tmp) | ||
if err == nil { | ||
*s = Spot(tmp) | ||
} | ||
return err | ||
} | ||
|
||
// Lights defines a list of Lights. | ||
type Lights []*Light | ||
|
||
// Light defines a directional, point, or spot light. | ||
// When a light's type is spot, the spot property on the light is required. | ||
type Light struct { | ||
Type string `json:"type"` | ||
Name string `json:"name,omitempty"` | ||
Color *gltf.RGB `json:"color,omitempty"` | ||
Intensity *float64 `json:"intensity,omitempty"` | ||
Range *float64 `json:"range,omitempty"` | ||
Spot *Spot `json:"spot,omitempty"` | ||
} | ||
|
||
// IntensityOrDefault returns the itensity if it is not nil, else return the default one. | ||
func (l *Light) IntensityOrDefault() float64 { | ||
if l.Intensity == nil { | ||
return 1 | ||
} | ||
return *l.Intensity | ||
} | ||
|
||
// ColorOrDefault returns the color if it is not nil, else return the default one. | ||
func (l *Light) ColorOrDefault() gltf.RGB { | ||
if l.Color == nil { | ||
return *gltf.NewRGB() | ||
} | ||
return *l.Color | ||
} | ||
|
||
// UnmarshalJSON unmarshal the light with the correct default values. | ||
func (l *Light) UnmarshalJSON(data []byte) error { | ||
type alias Light | ||
tmp := alias(Light{Color: gltf.NewRGB(), Intensity: gltf.Float64(1), Range: gltf.Float64(math.Inf(0))}) | ||
err := json.Unmarshal(data, &tmp) | ||
if err == nil { | ||
*l = Light(tmp) | ||
} | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package lightspuntual | ||
|
||
import ( | ||
"math" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/go-test/deep" | ||
"github.com/qmuntal/gltf" | ||
) | ||
|
||
func TestLight_IntensityOrDefault(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
l *Light | ||
want float64 | ||
}{ | ||
{"empty", &Light{}, 1}, | ||
{"other", &Light{Intensity: gltf.Float64(0.5)}, 0.5}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := tt.l.IntensityOrDefault(); got != tt.want { | ||
t.Errorf("Light.IntensityOrDefault() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestLight_ColorOrDefault(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
l *Light | ||
want gltf.RGB | ||
}{ | ||
{"empty", &Light{}, *gltf.NewRGB()}, | ||
{"other", &Light{Color: &gltf.RGB{R: 0.8, G: 0.8, B: 0.8}}, gltf.RGB{R: 0.8, G: 0.8, B: 0.8}}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := tt.l.ColorOrDefault(); !reflect.DeepEqual(got, tt.want) { | ||
t.Errorf("Light.ColorOrDefault() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestSpot_OuterConeAngleOrDefault(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
s *Spot | ||
want float64 | ||
}{ | ||
{"empty", &Spot{}, math.Pi / 4}, | ||
{"other", &Spot{OuterConeAngle: gltf.Float64(0.5)}, 0.5}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := tt.s.OuterConeAngleOrDefault(); got != tt.want { | ||
t.Errorf("Spot.OuterConeAngleOrDefault() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestLight_UnmarshalJSON(t *testing.T) { | ||
type args struct { | ||
data []byte | ||
} | ||
tests := []struct { | ||
name string | ||
l *Light | ||
args args | ||
want *Light | ||
wantErr bool | ||
}{ | ||
{"default", new(Light), args{[]byte("{}")}, &Light{ | ||
Color: gltf.NewRGB(), Intensity: gltf.Float64(1), Range: gltf.Float64(math.Inf(0)), | ||
}, false}, | ||
{"nodefault", new(Light), args{[]byte(`{ | ||
"color": [0.3, 0.7, 1.0], | ||
"name": "AAA", | ||
"intensity": 40.0, | ||
"type": "spot", | ||
"range": 10.0, | ||
"spot": { | ||
"innerConeAngle": 1.0, | ||
"outerConeAngle": 2.0 | ||
} | ||
}`)}, &Light{ | ||
Name: "AAA", Type: "spot", Color: &gltf.RGB{R: 0.3, G: 0.7, B: 1.0}, Intensity: gltf.Float64(40), Range: gltf.Float64(10), | ||
Spot: &Spot{ | ||
InnerConeAngle: 1.0, | ||
OuterConeAngle: gltf.Float64(2.0), | ||
}, | ||
}, false}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if err := tt.l.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { | ||
t.Errorf("Light.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) | ||
} | ||
if !reflect.DeepEqual(tt.l, tt.want) { | ||
t.Errorf("Light.UnmarshalJSON() = %+v, want %+v", tt.l, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestUnmarshal(t *testing.T) { | ||
type args struct { | ||
data []byte | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want interface{} | ||
wantErr bool | ||
}{ | ||
{"error", args{[]byte(`{"light: 1}`)}, nil, true}, | ||
{"index", args{[]byte(`{"light": 1}`)}, LightIndex(1), false}, | ||
{"lights", args{[]byte(`{"lights": [ | ||
{ | ||
"color": [1.0, 0.9, 0.7], | ||
"name": "Directional", | ||
"intensity": 3.0, | ||
"type": "directional" | ||
}, | ||
{ | ||
"color": [1.0, 0.0, 0.0], | ||
"name": "Point", | ||
"intensity": 20.0, | ||
"type": "point" | ||
} | ||
]}`)}, Lights{ | ||
{Color: &gltf.RGB{R: 1.0, G: 0.9, B: 0.7}, Name: "Directional", Intensity: gltf.Float64(3.0), Type: "directional", Range: gltf.Float64(math.Inf(0))}, | ||
{Color: &gltf.RGB{R: 1.0}, Name: "Point", Intensity: gltf.Float64(20.0), Type: "point", Range: gltf.Float64(math.Inf(0))}, | ||
}, false}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
got, err := Unmarshal(tt.args.data) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("Unmarshal() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if diff := deep.Equal(got, tt.want); diff != nil { | ||
t.Errorf("Unmarshal() = %v", diff) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.