- FogOption::color is now correctly multiplied by the exposure and environment intensity. - New option to exclude the skybox from the fog - better documentation and naming
289 lines
8.4 KiB
Go
289 lines
8.4 KiB
Go
/*
|
|
* Copyright (C) 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package emitters
|
|
|
|
import (
|
|
db "beamsplitter/database"
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"text/template"
|
|
)
|
|
|
|
func EditJava(definitions []db.TypeDefinition, classname string, folder string) {
|
|
path := filepath.Join(folder, classname+".java")
|
|
var codelines []string
|
|
{
|
|
sourceFile, err := os.Open(path)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer sourceFile.Close()
|
|
lineScanner := bufio.NewScanner(sourceFile)
|
|
foundMarker := false
|
|
for lineNumber := 1; lineScanner.Scan(); lineNumber++ {
|
|
codeline := lineScanner.Text()
|
|
if strings.Contains(codeline, kCodelineMarker) {
|
|
foundMarker = true
|
|
break
|
|
}
|
|
codelines = append(codelines, codeline)
|
|
}
|
|
if !foundMarker {
|
|
log.Fatal("Unable to find marker line in Java file.")
|
|
}
|
|
}
|
|
file, err := os.Create(path)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
defer fmt.Println("Edited " + path)
|
|
for _, codeline := range codelines {
|
|
file.WriteString(codeline)
|
|
file.WriteString("\n")
|
|
}
|
|
file.WriteString(" // " + kCodelineMarker + "\n")
|
|
|
|
// Forward declarations for usage in a closure.
|
|
var flattener func(*db.StructDefinition) string
|
|
var sharedExtensions template.FuncMap
|
|
|
|
javifyType := func(field db.StructField) string {
|
|
if _, exists := field.EmitterFlags["java_float"]; exists {
|
|
return " float"
|
|
}
|
|
switch field.TypeString {
|
|
case "math::float2", "math::float3", "math::float4", "LinearColor", "LinearColorA":
|
|
return " float[]"
|
|
case "bool":
|
|
return " boolean"
|
|
case "uint8_t", "uint16_t", "uint32_t":
|
|
return " int"
|
|
}
|
|
result := strings.ReplaceAll(field.TypeString, "*", "")
|
|
result = strings.ReplaceAll(result, "::", ".")
|
|
return " " + result
|
|
}
|
|
|
|
javifyValue := func(field db.StructField) string {
|
|
|
|
// When forcing an array to be bound to a float, extract the first component and use
|
|
// that as the default value.
|
|
if _, exists := field.EmitterFlags["java_float"]; exists {
|
|
arrayContents := strings.Trim(field.DefaultValue, " []")
|
|
if comma := strings.Index(arrayContents, ","); comma > -1 {
|
|
arrayContents = arrayContents[:comma]
|
|
}
|
|
if !strings.HasSuffix(arrayContents, "f") {
|
|
arrayContents += "f"
|
|
}
|
|
return " " + arrayContents
|
|
}
|
|
|
|
if field.DefaultValue == "nullptr" {
|
|
return " null"
|
|
}
|
|
|
|
// Handle enums
|
|
value := strings.ReplaceAll(field.DefaultValue, "::", ".")
|
|
|
|
// Add "f" suffix for scalars
|
|
if field.TypeString == "float" {
|
|
if field.DefaultValue == "INFINITY" {
|
|
return " Float.POSITIVE_INFINITY"
|
|
} else {
|
|
value += "f"
|
|
}
|
|
} else if c := len(value); c > 1 && value[0] == '[' && value[c-1] == ']' {
|
|
// For vector types, replace [] with {} and add "f" suffix to components.
|
|
switch field.TypeString {
|
|
case "math::float2", "math::float3", "math::float4", "LinearColor", "LinearColorA":
|
|
slices := strings.Split(value[1:c-1], ",")
|
|
for i := range slices {
|
|
slices[i] = strings.TrimSpace(slices[i])
|
|
if !strings.HasSuffix(slices[i], "f") {
|
|
slices[i] += "f"
|
|
}
|
|
}
|
|
value = "{" + strings.Join(slices, ", ") + "}"
|
|
default:
|
|
value = "{" + value[1:c-1] + "}"
|
|
}
|
|
}
|
|
return " " + value
|
|
}
|
|
|
|
getDocBlock := func(defn db.Documented, depth int) string {
|
|
doc := defn.GetDoc()
|
|
if doc == "" {
|
|
return ""
|
|
}
|
|
indent := strings.Repeat(" ", depth)
|
|
if strings.Count(doc, "\n") > 0 {
|
|
return strings.ReplaceAll(doc, "\n", "\n"+indent)
|
|
}
|
|
return "/**\n" + indent + " * " + doc + "\n" + indent + " */\n" + indent
|
|
}
|
|
|
|
getFieldAnnotation := func(field db.StructField, depth int) string {
|
|
if _, exists := field.EmitterFlags["java_float"]; exists {
|
|
return ""
|
|
}
|
|
annotation := ""
|
|
switch {
|
|
case field.DefaultValue == "nullptr":
|
|
annotation = "@Nullable"
|
|
case field.TypeString == "math::float2":
|
|
annotation = "@NonNull @Size(min = 2)"
|
|
case field.TypeString == "math::float3" || field.TypeString == "LinearColor":
|
|
annotation = "@NonNull @Size(min = 3)"
|
|
case field.TypeString == "math::float4" || field.TypeString == "LinearColorA":
|
|
annotation = "@NonNull @Size(min = 4)"
|
|
case strings.Contains(field.DefaultValue, "::"):
|
|
annotation = "@NonNull"
|
|
default:
|
|
return ""
|
|
}
|
|
return annotation + "\n" + strings.Repeat(" ", depth)
|
|
}
|
|
|
|
flattenStruct := func(defn *db.StructDefinition) string {
|
|
prefix := strings.ToLower(defn.BaseName())
|
|
buf := &bytes.Buffer{}
|
|
for _, field := range defn.Fields {
|
|
doc := getDocBlock(defn, 0)
|
|
buf.WriteString(doc)
|
|
buf.WriteString(getFieldAnnotation(field, 0))
|
|
buf.WriteString("public")
|
|
buf.WriteString(javifyType(field))
|
|
buf.WriteByte(' ')
|
|
buf.WriteString(prefix + strings.ToUpper(field.Name[0:1]) + field.Name[1:])
|
|
buf.WriteString(" =")
|
|
buf.WriteString(javifyValue(field))
|
|
buf.WriteString(";\n")
|
|
}
|
|
return indent(buf.String(), 2)
|
|
}
|
|
flattener = flattenStruct
|
|
|
|
// These template extensions are used to transmogrify C++ symbols and value literals to Java.
|
|
customExtensions := template.FuncMap{
|
|
"docblock": getDocBlock,
|
|
"nested_type_declarations": func(parent db.TypeDefinition) string {
|
|
// Look for all fields that request flattening since we should skip their emission.
|
|
flattenedTypes := make(map[db.TypeDefinition]struct{})
|
|
if structDefn, isStruct := parent.(*db.StructDefinition); isStruct {
|
|
for _, field := range structDefn.Fields {
|
|
_, flatten := field.EmitterFlags["java_flatten"]
|
|
if flatten && field.CustomType != nil {
|
|
flattenedTypes[field.CustomType] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find all child types and emit them.
|
|
generate := createJavaCodeGenerator(sharedExtensions)
|
|
buf := &bytes.Buffer{}
|
|
for _, definition := range definitions {
|
|
if definition.Parent() != parent {
|
|
continue
|
|
}
|
|
_, skip := flattenedTypes[definition]
|
|
if skip {
|
|
continue
|
|
}
|
|
switch definition.(type) {
|
|
case *db.StructDefinition:
|
|
generate(buf, "Struct", definition)
|
|
case *db.EnumDefinition:
|
|
generate(buf, "Enum", definition)
|
|
}
|
|
}
|
|
return indent(buf.String(), 1)
|
|
},
|
|
"annotation": getFieldAnnotation,
|
|
"java_type": javifyType,
|
|
"java_value": javifyValue,
|
|
"flatten": func(field *db.StructField) string {
|
|
if structDefn, isStruct := field.CustomType.(*db.StructDefinition); isStruct {
|
|
return strings.TrimLeft(flattener(structDefn), " ")
|
|
}
|
|
log.Fatal("Unexpected flatten flag.")
|
|
return ""
|
|
},
|
|
"flag": func(field *db.StructField, flag string) bool {
|
|
_, exists := field.EmitterFlags[flag]
|
|
return exists
|
|
},
|
|
}
|
|
|
|
sharedExtensions = customExtensions
|
|
|
|
generate := createJavaCodeGenerator(customExtensions)
|
|
for _, definition := range definitions {
|
|
if definition.Parent() != nil {
|
|
continue
|
|
}
|
|
switch definition.(type) {
|
|
case *db.StructDefinition:
|
|
generate(file, "Struct", definition)
|
|
case *db.EnumDefinition:
|
|
generate(file, "Enum", definition)
|
|
}
|
|
}
|
|
|
|
file.WriteString("}\n")
|
|
}
|
|
|
|
// Adds one level of indention to the given multi-line string.
|
|
// Isolated newlines are intentially not indented.
|
|
func indent(src string, depth int) string {
|
|
dst := &bytes.Buffer{}
|
|
buf := bytes.NewBufferString(src)
|
|
scanner := bufio.NewScanner(buf)
|
|
for scanner.Scan() {
|
|
codeline := scanner.Text()
|
|
if codeline != "" {
|
|
dst.WriteString(strings.Repeat(" ", depth))
|
|
dst.WriteString(scanner.Text())
|
|
}
|
|
dst.WriteByte('\n')
|
|
}
|
|
return dst.String()
|
|
}
|
|
|
|
// Wrapper for ExecuteTemplate that performs error checking. Takes an output stream, a template name
|
|
// to invoke, and a template context object.
|
|
type templateFn = func(io.Writer, string, db.TypeDefinition)
|
|
|
|
func createJavaCodeGenerator(customExtensions template.FuncMap) templateFn {
|
|
templ := template.New("beamsplitter").Funcs(customExtensions)
|
|
templ = template.Must(templ.ParseFiles("emitters/java.template"))
|
|
return func(writer io.Writer, section string, definition db.TypeDefinition) {
|
|
err := templ.ExecuteTemplate(writer, section, definition)
|
|
if err != nil {
|
|
log.Fatal(err.Error())
|
|
}
|
|
}
|
|
}
|