Make your WordPress site irresistible. Natural SEO attraction with: - robots.txt management - sitemap.xml generation - LLMs.txt support - Google integration (Analytics, Search Console, Tag Manager) - Schema.org structured data - Open Graph / Twitter Card meta tags - AMP support - Visual elements gallery - Built-in backup/restore module Includes build.sh and .distignore for WordPress-installable release ZIPs.
742 lines
25 KiB
PHP
742 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* glTF 3D Asset Metadata Extraction Module for TigerStyle Heat
|
|
* Implements glTF 2.0 file format detection, validation, and metadata extraction
|
|
*/
|
|
|
|
// Prevent direct access
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class TigerStyleSEO_glTF_Metadata {
|
|
|
|
/**
|
|
* Single instance
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Supported glTF file extensions
|
|
*/
|
|
private $supported_extensions = array('gltf', 'glb');
|
|
|
|
/**
|
|
* glTF MIME types
|
|
*/
|
|
private $mime_types = array(
|
|
'gltf' => 'model/gltf+json',
|
|
'glb' => 'model/gltf-binary'
|
|
);
|
|
|
|
/**
|
|
* Get instance
|
|
*/
|
|
public static function instance() {
|
|
if (is_null(self::$instance)) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->init();
|
|
}
|
|
|
|
/**
|
|
* Initialize the module
|
|
*/
|
|
private function init() {
|
|
// Media handling hooks
|
|
add_filter('wp_check_filetype_and_ext', array($this, 'check_gltf_filetype'), 10, 4);
|
|
add_filter('upload_mimes', array($this, 'add_gltf_mime_types'));
|
|
add_action('add_attachment', array($this, 'process_gltf_attachment'));
|
|
add_action('edit_attachment', array($this, 'process_gltf_attachment'));
|
|
|
|
// Admin hooks
|
|
if (is_admin()) {
|
|
add_filter('attachment_fields_to_edit', array($this, 'add_gltf_attachment_fields'), 10, 2);
|
|
add_filter('attachment_fields_to_save', array($this, 'save_gltf_attachment_fields'), 10, 2);
|
|
}
|
|
|
|
// Frontend hooks
|
|
add_filter('wp_get_attachment_metadata', array($this, 'enhance_gltf_metadata'), 10, 2);
|
|
}
|
|
|
|
/**
|
|
* Check if file is a valid glTF file
|
|
*/
|
|
public function check_gltf_filetype($data, $file, $filename, $mimes) {
|
|
error_log('TigerStyle Heat: check_gltf_filetype called for: ' . $filename);
|
|
|
|
$wp_filetype = wp_check_filetype($filename, $mimes);
|
|
$ext = $wp_filetype['ext'];
|
|
$type = $wp_filetype['type'];
|
|
|
|
error_log('TigerStyle Heat: wp_check_filetype ext=' . $ext . ', type=' . $type);
|
|
|
|
if (in_array($ext, $this->supported_extensions)) {
|
|
error_log('TigerStyle Heat: File extension supported, validating: ' . $file);
|
|
|
|
// Validate glTF file structure - pass the extension since temp files don't have extensions
|
|
if ($this->validate_gltf_file_with_extension($file, $ext)) {
|
|
error_log('TigerStyle Heat: File validation passed');
|
|
$data['ext'] = $ext;
|
|
$data['type'] = $this->mime_types[$ext];
|
|
} else {
|
|
error_log('TigerStyle Heat: File validation failed');
|
|
// Invalid glTF file
|
|
$data['ext'] = false;
|
|
$data['type'] = false;
|
|
}
|
|
} else {
|
|
error_log('TigerStyle Heat: File extension not supported: ' . $ext);
|
|
}
|
|
|
|
error_log('TigerStyle Heat: Final data: ' . print_r($data, true));
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Add glTF MIME types to WordPress
|
|
*/
|
|
public function add_gltf_mime_types($mimes) {
|
|
// Debug logging
|
|
error_log('TigerStyle Heat: Adding glTF MIME types');
|
|
|
|
$mimes['gltf'] = 'model/gltf+json';
|
|
$mimes['glb'] = 'model/gltf-binary';
|
|
|
|
// Log the updated mimes array
|
|
error_log('TigerStyle Heat: Updated MIME types: ' . print_r($mimes, true));
|
|
|
|
return $mimes;
|
|
}
|
|
|
|
/**
|
|
* Validate glTF file structure with known extension
|
|
*/
|
|
private function validate_gltf_file_with_extension($file_path, $extension) {
|
|
error_log('TigerStyle Heat: validate_gltf_file_with_extension called with: ' . $file_path . ', ext: ' . $extension);
|
|
|
|
if (!file_exists($file_path)) {
|
|
error_log('TigerStyle Heat: File does not exist: ' . $file_path);
|
|
return false;
|
|
}
|
|
|
|
if ($extension === 'gltf') {
|
|
error_log('TigerStyle Heat: Validating as glTF JSON');
|
|
return $this->validate_gltf_json($file_path);
|
|
} elseif ($extension === 'glb') {
|
|
error_log('TigerStyle Heat: Validating as GLB binary');
|
|
return $this->validate_glb_binary($file_path);
|
|
}
|
|
|
|
error_log('TigerStyle Heat: Unknown extension: ' . $extension);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Validate glTF file structure (legacy method - kept for compatibility)
|
|
*/
|
|
private function validate_gltf_file($file_path) {
|
|
error_log('TigerStyle Heat: validate_gltf_file called with: ' . $file_path);
|
|
|
|
if (!file_exists($file_path)) {
|
|
error_log('TigerStyle Heat: File does not exist: ' . $file_path);
|
|
return false;
|
|
}
|
|
|
|
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
|
|
error_log('TigerStyle Heat: File extension detected: ' . $extension);
|
|
|
|
if ($extension === 'gltf') {
|
|
error_log('TigerStyle Heat: Validating as glTF JSON');
|
|
return $this->validate_gltf_json($file_path);
|
|
} elseif ($extension === 'glb') {
|
|
error_log('TigerStyle Heat: Validating as GLB binary');
|
|
return $this->validate_glb_binary($file_path);
|
|
}
|
|
|
|
error_log('TigerStyle Heat: Unknown extension: ' . $extension);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Validate glTF JSON file
|
|
*/
|
|
private function validate_gltf_json($file_path) {
|
|
$content = file_get_contents($file_path);
|
|
if ($content === false) {
|
|
return false;
|
|
}
|
|
|
|
$json = json_decode($content, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
return false;
|
|
}
|
|
|
|
// Check for required glTF structure
|
|
return $this->validate_gltf_structure($json);
|
|
}
|
|
|
|
/**
|
|
* Validate GLB binary file
|
|
*/
|
|
private function validate_glb_binary($file_path) {
|
|
error_log('TigerStyle Heat: validate_glb_binary called');
|
|
|
|
$handle = fopen($file_path, 'rb');
|
|
if (!$handle) {
|
|
error_log('TigerStyle Heat: Could not open file for reading');
|
|
return false;
|
|
}
|
|
|
|
// Read GLB header (12 bytes)
|
|
$header = fread($handle, 12);
|
|
if (strlen($header) < 12) {
|
|
error_log('TigerStyle Heat: Header too short: ' . strlen($header) . ' bytes');
|
|
fclose($handle);
|
|
return false;
|
|
}
|
|
|
|
error_log('TigerStyle Heat: Header read successfully, length: ' . strlen($header));
|
|
|
|
// Check magic number (first 4 bytes should be "glTF")
|
|
$magic = substr($header, 0, 4);
|
|
if ($magic !== 'glTF') {
|
|
fclose($handle);
|
|
return false;
|
|
}
|
|
|
|
// Check version (bytes 4-7, should be 2 for glTF 2.0)
|
|
$version = unpack('V', substr($header, 4, 4))[1];
|
|
if ($version !== 2) {
|
|
fclose($handle);
|
|
return false;
|
|
}
|
|
|
|
fclose($handle);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Validate glTF JSON structure
|
|
*/
|
|
private function validate_gltf_structure($json) {
|
|
// Check required properties
|
|
if (!isset($json['asset'])) {
|
|
return false;
|
|
}
|
|
|
|
// Check asset version
|
|
if (!isset($json['asset']['version']) || $json['asset']['version'] !== '2.0') {
|
|
return false;
|
|
}
|
|
|
|
// Basic structure validation
|
|
$required_arrays = array('scenes', 'nodes', 'meshes', 'accessors', 'bufferViews', 'buffers');
|
|
foreach ($required_arrays as $array_name) {
|
|
if (isset($json[$array_name]) && !is_array($json[$array_name])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Extract metadata from glTF file
|
|
*/
|
|
public function extract_gltf_metadata($file_path) {
|
|
if (!$this->validate_gltf_file($file_path)) {
|
|
return false;
|
|
}
|
|
|
|
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
|
|
$metadata = array(
|
|
'file_format' => $extension,
|
|
'mime_type' => $this->mime_types[$extension],
|
|
'file_size' => filesize($file_path),
|
|
'version' => '2.0'
|
|
);
|
|
|
|
if ($extension === 'gltf') {
|
|
$gltf_data = $this->extract_gltf_json_metadata($file_path);
|
|
} else {
|
|
$gltf_data = $this->extract_glb_metadata($file_path);
|
|
}
|
|
|
|
return array_merge($metadata, $gltf_data);
|
|
}
|
|
|
|
/**
|
|
* Extract metadata from glTF JSON file
|
|
*/
|
|
private function extract_gltf_json_metadata($file_path) {
|
|
$content = file_get_contents($file_path);
|
|
$json = json_decode($content, true);
|
|
|
|
if (!$json) {
|
|
return array();
|
|
}
|
|
|
|
return $this->parse_gltf_json($json);
|
|
}
|
|
|
|
/**
|
|
* Extract metadata from GLB binary file
|
|
*/
|
|
private function extract_glb_metadata($file_path) {
|
|
$handle = fopen($file_path, 'rb');
|
|
if (!$handle) {
|
|
return array();
|
|
}
|
|
|
|
// Skip header (12 bytes)
|
|
fseek($handle, 12);
|
|
|
|
// Read first chunk header (8 bytes)
|
|
$chunk_header = fread($handle, 8);
|
|
if (strlen($chunk_header) < 8) {
|
|
fclose($handle);
|
|
return array();
|
|
}
|
|
|
|
$chunk_length = unpack('V', substr($chunk_header, 0, 4))[1];
|
|
$chunk_type = substr($chunk_header, 4, 4);
|
|
|
|
// First chunk should be JSON
|
|
if ($chunk_type !== 'JSON') {
|
|
fclose($handle);
|
|
return array();
|
|
}
|
|
|
|
// Read JSON data
|
|
$json_data = fread($handle, $chunk_length);
|
|
fclose($handle);
|
|
|
|
$json = json_decode($json_data, true);
|
|
if (!$json) {
|
|
return array();
|
|
}
|
|
|
|
return $this->parse_gltf_json($json);
|
|
}
|
|
|
|
/**
|
|
* Parse glTF JSON structure and extract metadata
|
|
*/
|
|
private function parse_gltf_json($json) {
|
|
$metadata = array();
|
|
|
|
// Asset information
|
|
if (isset($json['asset'])) {
|
|
$asset = $json['asset'];
|
|
$metadata['version'] = $asset['version'] ?? '2.0';
|
|
$metadata['generator'] = $asset['generator'] ?? '';
|
|
$metadata['copyright'] = $asset['copyright'] ?? '';
|
|
$metadata['min_version'] = $asset['minVersion'] ?? '';
|
|
}
|
|
|
|
// Scene information
|
|
$metadata['scene_count'] = isset($json['scenes']) ? count($json['scenes']) : 0;
|
|
$metadata['node_count'] = isset($json['nodes']) ? count($json['nodes']) : 0;
|
|
|
|
// Mesh information
|
|
$metadata['mesh_count'] = isset($json['meshes']) ? count($json['meshes']) : 0;
|
|
$metadata['primitive_count'] = $this->count_primitives($json);
|
|
$metadata['vertex_count'] = $this->estimate_vertex_count($json);
|
|
|
|
// Material information
|
|
$metadata['material_count'] = isset($json['materials']) ? count($json['materials']) : 0;
|
|
$metadata['texture_count'] = isset($json['textures']) ? count($json['textures']) : 0;
|
|
$metadata['image_count'] = isset($json['images']) ? count($json['images']) : 0;
|
|
|
|
// Animation information
|
|
$metadata['animation_count'] = isset($json['animations']) ? count($json['animations']) : 0;
|
|
$metadata['has_animations'] = $metadata['animation_count'] > 0;
|
|
|
|
// Extension information
|
|
$metadata['extensions_used'] = $json['extensionsUsed'] ?? array();
|
|
$metadata['extensions_required'] = $json['extensionsRequired'] ?? array();
|
|
|
|
// Buffer information
|
|
$metadata['buffer_count'] = isset($json['buffers']) ? count($json['buffers']) : 0;
|
|
$metadata['total_buffer_size'] = $this->calculate_total_buffer_size($json);
|
|
|
|
// Accessor information
|
|
$metadata['accessor_count'] = isset($json['accessors']) ? count($json['accessors']) : 0;
|
|
$metadata['buffer_view_count'] = isset($json['bufferViews']) ? count($json['bufferViews']) : 0;
|
|
|
|
// Camera information
|
|
$metadata['camera_count'] = isset($json['cameras']) ? count($json['cameras']) : 0;
|
|
|
|
// Light information (if KHR_lights_punctual extension is used)
|
|
if (isset($json['extensions']['KHR_lights_punctual']['lights'])) {
|
|
$metadata['light_count'] = count($json['extensions']['KHR_lights_punctual']['lights']);
|
|
} else {
|
|
$metadata['light_count'] = 0;
|
|
}
|
|
|
|
// Complexity assessment
|
|
$metadata['complexity_score'] = $this->calculate_complexity_score($metadata);
|
|
$metadata['complexity_level'] = $this->get_complexity_level($metadata['complexity_score']);
|
|
|
|
return $metadata;
|
|
}
|
|
|
|
/**
|
|
* Count total primitives across all meshes
|
|
*/
|
|
private function count_primitives($json) {
|
|
$total = 0;
|
|
if (isset($json['meshes'])) {
|
|
foreach ($json['meshes'] as $mesh) {
|
|
if (isset($mesh['primitives'])) {
|
|
$total += count($mesh['primitives']);
|
|
}
|
|
}
|
|
}
|
|
return $total;
|
|
}
|
|
|
|
/**
|
|
* Estimate total vertex count
|
|
*/
|
|
private function estimate_vertex_count($json) {
|
|
$total = 0;
|
|
if (isset($json['meshes']) && isset($json['accessors'])) {
|
|
foreach ($json['meshes'] as $mesh) {
|
|
if (isset($mesh['primitives'])) {
|
|
foreach ($mesh['primitives'] as $primitive) {
|
|
if (isset($primitive['attributes']['POSITION'])) {
|
|
$accessor_index = $primitive['attributes']['POSITION'];
|
|
if (isset($json['accessors'][$accessor_index])) {
|
|
$total += $json['accessors'][$accessor_index]['count'] ?? 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $total;
|
|
}
|
|
|
|
/**
|
|
* Calculate total buffer size
|
|
*/
|
|
private function calculate_total_buffer_size($json) {
|
|
$total = 0;
|
|
if (isset($json['buffers'])) {
|
|
foreach ($json['buffers'] as $buffer) {
|
|
$total += $buffer['byteLength'] ?? 0;
|
|
}
|
|
}
|
|
return $total;
|
|
}
|
|
|
|
/**
|
|
* Calculate complexity score for the 3D model
|
|
*/
|
|
private function calculate_complexity_score($metadata) {
|
|
$score = 0;
|
|
|
|
// Mesh complexity
|
|
$score += $metadata['mesh_count'] * 10;
|
|
$score += $metadata['primitive_count'] * 5;
|
|
$score += ($metadata['vertex_count'] / 1000) * 2; // Per thousand vertices
|
|
|
|
// Material complexity
|
|
$score += $metadata['material_count'] * 8;
|
|
$score += $metadata['texture_count'] * 12;
|
|
|
|
// Animation complexity
|
|
$score += $metadata['animation_count'] * 15;
|
|
|
|
// Extension complexity
|
|
$score += count($metadata['extensions_used']) * 5;
|
|
$score += count($metadata['extensions_required']) * 10;
|
|
|
|
// Buffer size impact
|
|
$score += ($metadata['total_buffer_size'] / (1024 * 1024)) * 3; // Per MB
|
|
|
|
return round($score);
|
|
}
|
|
|
|
/**
|
|
* Get complexity level based on score
|
|
*/
|
|
private function get_complexity_level($score) {
|
|
if ($score < 50) {
|
|
return 'Low';
|
|
} elseif ($score < 150) {
|
|
return 'Medium';
|
|
} elseif ($score < 300) {
|
|
return 'High';
|
|
} else {
|
|
return 'Very High';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process glTF attachment and extract metadata
|
|
*/
|
|
public function process_gltf_attachment($attachment_id) {
|
|
$file_path = get_attached_file($attachment_id);
|
|
if (!$file_path) {
|
|
return;
|
|
}
|
|
|
|
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
|
|
if (!in_array($extension, $this->supported_extensions)) {
|
|
return;
|
|
}
|
|
|
|
// Extract glTF metadata
|
|
$gltf_metadata = $this->extract_gltf_metadata($file_path);
|
|
if ($gltf_metadata) {
|
|
// Store metadata as post meta
|
|
foreach ($gltf_metadata as $key => $value) {
|
|
update_post_meta($attachment_id, '_gltf_' . $key, $value);
|
|
}
|
|
|
|
// Store structured metadata
|
|
update_post_meta($attachment_id, '_gltf_metadata', $gltf_metadata);
|
|
|
|
// Set attachment as 3D model
|
|
update_post_meta($attachment_id, '_wp_attachment_image_alt', '3D Model');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enhance WordPress attachment metadata with glTF data
|
|
*/
|
|
public function enhance_gltf_metadata($metadata, $attachment_id) {
|
|
$file_path = get_attached_file($attachment_id);
|
|
if (!$file_path) {
|
|
return $metadata;
|
|
}
|
|
|
|
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
|
|
if (!in_array($extension, $this->supported_extensions)) {
|
|
return $metadata;
|
|
}
|
|
|
|
// Get cached glTF metadata
|
|
$gltf_metadata = get_post_meta($attachment_id, '_gltf_metadata', true);
|
|
if (!$gltf_metadata) {
|
|
// Extract and cache metadata
|
|
$gltf_metadata = $this->extract_gltf_metadata($file_path);
|
|
if ($gltf_metadata) {
|
|
update_post_meta($attachment_id, '_gltf_metadata', $gltf_metadata);
|
|
}
|
|
}
|
|
|
|
if ($gltf_metadata) {
|
|
$metadata['gltf'] = $gltf_metadata;
|
|
}
|
|
|
|
return $metadata;
|
|
}
|
|
|
|
/**
|
|
* Add glTF-specific fields to attachment edit screen
|
|
*/
|
|
public function add_gltf_attachment_fields($form_fields, $post) {
|
|
$file_path = get_attached_file($post->ID);
|
|
if (!$file_path) {
|
|
return $form_fields;
|
|
}
|
|
|
|
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
|
|
if (!in_array($extension, $this->supported_extensions)) {
|
|
return $form_fields;
|
|
}
|
|
|
|
$gltf_metadata = get_post_meta($post->ID, '_gltf_metadata', true);
|
|
if (!$gltf_metadata) {
|
|
return $form_fields;
|
|
}
|
|
|
|
// Add 3D model information fields
|
|
$form_fields['gltf_info'] = array(
|
|
'label' => __('3D Model Information', 'tigerstyle-heat'),
|
|
'input' => 'html',
|
|
'html' => $this->render_gltf_info_html($gltf_metadata),
|
|
'helps' => __('Technical information about this glTF 3D model.', 'tigerstyle-heat')
|
|
);
|
|
|
|
// Add custom license field
|
|
$license = get_post_meta($post->ID, '_gltf_license', true);
|
|
$form_fields['gltf_license'] = array(
|
|
'label' => __('3D Model License', 'tigerstyle-heat'),
|
|
'input' => 'text',
|
|
'value' => $license,
|
|
'helps' => __('License information for this 3D model (e.g., CC BY 4.0, MIT, etc.)', 'tigerstyle-heat')
|
|
);
|
|
|
|
// Add creator field
|
|
$creator = get_post_meta($post->ID, '_gltf_creator', true);
|
|
$form_fields['gltf_creator'] = array(
|
|
'label' => __('3D Model Creator', 'tigerstyle-heat'),
|
|
'input' => 'text',
|
|
'value' => $creator,
|
|
'helps' => __('Name of the person or organization who created this 3D model.', 'tigerstyle-heat')
|
|
);
|
|
|
|
return $form_fields;
|
|
}
|
|
|
|
/**
|
|
* Save glTF-specific attachment fields
|
|
*/
|
|
public function save_gltf_attachment_fields($post, $attachment) {
|
|
if (isset($attachment['gltf_license'])) {
|
|
update_post_meta($post['ID'], '_gltf_license', sanitize_text_field($attachment['gltf_license']));
|
|
}
|
|
|
|
if (isset($attachment['gltf_creator'])) {
|
|
update_post_meta($post['ID'], '_gltf_creator', sanitize_text_field($attachment['gltf_creator']));
|
|
}
|
|
|
|
return $post;
|
|
}
|
|
|
|
/**
|
|
* Render glTF information HTML for admin
|
|
*/
|
|
private function render_gltf_info_html($metadata) {
|
|
ob_start();
|
|
?>
|
|
<div style="background: #f9f9f9; padding: 10px; border: 1px solid #ddd; border-radius: 4px;">
|
|
<h4 style="margin-top: 0;"><?php _e('glTF Technical Details', 'tigerstyle-heat'); ?></h4>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
|
|
<div>
|
|
<strong><?php _e('Format:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['file_format']); ?><br>
|
|
<strong><?php _e('Version:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['version']); ?><br>
|
|
<strong><?php _e('File Size:', 'tigerstyle-heat'); ?></strong> <?php echo size_format($metadata['file_size']); ?><br>
|
|
<strong><?php _e('Complexity:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['complexity_level']); ?>
|
|
</div>
|
|
|
|
<div>
|
|
<strong><?php _e('Meshes:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['mesh_count']); ?><br>
|
|
<strong><?php _e('Materials:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['material_count']); ?><br>
|
|
<strong><?php _e('Textures:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['texture_count']); ?><br>
|
|
<strong><?php _e('Animations:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['animation_count']); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!empty($metadata['vertex_count'])): ?>
|
|
<p><strong><?php _e('Vertices:', 'tigerstyle-heat'); ?></strong> <?php echo number_format($metadata['vertex_count']); ?></p>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!empty($metadata['generator'])): ?>
|
|
<p><strong><?php _e('Generated by:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html($metadata['generator']); ?></p>
|
|
<?php endif; ?>
|
|
|
|
<?php if (!empty($metadata['extensions_used'])): ?>
|
|
<p><strong><?php _e('Extensions Used:', 'tigerstyle-heat'); ?></strong> <?php echo esc_html(implode(', ', $metadata['extensions_used'])); ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php
|
|
return ob_get_clean();
|
|
}
|
|
|
|
/**
|
|
* Get structured data for glTF 3D model
|
|
*/
|
|
public function get_gltf_structured_data($attachment_id) {
|
|
$post = get_post($attachment_id);
|
|
if (!$post || $post->post_type !== 'attachment') {
|
|
return null;
|
|
}
|
|
|
|
$file_path = get_attached_file($attachment_id);
|
|
$file_url = wp_get_attachment_url($attachment_id);
|
|
$gltf_metadata = get_post_meta($attachment_id, '_gltf_metadata', true);
|
|
|
|
if (!$file_url || !$gltf_metadata) {
|
|
return null;
|
|
}
|
|
|
|
$schema = array(
|
|
'@type' => '3DModel',
|
|
'contentUrl' => $file_url,
|
|
'encodingFormat' => $gltf_metadata['mime_type'],
|
|
'name' => $post->post_title ?: basename($file_url),
|
|
'description' => $post->post_content,
|
|
'caption' => $post->post_excerpt
|
|
);
|
|
|
|
// Add technical metadata
|
|
$schema['fileSize'] = $gltf_metadata['file_size'];
|
|
$schema['fileFormat'] = $gltf_metadata['file_format'];
|
|
|
|
// Add 3D-specific properties
|
|
if (isset($gltf_metadata['mesh_count'])) {
|
|
$schema['additionalProperty'] = array(
|
|
array(
|
|
'@type' => 'PropertyValue',
|
|
'name' => 'meshCount',
|
|
'value' => $gltf_metadata['mesh_count']
|
|
),
|
|
array(
|
|
'@type' => 'PropertyValue',
|
|
'name' => 'materialCount',
|
|
'value' => $gltf_metadata['material_count']
|
|
),
|
|
array(
|
|
'@type' => 'PropertyValue',
|
|
'name' => 'complexityLevel',
|
|
'value' => $gltf_metadata['complexity_level']
|
|
)
|
|
);
|
|
}
|
|
|
|
// Add license information
|
|
$license = get_post_meta($attachment_id, '_gltf_license', true);
|
|
if ($license) {
|
|
$schema['license'] = $license;
|
|
}
|
|
|
|
// Add creator information
|
|
$creator = get_post_meta($attachment_id, '_gltf_creator', true);
|
|
if ($creator) {
|
|
$schema['creator'] = array(
|
|
'@type' => 'Person',
|
|
'name' => $creator
|
|
);
|
|
}
|
|
|
|
return $schema;
|
|
}
|
|
|
|
/**
|
|
* Check if attachment is a glTF 3D model
|
|
*/
|
|
public function is_gltf_attachment($attachment_id) {
|
|
$file_path = get_attached_file($attachment_id);
|
|
if (!$file_path) {
|
|
return false;
|
|
}
|
|
|
|
$extension = strtolower(pathinfo($file_path, PATHINFO_EXTENSION));
|
|
return in_array($extension, $this->supported_extensions);
|
|
}
|
|
|
|
/**
|
|
* Get glTF metadata for attachment
|
|
*/
|
|
public function get_gltf_metadata($attachment_id) {
|
|
if (!$this->is_gltf_attachment($attachment_id)) {
|
|
return null;
|
|
}
|
|
|
|
return get_post_meta($attachment_id, '_gltf_metadata', true);
|
|
}
|
|
}
|