add GPU morpher + Dart API
This commit is contained in:
BIN
example/assets/compiled.mat
Normal file
BIN
example/assets/compiled.mat
Normal file
Binary file not shown.
185
example/assets/ubershader_gpu.mat.in
Normal file
185
example/assets/ubershader_gpu.mat.in
Normal file
@@ -0,0 +1,185 @@
|
||||
material {
|
||||
name : ubershader_gpu,
|
||||
requires : [ uv0, uv1, color ],
|
||||
shadingModel : lit,
|
||||
blending : transparent,
|
||||
depthWrite : true,
|
||||
doubleSided : true,
|
||||
flipUV : false,
|
||||
specularAmbientOcclusion : simple,
|
||||
specularAntiAliasing : true,
|
||||
clearCoatIorChange : false,
|
||||
vertexDomain: world,
|
||||
parameters : [
|
||||
|
||||
{ type : float3, name : specularFactor },
|
||||
{ type : float, name : glossinessFactor },
|
||||
|
||||
// Base Color
|
||||
{ type : int, name : baseColorIndex },
|
||||
{ type : float4, name : baseColorFactor },
|
||||
{ type : sampler2d, name : baseColorMap },
|
||||
{ type : mat3, name : baseColorUvMatrix, precision: high },
|
||||
|
||||
// Metallic-Roughness Map
|
||||
{ type : int, name : metallicRoughnessIndex },
|
||||
{ type : float, name : metallicFactor },
|
||||
{ type : float, name : roughnessFactor },
|
||||
{ type : sampler2d, name : metallicRoughnessMap },
|
||||
{ type : mat3, name : metallicRoughnessUvMatrix, precision: high },
|
||||
|
||||
// Normal Map
|
||||
{ type : int, name : normalIndex },
|
||||
{ type : float, name : normalScale },
|
||||
{ type : sampler2d, name : normalMap },
|
||||
{ type : mat3, name : normalUvMatrix, precision: high },
|
||||
|
||||
// Ambient Occlusion
|
||||
{ type : int, name : aoIndex },
|
||||
{ type : float, name : aoStrength },
|
||||
{ type : sampler2d, name : occlusionMap },
|
||||
{ type : mat3, name : occlusionUvMatrix, precision: high },
|
||||
|
||||
// Emissive Map
|
||||
{ type : int, name : emissiveIndex },
|
||||
{ type : float3, name : emissiveFactor },
|
||||
{ type : sampler2d, name : emissiveMap },
|
||||
{ type : mat3, name : emissiveUvMatrix, precision: high },
|
||||
|
||||
// Cleat coat
|
||||
{ type : float, name : clearCoatFactor },
|
||||
{ type : float, name : clearCoatRoughnessFactor },
|
||||
{ type : int, name : clearCoatIndex },
|
||||
{ type : sampler2d, name : clearCoatMap },
|
||||
{ type : mat3, name : clearCoatUvMatrix, precision: high },
|
||||
{ type : int, name : clearCoatRoughnessIndex },
|
||||
{ type : sampler2d, name : clearCoatRoughnessMap },
|
||||
{ type : mat3, name : clearCoatRoughnessUvMatrix, precision: high },
|
||||
{ type : int, name : clearCoatNormalIndex },
|
||||
{ type : sampler2d, name : clearCoatNormalMap },
|
||||
{ type : mat3, name : clearCoatNormalUvMatrix, precision: high },
|
||||
{ type : float, name : clearCoatNormalScale },
|
||||
|
||||
// Reflectance
|
||||
{ type : float, name : reflectance },
|
||||
{ type : int3, name: dimensions },
|
||||
{ type : float[255], name:"morphTargetWeights" },
|
||||
{ type: sampler2dArray, format: float, name:"morphTargets" }
|
||||
],
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
highp float2 uvs[2];
|
||||
uvs[0] = getUV0();
|
||||
uvs[1] = getUV1();
|
||||
|
||||
if (materialParams.normalIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.normalIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.normalUvMatrix).xy;
|
||||
material.normal = texture(materialParams_normalMap, uv).xyz * 2.0 - 1.0;
|
||||
material.normal.xy *= materialParams.normalScale;
|
||||
}
|
||||
if (materialParams.clearCoatNormalIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.clearCoatNormalIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.clearCoatNormalUvMatrix).xy;
|
||||
material.clearCoatNormal = texture(materialParams_clearCoatNormalMap, uv).xyz * 2.0 - 1.0;
|
||||
material.clearCoatNormal.xy *= materialParams.clearCoatNormalScale;
|
||||
}
|
||||
|
||||
prepareMaterial(material);
|
||||
material.baseColor = materialParams.baseColorFactor;
|
||||
|
||||
if (materialParams.baseColorIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.baseColorIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.baseColorUvMatrix).xy;
|
||||
material.baseColor *= texture(materialParams_baseColorMap, uv);
|
||||
}
|
||||
|
||||
material.baseColor *= getColor();
|
||||
material.baseColor.rgb *= material.baseColor.a;
|
||||
|
||||
|
||||
material.roughness = materialParams.roughnessFactor;
|
||||
material.metallic = materialParams.metallicFactor;
|
||||
|
||||
// KHR_materials_clearcoat forbids clear coat from
|
||||
// being applied in the specular/glossiness model
|
||||
material.clearCoat = materialParams.clearCoatFactor;
|
||||
material.clearCoatRoughness = materialParams.clearCoatRoughnessFactor;
|
||||
|
||||
if (materialParams.clearCoatIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.clearCoatIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.clearCoatUvMatrix).xy;
|
||||
material.clearCoat *= texture(materialParams_clearCoatMap, uv).r;
|
||||
}
|
||||
if (materialParams.clearCoatRoughnessIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.clearCoatRoughnessIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.clearCoatRoughnessUvMatrix).xy;
|
||||
material.clearCoatRoughness *= texture(materialParams_clearCoatRoughnessMap, uv).g;
|
||||
}
|
||||
|
||||
material.emissive = vec4(materialParams.emissiveFactor.rgb, 0.0);
|
||||
|
||||
if (materialParams.metallicRoughnessIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.metallicRoughnessIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.metallicRoughnessUvMatrix).xy;
|
||||
|
||||
vec4 mr = texture(materialParams_metallicRoughnessMap, uv);
|
||||
material.roughness *= mr.g;
|
||||
material.metallic *= mr.b;
|
||||
}
|
||||
|
||||
if (materialParams.aoIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.aoIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.occlusionUvMatrix).xy;
|
||||
material.ambientOcclusion = texture(materialParams_occlusionMap, uv).r *
|
||||
materialParams.aoStrength;
|
||||
}
|
||||
if (materialParams.emissiveIndex > -1) {
|
||||
highp float2 uv = uvs[materialParams.emissiveIndex];
|
||||
uv = (vec3(uv, 1.0) * materialParams.emissiveUvMatrix).xy;
|
||||
material.emissive.rgb *= texture(materialParams_emissiveMap, uv).rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
vertex {
|
||||
vec3 getMorphTarget(int vertexIndex, int morphTargetIndex) {
|
||||
// our texture is laid out as (x,y,z) where y is 1, z is the number of morph targets, and x is the number of vertices * 2 (multiplication accounts for position + normal)
|
||||
// UV coordinates are normalized to (-1,1), so we divide the current vertex index by the total number of vertices to find the correct coordinate for this vertex
|
||||
vec3 uv = vec3(
|
||||
(float(vertexIndex) + 0.5) / float(materialParams.dimensions.x),
|
||||
0.0f,
|
||||
//(float(morphTargetIndex) + 0.5f) / float(materialParams.dimensions.z));
|
||||
float(morphTargetIndex));
|
||||
return texture(materialParams_morphTargets, uv).xyz;
|
||||
}
|
||||
|
||||
void materialVertex(inout MaterialVertexInputs material) {
|
||||
// for every morph target
|
||||
for(int morphTargetIndex = 0; morphTargetIndex < materialParams.dimensions.z; morphTargetIndex++) {
|
||||
|
||||
// get the weight to apply
|
||||
float weight = materialParams.morphTargetWeights[morphTargetIndex];
|
||||
|
||||
// get the ID of this vertex, which will be the x-offset of the position attribute in the texture sampler
|
||||
int vertexId = getVertexIndex();
|
||||
|
||||
// get the position of the target for this vertex
|
||||
vec3 morphTargetPosition = getMorphTarget(vertexId, morphTargetIndex);
|
||||
// update the world position of this vertex
|
||||
material.worldPosition.xyz += (weight * morphTargetPosition);
|
||||
|
||||
// increment the vertexID by half the size of the texture to get the x-offset of the normal (all positions stored in the first half, all normals stored in the second half)
|
||||
|
||||
vertexId += (materialParams.dimensions.x / 2);
|
||||
|
||||
// get the normal of this target for this vertex
|
||||
vec3 morphTargetNormal = getMorphTarget(vertexId, morphTargetIndex);
|
||||
material.worldNormal += (weight * morphTargetNormal);
|
||||
}
|
||||
mat4 transform = getWorldFromModelMatrix();
|
||||
material.worldPosition = mulMat4x4Float3(transform, material.worldPosition.xyz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:async';
|
||||
|
||||
@@ -19,6 +21,10 @@ class MyApp extends StatefulWidget {
|
||||
class _MyAppState extends State<MyApp> {
|
||||
final FilamentController _filamentController = MimeticFilamentController();
|
||||
|
||||
double _zoomValue = 0;
|
||||
int _primitiveIndex = 0;
|
||||
double _weight = 0.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -38,7 +44,6 @@ class _MyAppState extends State<MyApp> {
|
||||
details.localPosition.dx, details.localPosition.dy);
|
||||
},
|
||||
onPanUpdate: (details) {
|
||||
print(details.localPosition.dx);
|
||||
_filamentController.panUpdate(
|
||||
details.localPosition.dx, details.localPosition.dy);
|
||||
},
|
||||
@@ -51,22 +56,78 @@ class _MyAppState extends State<MyApp> {
|
||||
ElevatedButton(
|
||||
child: Text("initialize"),
|
||||
onPressed: () {
|
||||
_filamentController.initialize();
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: Text("load skybox"),
|
||||
onPressed: () {
|
||||
_filamentController.initialize(
|
||||
materialPath: "assets/compiled.mat");
|
||||
_filamentController.loadSkybox(
|
||||
"assets/default_env/default_env_skybox.ktx",
|
||||
"assets/default_env/default_env_ibl.ktx");
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: Text("load gltf"),
|
||||
onPressed: () {
|
||||
_filamentController.loadGltf(
|
||||
"assets/BusterDrone/scene.gltf",
|
||||
"assets/BusterDrone");
|
||||
"assets/guy.gltf", "assets", "Material");
|
||||
_filamentController.createMorpher(
|
||||
"CC_Base_Body.003", "CC_Base_Body.003",
|
||||
materialName: "Material");
|
||||
}),
|
||||
// ElevatedButton(
|
||||
// child: Text("load skybox"),
|
||||
// onPressed: () {
|
||||
// _filamentController.loadSkybox(
|
||||
// "assets/default_env/default_env_skybox.ktx",
|
||||
// "assets/default_env/default_env_ibl.ktx");
|
||||
// }),
|
||||
// ElevatedButton(
|
||||
// child: Text("load gltf"),
|
||||
// onPressed: () {
|
||||
// _filamentController.loadGltf(
|
||||
// "assets/guy.gltf", "assets", "Material");
|
||||
// }),
|
||||
// ElevatedButton(
|
||||
// child: Text("create morpher"),
|
||||
// onPressed: () {
|
||||
// _filamentController.createMorpher(
|
||||
// "CC_Base_Body.003", "CC_Base_Body.003",
|
||||
// materialName: "Material");
|
||||
// }),
|
||||
Row(children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(10),
|
||||
color: Colors.white,
|
||||
child: Text(_primitiveIndex.toString())),
|
||||
ElevatedButton(
|
||||
child: Text("+"),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_primitiveIndex = min(_primitiveIndex + 1, 5);
|
||||
});
|
||||
}),
|
||||
ElevatedButton(
|
||||
child: Text("-"),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_primitiveIndex = max(_primitiveIndex - 1, 0);
|
||||
});
|
||||
}),
|
||||
]),
|
||||
Slider(
|
||||
min: 0,
|
||||
max: 1,
|
||||
divisions: 10,
|
||||
value: _weight,
|
||||
onChanged: (v) {
|
||||
setState(() {
|
||||
_weight = v;
|
||||
_filamentController.applyWeights(
|
||||
List.filled(255, _weight), _primitiveIndex.toInt());
|
||||
print("Set $_primitiveIndex to $_weight");
|
||||
});
|
||||
}),
|
||||
Row(children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => _filamentController.zoom(1.0),
|
||||
child: Text("+")),
|
||||
ElevatedButton(
|
||||
onPressed: () => _filamentController.zoom(-1.0),
|
||||
child: Text("-"))
|
||||
])
|
||||
]),
|
||||
])),
|
||||
),
|
||||
|
||||
@@ -47,6 +47,7 @@ flutter:
|
||||
- assets/BusterDrone/textures/
|
||||
- assets/FlightHelmet/
|
||||
- assets/FlightHelmet/textures/
|
||||
- assets/textures/
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
@@ -42,7 +42,10 @@ static void* freeResourceGlobal(void* mem, size_t size, void* misc) {
|
||||
|
||||
- (void)handleMethodCall:(FlutterMethodCall* _Nonnull)call result:(FlutterResult _Nonnull )result {
|
||||
if([@"initialize" isEqualToString:call.method]) {
|
||||
_viewer = new mimetic::FilamentViewer(_layer, loadResourceGlobal, freeResourceGlobal);
|
||||
if(!call.arguments)
|
||||
_viewer = new mimetic::FilamentViewer(_layer, nullptr, loadResourceGlobal, freeResourceGlobal);
|
||||
else
|
||||
_viewer = new mimetic::FilamentViewer(_layer, [call.arguments UTF8String], loadResourceGlobal, freeResourceGlobal);
|
||||
[_controller setViewer:_viewer];
|
||||
[_controller startDisplayLink];
|
||||
result(@"OK");
|
||||
@@ -54,7 +57,7 @@ static void* freeResourceGlobal(void* mem, size_t size, void* misc) {
|
||||
} else if([@"loadGltf" isEqualToString:call.method]) {
|
||||
if(!_viewer)
|
||||
return;
|
||||
_viewer->loadGltf([call.arguments[0] UTF8String], [call.arguments[1] UTF8String]);
|
||||
_viewer->loadGltf([call.arguments[0] UTF8String], [call.arguments[1] UTF8String], [call.arguments[2] UTF8String]);
|
||||
result(@"OK");
|
||||
} else if([@"panStart" isEqualToString:call.method]) {
|
||||
if(!_viewer)
|
||||
@@ -68,6 +71,25 @@ static void* freeResourceGlobal(void* mem, size_t size, void* misc) {
|
||||
if(!_viewer)
|
||||
return;
|
||||
_viewer->manipulator->grabEnd();
|
||||
} else if([@"createMorpher" isEqualToString:call.method]) {
|
||||
_viewer->createMorpher([call.arguments[0] UTF8String], [call.arguments[1] UTF8String],[call.arguments[2] UTF8String]);
|
||||
} else if([@"applyWeights" isEqualToString:call.method]) {
|
||||
if(!_viewer)
|
||||
return;
|
||||
NSArray* nWeights = call.arguments[0];
|
||||
NSNumber* nPrimitiveIndex = call.arguments[1];
|
||||
int primitiveIndex = [nPrimitiveIndex intValue];
|
||||
|
||||
int count = [nWeights count];
|
||||
float weights[count];
|
||||
for(int i=0; i < count; i++) {
|
||||
weights[i] = [nWeights[i] floatValue];
|
||||
}
|
||||
_viewer->morphHelper->applyWeights(weights, count, primitiveIndex);
|
||||
} else if([@"zoom" isEqualToString:call.method]) {
|
||||
if(!_viewer)
|
||||
return;
|
||||
_viewer->manipulator->scroll(0.0f, 0.0f, [call.arguments floatValue]);
|
||||
} else {
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
|
||||
307
ios/include/trie/array-hash/array_growth_policy.h
Normal file
307
ios/include/trie/array-hash/array_growth_policy.h
Normal file
@@ -0,0 +1,307 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ARRAY_GROWTH_POLICY_H
|
||||
#define TSL_ARRAY_GROWTH_POLICY_H
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <ratio>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
#ifdef __EXCEPTIONS
|
||||
# define THROW(_e, _m) throw _e(_m)
|
||||
#else
|
||||
# include <stdio.h>
|
||||
# ifndef NDEBUG
|
||||
# define THROW(_e, _m) do { fprintf(stderr, _m); std::terminate(); } while(0)
|
||||
# else
|
||||
# define THROW(_e, _m) std::terminate()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
namespace tsl {
|
||||
namespace ah {
|
||||
|
||||
/**
|
||||
* Grow the hash table by a factor of GrowthFactor keeping the bucket count to a power of two. It allows
|
||||
* the table to use a mask operation instead of a modulo operation to map a hash to a bucket.
|
||||
*
|
||||
* GrowthFactor must be a power of two >= 2.
|
||||
*/
|
||||
template<std::size_t GrowthFactor>
|
||||
class power_of_two_growth_policy {
|
||||
public:
|
||||
/**
|
||||
* Called on the hash table creation and on rehash. The number of buckets for the table is passed in parameter.
|
||||
* This number is a minimum, the policy may update this value with a higher value if needed (but not lower).
|
||||
*
|
||||
* If 0 is given, min_bucket_count_in_out must still be 0 after the policy creation and
|
||||
* bucket_for_hash must always return 0 in this case.
|
||||
*/
|
||||
explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = round_up_to_power_of_two(min_bucket_count_in_out);
|
||||
m_mask = min_bucket_count_in_out - 1;
|
||||
}
|
||||
else {
|
||||
m_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bucket [0, bucket_count()) to which the hash belongs.
|
||||
* If bucket_count() is 0, it must always return 0.
|
||||
*/
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash & m_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of buckets that should be used on next growth.
|
||||
*/
|
||||
std::size_t next_bucket_count() const {
|
||||
if((m_mask + 1) > max_bucket_count() / GrowthFactor) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
return (m_mask + 1) * GrowthFactor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum number of buckets supported by the policy.
|
||||
*/
|
||||
std::size_t max_bucket_count() const {
|
||||
// Largest power of two.
|
||||
return (std::numeric_limits<std::size_t>::max() / 2) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the growth policy as if it was created with a bucket count of 0.
|
||||
* After a clear, the policy must always return 0 when bucket_for_hash is called.
|
||||
*/
|
||||
void clear() noexcept {
|
||||
m_mask = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::size_t round_up_to_power_of_two(std::size_t value) {
|
||||
if(is_power_of_two(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if(value == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
--value;
|
||||
for(std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) {
|
||||
value |= value >> i;
|
||||
}
|
||||
|
||||
return value + 1;
|
||||
}
|
||||
|
||||
static constexpr bool is_power_of_two(std::size_t value) {
|
||||
return value != 0 && (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, "GrowthFactor must be a power of two >= 2.");
|
||||
|
||||
std::size_t m_mask;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo to map a hash
|
||||
* to a bucket. Slower but it can be useful if you want a slower growth.
|
||||
*/
|
||||
template<class GrowthFactor = std::ratio<3, 2>>
|
||||
class mod_growth_policy {
|
||||
public:
|
||||
explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
if(min_bucket_count_in_out > max_bucket_count()) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
m_mod = min_bucket_count_in_out;
|
||||
}
|
||||
else {
|
||||
m_mod = 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return hash % m_mod;
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_mod == max_bucket_count()) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
const double next_bucket_count = std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR);
|
||||
if(!std::isnormal(next_bucket_count)) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
if(next_bucket_count > double(max_bucket_count())) {
|
||||
return max_bucket_count();
|
||||
}
|
||||
else {
|
||||
return std::size_t(next_bucket_count);
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return MAX_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_mod = 1;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = 1.0 * GrowthFactor::num / GrowthFactor::den;
|
||||
static const std::size_t MAX_BUCKET_COUNT =
|
||||
std::size_t(double(
|
||||
std::numeric_limits<std::size_t>::max() / REHASH_SIZE_MULTIPLICATION_FACTOR
|
||||
));
|
||||
|
||||
static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, "Growth factor should be >= 1.1.");
|
||||
|
||||
std::size_t m_mod;
|
||||
};
|
||||
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
static constexpr const std::array<std::size_t, 40> PRIMES = {{
|
||||
1ul, 5ul, 17ul, 29ul, 37ul, 53ul, 67ul, 79ul, 97ul, 131ul, 193ul, 257ul, 389ul, 521ul, 769ul, 1031ul,
|
||||
1543ul, 2053ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
|
||||
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul,
|
||||
402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul
|
||||
}};
|
||||
|
||||
template<unsigned int IPrime>
|
||||
static constexpr std::size_t mod(std::size_t hash) { return hash % PRIMES[IPrime]; }
|
||||
|
||||
// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for faster modulo as the
|
||||
// compiler can optimize the modulo code better with a constant known at the compilation.
|
||||
static constexpr const std::array<std::size_t(*)(std::size_t), 40> MOD_PRIME = {{
|
||||
&mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>,
|
||||
&mod<11>, &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, &mod<18>, &mod<19>, &mod<20>,
|
||||
&mod<21>, &mod<22>, &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, &mod<29>, &mod<30>,
|
||||
&mod<31>, &mod<32>, &mod<33>, &mod<34>, &mod<35>, &mod<36>, &mod<37> , &mod<38>, &mod<39>
|
||||
}};
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow the hash table by using prime numbers as bucket count. Slower than tsl::ah::power_of_two_growth_policy in
|
||||
* general but will probably distribute the values around better in the buckets with a poor hash function.
|
||||
*
|
||||
* To allow the compiler to optimize the modulo operation, a lookup table is used with constant primes numbers.
|
||||
*
|
||||
* With a switch the code would look like:
|
||||
* \code
|
||||
* switch(iprime) { // iprime is the current prime of the hash table
|
||||
* case 0: hash % 5ul;
|
||||
* break;
|
||||
* case 1: hash % 17ul;
|
||||
* break;
|
||||
* case 2: hash % 29ul;
|
||||
* break;
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Due to the constant variable in the modulo the compiler is able to optimize the operation
|
||||
* by a series of multiplications, substractions and shifts.
|
||||
*
|
||||
* The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) * 5' in a 64 bits environment.
|
||||
*/
|
||||
class prime_growth_policy {
|
||||
public:
|
||||
explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) {
|
||||
auto it_prime = std::lower_bound(detail::PRIMES.begin(),
|
||||
detail::PRIMES.end(), min_bucket_count_in_out);
|
||||
if(it_prime == detail::PRIMES.end()) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
m_iprime = static_cast<unsigned int>(std::distance(detail::PRIMES.begin(), it_prime));
|
||||
if(min_bucket_count_in_out > 0) {
|
||||
min_bucket_count_in_out = *it_prime;
|
||||
}
|
||||
else {
|
||||
min_bucket_count_in_out = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t bucket_for_hash(std::size_t hash) const noexcept {
|
||||
return detail::MOD_PRIME[m_iprime](hash);
|
||||
}
|
||||
|
||||
std::size_t next_bucket_count() const {
|
||||
if(m_iprime + 1 >= detail::PRIMES.size()) {
|
||||
THROW(std::length_error, "The hash table exceeds its maximum size.");
|
||||
}
|
||||
|
||||
return detail::PRIMES[m_iprime + 1];
|
||||
}
|
||||
|
||||
std::size_t max_bucket_count() const {
|
||||
return detail::PRIMES.back();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_iprime = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_iprime;
|
||||
|
||||
static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= detail::PRIMES.size(),
|
||||
"The type of m_iprime is not big enough.");
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
1766
ios/include/trie/array-hash/array_hash.h
Normal file
1766
ios/include/trie/array-hash/array_hash.h
Normal file
File diff suppressed because it is too large
Load Diff
863
ios/include/trie/array-hash/array_map.h
Normal file
863
ios/include/trie/array-hash/array_map.h
Normal file
@@ -0,0 +1,863 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ARRAY_MAP_H
|
||||
#define TSL_ARRAY_MAP_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "array_hash.h"
|
||||
|
||||
namespace tsl {
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of a cache-conscious string hash map.
|
||||
*
|
||||
* The map stores the strings as `const CharT*`. If `StoreNullTerminator` is true,
|
||||
* the strings are stored with the a null-terminator (the `key()` method of the iterators
|
||||
* will return a pointer to this null-terminated string). Otherwise the null character
|
||||
* is not stored (which allow an economy of 1 byte per string).
|
||||
*
|
||||
* The value `T` must be either nothrow move-constructible, copy-constructible or both.
|
||||
*
|
||||
* The size of a key string is limited to `std::numeric_limits<KeySizeT>::max() - 1`.
|
||||
* That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter.
|
||||
* See `max_key_size()` for an easy access to this limit.
|
||||
*
|
||||
* The number of elements in the map is limited to `std::numeric_limits<IndexSizeT>::max()`.
|
||||
* That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter.
|
||||
* See `max_size()` for an easy access to this limit.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=: always invalidate the iterators.
|
||||
* - insert, emplace, operator[]: always invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
* - shrink_to_fit: always invalidate the iterators.
|
||||
*/
|
||||
template<class CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t,
|
||||
class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>>
|
||||
class array_map {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using char_type = typename ht::char_type;
|
||||
using mapped_type = T;
|
||||
using key_size_type = typename ht::key_size_type;
|
||||
using index_size_type = typename ht::index_size_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
public:
|
||||
array_map(): array_map(ht::DEFAULT_INIT_BUCKET_COUNT) {
|
||||
}
|
||||
|
||||
explicit array_map(size_type bucket_count,
|
||||
const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
array_map(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_map(bucket_count, hash)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_map(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_map(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
array_map(std::initializer_list<std::pair<const CharT*, T>> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_map(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_map& operator=(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_map& operator=(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, const T& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), value);
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, const T& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), value);
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, const T& value) {
|
||||
return m_ht.emplace(key, key_size, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key, T&& value) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::move(value));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key, T&& value) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::move(value));
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size, T&& value) {
|
||||
return m_ht.emplace(key, key_size, std::move(value));
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::iterator_category>::value)
|
||||
{
|
||||
const auto nb_elements_insert = std::distance(first, last);
|
||||
const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size();
|
||||
|
||||
if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
|
||||
reserve(size() + std::size_t(nb_elements_insert));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto it = first; it != last; ++it) {
|
||||
insert_pair(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
void insert(std::initializer_list<std::pair<std::basic_string_view<CharT>, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<std::pair<const CharT*, T>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string_view<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#else
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const CharT* key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, std::char_traits<CharT>::length(key), std::forward<M>(obj));
|
||||
}
|
||||
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign(const std::basic_string<CharT>& key, M&& obj) {
|
||||
return m_ht.insert_or_assign(key.data(), key.size(), std::forward<M>(obj));
|
||||
}
|
||||
#endif
|
||||
template<class M>
|
||||
std::pair<iterator, bool> insert_or_assign_ks(const CharT* key, size_type key_size, M&& obj) {
|
||||
return m_ht.insert_or_assign(key, key_size, std::forward<M>(obj));
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#else
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const CharT* key, Args&&... args) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key, Args&&... args) {
|
||||
return m_ht.emplace(key.data(), key.size(), std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
template<class... Args>
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size, Args&&... args) {
|
||||
return m_ht.emplace(key, key_size, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Erase has an amortized O(1) runtime complexity, but even if it removes the key immediately,
|
||||
* it doesn't do the same for the associated value T.
|
||||
*
|
||||
* T will only be removed when the ratio between the size of the map and
|
||||
* the size of the map + the number of deleted values still stored is low enough.
|
||||
*
|
||||
* To force the deletion you can call shrink_to_fit.
|
||||
*/
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const CharT* key) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*/
|
||||
size_type erase_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.erase(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* @copydoc erase(const_iterator pos)
|
||||
*
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(array_map& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
T& at(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
T& at(const CharT* key) {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const T& at(const CharT* key) const {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
T& at(const std::basic_string<CharT>& key) {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
|
||||
const T& at(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.at(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
T& at_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.at(key, key_size);
|
||||
}
|
||||
|
||||
const T& at_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.at(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
T& at(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
T& at(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
T& at(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.at(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const T& at_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.at(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
T& operator[](const std::basic_string_view<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
|
||||
#else
|
||||
T& operator[](const CharT* key) { return m_ht.access_operator(key, std::char_traits<CharT>::length(key)); }
|
||||
T& operator[](const std::basic_string<CharT>& key) { return m_ht.access_operator(key.data(), key.size()); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
size_type count(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.count(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
size_type count(const CharT* key) const {
|
||||
return m_ht.count(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type count(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.count(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
size_type count_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.count(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
iterator find(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
iterator find_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
const_iterator find_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
/**
|
||||
* Return the `const_iterator it` as an `iterator`.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); }
|
||||
|
||||
/**
|
||||
* Serialize the map through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t`, `float` and `T` must be supported for U.
|
||||
* - `void operator()(const CharT* value, std::size_t value_size);`
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previously serialized map through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t`, `float` and `T` must be supported for U.
|
||||
* - `void operator()(CharT* value_out, std::size_t value_size);`
|
||||
*
|
||||
* If the deserialized hash map type is hash compatible with the serialized map, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
|
||||
* KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the
|
||||
* serialized map. Otherwise the behaviour is undefined with `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `CharT` and `T` of the `array_map` are not the same as the
|
||||
* types used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static array_map deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
array_map map(0);
|
||||
map.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
friend bool operator==(const array_map& lhs, const array_map& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
|
||||
const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size());
|
||||
if(it_element_rhs == rhs.cend() || it.value() != it_element_rhs.value()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const array_map& lhs, const array_map& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(array_map& lhs, array_map& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
template<class U, class V>
|
||||
void insert_pair(const std::pair<U, V>& value) {
|
||||
insert(value.first, value.second);
|
||||
}
|
||||
|
||||
template<class U, class V>
|
||||
void insert_pair(std::pair<U, V>&& value) {
|
||||
insert(value.first, std::move(value.second));
|
||||
}
|
||||
|
||||
public:
|
||||
static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE;
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as
|
||||
* `tsl::array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class T,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_map = array_map<CharT, T, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
||||
664
ios/include/trie/array-hash/array_set.h
Normal file
664
ios/include/trie/array-hash/array_set.h
Normal file
@@ -0,0 +1,664 @@
|
||||
/**
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef TSL_ARRAY_SET_H
|
||||
#define TSL_ARRAY_SET_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "array_hash.h"
|
||||
|
||||
namespace tsl {
|
||||
|
||||
/**
|
||||
* Implementation of a cache-conscious string hash set.
|
||||
*
|
||||
* The set stores the strings as `const CharT*`. If `StoreNullTerminator` is true,
|
||||
* the strings are stored with the a null-terminator (the `key()` method of the iterators
|
||||
* will return a pointer to this null-terminated string). Otherwise the null character
|
||||
* is not stored (which allow an economy of 1 byte per string).
|
||||
*
|
||||
* The size of a key string is limited to `std::numeric_limits<KeySizeT>::max() - 1`.
|
||||
* That is 65 535 characters by default, but can be raised with the `KeySizeT` template parameter.
|
||||
* See `max_key_size()` for an easy access to this limit.
|
||||
*
|
||||
* The number of elements in the set is limited to `std::numeric_limits<IndexSizeT>::max()`.
|
||||
* That is 4 294 967 296 elements, but can be raised with the `IndexSizeT` template parameter.
|
||||
* See `max_size()` for an easy access to this limit.
|
||||
*
|
||||
* Iterators invalidation:
|
||||
* - clear, operator=: always invalidate the iterators.
|
||||
* - insert, emplace, operator[]: always invalidate the iterators.
|
||||
* - erase: always invalidate the iterators.
|
||||
* - shrink_to_fit: always invalidate the iterators.
|
||||
*/
|
||||
template<class CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t,
|
||||
class GrowthPolicy = tsl::ah::power_of_two_growth_policy<2>>
|
||||
class array_set {
|
||||
private:
|
||||
template<typename U>
|
||||
using is_iterator = tsl::detail_array_hash::is_iterator<U>;
|
||||
|
||||
using ht = tsl::detail_array_hash::array_hash<CharT, void, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, GrowthPolicy>;
|
||||
|
||||
public:
|
||||
using char_type = typename ht::char_type;
|
||||
using key_size_type = typename ht::key_size_type;
|
||||
using index_size_type = typename ht::index_size_type;
|
||||
using size_type = typename ht::size_type;
|
||||
using hasher = typename ht::hasher;
|
||||
using key_equal = typename ht::key_equal;
|
||||
using iterator = typename ht::iterator;
|
||||
using const_iterator = typename ht::const_iterator;
|
||||
|
||||
array_set(): array_set(ht::DEFAULT_INIT_BUCKET_COUNT) {
|
||||
}
|
||||
|
||||
explicit array_set(size_type bucket_count,
|
||||
const Hash& hash = Hash()): m_ht(bucket_count, hash, ht::DEFAULT_MAX_LOAD_FACTOR)
|
||||
{
|
||||
}
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
array_set(InputIt first, InputIt last,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_set(bucket_count, hash)
|
||||
{
|
||||
insert(first, last);
|
||||
}
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_set(std::initializer_list<std::basic_string_view<CharT>> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_set(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#else
|
||||
array_set(std::initializer_list<const CharT*> init,
|
||||
size_type bucket_count = ht::DEFAULT_INIT_BUCKET_COUNT,
|
||||
const Hash& hash = Hash()): array_set(bucket_count, hash)
|
||||
{
|
||||
insert(init);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
array_set& operator=(std::initializer_list<std::basic_string_view<CharT>> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#else
|
||||
array_set& operator=(std::initializer_list<const CharT*> ilist) {
|
||||
clear();
|
||||
|
||||
reserve(ilist.size());
|
||||
insert(ilist);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Iterators
|
||||
*/
|
||||
iterator begin() noexcept { return m_ht.begin(); }
|
||||
const_iterator begin() const noexcept { return m_ht.begin(); }
|
||||
const_iterator cbegin() const noexcept { return m_ht.cbegin(); }
|
||||
|
||||
iterator end() noexcept { return m_ht.end(); }
|
||||
const_iterator end() const noexcept { return m_ht.end(); }
|
||||
const_iterator cend() const noexcept { return m_ht.cend(); }
|
||||
|
||||
|
||||
/*
|
||||
* Capacity
|
||||
*/
|
||||
bool empty() const noexcept { return m_ht.empty(); }
|
||||
size_type size() const noexcept { return m_ht.size(); }
|
||||
size_type max_size() const noexcept { return m_ht.max_size(); }
|
||||
size_type max_key_size() const noexcept { return m_ht.max_key_size(); }
|
||||
void shrink_to_fit() { m_ht.shrink_to_fit(); }
|
||||
|
||||
|
||||
/*
|
||||
* Modifiers
|
||||
*/
|
||||
void clear() noexcept { m_ht.clear(); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, bool> insert(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, bool> insert(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(const std::basic_string<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, bool> insert_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.emplace(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<class InputIt, typename std::enable_if<is_iterator<InputIt>::value>::type* = nullptr>
|
||||
void insert(InputIt first, InputIt last) {
|
||||
if(std::is_base_of<std::forward_iterator_tag,
|
||||
typename std::iterator_traits<InputIt>::iterator_category>::value)
|
||||
{
|
||||
const auto nb_elements_insert = std::distance(first, last);
|
||||
const std::size_t nb_free_buckets = std::size_t(float(bucket_count())*max_load_factor()) - size();
|
||||
|
||||
if(nb_elements_insert > 0 && nb_free_buckets < std::size_t(nb_elements_insert)) {
|
||||
reserve(size() + std::size_t(nb_elements_insert));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto it = first; it != last; ++it) {
|
||||
insert(*it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
void insert(std::initializer_list<std::basic_string_view<CharT>> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#else
|
||||
void insert(std::initializer_list<const CharT*> ilist) {
|
||||
insert(ilist.begin(), ilist.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const CharT* key) {
|
||||
return m_ht.emplace(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc emplace_ks(const CharT* key, size_type key_size)
|
||||
*/
|
||||
std::pair<iterator, bool> emplace(const std::basic_string<CharT>& key) {
|
||||
return m_ht.emplace(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* No difference compared to the insert method. Mainly here for coherence with array_map.
|
||||
*/
|
||||
std::pair<iterator, bool> emplace_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.emplace(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
iterator erase(const_iterator pos) { return m_ht.erase(pos); }
|
||||
iterator erase(const_iterator first, const_iterator last) { return m_ht.erase(first, last); }
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
size_type erase(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
size_type erase(const CharT* key) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
size_type erase(const std::basic_string<CharT>& key) {
|
||||
return m_ht.erase(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
size_type erase_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.erase(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
size_type erase(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type erase_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.erase(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void swap(array_set& other) { other.m_ht.swap(m_ht); }
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lookup
|
||||
*/
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
size_type count(const std::basic_string_view<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
|
||||
#else
|
||||
size_type count(const CharT* key) const { return m_ht.count(key, std::char_traits<CharT>::length(key)); }
|
||||
size_type count(const std::basic_string<CharT>& key) const { return m_ht.count(key.data(), key.size()); }
|
||||
#endif
|
||||
size_type count_ks(const CharT* key, size_type key_size) const { return m_ht.count(key, key_size); }
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const
|
||||
*/
|
||||
size_type count(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
size_type count_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.count(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
iterator find(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
iterator find(const CharT* key) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
const_iterator find(const CharT* key) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
iterator find(const std::basic_string<CharT>& key) {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
|
||||
const_iterator find(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.find(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
iterator find_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
const_iterator find_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.find(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
const_iterator find_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.find(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#else
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key));
|
||||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key) {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key) const {
|
||||
return m_ht.equal_range(key.data(), key.size());
|
||||
}
|
||||
#endif
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size) {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size) const {
|
||||
return m_ht.equal_range(key, key_size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef TSL_AH_HAS_STRING_VIEW
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string_view<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const CharT* key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const CharT* key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, std::char_traits<CharT>::length(key), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range(const std::basic_string<CharT>& key, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key.data(), key.size(), precalculated_hash);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Use the hash value 'precalculated_hash' instead of hashing the key. The hash value should be the same
|
||||
* as hash_function()(key). Useful to speed-up the lookup to the value if you already have the hash.
|
||||
*/
|
||||
std::pair<iterator, iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @copydoc equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash)
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator> equal_range_ks(const CharT* key, size_type key_size, std::size_t precalculated_hash) const {
|
||||
return m_ht.equal_range(key, key_size, precalculated_hash);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Bucket interface
|
||||
*/
|
||||
size_type bucket_count() const { return m_ht.bucket_count(); }
|
||||
size_type max_bucket_count() const { return m_ht.max_bucket_count(); }
|
||||
|
||||
|
||||
/*
|
||||
* Hash policy
|
||||
*/
|
||||
float load_factor() const { return m_ht.load_factor(); }
|
||||
float max_load_factor() const { return m_ht.max_load_factor(); }
|
||||
void max_load_factor(float ml) { m_ht.max_load_factor(ml); }
|
||||
|
||||
void rehash(size_type count) { m_ht.rehash(count); }
|
||||
void reserve(size_type count) { m_ht.reserve(count); }
|
||||
|
||||
|
||||
/*
|
||||
* Observers
|
||||
*/
|
||||
hasher hash_function() const { return m_ht.hash_function(); }
|
||||
key_equal key_eq() const { return m_ht.key_eq(); }
|
||||
|
||||
|
||||
/*
|
||||
* Other
|
||||
*/
|
||||
/**
|
||||
* Return the `const_iterator it` as an `iterator`.
|
||||
*/
|
||||
iterator mutable_iterator(const_iterator it) noexcept { return m_ht.mutable_iterator(it); }
|
||||
|
||||
/**
|
||||
* Serialize the set through the `serializer` parameter.
|
||||
*
|
||||
* The `serializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> void operator()(const U& value);` where the types `std::uint64_t` and `float` must be supported for U.
|
||||
* - `void operator()(const CharT* value, std::size_t value_size);`
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, ...) of the types it serializes
|
||||
* in the hands of the `Serializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Serializer>
|
||||
void serialize(Serializer& serializer) const {
|
||||
m_ht.serialize(serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize a previously serialized set through the `deserializer` parameter.
|
||||
*
|
||||
* The `deserializer` parameter must be a function object that supports the following calls:
|
||||
* - `template<typename U> U operator()();` where the types `std::uint64_t` and `float` must be supported for U.
|
||||
* - `void operator()(CharT* value_out, std::size_t value_size);`
|
||||
*
|
||||
* If the deserialized hash set type is hash compatible with the serialized set, the deserialization process can be
|
||||
* sped up by setting `hash_compatible` to true. To be hash compatible, the Hash (take care of the 32-bits vs 64 bits),
|
||||
* KeyEqual, GrowthPolicy, StoreNullTerminator, KeySizeT and IndexSizeT must behave the same than the ones used on the
|
||||
* serialized set. Otherwise the behaviour is undefined with `hash_compatible` sets to true.
|
||||
*
|
||||
* The behaviour is undefined if the type `CharT` of the `array_set` is not the same as the
|
||||
* type used during serialization.
|
||||
*
|
||||
* The implementation leaves binary compatibility (endianness, IEEE 754 for floats, size of int, ...) of the types it
|
||||
* deserializes in the hands of the `Deserializer` function object if compatibility is required.
|
||||
*/
|
||||
template<class Deserializer>
|
||||
static array_set deserialize(Deserializer& deserializer, bool hash_compatible = false) {
|
||||
array_set set(0);
|
||||
set.m_ht.deserialize(deserializer, hash_compatible);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
friend bool operator==(const array_set& lhs, const array_set& rhs) {
|
||||
if(lhs.size() != rhs.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(auto it = lhs.cbegin(); it != lhs.cend(); ++it) {
|
||||
const auto it_element_rhs = rhs.find_ks(it.key(), it.key_size());
|
||||
if(it_element_rhs == rhs.cend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
friend bool operator!=(const array_set& lhs, const array_set& rhs) {
|
||||
return !operator==(lhs, rhs);
|
||||
}
|
||||
|
||||
friend void swap(array_set& lhs, array_set& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
public:
|
||||
static const size_type MAX_KEY_SIZE = ht::MAX_KEY_SIZE;
|
||||
|
||||
private:
|
||||
ht m_ht;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Same as
|
||||
* `tsl::array_set<CharT, Hash, KeyEqual, StoreNullTerminator, KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>`.
|
||||
*/
|
||||
template<class CharT,
|
||||
class Hash = tsl::ah::str_hash<CharT>,
|
||||
class KeyEqual = tsl::ah::str_equal<CharT>,
|
||||
bool StoreNullTerminator = true,
|
||||
class KeySizeT = std::uint16_t,
|
||||
class IndexSizeT = std::uint32_t>
|
||||
using array_pg_set = array_set<CharT, Hash, KeyEqual, StoreNullTerminator,
|
||||
KeySizeT, IndexSizeT, tsl::ah::prime_growth_policy>;
|
||||
|
||||
} //end namespace tsl
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,7 @@ A new flutter plugin project.
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Your Company' => 'email@example.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*', 'src/*.*'
|
||||
s.source_files = 'Classes/**/*', 'src/*.*', 'src/morph/*'
|
||||
s.dependency 'Filament', '~> 1.12.3'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '12.1'
|
||||
@@ -23,13 +23,18 @@ A new flutter plugin project.
|
||||
s.xcconfig = {
|
||||
'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES',
|
||||
'ALWAYS_SEARCH_USER_PATHS' => 'YES',
|
||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/mimetic_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/mimetic_filament/ios/src"',
|
||||
|
||||
'USER_HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/../.symlinks/plugins/mimetic_filament/ios/include" "${PODS_ROOT}/../.symlinks/plugins/mimetic_filament/ios/src", "${PODS_ROOT}/../.symlinks/plugins/mimetic_filament/ios/morph"',
|
||||
'OTHER_CXXFLAGS' => '--std=c++17',
|
||||
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
|
||||
#"CLANG_CXX_LIBRARY" => "libc++"
|
||||
}
|
||||
s.pod_target_xcconfig = {
|
||||
'DEFINES_MODULE' => 'YES',
|
||||
'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386',
|
||||
'OTHER_CFLAGS' => '-fmodules -fcxx-modules'
|
||||
'OTHER_CFLAGS' => '-fmodules -fcxx-modules',
|
||||
#'OTHER_CXXFLAGS' => '--std=c++17',
|
||||
#"CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
|
||||
#"CLANG_CXX_LIBRARY" => "libc++"
|
||||
}
|
||||
s.swift_version = '5.0'
|
||||
end
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "FilamentViewer.hpp"
|
||||
|
||||
#include <filament/Camera.h>
|
||||
#include <filament/ColorGrading.h>
|
||||
#include <filament/Engine.h>
|
||||
@@ -37,6 +36,7 @@
|
||||
#include <gltfio/AssetLoader.h>
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
#include <gltfio/ResourceLoader.h>
|
||||
#include <gltfio/Animator.h>
|
||||
|
||||
#include <camutils/Manipulator.h>
|
||||
|
||||
@@ -50,13 +50,9 @@
|
||||
|
||||
#include <image/KtxUtility.h>
|
||||
|
||||
#include <gltfio/Animator.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace gltfio;
|
||||
@@ -67,6 +63,10 @@ namespace filament {
|
||||
class LightManager;
|
||||
}
|
||||
|
||||
namespace gltfio {
|
||||
MaterialProvider* createGPUMorphShaderLoader(const void* data, uint64_t size, Engine* engine);
|
||||
}
|
||||
|
||||
namespace mimetic {
|
||||
|
||||
const double kNearPlane = 0.05; // 5 cm
|
||||
@@ -76,10 +76,9 @@ const float kAperture = 16.0f;
|
||||
const float kShutterSpeed = 1.0f / 125.0f;
|
||||
const float kSensitivity = 100.0f;
|
||||
|
||||
MaterialProvider* createGPUShaderLoader(Engine* engine);
|
||||
|
||||
FilamentViewer::FilamentViewer(
|
||||
void* layer,
|
||||
const char* shaderPath,
|
||||
LoadResource loadResource,
|
||||
FreeResource freeResource) : _layer(layer),
|
||||
_loadResource(loadResource),
|
||||
@@ -99,8 +98,13 @@ FilamentViewer::FilamentViewer(
|
||||
|
||||
_swapChain = _engine->createSwapChain(_layer);
|
||||
|
||||
// _materialProvider = createGPUShaderLoader(_engine);
|
||||
_materialProvider = createUbershaderLoader(_engine);
|
||||
if(shaderPath) {
|
||||
ResourceBuffer rb = _loadResource(shaderPath);
|
||||
_materialProvider = createGPUMorphShaderLoader(rb.data, rb.size, _engine);
|
||||
// _freeResource((void*)rb.data, rb.size, nullptr);
|
||||
} else {
|
||||
_materialProvider = createUbershaderLoader(_engine);
|
||||
}
|
||||
EntityManager& em = EntityManager::get();
|
||||
_ncm = new NameComponentManager(em);
|
||||
_assetLoader = AssetLoader::create({_engine, _materialProvider, _ncm, &em});
|
||||
@@ -111,6 +115,7 @@ FilamentViewer::FilamentViewer(
|
||||
Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT);
|
||||
//Manipulator<float>::Builder().orbitHomePosition(0.0f, 0.0f, 0.0f).targetPosition(0.0f, 0.0f, 0).build(Mode::ORBIT);
|
||||
_asset = nullptr;
|
||||
|
||||
}
|
||||
|
||||
FilamentViewer::~FilamentViewer() {
|
||||
@@ -138,23 +143,26 @@ void FilamentViewer::loadResources(string relativeResourcePath) {
|
||||
}
|
||||
|
||||
_animator = _asset->getAnimator();
|
||||
_asset->releaseSourceData();
|
||||
// _asset->releaseSourceData();
|
||||
|
||||
_scene->addEntities(_asset->getEntities(), _asset->getEntityCount());
|
||||
};
|
||||
|
||||
void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath) {
|
||||
_resourceLoader->asyncCancelLoad();
|
||||
_resourceLoader->evictResourceData();
|
||||
void FilamentViewer::loadGltf(const char* const uri, const char* const relativeResourcePath, const char* materialInstanceName) {
|
||||
if(_asset) {
|
||||
_resourceLoader->evictResourceData();
|
||||
_scene->removeEntities(_asset->getEntities(), _asset->getEntityCount());
|
||||
_assetLoader->destroyAsset(_asset);
|
||||
}
|
||||
_asset = nullptr;
|
||||
_animator = nullptr;
|
||||
|
||||
ResourceBuffer rbuf = _loadResource(uri);
|
||||
|
||||
// Parse the glTF file and create Filament entities.
|
||||
_asset = _assetLoader->createAssetFromJson((uint8_t*)rbuf.data, rbuf.size);
|
||||
|
||||
|
||||
|
||||
if (!_asset) {
|
||||
std::cerr << "Unable to parse asset" << std::endl;
|
||||
exit(1);
|
||||
@@ -167,6 +175,11 @@ void FilamentViewer::loadGltf(const char* const uri, const char* const relativeR
|
||||
transformToUnitCube();
|
||||
|
||||
startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
}
|
||||
|
||||
void FilamentViewer::createMorpher(const char* meshName, const char* entityName, const char* materialInstanceName) {
|
||||
morphHelper = new gltfio::GPUMorphHelper((FFilamentAsset*)_asset, meshName, entityName, materialInstanceName);
|
||||
}
|
||||
|
||||
|
||||
@@ -241,13 +254,12 @@ void FilamentViewer::render() {
|
||||
_mainCamera->lookAt(eye, target, upward);
|
||||
|
||||
if(_animator) {
|
||||
// typedef std::chrono::high_resolution_clock clock;
|
||||
typedef std::chrono::duration<float, std::milli> duration;
|
||||
/*typedef std::chrono::duration<float, std::milli> duration;
|
||||
duration dur = std::chrono::high_resolution_clock::now() - startTime;
|
||||
if (_animator->getAnimationCount() > 0) {
|
||||
_animator->applyAnimation(0, dur.count() / 1000);
|
||||
}
|
||||
_animator->updateBoneMatrices();
|
||||
_animator->updateBoneMatrices(); */
|
||||
}
|
||||
|
||||
// Render the scene, unless the renderer wants to skip the frame.
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
#include "GPUMorphHelper.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
@@ -51,13 +53,16 @@ namespace mimetic {
|
||||
|
||||
class FilamentViewer {
|
||||
public:
|
||||
FilamentViewer(void* layer, LoadResource loadResource, FreeResource freeResource);
|
||||
FilamentViewer(void* layer, const char* shaderPath, LoadResource loadResource, FreeResource freeResource);
|
||||
~FilamentViewer();
|
||||
void loadGltf(const char* const uri, const char* relativeResourcePath);
|
||||
void loadGltf(const char* const uri, const char* relativeResourcePath, const char* materialInstanceName);
|
||||
void loadSkybox(const char* const skyboxUri, const char* const iblUri);
|
||||
void updateViewportAndCameraProjection(int height, int width, float scaleFactor);
|
||||
void render();
|
||||
void createMorpher(const char* meshName, const char* entityName, const char* materialInstanceName);
|
||||
Manipulator<float>* manipulator;
|
||||
GPUMorphHelper* morphHelper;
|
||||
|
||||
private:
|
||||
void loadResources(std::string relativeResourcePath);
|
||||
void transformToUnitCube();
|
||||
@@ -83,6 +88,7 @@ namespace mimetic {
|
||||
FilamentAsset* _asset = nullptr;
|
||||
NameComponentManager* _ncm;
|
||||
|
||||
|
||||
Entity _sun;
|
||||
Texture* _skyboxTexture;
|
||||
Skybox* _skybox;
|
||||
|
||||
122
ios/src/morph/DependencyGraph.h
Normal file
122
ios/src/morph/DependencyGraph.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_DEPENDENCY_GRAPH_H
|
||||
#define GLTFIO_DEPENDENCY_GRAPH_H
|
||||
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <tsl/robin_set.h>
|
||||
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
namespace filament {
|
||||
class MaterialInstance;
|
||||
class Texture;
|
||||
}
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
/**
|
||||
* Internal graph that enables FilamentAsset to discover "ready-to-render" entities by tracking
|
||||
* the loading status of Texture objects that each entity depends on.
|
||||
*
|
||||
* Renderables connect to a set of material instances, which in turn connect to a set of parameter
|
||||
* names, which in turn connect to a set of texture objects. These relationships are not easily
|
||||
* inspectable using the Filament API or ECS.
|
||||
*
|
||||
* One graph corresponds to a single glTF asset. The graph only contains weak references, it does
|
||||
* not have ownership over any Filament objects. Here's an example:
|
||||
*
|
||||
* Entity Entity Entity Entity
|
||||
* | / \ | /
|
||||
* | / \ | /
|
||||
* Material Material Material
|
||||
* / | \ |
|
||||
* / | \ |
|
||||
* Param Param Param Param
|
||||
* \ / | |
|
||||
* \ / | |
|
||||
* Texture Texture Texture
|
||||
*
|
||||
* Note that the left-most entity in the above graph has no textures, so it becomes ready as soon as
|
||||
* finalize is called.
|
||||
*/
|
||||
class DependencyGraph {
|
||||
public:
|
||||
using Material = filament::MaterialInstance;
|
||||
using Entity = utils::Entity;
|
||||
|
||||
// Pops up to "count" ready-to-render entities off the queue.
|
||||
// If "result" is non-null, returns the number of written items.
|
||||
// If "result" is null, returns the number of available entities.
|
||||
size_t popRenderables(Entity* result, size_t count) noexcept;
|
||||
|
||||
// These are called during the initial asset loader phase.
|
||||
void addEdge(Entity entity, Material* material);
|
||||
void addEdge(Material* material, const char* parameter);
|
||||
|
||||
// This is called at the end of the initial asset loading phase.
|
||||
// Makes a guarantee that no new material nodes or parameter nodes will be added to the graph.
|
||||
void finalize();
|
||||
|
||||
// This can be called after finalization to allow for dynamic addition of entities.
|
||||
// It is slower than finalize() because it checks the readiness of existing materials.
|
||||
void refinalize();
|
||||
|
||||
// These are called after textures have created and decoded.
|
||||
void addEdge(filament::Texture* texture, Material* material, const char* parameter);
|
||||
void markAsReady(filament::Texture* texture);
|
||||
|
||||
private:
|
||||
struct TextureNode {
|
||||
filament::Texture* texture;
|
||||
bool ready;
|
||||
};
|
||||
|
||||
struct MaterialNode {
|
||||
tsl::robin_map<std::string, TextureNode*> params;
|
||||
};
|
||||
|
||||
struct EntityNode {
|
||||
tsl::robin_set<Material*> materials;
|
||||
size_t numReadyMaterials = 0;
|
||||
};
|
||||
|
||||
void checkReadiness(Material* material);
|
||||
void markAsReady(Material* material);
|
||||
TextureNode* getStatus(filament::Texture* texture);
|
||||
|
||||
// The following maps contain the directed edges in the graph.
|
||||
tsl::robin_map<Entity, EntityNode> mEntityToMaterial;
|
||||
tsl::robin_map<Material*, tsl::robin_set<Entity>> mMaterialToEntity;
|
||||
tsl::robin_map<Material*, MaterialNode> mMaterialToTexture;
|
||||
tsl::robin_map<filament::Texture*, tsl::robin_set<Material*>> mTextureToMaterial;
|
||||
|
||||
// Each texture (and its readiness flag) can be referenced from multiple nodes, so we own
|
||||
// a collection of wrapper objects in the following map. This uses std::unique_ptr to allow
|
||||
// nodes to refer to a texture wrapper using a stable weak pointer.
|
||||
tsl::robin_map<filament::Texture*, std::unique_ptr<TextureNode>> mTextureNodes;
|
||||
|
||||
std::queue<Entity> mReadyRenderables;
|
||||
bool mFinalized = false;
|
||||
};
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
#endif // GLTFIO_DEPENDENCY_GRAPH_H
|
||||
71
ios/src/morph/DracoCache.h
Normal file
71
ios/src/morph/DracoCache.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_DRACO_CACHE_H
|
||||
#define GLTFIO_DRACO_CACHE_H
|
||||
|
||||
#include <cgltf.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifndef GLTFIO_DRACO_SUPPORTED
|
||||
#define GLTFIO_DRACO_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
class DracoMesh;
|
||||
|
||||
// Manages a set of Draco meshes that can be looked up using cgltf_buffer_view.
|
||||
//
|
||||
// The cache key is the buffer view that holds the compressed data. This allows the loader to
|
||||
// avoid duplicated work when a single Draco mesh is referenced from multiple primitives.
|
||||
class DracoCache {
|
||||
public:
|
||||
DracoMesh* findOrCreateMesh(const cgltf_buffer_view* key);
|
||||
private:
|
||||
tsl::robin_map<const cgltf_buffer_view*, std::unique_ptr<DracoMesh>> mCache;
|
||||
};
|
||||
|
||||
// Decodes a Draco mesh upon construction and retains the results.
|
||||
//
|
||||
// The DracoMesh API leverages cgltf accessor structs in a way that bears explanation. These are
|
||||
// read / write parameters that tell the decoder where to write the decoded data, and what format
|
||||
// is desired. The buffer_view in the accessor should be null unless decompressed data is already
|
||||
// loaded. This tells the decoder that it should create a buffer_view and a buffer. The buffer
|
||||
// view, the buffer, and the buffer's data are all automatically freed when DracoMesh is destroyed.
|
||||
//
|
||||
// Note that in the gltfio architecture, the AssetLoader has the job of constructing VertexBuffer
|
||||
// objects while the ResourceLoader has the job of populating them asychronously. This means that
|
||||
// our Draco decoder relies on the accessor fields being 100% correct. If we had to be robust
|
||||
// against faulty accessor information, we would need to replace the VertexBuffer object that was
|
||||
// created in the AssetLoader, which would be a messy process.
|
||||
class DracoMesh {
|
||||
public:
|
||||
static DracoMesh* decode(const uint8_t* compressedData, size_t compressedSize);
|
||||
bool getFaceIndices(cgltf_accessor* destination) const;
|
||||
bool getVertexAttributes(uint32_t attributeId, cgltf_accessor* destination) const;
|
||||
~DracoMesh();
|
||||
private:
|
||||
DracoMesh(struct DracoMeshDetails* details);
|
||||
std::unique_ptr<struct DracoMeshDetails> mDetails;
|
||||
};
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
#endif // GLTFIO_DRACO_CACHE_H
|
||||
286
ios/src/morph/FFilamentAsset.h
Normal file
286
ios/src/morph/FFilamentAsset.h
Normal file
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_FFILAMENTASSET_H
|
||||
#define GLTFIO_FFILAMENTASSET_H
|
||||
|
||||
#include <gltfio/FilamentAsset.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
|
||||
#include <gltfio/MaterialProvider.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <cgltf.h>
|
||||
|
||||
#include "upcast.h"
|
||||
#include "DependencyGraph.h"
|
||||
#include "DracoCache.h"
|
||||
#include "FFilamentInstance.h"
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <trie/htrie_map.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define GLTFIO_VERBOSE 0
|
||||
#define GLTFIO_WARN(msg)
|
||||
#else
|
||||
#define GLTFIO_VERBOSE 1
|
||||
#define GLTFIO_WARN(msg) slog.w << msg << io::endl
|
||||
#endif
|
||||
|
||||
namespace utils {
|
||||
class NameComponentManager;
|
||||
class EntityManager;
|
||||
}
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
class Animator;
|
||||
class Wireframe;
|
||||
|
||||
// Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer().
|
||||
struct BufferSlot {
|
||||
const cgltf_accessor* accessor;
|
||||
cgltf_attribute_type attribute;
|
||||
int bufferIndex; // for vertex buffers only
|
||||
int morphTarget; // 0 if no morphing, otherwise 1-based index
|
||||
filament::VertexBuffer* vertexBuffer;
|
||||
filament::IndexBuffer* indexBuffer;
|
||||
};
|
||||
|
||||
// Encapsulates a connection between Texture and MaterialInstance.
|
||||
struct TextureSlot {
|
||||
const cgltf_texture* texture;
|
||||
filament::MaterialInstance* materialInstance;
|
||||
const char* materialParameter;
|
||||
filament::TextureSampler sampler;
|
||||
bool srgb;
|
||||
};
|
||||
|
||||
// MeshCache
|
||||
// ---------
|
||||
// If a given glTF mesh is referenced by multiple glTF nodes, then it generates a separate Filament
|
||||
// renderable for each of those nodes. All renderables generated by a given mesh share a common set
|
||||
// of VertexBuffer and IndexBuffer objects. To achieve the sharing behavior, the loader maintains a
|
||||
// small cache. The cache keys are glTF mesh definitions and the cache entries are lists of
|
||||
// primitives, where a "primitive" is a reference to a Filament VertexBuffer and IndexBuffer.
|
||||
struct Primitive {
|
||||
filament::VertexBuffer* vertices = nullptr;
|
||||
filament::IndexBuffer* indices = nullptr;
|
||||
filament::Aabb aabb; // object-space bounding box
|
||||
UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes)
|
||||
uint8_t morphPositions[4] = {}; // Buffer indices for MORPH_POSITION_0, MORPH_POSITION_1 etc.
|
||||
uint8_t morphTangents[4] = {}; // Buffer indices for MORPH_TANGENTS_0, MORPH_TANGENTS_1, etc.
|
||||
};
|
||||
using MeshCache = tsl::robin_map<const cgltf_mesh*, std::vector<Primitive>>;
|
||||
|
||||
// MatInstanceCache
|
||||
// ----------------
|
||||
// Each glTF material definition corresponds to a single filament::MaterialInstance, which are
|
||||
// temporarily cached during loading. The filament::Material objects that are used to create instances are
|
||||
// cached in MaterialProvider. If a given glTF material is referenced by multiple glTF meshes, then
|
||||
// their corresponding filament primitives will share the same Filament MaterialInstance and UvMap.
|
||||
// The UvMap is a mapping from each texcoord slot in glTF to one of Filament's 2 texcoord sets.
|
||||
struct MaterialEntry {
|
||||
filament::MaterialInstance* instance;
|
||||
UvMap uvmap;
|
||||
};
|
||||
using MatInstanceCache = tsl::robin_map<intptr_t, MaterialEntry>;
|
||||
|
||||
struct FFilamentAsset : public FilamentAsset {
|
||||
FFilamentAsset(filament::Engine* engine, utils::NameComponentManager* names,
|
||||
utils::EntityManager* entityManager, const cgltf_data* srcAsset) :
|
||||
mEngine(engine), mNameManager(names), mEntityManager(entityManager) {
|
||||
mSourceAsset.reset(new SourceAsset {(cgltf_data*)srcAsset});
|
||||
}
|
||||
|
||||
~FFilamentAsset();
|
||||
|
||||
size_t getEntityCount() const noexcept {
|
||||
return mEntities.size();
|
||||
}
|
||||
|
||||
const utils::Entity* getEntities() const noexcept {
|
||||
return mEntities.empty() ? nullptr : mEntities.data();
|
||||
}
|
||||
|
||||
const utils::Entity* getLightEntities() const noexcept {
|
||||
return mLightEntities.empty() ? nullptr : mLightEntities.data();
|
||||
}
|
||||
|
||||
size_t getLightEntityCount() const noexcept {
|
||||
return mLightEntities.size();
|
||||
}
|
||||
|
||||
const utils::Entity* getCameraEntities() const noexcept {
|
||||
return mCameraEntities.empty() ? nullptr : mCameraEntities.data();
|
||||
}
|
||||
|
||||
size_t getCameraEntityCount() const noexcept {
|
||||
return mCameraEntities.size();
|
||||
}
|
||||
|
||||
utils::Entity getRoot() const noexcept {
|
||||
return mRoot;
|
||||
}
|
||||
|
||||
size_t popRenderables(utils::Entity* entities, size_t count) noexcept {
|
||||
return mDependencyGraph.popRenderables(entities, count);
|
||||
}
|
||||
|
||||
size_t getMaterialInstanceCount() const noexcept {
|
||||
return mMaterialInstances.size();
|
||||
}
|
||||
|
||||
const filament::MaterialInstance* const* getMaterialInstances() const noexcept {
|
||||
return mMaterialInstances.data();
|
||||
}
|
||||
|
||||
filament::MaterialInstance* const* getMaterialInstances() noexcept {
|
||||
return mMaterialInstances.data();
|
||||
}
|
||||
|
||||
size_t getResourceUriCount() const noexcept {
|
||||
return mResourceUris.size();
|
||||
}
|
||||
|
||||
const char* const* getResourceUris() const noexcept {
|
||||
return mResourceUris.data();
|
||||
}
|
||||
|
||||
filament::Aabb getBoundingBox() const noexcept {
|
||||
return mBoundingBox;
|
||||
}
|
||||
|
||||
const char* getName(utils::Entity entity) const noexcept;
|
||||
|
||||
const char* getExtras(utils::Entity entity) const noexcept;
|
||||
|
||||
utils::Entity getFirstEntityByName(const char* name) noexcept;
|
||||
|
||||
size_t getEntitiesByName(const char* name, utils::Entity* entities,
|
||||
size_t maxCount) const noexcept;
|
||||
|
||||
size_t getEntitiesByPrefix(const char* prefix, utils::Entity* entities,
|
||||
size_t maxCount) const noexcept;
|
||||
|
||||
Animator* getAnimator() noexcept;
|
||||
|
||||
utils::Entity getWireframe() noexcept;
|
||||
|
||||
filament::Engine* getEngine() const noexcept {
|
||||
return mEngine;
|
||||
}
|
||||
|
||||
void releaseSourceData() noexcept;
|
||||
|
||||
const void* getSourceAsset() const noexcept {
|
||||
return mSourceAsset.get() ? mSourceAsset->hierarchy : nullptr;
|
||||
}
|
||||
|
||||
FilamentInstance** getAssetInstances() noexcept {
|
||||
return (FilamentInstance**) mInstances.data();
|
||||
}
|
||||
|
||||
size_t getAssetInstanceCount() const noexcept {
|
||||
return mInstances.size();
|
||||
}
|
||||
|
||||
void takeOwnership(filament::Texture* texture) {
|
||||
mTextures.push_back(texture);
|
||||
}
|
||||
|
||||
void bindTexture(const TextureSlot& tb, filament::Texture* texture) {
|
||||
tb.materialInstance->setParameter(tb.materialParameter, texture, tb.sampler);
|
||||
mDependencyGraph.addEdge(texture, tb.materialInstance, tb.materialParameter);
|
||||
}
|
||||
|
||||
bool isInstanced() const {
|
||||
return mInstances.size() > 0;
|
||||
}
|
||||
|
||||
filament::Engine* mEngine;
|
||||
utils::NameComponentManager* mNameManager;
|
||||
utils::EntityManager* mEntityManager;
|
||||
std::vector<utils::Entity> mEntities;
|
||||
std::vector<utils::Entity> mLightEntities;
|
||||
std::vector<utils::Entity> mCameraEntities;
|
||||
std::vector<filament::MaterialInstance*> mMaterialInstances;
|
||||
std::vector<filament::VertexBuffer*> mVertexBuffers;
|
||||
std::vector<filament::BufferObject*> mBufferObjects;
|
||||
std::vector<filament::IndexBuffer*> mIndexBuffers;
|
||||
std::vector<filament::Texture*> mTextures;
|
||||
filament::Aabb mBoundingBox;
|
||||
utils::Entity mRoot;
|
||||
std::vector<FFilamentInstance*> mInstances;
|
||||
SkinVector mSkins; // unused for instanced assets
|
||||
Animator* mAnimator = nullptr;
|
||||
Wireframe* mWireframe = nullptr;
|
||||
bool mResourcesLoaded = false;
|
||||
DependencyGraph mDependencyGraph;
|
||||
tsl::htrie_map<char, std::vector<utils::Entity>> mNameToEntity;
|
||||
tsl::robin_map<utils::Entity, utils::CString> mNodeExtras;
|
||||
utils::CString mAssetExtras;
|
||||
|
||||
// Sentinels for situations where ResourceLoader needs to generate data.
|
||||
const cgltf_accessor mGenerateNormals = {};
|
||||
const cgltf_accessor mGenerateTangents = {};
|
||||
|
||||
// Encapsulates reference-counted source data, which includes the cgltf hierachy
|
||||
// and potentially also includes buffer data that can be uploaded to the GPU.
|
||||
struct SourceAsset {
|
||||
~SourceAsset() { cgltf_free(hierarchy); }
|
||||
cgltf_data* hierarchy;
|
||||
DracoCache dracoCache;
|
||||
utils::FixedCapacityVector<uint8_t> glbData;
|
||||
};
|
||||
|
||||
// We used shared ownership for the raw cgltf data in order to permit ResourceLoader to
|
||||
// complete various asynchronous work (e.g. uploading buffers to the GPU) even after the asset
|
||||
// or ResourceLoader have been destroyed.
|
||||
using SourceHandle = std::shared_ptr<SourceAsset>;
|
||||
SourceHandle mSourceAsset;
|
||||
|
||||
// Transient source data that can freed via releaseSourceData:
|
||||
std::vector<BufferSlot> mBufferSlots;
|
||||
std::vector<TextureSlot> mTextureSlots;
|
||||
std::vector<const char*> mResourceUris;
|
||||
NodeMap mNodeMap; // unused for instanced assets
|
||||
std::vector<std::pair<const cgltf_primitive*, filament::VertexBuffer*> > mPrimitives;
|
||||
MatInstanceCache mMatInstanceCache;
|
||||
MeshCache mMeshCache;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(FilamentAsset)
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
#endif // GLTFIO_FFILAMENTASSET_H
|
||||
64
ios/src/morph/FFilamentInstance.h
Normal file
64
ios/src/morph/FFilamentInstance.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_FFILAMENTINSTANCE_H
|
||||
#define GLTFIO_FFILAMENTINSTANCE_H
|
||||
|
||||
#include <gltfio/FilamentInstance.h>
|
||||
|
||||
#include <utils/Entity.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "upcast.h"
|
||||
|
||||
struct cgltf_node;
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
struct FFilamentAsset;
|
||||
class Animator;
|
||||
|
||||
struct Skin {
|
||||
std::string name;
|
||||
std::vector<filament::math::mat4f> inverseBindMatrices;
|
||||
std::vector<utils::Entity> joints;
|
||||
std::vector<utils::Entity> targets;
|
||||
};
|
||||
|
||||
using SkinVector = std::vector<Skin>;
|
||||
using NodeMap = tsl::robin_map<const cgltf_node*, utils::Entity>;
|
||||
|
||||
struct FFilamentInstance : public FilamentInstance {
|
||||
std::vector<utils::Entity> entities;
|
||||
utils::Entity root;
|
||||
Animator* animator;
|
||||
FFilamentAsset* owner;
|
||||
SkinVector skins;
|
||||
NodeMap nodeMap;
|
||||
Animator* getAnimator() noexcept;
|
||||
};
|
||||
|
||||
FILAMENT_UPCAST(FilamentInstance)
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
#endif // GLTFIO_FFILAMENTINSTANCE_H
|
||||
368
ios/src/morph/GPUMorphHelper.cpp
Normal file
368
ios/src/morph/GPUMorphHelper.cpp
Normal file
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
*/
|
||||
|
||||
#include "GPUMorphHelper.h"
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <filament/BufferObject.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <filamat/Package.h>
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
#include <filament/Color.h>
|
||||
|
||||
#include "GltfEnums.h"
|
||||
#include "TangentsJob.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
using namespace filament;
|
||||
using namespace filamat;
|
||||
using namespace filament::math;
|
||||
using namespace utils;
|
||||
namespace gltfio {
|
||||
|
||||
static constexpr uint8_t kUnused = 0xff;
|
||||
|
||||
uint32_t computeBindingSize(const cgltf_accessor *accessor);
|
||||
|
||||
uint32_t computeBindingSize(const cgltf_accessor *accessor);
|
||||
|
||||
uint32_t computeBindingOffset(const cgltf_accessor *accessor);
|
||||
|
||||
static const auto FREE_CALLBACK = [](void *mem, size_t s, void *) {
|
||||
free(mem);
|
||||
};
|
||||
|
||||
GPUMorphHelper::GPUMorphHelper(FFilamentAsset *asset, const char* meshName, const char* entityName, const char* materialInstanceName) : mAsset(asset) {
|
||||
|
||||
cgltf_size num_primitives = 0;
|
||||
NodeMap &sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap
|
||||
: asset->mNodeMap;
|
||||
|
||||
for (auto pair : sourceNodes) {
|
||||
cgltf_node const *node = pair.first;
|
||||
cgltf_mesh const *mesh = node->mesh;
|
||||
|
||||
if (mesh) {
|
||||
std::cout << "Mesh " << mesh->name <<std::endl;
|
||||
if(strcmp(meshName, mesh->name) == 0) {
|
||||
targetMesh = mesh;
|
||||
num_primitives = mesh->primitives_count;
|
||||
for (cgltf_size pi = 0, count = mesh->primitives_count; pi < count; ++pi) {
|
||||
addPrimitive(mesh, pi, &mMorphTable[pair.second]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createTextures();
|
||||
|
||||
|
||||
}
|
||||
|
||||
GPUMorphHelper::~GPUMorphHelper() {
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Creates the texture that will store the morphable attributes. The texture will be sized according to the total number of vertices in the mesh, meaning all primitives share the same texture.
|
||||
///
|
||||
void GPUMorphHelper::createTextures() {
|
||||
auto materialInstances = mAsset->getMaterialInstances();
|
||||
auto &engine = *(mAsset->mEngine);
|
||||
|
||||
for (auto &entry : mMorphTable) {
|
||||
for (auto prim : entry.second.primitives) {
|
||||
// for a single morph target, each vertex will be assigned 2 pixels, corresponding to a position vec3 and a normal vec3
|
||||
// these two vectors will be laid out adjacent in memory
|
||||
// the total texture "width" is the total number of these pixels
|
||||
// morph targets are then assigned to the depth channel
|
||||
auto textureWidth = prim.numVertices * 2;
|
||||
|
||||
// the total size of the texture in bytes
|
||||
// equal to (numVertices * numAttributes * vectorSize (3) * sizeof(float) * numMorphTargets)
|
||||
auto textureSize = textureWidth * 3 * sizeof(float) * prim.numTargets;
|
||||
auto textureBuffer = (float *const) malloc(textureSize);
|
||||
|
||||
if(!textureBuffer) {
|
||||
std::cout << "Error allocating texture buffer" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
uint32_t offset = 0;
|
||||
|
||||
// assume the primitive morph target source buffer is laid out like:
|
||||
// |target0_v0_pos * 3|target0_v0_norm * 3|target0_v1_pos * 3|target0_v1_norm * 3|...|target1_v0_pos * 3|target1_v0_norm * 3|target1_v1_pos * 3|target1_v1_norm * 3|...
|
||||
// where:
|
||||
// - target0/target1/etc is the first/second/etc morph target
|
||||
// - v0/v1/etc is the first/second/etc vertex
|
||||
// - pos/norm are each 3-float vectors
|
||||
for (auto &target : prim.targets) {
|
||||
if(target.type == cgltf_attribute_type_position
|
||||
|| target.type == cgltf_attribute_type_normal
|
||||
) {
|
||||
memcpy(textureBuffer+offset, target.bufferObject, target.bufferSize);
|
||||
offset += int(target.bufferSize / sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
prim.texture = Texture::Builder()
|
||||
.width(textureWidth) //
|
||||
.height(1)
|
||||
.depth(prim.numTargets)
|
||||
.sampler(Texture::Sampler::SAMPLER_2D_ARRAY)
|
||||
.format(Texture::InternalFormat::RGB32F)
|
||||
.levels(0x01)
|
||||
.build(engine);
|
||||
|
||||
Texture::PixelBufferDescriptor descriptor(
|
||||
textureBuffer,
|
||||
textureSize,
|
||||
Texture::Format::RGB,
|
||||
Texture::Type::FLOAT,
|
||||
FREE_CALLBACK,
|
||||
nullptr);
|
||||
prim.texture->setImage(engine, 0, 0,0, 0, textureWidth, 1, prim.numTargets, std::move(descriptor));
|
||||
|
||||
for(int i = 0; i < mAsset->getMaterialInstanceCount(); i++) {
|
||||
const char* name = materialInstances[i]->getName();
|
||||
std::cout << name << std::endl;
|
||||
if(strcmp(name, prim.materialName) == 0) {
|
||||
prim.materialInstance = materialInstances[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!prim.materialInstance) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// this won't work if material instance is shared between primitives?
|
||||
prim.materialInstance->setParameter("dimensions", filament::math::int3 { prim.numVertices * 2, numAttributes, prim.numTargets });
|
||||
prim.materialInstance->setParameter("morphTargets", prim.texture, TextureSampler());
|
||||
float weights[prim.numTargets];
|
||||
memset(weights, 0, prim.numTargets * sizeof(float));
|
||||
prim.materialInstance->setParameter("morphTargetWeights", weights, prim.numTargets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUMorphHelper::applyWeights(float const *weights, size_t count, int primitiveIndex) noexcept {
|
||||
auto materialInstance = mAsset->getMaterialInstances()[primitiveIndex];
|
||||
materialInstance->setParameter("morphTargetWeights", weights, count);
|
||||
// assert(count <= numTargets);
|
||||
}
|
||||
|
||||
void
|
||||
GPUMorphHelper::addPrimitive(cgltf_mesh const *mesh, int primitiveIndex, TableEntry *entry) {
|
||||
auto &engine = *mAsset->mEngine;
|
||||
const cgltf_primitive &prim = mesh->primitives[primitiveIndex];
|
||||
|
||||
const auto &gltfioPrim = mAsset->mMeshCache.at(mesh)[primitiveIndex];
|
||||
VertexBuffer *vertexBuffer = gltfioPrim.vertices;
|
||||
|
||||
entry->primitives.push_back({vertexBuffer});
|
||||
|
||||
auto &morphHelperPrim = entry->primitives.back();
|
||||
morphHelperPrim.materialName = prim.material->name;
|
||||
morphHelperPrim.numTargets = prim.targets_count;
|
||||
morphHelperPrim.numVertices = vertexBuffer->getVertexCount();
|
||||
cgltf_size maxIndex = 0;
|
||||
for(int i = 0; i < prim.indices->count; i++) {
|
||||
maxIndex = std::max(cgltf_accessor_read_index(prim.indices, i), maxIndex);
|
||||
}
|
||||
|
||||
std::cout << "Max index for primitive index " << primitiveIndex << " is " << maxIndex << " and numVertices was " << morphHelperPrim.numVertices << std::endl;
|
||||
|
||||
|
||||
const cgltf_accessor *previous = nullptr;
|
||||
|
||||
// for this primitive, iterate over every target
|
||||
for (int targetIndex = 0; targetIndex < prim.targets_count; targetIndex++) {
|
||||
const cgltf_morph_target &morphTarget = prim.targets[targetIndex];
|
||||
for (cgltf_size aindex = 0; aindex < morphTarget.attributes_count; aindex++) {
|
||||
const cgltf_attribute &attribute = morphTarget.attributes[aindex];
|
||||
const cgltf_accessor *accessor = attribute.data;
|
||||
const cgltf_attribute_type atype = attribute.type;
|
||||
if (atype == cgltf_attribute_type_tangent) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
atype == cgltf_attribute_type_normal ||
|
||||
atype == cgltf_attribute_type_position
|
||||
) {
|
||||
|
||||
//
|
||||
// the texture needs to be sized according to the total number of vertices in the mesh
|
||||
// this is identified by the highest vertex index of all primitives in the mesh
|
||||
//
|
||||
// if(numVertices == 0)
|
||||
// numVertices = accessor->count;
|
||||
//assert(numVertices == accessor->count);
|
||||
|
||||
// All position & normal attributes must have the same data type.
|
||||
assert_invariant(
|
||||
!previous || previous->component_type == accessor->component_type);
|
||||
assert_invariant(!previous || previous->type == accessor->type);
|
||||
previous = accessor;
|
||||
|
||||
// This should always be non-null, but don't crash if the glTF is malformed.
|
||||
if (accessor->buffer_view) {
|
||||
auto bufferData = (const uint8_t *) accessor->buffer_view->buffer->data;
|
||||
assert_invariant(bufferData);
|
||||
const uint8_t *data = computeBindingOffset(accessor) + bufferData;
|
||||
const uint32_t size = computeBindingSize(accessor);
|
||||
morphHelperPrim.targets.push_back({data, size, targetIndex, atype});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//VertexBuffer* vBuf = VertexBuffer::Builder()
|
||||
// .vertexCount(numVertices)
|
||||
// .bufferCount(numPrimitives)
|
||||
// .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT4, 0)
|
||||
// .build(*engine);
|
||||
|
||||
//numIndices = maxIndex+1; */
|
||||
//numIndices = prim.indices->count;
|
||||
|
||||
/*indicesBuffer = (uint32_t*)malloc(sizeof(unsigned int) * prim.indices->count);
|
||||
|
||||
//materialInstance->setParameter("vertexIndices", indicesBuffer, numIndices);
|
||||
|
||||
//.require(VertexAttribute::UV0)
|
||||
//.require(MaterialBuilder.VertexAttribute.CUSTOM0)
|
||||
//MaterialBuilder::init();
|
||||
//MaterialBuilder builder = MaterialBuilder()
|
||||
// .name("DefaultMaterial")
|
||||
// .platform(MaterialBuilder::Platform::MOBILE)
|
||||
// .targetApi(MaterialBuilder::TargetApi::ALL)
|
||||
// .optimization(MaterialBuilderBase::Optimization::NONE)
|
||||
// .shading(MaterialBuilder::Shading::LIT)
|
||||
// .parameter(MaterialBuilder::UniformType::FLOAT3, "baseColor")
|
||||
// .parameter(MaterialBuilder::UniformType::INT3, "dimensions")
|
||||
// .parameter(MaterialBuilder::UniformType::FLOAT, numTargets, MaterialBuilder::ParameterPrecision::DEFAULT, "morphTargetWeights")
|
||||
// .parameter(MaterialBuilder::SamplerType::SAMPLER_2D_ARRAY, MaterialBuilder::SamplerFormat::FLOAT, MaterialBuilder::ParameterPrecision::DEFAULT, "morphTargets")
|
||||
// .vertexDomain(VertexDomain::WORLD)
|
||||
// .material(R"SHADER(void material(inout MaterialInputs material) {
|
||||
// prepareMaterial(material);
|
||||
// material.baseColor.rgb = materialParams.baseColor;
|
||||
// })SHADER")
|
||||
// .materialVertex(R"SHADER(
|
||||
// vec3 getMorphTarget(int vertexIndex, int morphTargetIndex) {
|
||||
// // our texture is laid out as (x,y,z) where y is 1, z is the number of morph targets, and x is the number of vertices * 2 (multiplication accounts for position + normal)
|
||||
// // UV coordinates are normalized to (-1,1), so we divide the current vertex index by the total number of vertices to find the correct coordinate for this vertex
|
||||
// vec3 uv = vec3(
|
||||
// (float(vertexIndex) + 0.5) / float(materialParams.dimensions.x),
|
||||
// 0.0f,
|
||||
// //(float(morphTargetIndex) + 0.5f) / float(materialParams.dimensions.z));
|
||||
// float(morphTargetIndex));
|
||||
// return texture(materialParams_morphTargets, uv).xyz;
|
||||
// }
|
||||
//
|
||||
// void materialVertex(inout MaterialVertexInputs material) {
|
||||
// return;
|
||||
// // for every morph target
|
||||
// for(int morphTargetIndex = 0; morphTargetIndex < materialParams.dimensions.z; morphTargetIndex++) {
|
||||
//
|
||||
// // get the weight to apply
|
||||
// float weight = materialParams.morphTargetWeights[morphTargetIndex];
|
||||
//
|
||||
// // get the ID of this vertex, which will be the x-offset of the position attribute in the texture sampler
|
||||
// int vertexId = getVertexIndex();
|
||||
//
|
||||
// // get the position of the target for this vertex
|
||||
// vec3 morphTargetPosition = getMorphTarget(vertexId, morphTargetIndex);
|
||||
// // update the world position of this vertex
|
||||
// material.worldPosition.xyz += (weight * morphTargetPosition);
|
||||
//
|
||||
// // increment the vertexID by half the size of the texture to get the x-offset of the normal (all positions stored in the first half, all normals stored in the second half)
|
||||
//
|
||||
// vertexId += (materialParams.dimensions.x / 2);
|
||||
//
|
||||
// // get the normal of this target for this vertex
|
||||
// vec3 morphTargetNormal = getMorphTarget(vertexId, morphTargetIndex);
|
||||
// material.worldNormal += (weight * morphTargetNormal);
|
||||
// }
|
||||
// mat4 transform = getWorldFromModelMatrix();
|
||||
// material.worldPosition = mulMat4x4Float3(transform, material.worldPosition.xyz);
|
||||
// })SHADER");
|
||||
//
|
||||
//Package pkg = builder.build(mAsset->mEngine->getJobSystem());
|
||||
//Material* material = Material::Builder().package(pkg.getData(), pkg.getSize())
|
||||
// .build(*mAsset->mEngine);
|
||||
|
||||
//size_t normal_size = sizeof(short4);
|
||||
//assert(textureWidth * (position_size + normal_size) == textureSize);
|
||||
//assert(textureWidth * position_size == textureSize);
|
||||
/*__android_log_print(ANDROID_LOG_INFO, "MyTag", "Expected size %d width at level 0 %d height", Texture::PixelBufferDescriptor::computeDataSize(Texture::Format::RGB,
|
||||
Texture::Type::FLOAT, 24, 1, 4), texture->getWidth(0),texture->getHeight(0)); */
|
||||
|
||||
/* Texture::PixelBufferDescriptor descriptor(
|
||||
textureBuffer,
|
||||
textureSize,
|
||||
Texture::Format::RGB,
|
||||
Texture::Type::FLOAT,
|
||||
4, 0,0, 24,
|
||||
FREE_CALLBACK,
|
||||
nullptr); */
|
||||
|
||||
/*for(int i = 0; i < int(textureSize / sizeof(float)); i++) {
|
||||
__android_log_print(ANDROID_LOG_INFO, "MyTag", "offset %d %f", i, *(textureBuffer+i));
|
||||
//}*/
|
||||
//std::cout << "Checking for " << materialInstanceName << std::endl;
|
||||
//if(materialInstanceName) {
|
||||
// for(int i = 0; i < asset->getMaterialInstanceCount(); i++) {
|
||||
// const char* name = instances[i]->getName();
|
||||
// std::cout << name << std::endl;
|
||||
// if(strcmp(name, materialInstanceName) == 0) {
|
||||
// materialInstance = instances[i];
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//} else {
|
||||
// materialInstance = instances[0];
|
||||
//}
|
||||
//
|
||||
//if(!materialInstance) {
|
||||
// exit(-1);
|
||||
//}
|
||||
|
||||
// std::cout << std::endl;
|
||||
/* for (int i = 0; i < 4; i++) {
|
||||
morphHelperPrim.positions[i] = gltfioPrim.morphPositions[i];
|
||||
morphHelperPrim.tangents[i] = gltfioPrim.morphTangents[i];
|
||||
} */
|
||||
|
||||
// applyTextures(materialInstance);
|
||||
|
||||
/* const Entity* entities = mAsset->getEntities();
|
||||
for(int i=0; i < mAsset->getEntityCount();i++) {
|
||||
std::cout << mAsset->getName(entities[i]);
|
||||
} */
|
||||
|
||||
// Entity entity = mAsset->getFirstEntityByName(entityName);
|
||||
// RenderableManager::Instance rInst = mAsset->mEngine->getRenderableManager().getInstance(entity);
|
||||
// for(int i = 0; i<num_primitives;i++) {
|
||||
// mAsset->mEngine->getRenderableManager().setMaterialInstanceAt(rInst, i, materialInstance);
|
||||
// }
|
||||
87
ios/src/morph/GPUMorphHelper.h
Normal file
87
ios/src/morph/GPUMorphHelper.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
*/
|
||||
|
||||
#include "FFilamentAsset.h"
|
||||
#include "FFilamentInstance.h"
|
||||
#include "filament/Texture.h"
|
||||
#include "filament/Engine.h"
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
using namespace filament;
|
||||
|
||||
struct cgltf_node;
|
||||
struct cgltf_mesh;
|
||||
struct cgltf_primitive;
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
///
|
||||
/// A GPUMorphHelper instance can be created per mesh (this avoids creating textures for meshes that do not require animation).
|
||||
/// For each primitive in the mesh, a texture is created to store the target positions and normals.
|
||||
/// The texture is laid out as x * 1 * z, where z is the number of morph targets and x is the number of vertices for the primitive.
|
||||
/// A MaterialInstance is created for each primitive, then applied to the entity identified by entityName.
|
||||
///
|
||||
class GPUMorphHelper {
|
||||
public:
|
||||
using Entity = utils::Entity;
|
||||
|
||||
GPUMorphHelper(FFilamentAsset *asset, const char* meshName, const char* entityName, const char* materialInstanceName);
|
||||
|
||||
~GPUMorphHelper();
|
||||
|
||||
void applyWeights(float const *weights, size_t count, int primitiveIndex) noexcept;
|
||||
|
||||
private:
|
||||
struct GltfTarget {
|
||||
const void *bufferObject;
|
||||
uint32_t bufferSize;
|
||||
int morphTargetIndex;
|
||||
cgltf_attribute_type type;
|
||||
};
|
||||
|
||||
struct GltfPrimitive {
|
||||
filament::VertexBuffer *vertexBuffer;
|
||||
Texture* texture;
|
||||
std::vector <GltfTarget> targets; // TODO: flatten this?
|
||||
const char* materialName;
|
||||
cgltf_size numTargets = 0;
|
||||
cgltf_size numVertices = 0;
|
||||
MaterialInstance* materialInstance = nullptr;
|
||||
};
|
||||
|
||||
struct TableEntry {
|
||||
std::vector <GltfPrimitive> primitives; // TODO: flatten this?
|
||||
};
|
||||
|
||||
int numAttributes = 2; // position & normal
|
||||
|
||||
uint32_t* indicesBuffer = nullptr;
|
||||
|
||||
void addPrimitive(cgltf_mesh const *mesh, int primitiveIndex, TableEntry *entry);
|
||||
|
||||
void createTextures();
|
||||
|
||||
|
||||
cgltf_mesh const* targetMesh;
|
||||
|
||||
tsl::robin_map <Entity, TableEntry> mMorphTable;
|
||||
FFilamentAsset *mAsset;
|
||||
};
|
||||
}
|
||||
235
ios/src/morph/GPUMorphShaderLoader.cpp
Normal file
235
ios/src/morph/GPUMorphShaderLoader.cpp
Normal file
@@ -0,0 +1,235 @@
|
||||
#include <gltfio/MaterialProvider.h>
|
||||
|
||||
#include <filamat/MaterialBuilder.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "gltfio/resources/gltfresources_lite.h"
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::math;
|
||||
using namespace gltfio;
|
||||
using namespace utils;
|
||||
|
||||
namespace {
|
||||
|
||||
using CullingMode = MaterialInstance::CullingMode;
|
||||
|
||||
class GPUMorphShaderLoader : public MaterialProvider {
|
||||
public:
|
||||
GPUMorphShaderLoader(const void* mData, uint64_t size, filament::Engine* engine);
|
||||
~GPUMorphShaderLoader() {}
|
||||
|
||||
MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap,
|
||||
const char* label) override;
|
||||
|
||||
size_t getMaterialsCount() const noexcept override;
|
||||
const Material* const* getMaterials() const noexcept override;
|
||||
void destroyMaterials() override;
|
||||
|
||||
bool needsDummyData(VertexAttribute attrib) const noexcept override {
|
||||
switch (attrib) {
|
||||
case VertexAttribute::UV0:
|
||||
case VertexAttribute::UV1:
|
||||
case VertexAttribute::COLOR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const void* mData;
|
||||
const size_t mSize;
|
||||
|
||||
Material* getMaterial(const MaterialKey& config) const;
|
||||
|
||||
enum ShadingMode {
|
||||
UNLIT = 0,
|
||||
LIT = 1,
|
||||
SPECULAR_GLOSSINESS = 2,
|
||||
};
|
||||
|
||||
mutable Material* mMaterials[12] = {};
|
||||
Texture* mDummyTexture = nullptr;
|
||||
|
||||
Engine* mEngine;
|
||||
};
|
||||
|
||||
GPUMorphShaderLoader::GPUMorphShaderLoader(const void* data, uint64_t size, Engine* engine) : mData(data), mSize(size), mEngine(engine) {
|
||||
|
||||
unsigned char texels[4] = {};
|
||||
mDummyTexture = Texture::Builder()
|
||||
.width(1).height(1)
|
||||
.format(Texture::InternalFormat::RGBA8)
|
||||
.build(*mEngine);
|
||||
Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA,
|
||||
Texture::Type::UBYTE);
|
||||
mDummyTexture->setImage(*mEngine, 0, std::move(pbd));
|
||||
}
|
||||
|
||||
size_t GPUMorphShaderLoader::getMaterialsCount() const noexcept {
|
||||
return sizeof(mMaterials) / sizeof(mMaterials[0]);
|
||||
}
|
||||
|
||||
const Material* const* GPUMorphShaderLoader::getMaterials() const noexcept {
|
||||
return &mMaterials[0];
|
||||
}
|
||||
|
||||
void GPUMorphShaderLoader::destroyMaterials() {
|
||||
for (auto& material : mMaterials) {
|
||||
mEngine->destroy(material);
|
||||
material = nullptr;
|
||||
}
|
||||
mEngine->destroy(mDummyTexture);
|
||||
}
|
||||
|
||||
Material* GPUMorphShaderLoader::getMaterial(const MaterialKey& config) const {
|
||||
const ShadingMode shading = config.unlit ? UNLIT :
|
||||
(config.useSpecularGlossiness ? SPECULAR_GLOSSINESS : LIT);
|
||||
const int matindex = 0;
|
||||
if (mMaterials[matindex] != nullptr) {
|
||||
return mMaterials[matindex];
|
||||
}
|
||||
|
||||
filamat::Package pkg = filamat::Package(mData, mSize);
|
||||
return Material::Builder().package(pkg.getData(), pkg.getSize()).build(*mEngine);
|
||||
}
|
||||
|
||||
MaterialInstance* GPUMorphShaderLoader::createMaterialInstance(MaterialKey* config, UvMap* uvmap,
|
||||
const char* label) {
|
||||
// Diagnostics are not supported with LOAD_UBERSHADERS, please use GENERATE_SHADERS instead.
|
||||
if (config->enableDiagnostics) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (config->hasVolume && config->hasSheen) {
|
||||
slog.w << "Volume and sheen are not supported together in ubershader mode,"
|
||||
" removing sheen (" << label << ")." << io::endl;
|
||||
config->hasSheen = false;
|
||||
}
|
||||
|
||||
if (config->hasTransmission && config->hasSheen) {
|
||||
slog.w << "Transmission and sheen are not supported together in ubershader mode,"
|
||||
" removing sheen (" << label << ")." << io::endl;
|
||||
config->hasSheen = false;
|
||||
}
|
||||
|
||||
const bool clearCoatConflict = config->hasVolume || config->hasTransmission || config->hasSheen;
|
||||
|
||||
// Due to sampler overload, disable transmission if necessary and print a friendly warning.
|
||||
if (config->hasClearCoat && clearCoatConflict) {
|
||||
slog.w << "Volume, transmission and sheen are not supported in ubershader mode for clearcoat"
|
||||
" materials (" << label << ")." << io::endl;
|
||||
config->hasVolume = false;
|
||||
config->hasTransmission = false;
|
||||
config->hasSheen = false;
|
||||
}
|
||||
|
||||
constrainMaterial(config, uvmap);
|
||||
auto getUvIndex = [uvmap](uint8_t srcIndex, bool hasTexture) -> int {
|
||||
return hasTexture ? int(uvmap->at(srcIndex)) - 1 : -1;
|
||||
};
|
||||
Material* material = getMaterial(*config);
|
||||
MaterialInstance* mi = material->createInstance(label);
|
||||
mi->setParameter("baseColorIndex",
|
||||
getUvIndex(config->baseColorUV, config->hasBaseColorTexture));
|
||||
mi->setParameter("normalIndex", getUvIndex(config->normalUV, config->hasNormalTexture));
|
||||
mi->setParameter("metallicRoughnessIndex",
|
||||
getUvIndex(config->metallicRoughnessUV, config->hasMetallicRoughnessTexture));
|
||||
mi->setParameter("aoIndex", getUvIndex(config->aoUV, config->hasOcclusionTexture));
|
||||
mi->setParameter("emissiveIndex", getUvIndex(config->emissiveUV, config->hasEmissiveTexture));
|
||||
|
||||
mi->setDoubleSided(config->doubleSided);
|
||||
mi->setCullingMode(config->doubleSided ? CullingMode::NONE : CullingMode::BACK);
|
||||
|
||||
// Initially, assume that the clear coat texture can be honored. This is changed to false when
|
||||
// running into a sampler count limitation. TODO: check if these constraints can now be relaxed.
|
||||
bool clearCoatNeedsTexture = true;
|
||||
|
||||
mat3f identity;
|
||||
mi->setParameter("baseColorUvMatrix", identity);
|
||||
mi->setParameter("metallicRoughnessUvMatrix", identity);
|
||||
mi->setParameter("normalUvMatrix", identity);
|
||||
mi->setParameter("occlusionUvMatrix", identity);
|
||||
mi->setParameter("emissiveUvMatrix", identity);
|
||||
|
||||
if (config->hasClearCoat) {
|
||||
mi->setParameter("clearCoatIndex",
|
||||
getUvIndex(config->clearCoatUV, config->hasClearCoatTexture));
|
||||
mi->setParameter("clearCoatRoughnessIndex",
|
||||
getUvIndex(config->clearCoatRoughnessUV, config->hasClearCoatRoughnessTexture));
|
||||
mi->setParameter("clearCoatNormalIndex",
|
||||
getUvIndex(config->clearCoatNormalUV, config->hasClearCoatNormalTexture));
|
||||
mi->setParameter("clearCoatUvMatrix", identity);
|
||||
mi->setParameter("clearCoatRoughnessUvMatrix", identity);
|
||||
mi->setParameter("clearCoatNormalUvMatrix", identity);
|
||||
} else {
|
||||
if (config->hasSheen) {
|
||||
clearCoatNeedsTexture = false;
|
||||
mi->setParameter("sheenColorIndex",
|
||||
getUvIndex(config->sheenColorUV, config->hasSheenColorTexture));
|
||||
mi->setParameter("sheenRoughnessIndex",
|
||||
getUvIndex(config->sheenRoughnessUV, config->hasSheenRoughnessTexture));
|
||||
mi->setParameter("sheenColorUvMatrix", identity);
|
||||
mi->setParameter("sheenRoughnessUvMatrix", identity);
|
||||
|
||||
}
|
||||
if (config->hasVolume) {
|
||||
clearCoatNeedsTexture = false;
|
||||
mi->setParameter("volumeThicknessUvMatrix", identity);
|
||||
mi->setParameter("volumeThicknessIndex",
|
||||
getUvIndex(config->transmissionUV, config->hasVolumeThicknessTexture));
|
||||
}
|
||||
if (config->hasTransmission) {
|
||||
clearCoatNeedsTexture = false;
|
||||
mi->setParameter("transmissionUvMatrix", identity);
|
||||
mi->setParameter("transmissionIndex",
|
||||
getUvIndex(config->transmissionUV, config->hasTransmissionTexture));
|
||||
}
|
||||
}
|
||||
|
||||
TextureSampler sampler;
|
||||
mi->setParameter("normalMap", mDummyTexture, sampler);
|
||||
mi->setParameter("baseColorMap", mDummyTexture, sampler);
|
||||
mi->setParameter("metallicRoughnessMap", mDummyTexture, sampler);
|
||||
mi->setParameter("occlusionMap", mDummyTexture, sampler);
|
||||
mi->setParameter("emissiveMap", mDummyTexture, sampler);
|
||||
if (clearCoatNeedsTexture) {
|
||||
mi->setParameter("clearCoatMap", mDummyTexture, sampler);
|
||||
mi->setParameter("clearCoatRoughnessMap", mDummyTexture, sampler);
|
||||
mi->setParameter("clearCoatNormalMap", mDummyTexture, sampler);
|
||||
}
|
||||
if (!config->hasClearCoat) {
|
||||
if (config->hasTransmission) {
|
||||
mi->setParameter("transmissionMap", mDummyTexture, sampler);
|
||||
}
|
||||
if (config->hasSheen) {
|
||||
mi->setParameter("sheenColorMap", mDummyTexture, sampler);
|
||||
mi->setParameter("sheenRoughnessMap", mDummyTexture, sampler);
|
||||
}
|
||||
}
|
||||
|
||||
if (mi->getMaterial()->hasParameter("ior")) {
|
||||
mi->setParameter("ior", 1.5f);
|
||||
}
|
||||
if (mi->getMaterial()->hasParameter("reflectance")) {
|
||||
mi->setParameter("reflectance", 0.5f);
|
||||
}
|
||||
|
||||
return mi;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
MaterialProvider* createGPUMorphShaderLoader(const void* data, uint64_t size, filament::Engine* engine) {
|
||||
return new GPUMorphShaderLoader(data, size,engine);
|
||||
}
|
||||
|
||||
} // namespace gltfio
|
||||
|
||||
269
ios/src/morph/GltfEnums.h
Normal file
269
ios/src/morph/GltfEnums.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_GLTFENUMS_H
|
||||
#define GLTFIO_GLTFENUMS_H
|
||||
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
|
||||
#include <cgltf.h>
|
||||
|
||||
#define GL_NEAREST 0x2600
|
||||
#define GL_LINEAR 0x2601
|
||||
#define GL_NEAREST_MIPMAP_NEAREST 0x2700
|
||||
#define GL_LINEAR_MIPMAP_NEAREST 0x2701
|
||||
#define GL_NEAREST_MIPMAP_LINEAR 0x2702
|
||||
#define GL_LINEAR_MIPMAP_LINEAR 0x2703
|
||||
#define GL_REPEAT 0x2901
|
||||
#define GL_MIRRORED_REPEAT 0x8370
|
||||
#define GL_CLAMP_TO_EDGE 0x812F
|
||||
|
||||
inline filament::TextureSampler::WrapMode getWrapMode(cgltf_int wrap) {
|
||||
switch (wrap) {
|
||||
case GL_REPEAT:
|
||||
return filament::TextureSampler::WrapMode::REPEAT;
|
||||
case GL_MIRRORED_REPEAT:
|
||||
return filament::TextureSampler::WrapMode::MIRRORED_REPEAT;
|
||||
case GL_CLAMP_TO_EDGE:
|
||||
return filament::TextureSampler::WrapMode::CLAMP_TO_EDGE;
|
||||
}
|
||||
return filament::TextureSampler::WrapMode::REPEAT;
|
||||
}
|
||||
|
||||
inline filament::TextureSampler::MinFilter getMinFilter(cgltf_int minFilter) {
|
||||
switch (minFilter) {
|
||||
case GL_NEAREST:
|
||||
return filament::TextureSampler::MinFilter::NEAREST;
|
||||
case GL_LINEAR:
|
||||
return filament::TextureSampler::MinFilter::LINEAR;
|
||||
case GL_NEAREST_MIPMAP_NEAREST:
|
||||
return filament::TextureSampler::MinFilter::NEAREST_MIPMAP_NEAREST;
|
||||
case GL_LINEAR_MIPMAP_NEAREST:
|
||||
return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_NEAREST;
|
||||
case GL_NEAREST_MIPMAP_LINEAR:
|
||||
return filament::TextureSampler::MinFilter::NEAREST_MIPMAP_LINEAR;
|
||||
case GL_LINEAR_MIPMAP_LINEAR:
|
||||
return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR;
|
||||
}
|
||||
return filament::TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR;
|
||||
}
|
||||
|
||||
inline filament::TextureSampler::MagFilter getMagFilter(cgltf_int magFilter) {
|
||||
switch (magFilter) {
|
||||
case GL_NEAREST:
|
||||
return filament::TextureSampler::MagFilter::NEAREST;
|
||||
case GL_LINEAR:
|
||||
return filament::TextureSampler::MagFilter::LINEAR;
|
||||
}
|
||||
return filament::TextureSampler::MagFilter::LINEAR;
|
||||
}
|
||||
|
||||
inline bool getVertexAttrType(cgltf_attribute_type atype, filament::VertexAttribute* attrType) {
|
||||
switch (atype) {
|
||||
case cgltf_attribute_type_position:
|
||||
*attrType = filament::VertexAttribute::POSITION;
|
||||
return true;
|
||||
case cgltf_attribute_type_texcoord:
|
||||
*attrType = filament::VertexAttribute::UV0;
|
||||
return true;
|
||||
case cgltf_attribute_type_color:
|
||||
*attrType = filament::VertexAttribute::COLOR;
|
||||
return true;
|
||||
case cgltf_attribute_type_joints:
|
||||
*attrType = filament::VertexAttribute::BONE_INDICES;
|
||||
return true;
|
||||
case cgltf_attribute_type_weights:
|
||||
*attrType = filament::VertexAttribute::BONE_WEIGHTS;
|
||||
return true;
|
||||
case cgltf_attribute_type_normal:
|
||||
case cgltf_attribute_type_tangent:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool getIndexType(cgltf_component_type ctype, filament::IndexBuffer::IndexType* itype) {
|
||||
switch (ctype) {
|
||||
case cgltf_component_type_r_8u:
|
||||
case cgltf_component_type_r_16u:
|
||||
*itype = filament::IndexBuffer::IndexType::USHORT;
|
||||
return true;
|
||||
case cgltf_component_type_r_32u:
|
||||
*itype = filament::IndexBuffer::IndexType::UINT;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool getPrimitiveType(cgltf_primitive_type in,
|
||||
filament::RenderableManager::PrimitiveType* out) {
|
||||
switch (in) {
|
||||
case cgltf_primitive_type_points:
|
||||
*out = filament::RenderableManager::PrimitiveType::POINTS;
|
||||
return true;
|
||||
case cgltf_primitive_type_lines:
|
||||
*out = filament::RenderableManager::PrimitiveType::LINES;
|
||||
return true;
|
||||
case cgltf_primitive_type_triangles:
|
||||
*out = filament::RenderableManager::PrimitiveType::TRIANGLES;
|
||||
return true;
|
||||
case cgltf_primitive_type_line_loop:
|
||||
case cgltf_primitive_type_line_strip:
|
||||
case cgltf_primitive_type_triangle_strip:
|
||||
case cgltf_primitive_type_triangle_fan:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// This converts a cgltf component type into a Filament Attribute type.
|
||||
//
|
||||
// This function has two out parameters. One result is a safe "permitted type" which we know is
|
||||
// universally accepted across GPU's and backends, but may require conversion (see Transcoder). The
|
||||
// other result is the "actual type" which requires no conversion.
|
||||
//
|
||||
// Returns false if the given component type is invalid.
|
||||
inline bool getElementType(cgltf_type type, cgltf_component_type ctype,
|
||||
filament::VertexBuffer::AttributeType* permitType,
|
||||
filament::VertexBuffer::AttributeType* actualType) {
|
||||
switch (type) {
|
||||
case cgltf_type_scalar:
|
||||
switch (ctype) {
|
||||
case cgltf_component_type_r_8:
|
||||
*permitType = filament::VertexBuffer::AttributeType::BYTE;
|
||||
*actualType = filament::VertexBuffer::AttributeType::BYTE;
|
||||
return true;
|
||||
case cgltf_component_type_r_8u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::UBYTE;
|
||||
*actualType = filament::VertexBuffer::AttributeType::UBYTE;
|
||||
return true;
|
||||
case cgltf_component_type_r_16:
|
||||
*permitType = filament::VertexBuffer::AttributeType::SHORT;
|
||||
*actualType = filament::VertexBuffer::AttributeType::SHORT;
|
||||
return true;
|
||||
case cgltf_component_type_r_16u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::USHORT;
|
||||
*actualType = filament::VertexBuffer::AttributeType::USHORT;
|
||||
return true;
|
||||
case cgltf_component_type_r_32u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::UINT;
|
||||
*actualType = filament::VertexBuffer::AttributeType::UINT;
|
||||
return true;
|
||||
case cgltf_component_type_r_32f:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT;
|
||||
*actualType = filament::VertexBuffer::AttributeType::FLOAT;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case cgltf_type_vec2:
|
||||
switch (ctype) {
|
||||
case cgltf_component_type_r_8:
|
||||
*permitType = filament::VertexBuffer::AttributeType::BYTE2;
|
||||
*actualType = filament::VertexBuffer::AttributeType::BYTE2;
|
||||
return true;
|
||||
case cgltf_component_type_r_8u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::UBYTE2;
|
||||
*actualType = filament::VertexBuffer::AttributeType::UBYTE2;
|
||||
return true;
|
||||
case cgltf_component_type_r_16:
|
||||
*permitType = filament::VertexBuffer::AttributeType::SHORT2;
|
||||
*actualType = filament::VertexBuffer::AttributeType::SHORT2;
|
||||
return true;
|
||||
case cgltf_component_type_r_16u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::USHORT2;
|
||||
*actualType = filament::VertexBuffer::AttributeType::USHORT2;
|
||||
return true;
|
||||
case cgltf_component_type_r_32f:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT2;
|
||||
*actualType = filament::VertexBuffer::AttributeType::FLOAT2;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case cgltf_type_vec3:
|
||||
switch (ctype) {
|
||||
case cgltf_component_type_r_8:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
|
||||
*actualType = filament::VertexBuffer::AttributeType::BYTE3;
|
||||
return true;
|
||||
case cgltf_component_type_r_8u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
|
||||
*actualType = filament::VertexBuffer::AttributeType::UBYTE3;
|
||||
return true;
|
||||
case cgltf_component_type_r_16:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
|
||||
*actualType = filament::VertexBuffer::AttributeType::SHORT3;
|
||||
return true;
|
||||
case cgltf_component_type_r_16u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
|
||||
*actualType = filament::VertexBuffer::AttributeType::USHORT3;
|
||||
return true;
|
||||
case cgltf_component_type_r_32f:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT3;
|
||||
*actualType = filament::VertexBuffer::AttributeType::FLOAT3;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case cgltf_type_vec4:
|
||||
switch (ctype) {
|
||||
case cgltf_component_type_r_8:
|
||||
*permitType = filament::VertexBuffer::AttributeType::BYTE4;
|
||||
*actualType = filament::VertexBuffer::AttributeType::BYTE4;
|
||||
return true;
|
||||
case cgltf_component_type_r_8u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::UBYTE4;
|
||||
*actualType = filament::VertexBuffer::AttributeType::UBYTE4;
|
||||
return true;
|
||||
case cgltf_component_type_r_16:
|
||||
*permitType = filament::VertexBuffer::AttributeType::SHORT4;
|
||||
*actualType = filament::VertexBuffer::AttributeType::SHORT4;
|
||||
return true;
|
||||
case cgltf_component_type_r_16u:
|
||||
*permitType = filament::VertexBuffer::AttributeType::USHORT4;
|
||||
*actualType = filament::VertexBuffer::AttributeType::USHORT4;
|
||||
return true;
|
||||
case cgltf_component_type_r_32f:
|
||||
*permitType = filament::VertexBuffer::AttributeType::FLOAT4;
|
||||
*actualType = filament::VertexBuffer::AttributeType::FLOAT4;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool requiresConversion(cgltf_type type, cgltf_component_type ctype) {
|
||||
filament::VertexBuffer::AttributeType permitted;
|
||||
filament::VertexBuffer::AttributeType actual;
|
||||
bool supported = getElementType(type, ctype, &permitted, &actual);
|
||||
return supported && permitted != actual;
|
||||
}
|
||||
|
||||
#endif // GLTFIO_GLTFENUMS_H
|
||||
120
ios/src/morph/GltfHelpers.h
Normal file
120
ios/src/morph/GltfHelpers.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef GLTFIO_GLTFHELPERS_H
|
||||
#define GLTFIO_GLTFHELPERS_H
|
||||
|
||||
#include <cgltf.h>
|
||||
|
||||
static const uint8_t* cgltf_buffer_view_data(const cgltf_buffer_view* view) {
|
||||
if (view->data)
|
||||
return (const uint8_t*)view->data;
|
||||
|
||||
if (!view->buffer->data)
|
||||
return NULL;
|
||||
|
||||
const uint8_t* result = (const uint8_t*)view->buffer->data;
|
||||
result += view->offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
static cgltf_size cgltf_component_size(cgltf_component_type component_type) {
|
||||
switch (component_type)
|
||||
{
|
||||
case cgltf_component_type_r_8:
|
||||
case cgltf_component_type_r_8u:
|
||||
return 1;
|
||||
case cgltf_component_type_r_16:
|
||||
case cgltf_component_type_r_16u:
|
||||
return 2;
|
||||
case cgltf_component_type_r_32u:
|
||||
case cgltf_component_type_r_32f:
|
||||
return 4;
|
||||
case cgltf_component_type_invalid:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_type component_type) {
|
||||
switch (component_type)
|
||||
{
|
||||
case cgltf_component_type_r_16:
|
||||
return *((const int16_t*) in);
|
||||
case cgltf_component_type_r_16u:
|
||||
return *((const uint16_t*) in);
|
||||
case cgltf_component_type_r_32u:
|
||||
return *((const uint32_t*) in);
|
||||
case cgltf_component_type_r_32f:
|
||||
return (cgltf_size)*((const float*) in);
|
||||
case cgltf_component_type_r_8:
|
||||
return *((const int8_t*) in);
|
||||
case cgltf_component_type_r_8u:
|
||||
return *((const uint8_t*) in);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_type component_type,
|
||||
cgltf_bool normalized) {
|
||||
if (component_type == cgltf_component_type_r_32f)
|
||||
{
|
||||
return *((const float*) in);
|
||||
}
|
||||
|
||||
if (normalized)
|
||||
{
|
||||
switch (component_type)
|
||||
{
|
||||
// note: glTF spec doesn't currently define normalized conversions for 32-bit integers
|
||||
case cgltf_component_type_r_16:
|
||||
return *((const int16_t*) in) / (cgltf_float)32767;
|
||||
case cgltf_component_type_r_16u:
|
||||
return *((const uint16_t*) in) / (cgltf_float)65535;
|
||||
case cgltf_component_type_r_8:
|
||||
return *((const int8_t*) in) / (cgltf_float)127;
|
||||
case cgltf_component_type_r_8u:
|
||||
return *((const uint8_t*) in) / (cgltf_float)255;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return (cgltf_float)cgltf_component_read_index(in, component_type);
|
||||
}
|
||||
|
||||
static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type,
|
||||
cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out,
|
||||
cgltf_size element_size) {
|
||||
cgltf_size num_components = cgltf_num_components(type);
|
||||
|
||||
if (element_size < num_components) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// There are three special cases for component extraction, see #data-alignment in the 2.0 spec.
|
||||
|
||||
cgltf_size component_size = cgltf_component_size(component_type);
|
||||
|
||||
for (cgltf_size i = 0; i < num_components; ++i)
|
||||
{
|
||||
out[i] = cgltf_component_read_float(element + component_size * i, component_type, normalized);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // GLTFIO_GLTFHELPERS_H
|
||||
43
ios/src/morph/Log.h
Normal file
43
ios/src/morph/Log.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_UTILS_LOG_H
|
||||
#define TNT_UTILS_LOG_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
namespace utils {
|
||||
|
||||
struct UTILS_PUBLIC Loggers {
|
||||
// DEBUG level logging stream
|
||||
io::ostream& d;
|
||||
|
||||
// ERROR level logging stream
|
||||
io::ostream& e;
|
||||
|
||||
// WARNING level logging stream
|
||||
io::ostream& w;
|
||||
|
||||
// INFORMATION level logging stream
|
||||
io::ostream& i;
|
||||
};
|
||||
|
||||
extern UTILS_PUBLIC Loggers const slog;
|
||||
|
||||
} // namespace utils
|
||||
|
||||
#endif // TNT_UTILS_LOG_H
|
||||
66
ios/src/morph/TangentsJob.h
Normal file
66
ios/src/morph/TangentsJob.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
*/
|
||||
|
||||
#include <cgltf.h>
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
namespace filament { class VertexBuffer; }
|
||||
|
||||
namespace gltfio {
|
||||
|
||||
/**
|
||||
* Internal helper that examines a cgltf primitive and generates data suitable for Filament's
|
||||
* TANGENTS attribute. This has been designed to be run as a JobSystem job, but clients are not
|
||||
* required to do so.
|
||||
*/
|
||||
struct TangentsJob {
|
||||
static constexpr int kMorphTargetUnused = -1;
|
||||
|
||||
// The inputs to the procedure. The prim is owned by the client, which should ensure that it
|
||||
// stays alive for the duration of the procedure.
|
||||
struct InputParams {
|
||||
const cgltf_primitive* prim;
|
||||
const int morphTargetIndex = kMorphTargetUnused;
|
||||
};
|
||||
|
||||
// The context of the procedure. These fields are not used by the procedure but are provided as
|
||||
// a convenience to clients. You can think of this as a scratch space for clients.
|
||||
struct Context {
|
||||
filament::VertexBuffer* const vb;
|
||||
const uint8_t slot;
|
||||
};
|
||||
|
||||
// The outputs of the procedure. The results array gets malloc'd by the procedure, so clients
|
||||
// should remember to free it.
|
||||
struct OutputParams {
|
||||
cgltf_size vertexCount;
|
||||
filament::math::short4* results;
|
||||
};
|
||||
|
||||
// Clients might want to track the jobs in an array, so the arguments are bundled into a struct.
|
||||
struct Params {
|
||||
InputParams in;
|
||||
Context context;
|
||||
OutputParams out;
|
||||
};
|
||||
|
||||
// Performs tangents generation synchronously. This can be invoked from inside a job if desired.
|
||||
// The parameters structure is owned by the client.
|
||||
static void run(Params* params);
|
||||
};
|
||||
|
||||
} // namespace gltfio
|
||||
48
ios/src/morph/upcast.h
Normal file
48
ios/src/morph/upcast.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_UPCAST_H
|
||||
#define TNT_FILAMENT_UPCAST_H
|
||||
|
||||
/*
|
||||
* Generates functions to safely upcast a pointer Bar* to FBar*
|
||||
* FILAMENT_UPCAST() should be included in the header file
|
||||
* declaring FBar, e.g.:
|
||||
*
|
||||
* #include <Bar.h>
|
||||
*
|
||||
* class FBar : public Bar {
|
||||
* };
|
||||
*
|
||||
* FILAMENT_UPCAST(Bar)
|
||||
*
|
||||
*/
|
||||
|
||||
#define FILAMENT_UPCAST(CLASS) \
|
||||
inline F##CLASS& upcast(CLASS& that) noexcept { \
|
||||
return static_cast<F##CLASS &>(that); \
|
||||
} \
|
||||
inline const F##CLASS& upcast(const CLASS& that) noexcept { \
|
||||
return static_cast<const F##CLASS &>(that); \
|
||||
} \
|
||||
inline F##CLASS* upcast(CLASS* that) noexcept { \
|
||||
return static_cast<F##CLASS *>(that); \
|
||||
} \
|
||||
inline F##CLASS const* upcast(CLASS const* that) noexcept { \
|
||||
return static_cast<F##CLASS const *>(that); \
|
||||
}
|
||||
|
||||
#endif // TNT_FILAMENT_UPCAST_H
|
||||
@@ -2,13 +2,18 @@ import 'package:flutter/services.dart';
|
||||
|
||||
abstract class FilamentController {
|
||||
void onFilamentViewCreated(int id);
|
||||
Future initialize();
|
||||
Future initialize({String? materialPath});
|
||||
Future loadSkybox(String skyboxPath, String lightingPath);
|
||||
Future loadGlb(String path);
|
||||
Future loadGltf(String path, String relativeResourcePath);
|
||||
Future loadGltf(
|
||||
String path, String relativeResourcePath, String materialInstanceName);
|
||||
Future panStart(double x, double y);
|
||||
Future panUpdate(double x, double y);
|
||||
Future panEnd();
|
||||
Future applyWeights(List<double> weights, int primitiveIndex);
|
||||
Future createMorpher(String meshName, String entityName,
|
||||
{String? materialName});
|
||||
Future zoom(double z);
|
||||
}
|
||||
|
||||
class MimeticFilamentController extends FilamentController {
|
||||
@@ -22,8 +27,8 @@ class MimeticFilamentController extends FilamentController {
|
||||
}
|
||||
|
||||
@override
|
||||
Future initialize() async {
|
||||
await _channel.invokeMethod("initialize");
|
||||
Future initialize({String? materialPath}) async {
|
||||
await _channel.invokeMethod("initialize", materialPath);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -35,8 +40,10 @@ class MimeticFilamentController extends FilamentController {
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
Future loadGltf(String path, String relativeResourcePath) async {
|
||||
await _channel.invokeMethod("loadGltf", [path, relativeResourcePath]);
|
||||
Future loadGltf(String path, String relativeResourcePath,
|
||||
String materialInstanceName) async {
|
||||
await _channel.invokeMethod(
|
||||
"loadGltf", [path, relativeResourcePath, materialInstanceName]);
|
||||
}
|
||||
|
||||
Future panStart(double x, double y) async {
|
||||
@@ -50,4 +57,18 @@ class MimeticFilamentController extends FilamentController {
|
||||
Future panEnd() async {
|
||||
await _channel.invokeMethod("panEnd");
|
||||
}
|
||||
|
||||
Future applyWeights(List<double> weights, int primitiveIndex) async {
|
||||
await _channel.invokeMethod("applyWeights", [weights, primitiveIndex]);
|
||||
}
|
||||
|
||||
Future zoom(double z) async {
|
||||
await _channel.invokeMethod("zoom", z);
|
||||
}
|
||||
|
||||
Future createMorpher(String meshName, String entityName,
|
||||
{String? materialName}) async {
|
||||
await _channel
|
||||
.invokeMethod("createMorpher", [meshName, entityName, materialName]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user