Compare commits

...

76 Commits

Author SHA1 Message Date
dilithjay
162a3d263d Made changes as per revision suggestions 2021-09-20 11:50:34 +05:30
dilithjay
0ed4945aab Merge branch 'master' into soc-2021-curve-fillet 2021-09-20 09:58:53 +05:30
dilithjay
22471e847e Set handle type of outer handles of arc to Vector 2021-09-14 23:32:03 +05:30
dilithjay
73371635ba Removed redundancy in spline position calculations 2021-09-14 21:48:59 +05:30
dilithjay
544519b49e Fixed out of scope error 2021-09-14 16:26:02 +05:30
dilithjay
a88037c64d Made changes according to revision suggestions 2021-09-14 16:00:43 +05:30
dilithjay
d216ac4d21 Switched radius mode to take a field input 2021-09-14 14:30:48 +05:30
dilithjay
cbdda7c82f Made changes according to revision 2021-09-14 13:43:01 +05:30
dilithjay
2312ef78f7 Merge branch 'master' into soc-2021-curve-fillet 2021-09-14 13:42:10 +05:30
dilithjay
de1a6b0ca8 Merge branch 'master' into soc-2021-curve-fillet 2021-09-10 22:16:52 +05:30
dilithjay
d71aaf0d50 Merge branch 'master' into soc-2021-curve-fillet 2021-09-10 21:50:47 +05:30
dilithjay
3b1477c27d Curve fillet fields implementation 2021-09-10 19:32:26 +05:30
dilithjay
9d2e9c5142 Switched to node declare 2021-09-10 17:44:03 +05:30
dilithjay
2a7b2bf41b Changes according to revision suggestions 2021-09-10 12:08:21 +05:30
dilithjay
625fe5968f Merge branch 'master' into soc-2021-curve-fillet 2021-09-09 18:37:59 +05:30
dilithjay
b644728ac6 Removed adaptive mode and added bezier/poly modes 2021-09-06 15:36:18 +05:30
dilithjay
e9f1cc6881 Merge branch 'master' into soc-2021-curve-fillet 2021-09-06 14:10:58 +05:30
dilithjay
f5785921eb Merge branch 'master' into soc-2021-curve-fillet 2021-09-05 21:39:44 +05:30
dilithjay
5aa5a349d8 Specified ints as floats where necessary 2021-09-04 14:51:24 +05:30
dilithjay
c826522669 Renamed FilletModeParam to FilletParam 2021-09-04 14:46:06 +05:30
dilithjay
c07be1e484 Change "Radii" attribute name to "Radius" 2021-09-04 14:40:39 +05:30
dilithjay
ca91b7f526 Merge branch 'master' into soc-2021-curve-fillet 2021-09-04 13:56:16 +05:30
dilithjay
94ef272b43 Merge branch 'master' into soc-2021-curve-fillet 2021-08-24 23:00:40 +05:30
dilithjay
9b1af3d9b5 Merge branch 'master' into soc-2021-curve-fillet 2021-08-19 18:34:54 +05:30
dilithjay
d9d7c5c028 Minor cleanup 2021-08-17 14:24:46 +05:30
dilithjay
6ab7bb9483 Revert "Refactored limit radius" 2021-08-17 13:46:51 +05:30
dilithjay
fbfcbc740e Refactored limit radius 2021-08-17 11:21:45 +05:30
dilithjay
14e8e8826e Refactored limit radius 2021-08-17 10:35:45 +05:30
dilithjay
381babb958 Merge branch 'master' into soc-2021-curve-fillet 2021-08-17 07:53:06 +05:30
dilithjay
f15b3c0f10 Made changes according to patch suggestions 2021-08-16 20:58:54 +05:30
dilithjay
b662173428 Switched to using one array of dirs for fillet 2021-08-16 16:38:32 +05:30
dilithjay
658e59bb0b Changed according to code review suggestions 2021-08-16 08:18:27 +05:30
dilithjay
e515553bd3 Merge branch 'master' into soc-2021-curve-fillet 2021-08-15 18:06:45 +05:30
dilithjay
dce4331af7 Merge branch 'master' into soc-2021-curve-fillet 2021-08-12 23:52:53 +05:30
dilithjay
2c4da9b27e Removed spline copying where possible 2021-08-12 23:49:27 +05:30
dilithjay
e1058b69fa Changed radii attribute to use existing enum 2021-08-12 20:02:37 +05:30
dilithjay
36c3958a50 Remove unnecessary braces 2021-08-12 15:23:22 +05:30
dilithjay
42d0360d92 Refactored FilletData calculation 2021-08-12 15:13:59 +05:30
dilithjay
772a053ee8 Implemented some suggestions from code review 2021-08-12 11:26:33 +05:30
dilithjay
539fb9d04a Merge branch 'master' into soc-2021-curve-fillet 2021-08-12 08:49:55 +05:30
dilithjay
b658b53984 Added const for more clarity 2021-08-12 00:05:35 +05:30
dilithjay
a52c51e3df Fixed crash due to unexpected attribute 2021-08-10 18:41:06 +05:30
dilithjay
242814f34d Fixed bug with wrong limit radius at endpoint 2021-08-10 16:19:15 +05:30
dilithjay
e4d3a5518c Update curve fillet code based on patch review 2021-08-10 14:19:03 +05:30
dilithjay
caa88b3087 Fixed build warnings in curve fillet node. 2021-08-10 07:51:58 +05:30
dilithjay
dea495fd3a Merge branch 'master' into soc-2021-curve-fillet 2021-08-08 22:26:42 +05:30
dilithjay
38c199ab3e Removed redundant code in curve fillet node 2021-08-03 20:11:55 +05:30
dilithjay
9212e59ffe Fixed bug with multiple spline curve fillet 2021-08-03 20:10:57 +05:30
dilithjay
ef6db37b1f Merge branch 'soc-2021-curve-fillet' of git.blender.org:blender into soc-2021-curve-fillet 2021-08-03 20:08:15 +05:30
dilithjay
320d158c31 Basoc Curve Fillet node implementation
Basic Curve Fillet node implementation

Merge branch 'soc-2021-curve-fillet' of git.blender.org:blender into soc-2021-curve-fillet

Fixed issue with incorrect radius for fillet

Radius can now accept builtin attributes as well

Cleanup: Refactored fillet code

Added support for multiple resolution curve fillet segments

Added curve fillet support for Poly Splines

Fixed error with accessing empty optional variable

Merge branch 'master' into soc-2021-curve-fillet

Fixed two warnings on curve fillet code

Merge branch 'master' into soc-2021-curve-fillet

Cleanup: Commenting and minor refactoring

Specified function arguments as const

Curve fillet support for non-vector handles

Cleanup: Comment formatting fix

Merge branch 'master' into soc-2021-curve-fillet

Added curve fillet support for NURBS curves (temp)

Cleanup: Renoved redundant spline type check.

Merge branch 'master' into soc-2021-curve-fillet

Added option to avoid overlap of curve fillet

Merge branch 'master' into soc-2021-curve-fillet

Improved limit radius algorithm in curve fillet

Improved limit radius behavior in curve fillet

Merge branch 'master' into soc-2021-curve-fillet

Merge branch 'master' into soc-2021-curve-fillet

Differential Revision: https://developer.blender.org/D12115
2021-08-03 17:59:03 +05:30
dilithjay
2aca184623 Merge branch 'master' into soc-2021-curve-fillet 2021-08-03 17:26:25 +05:30
dilithjay
8615da0e77 Merge branch 'master' into soc-2021-curve-fillet 2021-08-03 17:24:58 +05:30
dilithjay
16b65ce31a Improved limit radius behavior in curve fillet 2021-08-02 22:32:24 +05:30
dilithjay
a9488cead9 Improved limit radius algorithm in curve fillet 2021-08-02 11:32:19 +05:30
dilithjay
4511fc6265 Merge branch 'master' into soc-2021-curve-fillet 2021-08-01 19:42:49 +05:30
dilithjay
49048ce946 Added option to avoid overlap of curve fillet 2021-07-30 12:05:32 +05:30
dilithjay
c91f1db9c6 Merge branch 'master' into soc-2021-curve-fillet 2021-07-29 21:46:44 +05:30
dilithjay
cf5d9cf2fc Cleanup: Renoved redundant spline type check. 2021-07-26 08:37:01 +05:30
dilithjay
bb8bdd55c2 Added curve fillet support for NURBS curves (temp) 2021-07-26 08:30:01 +05:30
dilithjay
44288a4538 Merge branch 'master' into soc-2021-curve-fillet 2021-07-25 23:10:01 +05:30
dilithjay
58a3aafa26 Cleanup: Comment formatting fix 2021-07-22 09:42:43 +05:30
dilithjay
b4a358b014 Curve fillet support for non-vector handles 2021-07-22 09:39:59 +05:30
dilithjay
73a58be3fa Specified function arguments as const 2021-07-21 22:24:52 +05:30
dilithjay
cd79bf179d Cleanup: Commenting and minor refactoring 2021-07-20 08:13:16 +05:30
dilithjay
2323267ed3 Merge branch 'master' into soc-2021-curve-fillet 2021-07-19 09:58:31 +05:30
dilithjay
8141d4a835 Fixed two warnings on curve fillet code 2021-07-17 16:50:56 +05:30
dilithjay
76750c3ee2 Merge branch 'master' into soc-2021-curve-fillet 2021-07-17 10:24:48 +05:30
dilithjay
165a390dc4 Fixed error with accessing empty optional variable 2021-07-17 10:16:38 +05:30
dilithjay
69993d08f9 Added curve fillet support for Poly Splines 2021-07-16 17:33:03 +05:30
dilithjay
6808e774a3 Added support for multiple resolution curve fillet segments 2021-07-15 18:40:03 +05:30
dilithjay
8d7a1615b7 Cleanup: Refactored fillet code 2021-07-14 23:21:43 +05:30
dilithjay
012aa1c472 Radius can now accept builtin attributes as well 2021-07-13 22:54:03 +05:30
dilithjay
80c7a228fa Fixed issue with incorrect radius for fillet 2021-07-13 18:24:57 +05:30
dilithjay
1d4c5d25c3 Merge branch 'soc-2021-curve-fillet' of git.blender.org:blender into soc-2021-curve-fillet 2021-07-13 09:23:24 +05:30
dilithjay
3b3a08f439 Basic Curve Fillet node implementation 2021-07-13 09:21:27 +05:30
dilithjay
b4de48d3b2 Basoc Curve Fillet node implementation 2021-07-13 09:13:02 +05:30
288 changed files with 7143 additions and 1721 deletions

View File

@@ -14,7 +14,7 @@ sound = aud.Sound('music.ogg')
# play the audio, this return a handle to control play/pause
handle = device.play(sound)
# if the audio is not too big and will be used often you can buffer it
sound_buffered = aud.Sound.buffer(sound)
sound_buffered = aud.Sound.cache(sound)
handle_buffered = device.play(sound_buffered)
# stop the sounds (otherwise they play until their ends)

View File

@@ -10,4 +10,4 @@ requests==2.26.0
# Only needed to match the theme used for the official documentation.
# Without this theme, the default theme will be used.
sphinx_rtd_theme==1.0.0rc1
sphinx_rtd_theme==1.0.0

View File

@@ -152,6 +152,7 @@ set(PUBLIC_HDR
include/devices/ThreadedDevice.h
include/Exception.h
include/file/File.h
include/file/FileInfo.h
include/file/FileManager.h
include/file/FileWriter.h
include/file/IFileInput.h
@@ -960,7 +961,10 @@ endif()
if(BUILD_DEMOS)
include_directories(${INCLUDE})
set(DEMOS audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager)
set(DEMOS audainfo audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager)
add_executable(audainfo demos/audainfo.cpp)
target_link_libraries(audainfo audaspace)
add_executable(audaplay demos/audaplay.cpp)
target_link_libraries(audaplay audaspace)

View File

@@ -39,7 +39,7 @@ extern AUD_API void AUD_PlaybackManager_free(AUD_PlaybackManager* manager);
* Plays a sound through the playback manager, adding it into a category.
* \param manager The PlaybackManager object.
* \param sound The sound to be played.
* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be creatd.
* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be created.
*/
extern AUD_API void AUD_PlaybackManager_play(AUD_PlaybackManager* manager, AUD_Sound* sound, unsigned int catKey);

View File

@@ -94,6 +94,36 @@ AUD_API int AUD_Sound_getLength(AUD_Sound* sound)
return (*sound)->createReader()->getLength();
}
AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_infos)
{
assert(sound);
std::shared_ptr<File> file = std::dynamic_pointer_cast<File>(*sound);
if(file)
{
auto streams = file->queryStreams();
size_t size = sizeof(AUD_StreamInfo) * streams.size();
if(!size)
{
*stream_infos = nullptr;
return 0;
}
*stream_infos = reinterpret_cast<AUD_StreamInfo*>(std::malloc(size));
std::memcpy(*stream_infos, streams.data(), size);
return streams.size();
}
else
{
*stream_infos = nullptr;
return 0;
}
}
AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs)
{
assert(sound);
@@ -252,6 +282,12 @@ AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size)
return new AUD_Sound(new File(buffer, size));
}
AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream)
{
assert(buffer);
return new AUD_Sound(new File(buffer, size, stream));
}
AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound)
{
assert(sound);
@@ -272,6 +308,12 @@ AUD_API AUD_Sound* AUD_Sound_file(const char* filename)
return new AUD_Sound(new File(filename));
}
AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream)
{
assert(filename);
return new AUD_Sound(new File(filename, stream));
}
AUD_API AUD_Sound* AUD_Sound_sawtooth(float frequency, AUD_SampleRate rate)
{
return new AUD_Sound(new Sawtooth(frequency, rate));

View File

@@ -36,7 +36,15 @@ extern AUD_API AUD_Specs AUD_Sound_getSpecs(AUD_Sound* sound);
* \return The length of the sound in samples.
* \note This function creates a reader from the sound and deletes it again.
*/
extern AUD_API int AUD_getLength(AUD_Sound* sound);
extern AUD_API int AUD_Sound_getLength(AUD_Sound* sound);
/**
* Retrieves the stream infos of a sound file.
* \param sound The sound to retrieve from which must be a file sound.
* \param infos A pointer to a AUD_StreamInfo array that will be allocated and must afterwards be freed by the caller.
* \return The number of items in the infos array.
*/
extern AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo** stream_infos);
/**
* Reads a sound's samples into memory.
@@ -89,6 +97,15 @@ extern AUD_API AUD_Sound* AUD_Sound_buffer(sample_t* data, int length, AUD_Specs
*/
extern AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size);
/**
* Loads a sound file from a memory buffer.
* \param buffer The buffer which contains the sound file.
* \param size The size of the buffer.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \return A handle of the sound file.
*/
extern AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream);
/**
* Caches a sound into a memory buffer.
* \param sound The sound to cache.
@@ -103,6 +120,14 @@ extern AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound);
*/
extern AUD_API AUD_Sound* AUD_Sound_file(const char* filename);
/**
* Loads a sound file.
* \param filename The filename of the sound file.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \return A handle of the sound file.
*/
extern AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream);
/**
* Creates a sawtooth sound.
* \param frequency The frequency of the generated sawtooth sound.

View File

@@ -86,7 +86,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
info.specs.channels = AUD_CHANNELS_INVALID;
info.specs.rate = AUD_RATE_INVALID;
info.length = 0.0f;
info.start_offset = 0.0f;
try
{
@@ -96,7 +95,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
{
info.specs = convSpecToC(reader->getSpecs());
info.length = reader->getLength() / (float) info.specs.rate;
info.start_offset = reader->getStartOffset();
}
}
catch(Exception&)
@@ -109,7 +107,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high,
float attack, float release, float threshold,
int accumulate, int additive, int square,
float sthreshold, double samplerate, int* length)
float sthreshold, double samplerate, int* length, int stream)
{
Buffer buffer;
DeviceSpecs specs;
@@ -117,7 +115,7 @@ AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high,
specs.rate = (SampleRate)samplerate;
std::shared_ptr<ISound> sound;
std::shared_ptr<ISound> file = std::shared_ptr<ISound>(new File(filename));
std::shared_ptr<ISound> file = std::shared_ptr<ISound>(new File(filename, stream));
int position = 0;
@@ -247,7 +245,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl
buffer[i * 3] = min;
buffer[i * 3 + 1] = max;
buffer[i * 3 + 2] = sqrt(power / len); // RMS
buffer[i * 3 + 2] = std::sqrt(power / len);
if(overallmax < max)
overallmax = max;

View File

@@ -37,7 +37,7 @@ extern AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float
float attack, float release, float threshold,
int accumulate, int additive, int square,
float sthreshold, double samplerate,
int* length);
int* length, int stream);
/**
* Pauses a playing sound after a specific amount of time.

View File

@@ -176,5 +176,17 @@ typedef struct
{
AUD_Specs specs;
float length;
double start_offset;
} AUD_SoundInfo;
/// Specification of a sound source.
typedef struct
{
/// Start time in seconds.
double start;
/// Duration in seconds. May be estimated or 0 if unknown.
double duration;
/// Audio data parameters.
AUD_DeviceSpecs specs;
} AUD_StreamInfo;

View File

@@ -89,10 +89,11 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
self = (Sound*)type->tp_alloc(type, 0);
if(self != nullptr)
{
static const char* kwlist[] = {"filename", nullptr};
static const char* kwlist[] = {"filename", "stream", nullptr};
const char* filename = nullptr;
int stream = 0;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s:Sound", const_cast<char**>(kwlist), &filename))
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:Sound", const_cast<char**>(kwlist), &filename, &stream))
{
Py_DECREF(self);
return nullptr;
@@ -100,7 +101,7 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
try
{
self->sound = new std::shared_ptr<ISound>(new File(filename));
self->sound = new std::shared_ptr<ISound>(new File(filename, stream));
}
catch(Exception& e)
{
@@ -407,8 +408,9 @@ static PyObject *
Sound_file(PyTypeObject* type, PyObject* args)
{
const char* filename = nullptr;
int stream = 0;
if(!PyArg_ParseTuple(args, "s:file", &filename))
if(!PyArg_ParseTuple(args, "s|i:file", &filename, &stream))
return nullptr;
Sound* self;
@@ -418,7 +420,7 @@ Sound_file(PyTypeObject* type, PyObject* args)
{
try
{
self->sound = new std::shared_ptr<ISound>(new File(filename));
self->sound = new std::shared_ptr<ISound>(new File(filename, stream));
}
catch(Exception& e)
{

View File

@@ -70,12 +70,6 @@ public:
*/
virtual int getPosition() const=0;
/**
* Returns the start offset the sound should have to line up with related sources.
* \return The required start offset in seconds.
*/
virtual double getStartOffset() const { return 0.0;}
/**
* Returns the specification of the reader.
* \return The Specs structure.

View File

@@ -23,9 +23,11 @@
*/
#include "ISound.h"
#include "FileInfo.h"
#include <string>
#include <memory>
#include <vector>
AUD_NAMESPACE_BEGIN
@@ -48,6 +50,14 @@ private:
*/
std::shared_ptr<Buffer> m_buffer;
/**
* The index of the stream within the file if it contains multiple.
* The first audio stream in the file has index 0 and the index increments by one
* for every other audio stream in the file. Other types of streams in the file
* do not count.
*/
int m_stream;
// delete copy constructor and operator=
File(const File&) = delete;
File& operator=(const File&) = delete;
@@ -57,16 +67,25 @@ public:
* Creates a new sound.
* The file is read from the file system using the given path.
* \param filename The sound file path.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
*/
File(std::string filename);
File(std::string filename, int stream = 0);
/**
* Creates a new sound.
* The file is read from memory using the supplied buffer.
* \param buffer The buffer to read from.
* \param size The size of the buffer.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
*/
File(const data_t* buffer, int size);
File(const data_t* buffer, int size, int stream = 0);
/**
* Queries the streams of the file.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
std::vector<StreamInfo> queryStreams();
virtual std::shared_ptr<IReader> createReader();
};

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright 2009-2016 Jörg Müller
*
* 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.
******************************************************************************/
#pragma once
/**
* @file FileInfo.h
* @ingroup file
* The FileInfo data structures.
*/
#include "respec/Specification.h"
AUD_NAMESPACE_BEGIN
/// Specification of a sound source.
struct StreamInfo
{
/// Start time in seconds.
double start;
/// Duration in seconds. May be estimated or 0 if unknown.
double duration;
/// Audio data parameters.
DeviceSpecs specs;
};
AUD_NAMESPACE_END

View File

@@ -22,12 +22,14 @@
* The FileManager class.
*/
#include "FileInfo.h"
#include "respec/Specification.h"
#include "IWriter.h"
#include <list>
#include <memory>
#include <string>
#include <vector>
AUD_NAMESPACE_BEGIN
@@ -66,18 +68,36 @@ public:
/**
* Creates a file reader for the given filename if a registed IFileInput is able to read it.
* @param filename The path to the file.
* @param stream The index of the audio stream within the file if it contains multiple audio streams.
* @return The reader created.
* @exception Exception If no file input can read the file an exception is thrown.
*/
static std::shared_ptr<IReader> createReader(std::string filename);
static std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
/**
* Creates a file reader for the given buffer if a registed IFileInput is able to read it.
* @param buffer The buffer to read the file from.
* @param stream The index of the audio stream within the file if it contains multiple audio streams.
* @return The reader created.
* @exception Exception If no file input can read the file an exception is thrown.
*/
static std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
static std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
/**
* Queries the streams of a sound file.
* \param filename Path to the file to be read.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
static std::vector<StreamInfo> queryStreams(std::string filename);
/**
* Queries the streams of a sound file.
* \param buffer The in-memory file buffer.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
static std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
/**
* Creates a file writer that writes a sound to the given file path.

View File

@@ -23,9 +23,11 @@
*/
#include "Audaspace.h"
#include "FileInfo.h"
#include <memory>
#include <string>
#include <vector>
AUD_NAMESPACE_BEGIN
@@ -48,18 +50,36 @@ public:
/**
* Creates a reader for a file to be read.
* \param filename Path to the file to be read.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \return The reader that reads the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::shared_ptr<IReader> createReader(std::string filename)=0;
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0)=0;
/**
* Creates a reader for a file to be read from memory.
* \param buffer The in-memory file buffer.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \return The reader that reads the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer)=0;
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0)=0;
/**
* Queries the streams of a sound file.
* \param filename Path to the file to be read.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::vector<StreamInfo> queryStreams(std::string filename)=0;
/**
* Queries the streams of a sound file.
* \param buffer The in-memory file buffer.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer)=0;
};
AUD_NAMESPACE_END

View File

@@ -67,4 +67,4 @@ public:
virtual void read(int& length, bool& eos, sample_t* buffer);
};
AUD_NAMESPACE_END
AUD_NAMESPACE_END

View File

@@ -35,14 +35,24 @@ void FFMPEG::registerPlugin()
FileManager::registerOutput(plugin);
}
std::shared_ptr<IReader> FFMPEG::createReader(std::string filename)
std::shared_ptr<IReader> FFMPEG::createReader(std::string filename, int stream)
{
return std::shared_ptr<IReader>(new FFMPEGReader(filename));
return std::shared_ptr<IReader>(new FFMPEGReader(filename, stream));
}
std::shared_ptr<IReader> FFMPEG::createReader(std::shared_ptr<Buffer> buffer)
std::shared_ptr<IReader> FFMPEG::createReader(std::shared_ptr<Buffer> buffer, int stream)
{
return std::shared_ptr<IReader>(new FFMPEGReader(buffer));
return std::shared_ptr<IReader>(new FFMPEGReader(buffer, stream));
}
std::vector<StreamInfo> FFMPEG::queryStreams(std::string filename)
{
return FFMPEGReader(filename).queryStreams();
}
std::vector<StreamInfo> FFMPEG::queryStreams(std::shared_ptr<Buffer> buffer)
{
return FFMPEGReader(buffer).queryStreams();
}
std::shared_ptr<IWriter> FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)

View File

@@ -52,8 +52,10 @@ public:
*/
static void registerPlugin();
virtual std::shared_ptr<IReader> createReader(std::string filename);
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
virtual std::vector<StreamInfo> queryStreams(std::string filename);
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
};

View File

@@ -31,6 +31,25 @@ AUD_NAMESPACE_BEGIN
#define FFMPEG_OLD_CODE
#endif
SampleFormat FFMPEGReader::convertSampleFormat(AVSampleFormat format)
{
switch(av_get_packed_sample_fmt(format))
{
case AV_SAMPLE_FMT_U8:
return FORMAT_U8;
case AV_SAMPLE_FMT_S16:
return FORMAT_S16;
case AV_SAMPLE_FMT_S32:
return FORMAT_S32;
case AV_SAMPLE_FMT_FLT:
return FORMAT_FLOAT32;
case AV_SAMPLE_FMT_DBL:
return FORMAT_FLOAT64;
default:
AUD_THROW(FileException, "FFMPEG sample format unknown.");
}
}
int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
{
int buf_size = buffer.getSize();
@@ -68,7 +87,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
for(int i = 0; i < m_frame->nb_samples; i++)
{
std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size,
m_frame->data[channel] + i * single_size, single_size);
m_frame->data[channel] + i * single_size, single_size);
}
}
}
@@ -109,7 +128,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
for(int i = 0; i < m_frame->nb_samples; i++)
{
std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size,
m_frame->data[channel] + i * single_size, single_size);
m_frame->data[channel] + i * single_size, single_size);
}
}
}
@@ -123,13 +142,10 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
return buf_pos;
}
void FFMPEGReader::init()
void FFMPEGReader::init(int stream)
{
m_position = 0;
m_start_offset = 0.0f;
m_pkgbuf_left = 0;
m_st_time = 0;
m_duration = 0;
if(avformat_find_stream_info(m_formatCtx, nullptr) < 0)
AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info.");
@@ -137,43 +153,22 @@ void FFMPEGReader::init()
// find audio stream and codec
m_stream = -1;
double dur_sec = 0;
for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++)
{
#ifdef FFMPEG_OLD_CODE
if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
#else
if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
if((m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
#endif
&& (m_stream < 0))
{
AVStream *audio_stream = m_formatCtx->streams[i];
double audio_timebase = av_q2d(audio_stream->time_base);
if (audio_stream->start_time != AV_NOPTS_VALUE)
if(stream == 0)
{
m_st_time = audio_stream->start_time;
}
int64_t ctx_start_time = 0;
if (m_formatCtx->start_time != AV_NOPTS_VALUE) {
ctx_start_time = m_formatCtx->start_time;
}
m_start_offset = m_st_time * audio_timebase - (double)ctx_start_time / AV_TIME_BASE;
if(audio_stream->duration != AV_NOPTS_VALUE)
{
dur_sec = audio_stream->duration * audio_timebase;
m_stream=i;
break;
}
else
{
/* If the audio starts after the stream start time, subract this from the total duration. */
dur_sec = (double)m_formatCtx->duration / AV_TIME_BASE - m_start_offset;
}
m_stream=i;
break;
stream--;
}
}
@@ -242,10 +237,9 @@ void FFMPEGReader::init()
}
m_specs.rate = (SampleRate) m_codecCtx->sample_rate;
m_duration = lround(dur_sec * m_codecCtx->sample_rate);
}
FFMPEGReader::FFMPEGReader(std::string filename) :
FFMPEGReader::FFMPEGReader(std::string filename, int stream) :
m_pkgbuf(),
m_formatCtx(nullptr),
m_codecCtx(nullptr),
@@ -259,7 +253,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) :
try
{
init();
init(stream);
}
catch(Exception&)
{
@@ -268,7 +262,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) :
}
}
FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer) :
FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer, int stream) :
m_pkgbuf(),
m_codecCtx(nullptr),
m_frame(nullptr),
@@ -295,7 +289,7 @@ FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer) :
try
{
init();
init(stream);
}
catch(Exception&)
{
@@ -318,6 +312,51 @@ FFMPEGReader::~FFMPEGReader()
avformat_close_input(&m_formatCtx);
}
std::vector<StreamInfo> FFMPEGReader::queryStreams()
{
std::vector<StreamInfo> result;
for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++)
{
#ifdef FFMPEG_OLD_CODE
if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
#else
if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
#endif
{
StreamInfo info;
double time_base = av_q2d(m_formatCtx->streams[i]->time_base);
if(m_formatCtx->streams[i]->start_time != AV_NOPTS_VALUE)
info.start = m_formatCtx->streams[i]->start_time * time_base;
else
info.start = 0;
if(m_formatCtx->streams[i]->duration != AV_NOPTS_VALUE)
info.duration = m_formatCtx->streams[i]->duration * time_base;
else if(m_formatCtx->duration != AV_NOPTS_VALUE)
info.duration = double(m_formatCtx->duration) / AV_TIME_BASE - info.start;
else
info.duration = 0;
#ifdef FFMPEG_OLD_CODE
info.specs.channels = Channels(m_formatCtx->streams[i]->codec->channels);
info.specs.rate = m_formatCtx->streams[i]->codec->sample_rate;
info.specs.format = convertSampleFormat(m_formatCtx->streams[i]->codec->sample_fmt);
#else
info.specs.channels = Channels(m_formatCtx->streams[i]->codecpar->channels);
info.specs.rate = m_formatCtx->streams[i]->codecpar->sample_rate;
info.specs.format = convertSampleFormat(AVSampleFormat(m_formatCtx->streams[i]->codecpar->format));
#endif
result.emplace_back(info);
}
}
return result;
}
int FFMPEGReader::read_packet(void* opaque, uint8_t* buf, int buf_size)
{
FFMPEGReader* reader = reinterpret_cast<FFMPEGReader*>(opaque);
@@ -368,18 +407,16 @@ void FFMPEGReader::seek(int position)
{
if(position >= 0)
{
double pts_time_base =
av_q2d(m_formatCtx->streams[m_stream]->time_base);
double pts_time_base = av_q2d(m_formatCtx->streams[m_stream]->time_base);
uint64_t seek_pts = (((uint64_t)position) / ((uint64_t)m_specs.rate)) / pts_time_base;
uint64_t st_time = m_formatCtx->streams[m_stream]->start_time;
uint64_t seek_pos = (uint64_t)(position / (pts_time_base * m_specs.rate));
if(m_st_time != AV_NOPTS_VALUE) {
seek_pts += m_st_time;
}
if(st_time != AV_NOPTS_VALUE)
seek_pos += st_time;
// a value < 0 tells us that seeking failed
if(av_seek_frame(m_formatCtx, m_stream, seek_pts,
AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0)
if(av_seek_frame(m_formatCtx, m_stream, seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0)
{
avcodec_flush_buffers(m_codecCtx);
m_position = position;
@@ -400,7 +437,7 @@ void FFMPEGReader::seek(int position)
if(packet.pts != AV_NOPTS_VALUE)
{
// calculate real position, and read to frame!
m_position = (packet.pts - m_st_time) * pts_time_base * m_specs.rate;
m_position = (packet.pts - (st_time != AV_NOPTS_VALUE ? st_time : 0)) * pts_time_base * m_specs.rate;
if(m_position < position)
{
@@ -430,8 +467,25 @@ void FFMPEGReader::seek(int position)
int FFMPEGReader::getLength() const
{
auto stream = m_formatCtx->streams[m_stream];
double time_base = av_q2d(stream->time_base);
double duration;
if(stream->duration != AV_NOPTS_VALUE)
duration = stream->duration * time_base;
else if(m_formatCtx->duration != AV_NOPTS_VALUE)
{
duration = float(m_formatCtx->duration) / AV_TIME_BASE;
if(stream->start_time != AV_NOPTS_VALUE)
duration -= stream->start_time * time_base;
}
else
duration = -1;
// return approximated remaning size
return m_duration - m_position;
return (int)(duration * m_codecCtx->sample_rate) - m_position;
}
int FFMPEGReader::getPosition() const
@@ -439,11 +493,6 @@ int FFMPEGReader::getPosition() const
return m_position;
}
double FFMPEGReader::getStartOffset() const
{
return m_start_offset;
}
Specs FFMPEGReader::getSpecs() const
{
return m_specs.specs;
@@ -480,13 +529,11 @@ void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer)
// decode the package
pkgbuf_pos = decode(packet, m_pkgbuf);
if (packet.pts >= m_st_time) {
// copy to output buffer
data_size = std::min(pkgbuf_pos, left * sample_size);
m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format));
buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
left -= data_size / sample_size;
}
// copy to output buffer
data_size = std::min(pkgbuf_pos, left * sample_size);
m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format));
buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
left -= data_size / sample_size;
}
av_packet_unref(&packet);
}

View File

@@ -29,9 +29,11 @@
#include "respec/ConverterFunctions.h"
#include "IReader.h"
#include "util/Buffer.h"
#include "file/FileInfo.h"
#include <string>
#include <memory>
#include <vector>
struct AVCodecContext;
extern "C" {
@@ -54,22 +56,6 @@ private:
*/
int m_position;
/**
* The start offset in seconds relative to the media container start time.
* IE how much the sound should be delayed to be kept in sync with the rest of the containter streams.
*/
double m_start_offset;
/**
* The start time pts of the stream. All packets before this timestamp shouldn't be played back (only decoded).
*/
int64_t m_st_time;
/**
* The duration of the audio stream in samples.
*/
int64_t m_duration;
/**
* The specification of the audio data.
*/
@@ -135,6 +121,13 @@ private:
*/
bool m_tointerleave;
/**
* Converts an ffmpeg sample format to an audaspace one.
* \param format The AVSampleFormat sample format.
* \return The sample format as SampleFormat.
*/
AUD_LOCAL static SampleFormat convertSampleFormat(AVSampleFormat format);
/**
* Decodes a packet into the given buffer.
* \param packet The AVPacket to decode.
@@ -145,8 +138,9 @@ private:
/**
* Initializes the object.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
*/
AUD_LOCAL void init();
AUD_LOCAL void init(int stream);
// delete copy constructor and operator=
FFMPEGReader(const FFMPEGReader&) = delete;
@@ -156,24 +150,33 @@ public:
/**
* Creates a new reader.
* \param filename The path to the file to be read.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \exception Exception Thrown if the file specified does not exist or
* cannot be read with ffmpeg.
*/
FFMPEGReader(std::string filename);
FFMPEGReader(std::string filename, int stream = 0);
/**
* Creates a new reader.
* \param buffer The buffer to read from.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \exception Exception Thrown if the buffer specified cannot be read
* with ffmpeg.
*/
FFMPEGReader(std::shared_ptr<Buffer> buffer);
FFMPEGReader(std::shared_ptr<Buffer> buffer, int stream = 0);
/**
* Destroys the reader and closes the file.
*/
virtual ~FFMPEGReader();
/**
* Queries the streams of a sound file.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::vector<StreamInfo> queryStreams();
/**
* Reads data to a memory buffer.
* This function is used for avio only.
@@ -198,7 +201,6 @@ public:
virtual void seek(int position);
virtual int getLength() const;
virtual int getPosition() const;
virtual double getStartOffset() const;
virtual Specs getSpecs() const;
virtual void read(int& length, bool& eos, sample_t* buffer);
};

View File

@@ -32,16 +32,26 @@ void SndFile::registerPlugin()
FileManager::registerOutput(plugin);
}
std::shared_ptr<IReader> SndFile::createReader(std::string filename)
std::shared_ptr<IReader> SndFile::createReader(std::string filename, int stream)
{
return std::shared_ptr<IReader>(new SndFileReader(filename));
}
std::shared_ptr<IReader> SndFile::createReader(std::shared_ptr<Buffer> buffer)
std::shared_ptr<IReader> SndFile::createReader(std::shared_ptr<Buffer> buffer, int stream)
{
return std::shared_ptr<IReader>(new SndFileReader(buffer));
}
std::vector<StreamInfo> SndFile::queryStreams(std::string filename)
{
return SndFileReader(filename).queryStreams();
}
std::vector<StreamInfo> SndFile::queryStreams(std::shared_ptr<Buffer> buffer)
{
return SndFileReader(buffer).queryStreams();
}
std::shared_ptr<IWriter> SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
{
return std::shared_ptr<IWriter>(new SndFileWriter(filename, specs, format, codec, bitrate));

View File

@@ -52,8 +52,10 @@ public:
*/
static void registerPlugin();
virtual std::shared_ptr<IReader> createReader(std::string filename);
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
virtual std::vector<StreamInfo> queryStreams(std::string filename);
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
};

View File

@@ -118,6 +118,21 @@ SndFileReader::~SndFileReader()
sf_close(m_sndfile);
}
std::vector<StreamInfo> SndFileReader::queryStreams()
{
std::vector<StreamInfo> result;
StreamInfo info;
info.start = 0;
info.duration = double(getLength()) / m_specs.rate;
info.specs.specs = m_specs;
info.specs.format = FORMAT_FLOAT32;
result.emplace_back(info);
return result;
}
bool SndFileReader::isSeekable() const
{
return m_seekable;

View File

@@ -28,9 +28,12 @@
* The SndFileReader class.
*/
#include "file/FileInfo.h"
#include <string>
#include <sndfile.h>
#include <memory>
#include <vector>
AUD_NAMESPACE_BEGIN
@@ -96,6 +99,7 @@ public:
/**
* Creates a new reader.
* \param filename The path to the file to be read.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \exception Exception Thrown if the file specified does not exist or
* cannot be read with libsndfile.
*/
@@ -104,6 +108,7 @@ public:
/**
* Creates a new reader.
* \param buffer The buffer to read from.
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
* \exception Exception Thrown if the buffer specified cannot be read
* with libsndfile.
*/
@@ -114,6 +119,13 @@ public:
*/
virtual ~SndFileReader();
/**
* Queries the streams of a sound file.
* \return A vector with as many streams as there are in the file.
* \exception Exception Thrown if the file specified cannot be read.
*/
virtual std::vector<StreamInfo> queryStreams();
virtual bool isSeekable() const;
virtual void seek(int position);
virtual int getLength() const;

View File

@@ -23,23 +23,31 @@
AUD_NAMESPACE_BEGIN
File::File(std::string filename) :
m_filename(filename)
File::File(std::string filename, int stream) :
m_filename(filename), m_stream(stream)
{
}
File::File(const data_t* buffer, int size) :
m_buffer(new Buffer(size))
File::File(const data_t* buffer, int size, int stream) :
m_buffer(new Buffer(size)), m_stream(stream)
{
std::memcpy(m_buffer->getBuffer(), buffer, size);
}
std::vector<StreamInfo> File::queryStreams()
{
if(m_buffer.get())
return FileManager::queryStreams(m_buffer);
else
return FileManager::queryStreams(m_filename);
}
std::shared_ptr<IReader> File::createReader()
{
if(m_buffer.get())
return FileManager::createReader(m_buffer);
return FileManager::createReader(m_buffer, m_stream);
else
return FileManager::createReader(m_filename);
return FileManager::createReader(m_filename, m_stream);
}
AUD_NAMESPACE_END

View File

@@ -43,13 +43,13 @@ void FileManager::registerOutput(std::shared_ptr<aud::IFileOutput> output)
outputs().push_back(output);
}
std::shared_ptr<IReader> FileManager::createReader(std::string filename)
std::shared_ptr<IReader> FileManager::createReader(std::string filename, int stream)
{
for(std::shared_ptr<IFileInput> input : inputs())
{
try
{
return input->createReader(filename);
return input->createReader(filename, stream);
}
catch(Exception&) {}
}
@@ -57,13 +57,41 @@ std::shared_ptr<IReader> FileManager::createReader(std::string filename)
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
}
std::shared_ptr<IReader> FileManager::createReader(std::shared_ptr<Buffer> buffer)
std::shared_ptr<IReader> FileManager::createReader(std::shared_ptr<Buffer> buffer, int stream)
{
for(std::shared_ptr<IFileInput> input : inputs())
{
try
{
return input->createReader(buffer);
return input->createReader(buffer, stream);
}
catch(Exception&) {}
}
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
}
std::vector<StreamInfo> FileManager::queryStreams(std::string filename)
{
for(std::shared_ptr<IFileInput> input : inputs())
{
try
{
return input->queryStreams(filename);
}
catch(Exception&) {}
}
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
}
std::vector<StreamInfo> FileManager::queryStreams(std::shared_ptr<Buffer> buffer)
{
for(std::shared_ptr<IFileInput> input : inputs())
{
try
{
return input->queryStreams(buffer);
}
catch(Exception&) {}
}

View File

@@ -57,4 +57,4 @@ void VolumeReader::read(int& length, bool& eos, sample_t* buffer)
buffer[i] = buffer[i] * m_volumeStorage->getVolume();
}
AUD_NAMESPACE_END
AUD_NAMESPACE_END

View File

@@ -231,7 +231,7 @@ const UserDef U_default = {
.collection_instance_empty_size = 1.0f,
.statusbar_flag = STATUSBAR_SHOW_VERSION,
.file_preview_type = USER_FILE_PREVIEW_CAMERA,
.file_preview_type = USER_FILE_PREVIEW_AUTO,
.runtime =
{

View File

@@ -63,16 +63,11 @@ def kmi_args_as_data(kmi):
if kmi.any:
s.append("\"any\": True")
else:
if kmi.shift:
s.append("\"shift\": True")
if kmi.ctrl:
s.append("\"ctrl\": True")
if kmi.alt:
s.append("\"alt\": True")
if kmi.oskey:
s.append("\"oskey\": True")
if kmi.key_modifier and kmi.key_modifier != 'NONE':
s.append(f"\"key_modifier\": '{kmi.key_modifier}'")
for attr in ("shift", "ctrl", "alt", "oskey"):
if mod := getattr(kmi, attr):
s.append(f"\"{attr:s}\": " + ("-1" if mod == -1 else "True"))
if (mod := kmi.key_modifier) and (mod != 'NONE'):
s.append(f"\"key_modifier\": '{mod:s}'")
if kmi.repeat:
if (

View File

@@ -199,10 +199,12 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
subrow = sub.row()
subrow.scale_x = 0.75
subrow.prop(kmi, "any", toggle=True)
subrow.prop(kmi, "shift", toggle=True)
subrow.prop(kmi, "ctrl", toggle=True)
subrow.prop(kmi, "alt", toggle=True)
subrow.prop(kmi, "oskey", text="Cmd", toggle=True)
# Use `*_ui` properties as integers aren't practical.
subrow.prop(kmi, "shift_ui", toggle=True)
subrow.prop(kmi, "ctrl_ui", toggle=True)
subrow.prop(kmi, "alt_ui", toggle=True)
subrow.prop(kmi, "oskey_ui", text="Cmd", toggle=True)
subrow.prop(kmi, "key_modifier", text="", event=True)
# Operator properties

View File

@@ -85,9 +85,9 @@ class ASSET_OT_open_containing_blend_file(Operator):
@classmethod
def poll(cls, context):
asset_file_handle = getattr(context, 'asset_file_handle', None)
asset_library = getattr(context, 'asset_library', None)
asset_library_ref = getattr(context, 'asset_library_ref', None)
if not asset_library:
if not asset_library_ref:
cls.poll_message_set("No asset library selected")
return False
if not asset_file_handle:
@@ -100,13 +100,13 @@ class ASSET_OT_open_containing_blend_file(Operator):
def execute(self, context):
asset_file_handle = context.asset_file_handle
asset_library = context.asset_library
asset_library_ref = context.asset_library_ref
if asset_file_handle.local_id:
self.report({'WARNING'}, "This asset is stored in the current blend file")
return {'CANCELLED'}
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library)
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref)
self.open_in_new_blender(asset_lib_path)
wm = context.window_manager

View File

@@ -168,8 +168,8 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel):
col.prop(freestyle, "as_render_pass", text="As Render Pass")
class VIEWLAYER_PT_freestyle_edge_detection_options(ViewLayerFreestyleButtonsPanel, Panel):
bl_label = "Edge Detection Options"
class VIEWLAYER_PT_freestyle_edge_detection(ViewLayerFreestyleButtonsPanel, Panel):
bl_label = "Edge Detection"
bl_parent_id = "VIEWLAYER_PT_freestyle"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
@@ -435,7 +435,7 @@ class VIEWLAYER_PT_freestyle_lineset_collection(ViewLayerFreestyleLineStyle, Pan
lineset = freestyle.linesets.active
layout.active = lineset.select_by_collection
layout.row().prop(lineset, "collection", text="Freestyle Lineset Collection")
layout.row().prop(lineset, "collection", text="Line Set Collection")
layout.row().prop(lineset, "collection_negation", expand=True, text="Negation")
@@ -1258,12 +1258,14 @@ class MATERIAL_PT_freestyle_line(MaterialFreestyleButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
mat = context.material
row = layout.row()
row.prop(mat, "line_color", text="")
row.prop(mat, "line_priority", text="Priority")
col = layout.column()
col.prop(mat, "line_color")
col.prop(mat, "line_priority", text="Priority")
classes = (
@@ -1271,7 +1273,7 @@ classes = (
VIEWLAYER_UL_linesets,
RENDER_MT_lineset_context_menu,
VIEWLAYER_PT_freestyle,
VIEWLAYER_PT_freestyle_edge_detection_options,
VIEWLAYER_PT_freestyle_edge_detection,
VIEWLAYER_PT_freestyle_style_modules,
VIEWLAYER_PT_freestyle_lineset,
VIEWLAYER_PT_freestyle_lineset_visibilty,

View File

@@ -417,6 +417,7 @@ class MASK_MT_select(Menu):
layout.operator("mask.select_box")
layout.operator("mask.select_circle")
layout.operator_menu_enum("mask.select_lasso", "mode")
layout.separator()

View File

@@ -1578,6 +1578,7 @@ class CLIP_MT_select(Menu):
layout.operator("clip.select_box")
layout.operator("clip.select_circle")
layout.operator_menu_enum("clip.select_lasso", "mode")
layout.separator()

View File

@@ -394,6 +394,7 @@ class DOPESHEET_MT_select(Menu):
layout.operator("action.select_box", text="Box Select (Axis Range)").axis_range = True
layout.operator("action.select_circle")
layout.operator_menu_enum("action.select_lasso", "mode")
layout.separator()
layout.operator("action.select_column", text="Columns on Selected Keys").mode = 'KEYS'

View File

@@ -179,6 +179,7 @@ class GRAPH_MT_select(Menu):
props.include_handles = True
layout.operator("graph.select_circle")
layout.operator_menu_enum("graph.select_lasso", "mode")
layout.separator()
layout.operator("graph.select_column", text="Columns on Selected Keys").mode = 'KEYS'

View File

@@ -289,6 +289,7 @@ class NODE_MT_select(Menu):
layout.operator("node.select_box").tweak = False
layout.operator("node.select_circle")
layout.operator_menu_enum("node.select_lasso", "mode")
layout.separator()
layout.operator("node.select_all").action = 'TOGGLE'

View File

@@ -209,9 +209,9 @@ class TOPBAR_MT_editor_menus(Menu):
# Allow calling this menu directly (this might not be a header area).
if getattr(context.area, "show_menus", False):
layout.menu("TOPBAR_MT_app", text="", icon='BLENDER')
layout.menu("TOPBAR_MT_blender", text="", icon='BLENDER')
else:
layout.menu("TOPBAR_MT_app", text="Blender")
layout.menu("TOPBAR_MT_blender", text="Blender")
layout.menu("TOPBAR_MT_file")
layout.menu("TOPBAR_MT_edit")
@@ -222,7 +222,7 @@ class TOPBAR_MT_editor_menus(Menu):
layout.menu("TOPBAR_MT_help")
class TOPBAR_MT_app(Menu):
class TOPBAR_MT_blender(Menu):
bl_label = "Blender"
def draw(self, _context):
@@ -238,7 +238,7 @@ class TOPBAR_MT_app(Menu):
layout.separator()
layout.menu("TOPBAR_MT_app_system")
layout.menu("TOPBAR_MT_blender_system")
class TOPBAR_MT_file_cleanup(Menu):
@@ -430,7 +430,7 @@ class TOPBAR_MT_file_defaults(Menu):
# Include technical operators here which would otherwise have no way for users to access.
class TOPBAR_MT_app_system(Menu):
class TOPBAR_MT_blender_system(Menu):
bl_label = "System"
def draw(self, _context):
@@ -655,6 +655,7 @@ class TOPBAR_MT_window(Menu):
layout.separator()
layout.operator("screen.screenshot")
layout.operator("screen.screenshot_area")
if sys.platform[:3] == "win":
layout.separator()
@@ -854,8 +855,8 @@ classes = (
TOPBAR_MT_file_context_menu,
TOPBAR_MT_workspace_menu,
TOPBAR_MT_editor_menus,
TOPBAR_MT_app,
TOPBAR_MT_app_system,
TOPBAR_MT_blender,
TOPBAR_MT_blender_system,
TOPBAR_MT_file,
TOPBAR_MT_file_new,
TOPBAR_MT_file_recover,

View File

@@ -267,7 +267,6 @@ class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel):
col = layout.column()
col.prop(system, "use_region_overlap")
col.prop(view, "show_layout_ui", text="Corner Splitting")
col.prop(view, "show_navigate_ui")
col.prop(view, "color_picker_type")
col.row().prop(view, "header_align")

View File

@@ -1362,6 +1362,7 @@ class VIEW3D_MT_select_object(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1422,6 +1423,7 @@ class VIEW3D_MT_select_pose(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1456,6 +1458,7 @@ class VIEW3D_MT_select_particle(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1562,6 +1565,7 @@ class VIEW3D_MT_select_edit_mesh(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1615,6 +1619,7 @@ class VIEW3D_MT_select_edit_curve(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1650,6 +1655,7 @@ class VIEW3D_MT_select_edit_surface(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1711,6 +1717,7 @@ class VIEW3D_MT_select_edit_metaball(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1752,6 +1759,7 @@ class VIEW3D_MT_select_edit_lattice(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1782,6 +1790,7 @@ class VIEW3D_MT_select_edit_armature(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1849,6 +1858,7 @@ class VIEW3D_MT_select_gpencil(Menu):
layout.operator("gpencil.select_box")
layout.operator("gpencil.select_circle")
layout.operator_menu_enum("gpencil.select_lasso", "mode")
layout.separator()
@@ -1885,6 +1895,7 @@ class VIEW3D_MT_select_paint_mask(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -1905,6 +1916,7 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
layout.operator("view3d.select_box")
layout.operator("view3d.select_circle")
layout.operator_menu_enum("view3d.select_lasso", "mode")
layout.separator()
@@ -3983,6 +3995,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("mesh.extrude_vertices_move", text="Extrude Vertices")
layout.operator("mesh.dupli_extrude_cursor").rotate_source = True
layout.operator("mesh.bevel", text="Bevel Vertices").affect = 'VERTICES'
layout.separator()
@@ -4414,6 +4427,7 @@ class VIEW3D_MT_edit_curve_ctrlpoints(Menu):
if edit_object.type in {'CURVE', 'SURFACE'}:
layout.operator("curve.extrude_move")
layout.operator("curve.vertex_add")
layout.separator()
@@ -4742,6 +4756,7 @@ class VIEW3D_MT_edit_armature(Menu):
layout.separator()
layout.operator("armature.extrude_move")
layout.operator("armature.click_extrude")
if arm.use_mirror_x:
layout.operator("armature.extrude_forked")

View File

@@ -500,8 +500,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeAttributeRemove"),
NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
@@ -526,6 +526,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeCurveFill"),
NodeItem("GeometryNodeCurveTrim"),
NodeItem("GeometryNodeCurveLength"),
NodeItem("GeometryNodeCurveFillet"),
]),
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
NodeItem("GeometryNodeCurvePrimitiveLine"),
@@ -564,6 +565,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeMaterialAssign", poll=geometry_nodes_fields_poll),
NodeItem("GeometryNodeMaterialSelection", poll=geometry_nodes_fields_poll),
NodeItem("GeometryNodeMaterialReplace"),
]),
GeometryNodeCategory("GEO_MESH", "Mesh", items=[

View File

@@ -92,6 +92,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_texture_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_userdef_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_uuid_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vec_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vfont_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h

View File

@@ -247,7 +247,7 @@ class OutputAttribute {
GVMutableArrayPtr varray_;
AttributeDomain domain_;
SaveFn save_;
std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_;
bool ignore_old_values_ = false;
bool save_has_been_called_ = false;
@@ -306,9 +306,10 @@ class OutputAttribute {
fn::GMutableSpan as_span()
{
if (!optional_span_varray_.has_value()) {
if (!optional_span_varray_) {
const bool materialize_old_values = !ignore_old_values_;
optional_span_varray_.emplace(*varray_, materialize_old_values);
optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(*varray_,
materialize_old_values);
}
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
return span_varray;

View File

@@ -103,6 +103,7 @@ class GeometryComponent {
virtual int attribute_domain_size(const AttributeDomain domain) const;
bool attribute_is_builtin(const blender::StringRef attribute_name) const;
bool attribute_is_builtin(const blender::bke::AttributeIDRef &attribute_id) const;
/* Get read-only access to the highest priority attribute with the given name.
* Returns null if the attribute does not exist. */

View File

@@ -124,7 +124,10 @@ enum {
/** Don't overwrite these flags when reading a file. */
#define G_FLAG_ALL_RUNTIME \
(G_FLAG_SCRIPT_AUTOEXEC | G_FLAG_SCRIPT_OVERRIDE_PREF | G_FLAG_EVENT_SIMULATE | \
G_FLAG_USERPREF_NO_SAVE_ON_EXIT)
G_FLAG_USERPREF_NO_SAVE_ON_EXIT | \
\
/* #BPY_python_reset is responsible for resetting these flags on file load. */ \
G_FLAG_SCRIPT_AUTOEXEC_FAIL | G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)
/** Flags to read from blend file. */
#define G_FLAG_ALL_READFILE 0

View File

@@ -45,8 +45,10 @@ enum {
IDTYPE_FLAGS_NO_COPY = 1 << 0,
/** Indicates that the given IDType does not support linking/appending from a library file. */
IDTYPE_FLAGS_NO_LIBLINKING = 1 << 1,
/** Indicates that the given IDType does not support making a library-linked ID local. */
IDTYPE_FLAGS_NO_MAKELOCAL = 1 << 2,
/** Indicates that the given IDType should not be directly linked from a library file, but may be
* appended.
* NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */
IDTYPE_FLAGS_ONLY_APPEND = 1 << 2,
/** Indicates that the given IDType does not have animation data. */
IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3,
};
@@ -283,9 +285,14 @@ const struct IDTypeInfo *BKE_idtype_get_info_from_id(const struct ID *id);
const char *BKE_idtype_idcode_to_name(const short idcode);
const char *BKE_idtype_idcode_to_name_plural(const short idcode);
const char *BKE_idtype_idcode_to_translation_context(const short idcode);
bool BKE_idtype_idcode_is_linkable(const short idcode);
bool BKE_idtype_idcode_is_valid(const short idcode);
bool BKE_idtype_idcode_is_linkable(const short idcode);
bool BKE_idtype_idcode_is_only_appendable(const short idcode);
/* Macro currently, since any linkable IDtype should be localizable. */
#define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable
short BKE_idtype_idcode_from_name(const char *idtype_name);
uint64_t BKE_idtype_idcode_to_idfilter(const short idcode);

View File

@@ -230,7 +230,7 @@ void id_us_plus(struct ID *id);
void id_us_min(struct ID *id);
void id_fake_user_set(struct ID *id);
void id_fake_user_clear(struct ID *id);
void BKE_id_clear_newpoin(struct ID *id);
void BKE_id_newptr_and_tag_clear(struct ID *id);
/** Flags to control make local code behavior. */
enum {
@@ -248,7 +248,7 @@ enum {
};
void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, const int flags);
bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const bool test, const int flags);
bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const int flags);
bool id_single_user(struct bContext *C,
struct ID *id,
struct PointerRNA *ptr,

View File

@@ -112,6 +112,7 @@ void BKE_libblock_relink_ex(struct Main *bmain,
const short remap_flags) ATTR_NONNULL(1, 2);
void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL();
void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL();
typedef void (*BKE_library_free_notifier_reference_cb)(const void *);
typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *);

View File

@@ -123,7 +123,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval);
/* Performs copy for use during evaluation,
* optional referencing original arrays to reduce memory. */
struct Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference);
struct Mesh *BKE_mesh_copy_for_eval(const struct Mesh *source, bool reference);
/* These functions construct a new Mesh,
* contrary to BKE_mesh_to_curve_nurblist which modifies ob itself. */

View File

@@ -731,6 +731,8 @@ void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available);
int nodeSocketLinkLimit(const struct bNodeSocket *sock);
void nodeDeclarationEnsure(struct bNodeTree *ntree, struct bNode *node);
/* Node Clipboard */
void BKE_node_clipboard_init(const struct bNodeTree *ntree);
void BKE_node_clipboard_clear(void);
@@ -1489,6 +1491,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_INPUT_INDEX 1078
#define GEO_NODE_INPUT_NORMAL 1079
#define GEO_NODE_ATTRIBUTE_CAPTURE 1080
#define GEO_NODE_MATERIAL_SELECTION 1081
#define GEO_NODE_MATERIAL_ASSIGN 1082
#define GEO_NODE_CURVE_FILLET 1083
/** \} */

View File

@@ -77,7 +77,7 @@ char *BKE_packedfile_unpack_to_file(struct ReportList *reports,
char *BKE_packedfile_unpack(struct Main *bmain,
struct ReportList *reports,
struct ID *id,
const char *orig_file_name,
const char *orig_file_path,
struct PackedFile *pf,
enum ePF_FileStatus how);
int BKE_packedfile_unpack_vfont(struct Main *bmain,

View File

@@ -96,13 +96,24 @@ typedef struct SoundInfo {
eSoundChannels channels;
} specs;
float length;
double start_offset;
} SoundInfo;
typedef struct SoundStreamInfo {
double duration;
double start;
} SoundStreamInfo;
/* Get information about given sound. Returns truth on success., false if sound can not be loaded
* or if the codes is not supported. */
bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info);
/* Get information about given sound. Returns truth on success., false if sound can not be loaded
* or if the codes is not supported. */
bool BKE_sound_stream_info_get(struct Main *main,
const char *filepath,
int stream,
SoundStreamInfo *sound_info);
#if defined(WITH_AUDASPACE)
AUD_Device *BKE_sound_mixdown(const struct Scene *scene,
AUD_DeviceSpecs specs,

View File

@@ -130,6 +130,11 @@ class Spline {
virtual void translate(const blender::float3 &translation);
virtual void transform(const blender::float4x4 &matrix);
/**
* Change the direction of the spline (switch the start and end) without changing its shape.
*/
void reverse();
/**
* Mark all caches for re-computation. This must be called after any operation that would
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
@@ -210,6 +215,7 @@ class Spline {
virtual void correct_end_tangents() const = 0;
virtual void copy_settings(Spline &dst) const = 0;
virtual void copy_data(Spline &dst) const = 0;
virtual void reverse_impl() = 0;
};
/**
@@ -353,6 +359,9 @@ class BezierSpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
protected:
void reverse_impl() override;
};
/**
@@ -469,6 +478,7 @@ class NURBSpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
void reverse_impl() override;
void calculate_knots() const;
blender::Span<BasisCache> calculate_basis_cache() const;
@@ -519,6 +529,7 @@ class PolySpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
void reverse_impl() override;
};
/**

View File

@@ -186,7 +186,7 @@ set(SRC
intern/mball_tessellate.c
intern/mesh.c
intern/mesh_boolean_convert.cc
intern/mesh_convert.c
intern/mesh_convert.cc
intern/mesh_evaluate.cc
intern/mesh_fair.cc
intern/mesh_iterators.c

View File

@@ -190,7 +190,7 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
void OutputAttribute::save()
{
save_has_been_called_ = true;
if (optional_span_varray_.has_value()) {
if (optional_span_varray_) {
optional_span_varray_->save();
}
if (save_) {
@@ -817,6 +817,12 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_
return providers->builtin_attribute_providers().contains_as(attribute_name);
}
bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const
{
/* Anonymous attributes cannot be built-in. */
return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name());
}
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
const AttributeIDRef &attribute_id) const
{
@@ -1210,7 +1216,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
BLI_assert(cpp_type != nullptr);
const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) {
if (component.attribute_is_builtin(attribute_id)) {
const StringRef attribute_name = attribute_id.name();
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
if (!attribute) {
@@ -1315,7 +1321,9 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex
const AttributeDomain domain = geometry_context->domain();
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type);
return scope.add(std::move(attribute), __func__);
if (attribute) {
return scope.add(std::move(attribute));
}
}
return nullptr;
}
@@ -1350,7 +1358,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context(
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
GVArrayPtr attribute = component.attribute_try_get_for_read(
anonymous_id_.get(), domain, data_type);
return scope.add(std::move(attribute), __func__);
return scope.add(std::move(attribute));
}
return nullptr;
}

View File

@@ -161,7 +161,13 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
if (brush->clone.image) {
/* Special case: ima always local immediately. Clone image should only have one user anyway. */
BKE_lib_id_make_local(bmain, &brush->clone.image->id, false, 0);
/* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided
* in IDType callbacks. Higher-level ID management code usually does not expect such things and
* does not deal properly with it. */
/* NOTE: assert below ensures that the comment above is valid, and that that exception is
* acceptable for the time being. */
BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0);
BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL);
}
if (!force_local && !force_copy) {

View File

@@ -110,7 +110,7 @@ void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluate
}
/**
* Return the start indices for each of the curve spline's evaluated points, as if they were part
* Return the start indices for each of the curve spline's control points, if they were part
* of a flattened array. This can be used to facilitate parallelism by avoiding the need to
* accumulate an offset while doing more complex calculations.
*

View File

@@ -1504,7 +1504,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
cow_curve.curve_eval = nullptr;
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__);
ListBase *dispbase = &(ob->runtime.curve_cache->disp);
ListBase *dispbase = &ob->runtime.curve_cache->disp;
if (ob->type == OB_SURF) {
Mesh *mesh_eval;

View File

@@ -2064,7 +2064,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
}
if (update_normals) {
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(result);
}
}
/* make a copy of mesh to use as brush data */

View File

@@ -3573,7 +3573,7 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obje
}
BKE_mesh_calc_edges(result, false, false);
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(result);
return result;
}

View File

@@ -1419,17 +1419,19 @@ static float eval_fmodifier_influence(FModifier *fcm, float evaltime)
/* restricted range or full range? */
if (fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) {
if ((evaltime <= fcm->sfra) || (evaltime >= fcm->efra)) {
if ((evaltime < fcm->sfra) || (evaltime > fcm->efra)) {
/* out of range */
return 0.0f;
}
if ((evaltime > fcm->sfra) && (evaltime < fcm->sfra + fcm->blendin)) {
if ((fcm->blendin != 0.0f) && (evaltime >= fcm->sfra) &&
(evaltime <= fcm->sfra + fcm->blendin)) {
/* blend in range */
float a = fcm->sfra;
float b = fcm->sfra + fcm->blendin;
return influence * (evaltime - a) / (b - a);
}
if ((evaltime < fcm->efra) && (evaltime > fcm->efra - fcm->blendout)) {
if ((fcm->blendout != 0.0f) && (evaltime <= fcm->efra) &&
(evaltime >= fcm->efra - fcm->blendout)) {
/* blend out range */
float a = fcm->efra;
float b = fcm->efra - fcm->blendout;

View File

@@ -719,6 +719,9 @@ typedef struct VFontToCurveIter {
*
* Currently only disabled when scale-to-fit is enabled,
* so floating-point error doesn't cause unexpected wrapping, see T89241.
*
* \note This should only be set once, in the #VFONT_TO_CURVE_INIT pass
* otherwise iterations wont behave predictably, see T91401.
*/
bool word_wrap;
int status;
@@ -750,8 +753,15 @@ enum {
*
* The em_height here is relative to FT_Face->bbox.
*/
#define ASCENT(vfd) ((vfd)->ascender * (vfd)->em_height)
#define DESCENT(vfd) ((vfd)->em_height - ASCENT(vfd))
static float vfont_ascent(const VFontData *vfd)
{
return vfd->ascender * vfd->em_height;
}
static float vfont_descent(const VFontData *vfd)
{
return vfd->em_height - vfont_ascent(vfd);
}
static bool vfont_to_curve(Object *ob,
Curve *cu,
@@ -1234,17 +1244,17 @@ static bool vfont_to_curve(Object *ob,
case CU_ALIGN_Y_TOP_BASELINE:
break;
case CU_ALIGN_Y_TOP:
yoff = textbox_y_origin - ASCENT(vfd);
yoff = textbox_y_origin - vfont_ascent(vfd);
break;
case CU_ALIGN_Y_CENTER:
yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - ASCENT(vfd)) -
yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - vfont_ascent(vfd)) -
(tb_scale.h * 0.5f) + textbox_y_origin);
break;
case CU_ALIGN_Y_BOTTOM_BASELINE:
yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h;
break;
case CU_ALIGN_Y_BOTTOM:
yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + DESCENT(vfd);
yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + vfont_descent(vfd);
break;
}
@@ -1265,16 +1275,16 @@ static bool vfont_to_curve(Object *ob,
case CU_ALIGN_Y_TOP_BASELINE:
break;
case CU_ALIGN_Y_TOP:
yoff = -ASCENT(vfd);
yoff = -vfont_ascent(vfd);
break;
case CU_ALIGN_Y_CENTER:
yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - ASCENT(vfd);
yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - vfont_ascent(vfd);
break;
case CU_ALIGN_Y_BOTTOM_BASELINE:
yoff = (lnr - 1) * linedist;
break;
case CU_ALIGN_Y_BOTTOM:
yoff = (lnr - 1) * linedist + DESCENT(vfd);
yoff = (lnr - 1) * linedist + vfont_descent(vfd);
break;
}
@@ -1640,7 +1650,6 @@ static bool vfont_to_curve(Object *ob,
else {
iter_data->scale_to_fit = iter_data->bisect.min;
iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
iter_data->word_wrap = false;
}
}
}

View File

@@ -222,6 +222,37 @@ static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
mixer.finalize();
}
/**
* A spline is selected if all of its control points were selected.
*
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<>
void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
const int splines_len = curve.splines().size();
Array<int> offsets = curve.control_point_offsets();
BLI_assert(r_values.size() == splines_len);
r_values.fill(true);
for (const int i_spline : IndexRange(splines_len)) {
const int spline_offset = offsets[i_spline];
const int spline_point_len = offsets[i_spline + 1] - spline_offset;
for (const int i_point : IndexRange(spline_point_len)) {
if (!old_values[spline_offset + i_point]) {
r_values[i_spline] = false;
break;
}
}
}
}
static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
{
GVArrayPtr new_varray;

View File

@@ -175,6 +175,34 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
/* A vertex is selected if all connected face corners were selected and it is not loose. */
template<>
void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
Array<bool> loose_verts(mesh.totvert, true);
r_values.fill(true);
for (const int loop_index : IndexRange(mesh.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const int point_index = loop.v;
loose_verts[point_index] = false;
if (!old_values[loop_index]) {
r_values[point_index] = false;
}
}
/* Deselect loose vertices without corners that are still selected from the 'true' default. */
for (const int vert_index : IndexRange(mesh.totvert)) {
if (loose_verts[vert_index]) {
r_values[vert_index] = false;
}
}
}
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -191,6 +219,13 @@ static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr
return new_varray;
}
/**
* Each corner's value is simply a copy of the value at its vertex.
*
* \note Theoretically this interpolation does not need to compute all values at once.
* However, doing that makes the implementation simpler, and this can be optimized in the future if
* only some values are required.
*/
template<typename T>
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
const VArray<T> &old_values,
@@ -209,10 +244,6 @@ static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr
GVArrayPtr new_varray;
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
using T = decltype(dummy);
/* It is not strictly necessary to compute the value for all corners here. Instead one could
* lazily lookup the mesh topology when a specific index accessed. This can be more efficient
* when an algorithm only accesses very few of the corner values. However, for the algorithms
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
Array<T> values(mesh.totloop);
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
@@ -244,6 +275,26 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
/* A face is selected if all of its corners were selected. */
template<>
void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
if (!old_values[loop_index]) {
r_values[poly_index] = false;
break;
}
}
}
}
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -282,6 +333,41 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
/* An edge is selected if all corners on adjacent faces were selected. */
template<>
void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
/* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */
Array<bool> loose_edges(mesh.totedge, true);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1);
const MLoop &loop = mesh.mloop[loop_index];
const int edge_index = loop.e;
loose_edges[edge_index] = false;
if (!old_values[loop_index] || !old_values[loop_index_next]) {
r_values[edge_index] = false;
}
}
}
/* Deselect loose edges without corners that are still selected from the 'true' default. */
for (const int edge_index : IndexRange(mesh.totedge)) {
if (loose_edges[edge_index]) {
r_values[edge_index] = false;
}
}
}
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -317,6 +403,27 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
/* A vertex is selected if any of the connected faces were selected. */
template<>
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
r_values.fill(false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (old_values[poly_index]) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const int vert_index = loop.v;
r_values[vert_index] = true;
}
}
}
}
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -331,6 +438,7 @@ static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr v
return new_varray;
}
/* Each corner's value is simply a copy of the value at its face. */
template<typename T>
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
const VArray<T> &old_values,
@@ -378,6 +486,27 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
/* An edge is selected if any connected face was selected. */
template<>
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
r_values.fill(false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
if (old_values[poly_index]) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const int edge_index = loop.e;
r_values[edge_index] = true;
}
}
}
}
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -416,6 +545,28 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
/* A face is selected if all of its vertices were selected too. */
template<>
void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
MLoop &loop = mesh.mloop[loop_index];
const int vert_index = loop.v;
if (!old_values[vert_index]) {
r_values[poly_index] = false;
break;
}
}
}
}
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -452,6 +603,20 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
mixer.finalize();
}
/* An edge is selected if both of its vertices were selected. */
template<>
void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
r_values[edge_index] = old_values[edge.v1] && old_values[edge.v2];
}
}
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -490,6 +655,29 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
mixer.finalize();
}
/* A corner is selected if its two adjacent edges were selected. */
template<>
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
r_values.fill(false);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop;
const MLoop &loop = mesh.mloop[loop_index];
const MLoop &loop_prev = mesh.mloop[loop_index_prev];
if (old_values[loop.e] && old_values[loop_prev.e]) {
r_values[loop_index] = true;
}
}
}
}
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -522,6 +710,24 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
mixer.finalize();
}
/* A vertex is selected if any connected edge was selected. */
template<>
void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
r_values.fill(false);
for (const int edge_index : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[edge_index];
if (old_values[edge_index]) {
r_values[edge.v1] = true;
r_values[edge.v2] = true;
}
}
}
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -560,6 +766,28 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
mixer.finalize();
}
/* A face is selected if all of its edges are selected. */
template<>
void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
const VArray<bool> &old_values,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totpoly);
r_values.fill(true);
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
const int edge_index = loop.e;
if (!old_values[edge_index]) {
r_values[poly_index] = false;
break;
}
}
}
}
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
{
GVArrayPtr new_varray;
@@ -698,7 +926,7 @@ static void tag_normals_dirty_when_writing_position(GeometryComponent &component
{
Mesh *mesh = get_mesh_from_component_for_write(component);
if (mesh != nullptr) {
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(mesh);
}
}

View File

@@ -3063,13 +3063,12 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob)
Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent);
/* calculate new matrix */
if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) {
copy_m4_m4(cur_mat, ob_parent->obmat);
mul_m4_m4m4(cur_mat, ob->imat, ob_parent->obmat);
}
else if (gpl->partype == PARBONE) {
bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr);
if (pchan != NULL) {
copy_m4_m4(cur_mat, ob->imat);
mul_m4_m4m4(cur_mat, ob_parent->obmat, pchan->pose_mat);
mul_m4_series(cur_mat, ob->imat, ob_parent->obmat, pchan->pose_mat);
}
else {
unit_m4(cur_mat);

View File

@@ -938,6 +938,11 @@ void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase)
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
}
}
else if (md->type == eGpencilModifierType_Dash) {
DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md;
BLO_write_struct_array(
writer, DashGpencilModifierSegment, gpmd->segments_len, gpmd->segments);
}
}
}
@@ -1017,6 +1022,13 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb)
BKE_curvemapping_init(gpmd->curve_intensity);
}
}
else if (md->type == eGpencilModifierType_Dash) {
DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md;
BLO_read_data_address(reader, &gpmd->segments);
for (int i = 0; i < gpmd->segments_len; i++) {
gpmd->segments[i].dmd = gpmd;
}
}
}
}

View File

@@ -633,12 +633,6 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
}
PreviewImage prv_copy = *prv;
/* don't write out large previews if not requested */
if (U.file_preview_type == USER_FILE_PREVIEW_NONE) {
prv_copy.w[1] = 0;
prv_copy.h[1] = 0;
prv_copy.rect[1] = nullptr;
}
BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy);
if (prv_copy.rect[0]) {
BLO_write_uint32_array(writer, prv_copy.w[0] * prv_copy.h[0], prv_copy.rect[0]);

View File

@@ -224,10 +224,10 @@ bool BKE_idtype_idcode_is_valid(const short idcode)
}
/**
* Return non-zero when an ID type is linkable.
* Check if an ID type is linkable.
*
* \param idcode: The code to check.
* \return Boolean, 0 when non linkable.
* \param idcode: The IDType code to check.
* \return Boolean, false when non linkable, true otherwise.
*/
bool BKE_idtype_idcode_is_linkable(const short idcode)
{
@@ -236,6 +236,24 @@ bool BKE_idtype_idcode_is_linkable(const short idcode)
return id_type != NULL ? (id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 : false;
}
/**
* Check if an ID type is only appendable.
*
* \param idcode: The IDType code to check.
* \return Boolean, false when also linkable, true when only appendable.
*/
bool BKE_idtype_idcode_is_only_appendable(const short idcode)
{
const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode);
BLI_assert(id_type != NULL);
if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_ONLY_APPEND) != 0) {
/* Only appendable ID types should also always be linkable. */
BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0);
return true;
}
return false;
}
/**
* Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB).
*/

View File

@@ -184,8 +184,7 @@ IDTypeInfo IDType_ID_IP = {
.name = "Ipo",
.name_plural = "ipos",
.translation_context = "",
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL |
IDTYPE_FLAGS_NO_ANIMDATA,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA,
.init_data = NULL,
.copy_data = NULL,

View File

@@ -212,7 +212,7 @@ IDTypeInfo IDType_ID_KE = {
.name = "Key",
.name_plural = "shape_keys",
.translation_context = BLT_I18NCONTEXT_ID_SHAPEKEY,
.flags = IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL,
.flags = IDTYPE_FLAGS_NO_LIBLINKING,
.init_data = NULL,
.copy_data = shapekey_copy_data,

View File

@@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = {
.name = "LinkPlaceholder",
.name_plural = "link_placeholders",
.translation_context = BLT_I18NCONTEXT_ID_ID,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING,
.init_data = NULL,
.copy_data = NULL,
@@ -336,12 +336,34 @@ void id_fake_user_clear(ID *id)
}
}
void BKE_id_clear_newpoin(ID *id)
void BKE_id_newptr_and_tag_clear(ID *id)
{
if (id->newid) {
id->newid->tag &= ~LIB_TAG_NEW;
/* We assume that if this ID has no new ID, its embedded data has not either. */
if (id->newid == NULL) {
return;
}
id->newid->tag &= ~LIB_TAG_NEW;
id->newid = NULL;
/* Deal with embedded data too. */
/* NOTE: even though ShapeKeys are not technically embedded data currently, they behave as such
* in most cases, so for sake of consistency treat them as such here. Also mirrors the behavior
* in `BKE_lib_id_make_local`. */
Key *key = BKE_key_from_id(id);
if (key != NULL) {
BKE_id_newptr_and_tag_clear(&key->id);
}
bNodeTree *ntree = ntreeFromID(id);
if (ntree != NULL) {
BKE_id_newptr_and_tag_clear(&ntree->id);
}
if (GS(id->name) == ID_SCE) {
Collection *master_collection = ((Scene *)id)->master_collection;
if (master_collection != NULL) {
BKE_id_newptr_and_tag_clear(&master_collection->id);
}
}
}
static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data)
@@ -480,10 +502,9 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
*
* \param flags: Special flag used when making a whole library's content local,
* it needs specific handling.
*
* \return true if the block can be made local.
* \return true is the ID has successfully been made local.
*/
bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags)
bool BKE_lib_id_make_local(Main *bmain, ID *id, const int flags)
{
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
@@ -495,23 +516,21 @@ bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags
const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id);
if (idtype_info != NULL) {
if ((idtype_info->flags & IDTYPE_FLAGS_NO_MAKELOCAL) == 0) {
if (!test) {
if (idtype_info->make_local != NULL) {
idtype_info->make_local(bmain, id, flags);
}
else {
BKE_lib_id_make_local_generic(bmain, id, flags);
}
}
return true;
}
if (idtype_info == NULL) {
BLI_assert_msg(0, "IDType Missing IDTypeInfo");
return false;
}
BLI_assert_msg(0, "IDType Missing IDTypeInfo");
return false;
BLI_assert((idtype_info->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0);
if (idtype_info->make_local != NULL) {
idtype_info->make_local(bmain, id, flags);
}
else {
BKE_lib_id_make_local_generic(bmain, id, flags);
}
return true;
}
struct IDCopyLibManagementData {
@@ -1706,7 +1725,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
*
* Only for local IDs (linked ones already have a unique ID in their library).
*
* \param do_linked_data if true, also ensure a unique name in case the given \a id is linked
* \param do_linked_data: if true, also ensure a unique name in case the given \a id is linked
* (otherwise, just ensure that it is properly sorted).
*
* \return true if a new name had to be created.
@@ -1766,8 +1785,7 @@ void BKE_main_id_newptr_and_tag_clear(Main *bmain)
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
id->newid = NULL;
id->tag &= ~LIB_TAG_NEW;
BKE_id_newptr_and_tag_clear(id);
}
FOREACH_MAIN_ID_END;
}
@@ -2034,11 +2052,8 @@ void BKE_library_make_local(Main *bmain,
* Note that for objects, we don't want proxy pointers to be cleared yet. This will happen
* down the road in this function.
*/
BKE_lib_id_make_local(bmain,
id,
false,
LIB_ID_MAKELOCAL_FULL_LIBRARY |
LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
BKE_lib_id_make_local(
bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
if (id->newid) {
if (GS(id->newid->name) == ID_OB) {

View File

@@ -333,11 +333,11 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
* LIB_TAG_DOIT tag.
*
* \param reference_library the library from which the linked data being overridden come from
* \param reference_library: the library from which the linked data being overridden come from
* (i.e. the library of the linked reference ID).
*
* \param do_no_main Create the new override data outside of Main database. Used for resyncing of
* linked overrides.
* \param do_no_main: Create the new override data outside of Main database.
* Used for resyncing of linked overrides.
*
* \return \a true on success, \a false otherwise.
*/
@@ -901,7 +901,7 @@ static void lib_override_library_create_post_process(Main *bmain,
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
* override, currently.
* \param r_id_root_override if not NULL, the override generated for the given \a id_root.
* \param r_id_root_override: if not NULL, the override generated for the given \a id_root.
* \return true if override was successfully created.
*/
bool BKE_lib_override_library_create(Main *bmain,

View File

@@ -720,9 +720,9 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
* Valid usages here are defined as ref-counting usages, which are not towards embedded or
* loop-back data.
*
* \param r_num_tagged If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers.
* Number of tagged-as-unused IDs is then set for each type, and as total in
* #INDEX_ID_NULL item.
* \param r_num_tagged: If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers.
* Number of tagged-as-unused IDs is then set for each type, and as total in
* #INDEX_ID_NULL item.
*/
void BKE_lib_query_unused_ids_tag(Main *bmain,
const int tag,

View File

@@ -699,6 +699,9 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data)
*
* Very specific usage, not sure we'll keep it on the long run,
* currently only used in Object/Collection duplication code...
*
* WARNING: This is a deprecated version of this function, should not be used by new code. See
* #BKE_libblock_relink_to_newid_new below.
*/
void BKE_libblock_relink_to_newid(ID *id)
{
@@ -708,3 +711,53 @@ void BKE_libblock_relink_to_newid(ID *id)
BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0);
}
/* ************************
* FIXME: Port all usages of #BKE_libblock_relink_to_newid to this
* #BKE_libblock_relink_to_newid_new new code and remove old one.
************************** */
static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data)
{
const int cb_flag = cb_data->cb_flag;
if (cb_flag & IDWALK_CB_EMBEDDED) {
return IDWALK_RET_NOP;
}
Main *bmain = cb_data->bmain;
ID *id_owner = cb_data->id_owner;
ID **id_pointer = cb_data->id_pointer;
ID *id = *id_pointer;
if (id) {
/* See: NEW_ID macro */
if (id->newid != NULL) {
BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE);
id = id->newid;
}
if (id->tag & LIB_TAG_NEW) {
id->tag &= ~LIB_TAG_NEW;
BKE_libblock_relink_to_newid_new(bmain, id);
}
}
return IDWALK_RET_NOP;
}
/**
* Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively
* in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`.
*
* NOTE: `LIB_TAG_NEW` is cleared
*
* Very specific usage, not sure we'll keep it on the long run,
* currently only used in Object/Collection duplication code...
*/
void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id)
{
if (ID_IS_LINKED(id)) {
return;
}
/* We do not want to have those cached relationship data here. */
BLI_assert(bmain->relations == NULL);
id->tag &= ~LIB_TAG_NEW;
BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0);
}

View File

@@ -68,8 +68,7 @@ IDTypeInfo IDType_ID_LI = {
.name = "Library",
.name_plural = "libraries",
.translation_context = BLT_I18NCONTEXT_ID_LIBRARY,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL |
IDTYPE_FLAGS_NO_ANIMDATA,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA,
.init_data = NULL,
.copy_data = NULL,

View File

@@ -1111,7 +1111,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval)
MEM_freeN(mesh_eval);
}
Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference)
Mesh *BKE_mesh_copy_for_eval(const Mesh *source, bool reference)
{
int flags = LIB_ID_COPY_LOCALIZE;
@@ -1859,7 +1859,7 @@ void BKE_mesh_vert_coords_apply(Mesh *mesh, const float (*vert_coords)[3])
for (int i = 0; i < mesh->totvert; i++, mv++) {
copy_v3_v3(mv->co, vert_coords[i]);
}
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(mesh);
}
void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh,
@@ -1872,7 +1872,7 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh,
for (int i = 0; i < mesh->totvert; i++, mv++) {
mul_v3_m4v3(mv->co, mat, vert_coords[i]);
}
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(mesh);
}
void BKE_mesh_vert_normals_apply(Mesh *mesh, const short (*vert_normals)[3])

View File

@@ -68,7 +68,7 @@
#ifdef VALIDATE_MESH
# define ASSERT_IS_VALID_MESH(mesh) \
(BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true)))
(BLI_assert((mesh == nullptr) || (BKE_mesh_is_valid(mesh) == true)))
#else
# define ASSERT_IS_VALID_MESH(mesh)
#endif
@@ -84,15 +84,16 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me)
const float *nors, *verts;
int a, *index;
dl = lb->first;
if (dl == NULL) {
dl = (DispList *)lb->first;
if (dl == nullptr) {
return;
}
if (dl->type == DL_INDEX4) {
mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, dl->nr);
allloop = mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, dl->parts * 4);
mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, dl->parts);
mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, dl->nr);
allloop = mloop = (MLoop *)CustomData_add_layer(
&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, dl->parts * 4);
mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, dl->parts);
me->mvert = mvert;
me->mloop = mloop;
me->mpoly = mpoly;
@@ -177,9 +178,10 @@ static void make_edges_mdata_extend(
MEdge *medge;
uint e_index = totedge;
*r_alledge = medge = (*r_alledge ?
MEM_reallocN(*r_alledge, sizeof(MEdge) * (totedge + totedge_new)) :
MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__));
*r_alledge = medge = (MEdge *)(*r_alledge ?
MEM_reallocN(*r_alledge,
sizeof(MEdge) * (totedge + totedge_new)) :
MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__));
medge += totedge;
totedge += totedge_new;
@@ -209,7 +211,7 @@ static void make_edges_mdata_extend(
}
}
BLI_edgehash_free(eh, NULL);
BLI_edgehash_free(eh, nullptr);
}
/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */
@@ -229,7 +231,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
MVert *mvert;
MPoly *mpoly;
MLoop *mloop;
MLoopUV *mloopuv = NULL;
MLoopUV *mloopuv = nullptr;
MEdge *medge;
const float *data;
int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0;
@@ -277,14 +279,15 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
return -1;
}
*r_allvert = mvert = MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert");
*r_alledge = medge = MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge");
*r_allloop = mloop = MEM_calloc_arrayN(
*r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert");
*r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge");
*r_allloop = mloop = (MLoop *)MEM_calloc_arrayN(
totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */
*r_allpoly = mpoly = MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop");
*r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop");
if (r_alluv) {
*r_alluv = mloopuv = MEM_calloc_arrayN(totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv");
*r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN(
totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv");
}
/* verts and faces */
@@ -500,13 +503,13 @@ static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me)
Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase)
{
const Curve *cu = ob->data;
const Curve *cu = (const Curve *)ob->data;
Mesh *mesh;
MVert *allvert;
MEdge *alledge;
MLoop *allloop;
MPoly *allpoly;
MLoopUV *alluv = NULL;
MLoopUV *alluv = nullptr;
int totvert, totedge, totloop, totpoly;
if (mesh_nurbs_displist_to_mdata(cu,
@@ -525,7 +528,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *
}
mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly);
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(mesh);
if (totvert != 0) {
memcpy(mesh->mvert, allvert, totvert * sizeof(MVert));
@@ -561,7 +564,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *
Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob)
{
ListBase disp = {NULL, NULL};
ListBase disp = {nullptr, nullptr};
if (ob->runtime.curve_cache) {
disp = ob->runtime.curve_cache->disp;
@@ -578,16 +581,16 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
Mesh *me_eval = (Mesh *)ob->runtime.data_eval;
Mesh *me;
MVert *allvert = NULL;
MEdge *alledge = NULL;
MLoop *allloop = NULL;
MLoopUV *alluv = NULL;
MPoly *allpoly = NULL;
MVert *allvert = nullptr;
MEdge *alledge = nullptr;
MLoop *allloop = nullptr;
MLoopUV *alluv = nullptr;
MPoly *allpoly = nullptr;
int totvert, totedge, totloop, totpoly;
Curve *cu = ob->data;
Curve *cu = (Curve *)ob->data;
if (me_eval == NULL) {
if (me_eval == nullptr) {
if (mesh_nurbs_displist_to_mdata(cu,
dispbase,
&allvert,
@@ -604,30 +607,34 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
}
/* make mesh */
me = BKE_id_new_nomain(ID_ME, obdata_name);
me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name);
me->totvert = totvert;
me->totedge = totedge;
me->totloop = totloop;
me->totpoly = totpoly;
me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert);
me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge);
me->mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop);
me->mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly);
me->mvert = (MVert *)CustomData_add_layer(
&me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert);
me->medge = (MEdge *)CustomData_add_layer(
&me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge);
me->mloop = (MLoop *)CustomData_add_layer(
&me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop);
me->mpoly = (MPoly *)CustomData_add_layer(
&me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly);
if (alluv) {
const char *uvname = "UVMap";
me->mloopuv = CustomData_add_layer_named(
me->mloopuv = (MLoopUV *)CustomData_add_layer_named(
&me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname);
}
BKE_mesh_calc_normals(me);
}
else {
me = BKE_id_new_nomain(ID_ME, obdata_name);
me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name);
ob->runtime.data_eval = NULL;
ob->runtime.data_eval = nullptr;
BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true);
}
@@ -636,7 +643,7 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
mesh_copy_texture_space_from_curve_type(cu, me);
cu->mat = NULL;
cu->mat = nullptr;
cu->totcol = 0;
/* Do not decrement ob->data usercount here,
@@ -647,29 +654,29 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
/* For temporary objects in BKE_mesh_new_from_object don't remap
* the entire scene with associated depsgraph updates, which are
* problematic for renderers exporting data. */
BKE_id_free(NULL, cu);
BKE_id_free(nullptr, cu);
}
typedef struct EdgeLink {
struct EdgeLink {
struct EdgeLink *next, *prev;
void *edge;
} EdgeLink;
};
typedef struct VertLink {
struct VertLink {
Link *next, *prev;
uint index;
} VertLink;
};
static void prependPolyLineVert(ListBase *lb, uint index)
{
VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink");
VertLink *vl = (VertLink *)MEM_callocN(sizeof(VertLink), "VertLink");
vl->index = index;
BLI_addhead(lb, vl);
}
static void appendPolyLineVert(ListBase *lb, uint index)
{
VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink");
VertLink *vl = (VertLink *)MEM_callocN(sizeof(VertLink), "VertLink");
vl->index = index;
BLI_addtail(lb, vl);
}
@@ -689,10 +696,10 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
/* only to detect edge polylines */
int *edge_users;
ListBase edges = {NULL, NULL};
ListBase edges = {nullptr, nullptr};
/* get boundary edges */
edge_users = MEM_calloc_arrayN(medge_len, sizeof(int), __func__);
edge_users = (int *)MEM_calloc_arrayN(medge_len, sizeof(int), __func__);
for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
MLoop *ml = &mloop[mp->loopstart];
int j;
@@ -705,7 +712,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
med = medge;
for (i = 0; i < medge_len; i++, med++) {
if (edge_users[i] == edge_users_test) {
EdgeLink *edl = MEM_callocN(sizeof(EdgeLink), "EdgeLink");
EdgeLink *edl = (EdgeLink *)MEM_callocN(sizeof(EdgeLink), "EdgeLink");
edl->edge = med;
BLI_addtail(&edges, edl);
@@ -718,10 +725,10 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
while (edges.first) {
/* each iteration find a polyline and add this as a nurbs poly spline */
ListBase polyline = {NULL, NULL}; /* store a list of VertLink's */
ListBase polyline = {nullptr, nullptr}; /* store a list of VertLink's */
bool closed = false;
int totpoly = 0;
MEdge *med_current = ((EdgeLink *)edges.last)->edge;
MEdge *med_current = (MEdge *)((EdgeLink *)edges.last)->edge;
uint startVert = med_current->v1;
uint endVert = med_current->v2;
bool ok = true;
@@ -734,12 +741,12 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
totedges--;
while (ok) { /* while connected edges are found... */
EdgeLink *edl = edges.last;
EdgeLink *edl = (EdgeLink *)edges.last;
ok = false;
while (edl) {
EdgeLink *edl_prev = edl->prev;
med = edl->edge;
med = (MEdge *)edl->edge;
if (med->v1 == endVert) {
endVert = med->v2;
@@ -803,7 +810,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
nu->bp = (BPoint *)MEM_calloc_arrayN(totpoly, sizeof(BPoint), "bpoints");
/* add points */
vl = polyline.first;
vl = (VertLink *)polyline.first;
for (i = 0, bp = nu->bp; i < totpoly; i++, bp++, vl = (VertLink *)vl->next) {
copy_v3_v3(bp->vec, mvert[vl->index].co);
bp->f1 = SELECT;
@@ -825,7 +832,7 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
ListBase nurblist = {NULL, NULL};
ListBase nurblist = {nullptr, nullptr};
BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 0);
BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1);
@@ -846,16 +853,13 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud)
{
BLI_assert(me != NULL);
BLI_assert(me != nullptr);
pointcloud->totpoint = me->totvert;
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
/* Copy over all attributes. */
const CustomData_MeshMasks mask = {
.vmask = CD_MASK_PROP_ALL,
};
CustomData_merge(&me->vdata, &pointcloud->pdata, mask.vmask, CD_DUPLICATE, me->totvert);
CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert);
BKE_pointcloud_update_customdata_pointers(pointcloud);
CustomData_update_typemap(&pointcloud->pdata);
@@ -874,7 +878,7 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
PointCloud *pointcloud = BKE_pointcloud_add(bmain, ob->id.name + 2);
PointCloud *pointcloud = (PointCloud *)BKE_pointcloud_add(bmain, ob->id.name + 2);
BKE_pointcloud_from_mesh(me_eval, pointcloud);
@@ -889,24 +893,22 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce
void BKE_mesh_from_pointcloud(const PointCloud *pointcloud, Mesh *me)
{
BLI_assert(pointcloud != NULL);
BLI_assert(pointcloud != nullptr);
me->totvert = pointcloud->totpoint;
/* Merge over all attributes. */
const CustomData_MeshMasks mask = {
.vmask = CD_MASK_PROP_ALL,
};
CustomData_merge(&pointcloud->pdata, &me->vdata, mask.vmask, CD_DUPLICATE, pointcloud->totpoint);
CustomData_merge(
&pointcloud->pdata, &me->vdata, CD_MASK_PROP_ALL, CD_DUPLICATE, pointcloud->totpoint);
/* Convert the Position attribute to a mesh vertex. */
me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert);
me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, me->totvert);
CustomData_update_typemap(&me->vdata);
const int layer_idx = CustomData_get_named_layer_index(
&me->vdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION);
CustomDataLayer *pos_layer = &me->vdata.layers[layer_idx];
float(*positions)[3] = pos_layer->data;
float(*positions)[3] = (float(*)[3])pos_layer->data;
MVert *mvert;
mvert = me->mvert;
@@ -956,7 +958,8 @@ static Object *object_for_curve_to_mesh_create(Object *object)
Curve *curve = (Curve *)object->data;
/* Create object itself. */
Object *temp_object = (Object *)BKE_id_copy_ex(NULL, &object->id, NULL, LIB_ID_COPY_LOCALIZE);
Object *temp_object = (Object *)BKE_id_copy_ex(
nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE);
/* Remove all modifiers, since we don't want them to be applied. */
BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT);
@@ -965,26 +968,27 @@ static Object *object_for_curve_to_mesh_create(Object *object)
*
* Note that there are extra fields in there like bevel and path, but those are not needed during
* conversion, so they are not copied to save unnecessary allocations. */
if (temp_object->runtime.curve_cache == NULL) {
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
if (temp_object->runtime.curve_cache == nullptr) {
temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
"CurveCache for curve types");
}
if (object->runtime.curve_cache != NULL) {
if (object->runtime.curve_cache != nullptr) {
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
}
/* Constructive modifiers will use mesh to store result. */
if (object->runtime.data_eval != NULL) {
if (object->runtime.data_eval != nullptr) {
BKE_id_copy_ex(
NULL, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE);
nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE);
}
/* Need to create copy of curve itself as well, it will be freed by underlying conversion
* functions.
*
* NOTE: Copies the data, but not the shapekeys. */
BKE_id_copy_ex(NULL, object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE);
BKE_id_copy_ex(
nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE);
Curve *temp_curve = (Curve *)temp_object->data;
/* Make sure texture space is calculated for a copy of curve, it will be used for the final
@@ -1011,8 +1015,9 @@ static void curve_to_mesh_eval_ensure(Object *object)
remapped_object.data = &remapped_curve;
if (object->runtime.curve_cache == NULL) {
object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
if (object->runtime.curve_cache == nullptr) {
object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
"CurveCache for Curve");
}
/* Temporarily share the curve-cache with the temporary object, owned by `object`. */
@@ -1025,8 +1030,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
*
* So we create temporary copy of the object which will use same data as the original bevel, but
* will have no modifiers. */
Object bevel_object = {{NULL}};
if (remapped_curve.bevobj != NULL) {
Object bevel_object = {{nullptr}};
if (remapped_curve.bevobj != nullptr) {
bevel_object = *remapped_curve.bevobj;
BLI_listbase_clear(&bevel_object.modifiers);
BKE_object_runtime_reset(&bevel_object);
@@ -1034,34 +1039,34 @@ static void curve_to_mesh_eval_ensure(Object *object)
}
/* Same thing for taper. */
Object taper_object = {{NULL}};
if (remapped_curve.taperobj != NULL) {
Object taper_object = {{nullptr}};
if (remapped_curve.taperobj != nullptr) {
taper_object = *remapped_curve.taperobj;
BLI_listbase_clear(&taper_object.modifiers);
BKE_object_runtime_reset(&taper_object);
remapped_curve.taperobj = &taper_object;
}
/* NOTE: We don't have dependency graph or scene here, so we pass NULL. This is all fine since
/* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since
* they are only used for modifier stack, which we have explicitly disabled for all objects.
*
* TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a
* bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also
* Mesh From Curve operator.
* Brecht says hold off with that. */
Mesh *mesh_eval = NULL;
Mesh *mesh_eval = nullptr;
BKE_displist_make_curveTypes_forRender(
NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval);
nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval);
/* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a
* real issue currently, code here is broken in more than one way, fix(es) will be done
* separately. */
if (mesh_eval != NULL) {
if (mesh_eval != nullptr) {
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
}
/* Owned by `object` & needed by the caller to create the mesh. */
remapped_object.runtime.curve_cache = NULL;
remapped_object.runtime.curve_cache = nullptr;
BKE_object_runtime_free_data(&remapped_object);
BKE_object_runtime_free_data(&taper_object);
@@ -1070,7 +1075,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
static Mesh *mesh_new_from_curve_type_object(Object *object)
{
Curve *curve = object->data;
Curve *curve = (Curve *)object->data;
Object *temp_object = object_for_curve_to_mesh_create(object);
Curve *temp_curve = (Curve *)temp_object->data;
@@ -1081,8 +1086,8 @@ static Mesh *mesh_new_from_curve_type_object(Object *object)
}
/* Reset pointers before conversion. */
temp_curve->editfont = NULL;
temp_curve->editnurb = NULL;
temp_curve->editfont = nullptr;
temp_curve->editnurb = nullptr;
/* Convert to mesh. */
mesh_from_nurbs_displist(
@@ -1091,14 +1096,14 @@ static Mesh *mesh_new_from_curve_type_object(Object *object)
/* #mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't
* the curve did not have any segments or otherwise would have generated an empty mesh. */
if (temp_object->type != OB_MESH) {
BKE_id_free(NULL, temp_object->data);
BKE_id_free(NULL, temp_object);
return NULL;
BKE_id_free(nullptr, temp_object->data);
BKE_id_free(nullptr, temp_object);
return nullptr;
}
Mesh *mesh_result = temp_object->data;
Mesh *mesh_result = (Mesh *)temp_object->data;
BKE_id_free(NULL, temp_object);
BKE_id_free(nullptr, temp_object);
/* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */
@@ -1114,19 +1119,19 @@ static Mesh *mesh_new_from_mball_object(Object *object)
* ball).
*
* We create empty mesh so scripters don't run into None objects. */
if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == NULL ||
if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == nullptr ||
BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) {
return BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
}
Mesh *mesh_result = BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
Mesh *mesh_result = (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
BKE_mesh_from_metaball(&object->runtime.curve_cache->disp, mesh_result);
BKE_mesh_texspace_copy_from_object(mesh_result, object);
/* Copy materials. */
mesh_result->totcol = mball->totcol;
mesh_result->mat = MEM_dupallocN(mball->mat);
if (mball->mat != NULL) {
mesh_result->mat = (Material **)MEM_dupallocN(mball->mat);
if (mball->mat != nullptr) {
for (int i = mball->totcol; i-- > 0;) {
mesh_result->mat[i] = BKE_object_material_get(object, i + 1);
}
@@ -1142,7 +1147,7 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh)
BKE_mesh_wrapper_ensure_mdata(mesh);
Mesh *mesh_result = (Mesh *)BKE_id_copy_ex(
NULL, &mesh->id, NULL, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT);
nullptr, &mesh->id, nullptr, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT);
/* NOTE: Materials should already be copied. */
/* Copy original mesh name. This is because edit meshes might not have one properly set name. */
BLI_strncpy(mesh_result->id.name, ((ID *)object->data)->name, sizeof(mesh_result->id.name));
@@ -1157,12 +1162,12 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
return mesh_new_from_mesh(object, (Mesh *)object->data);
}
if (depsgraph == NULL) {
return NULL;
if (depsgraph == nullptr) {
return nullptr;
}
Object object_for_eval = *object;
if (object_for_eval.runtime.data_orig != NULL) {
if (object_for_eval.runtime.data_orig != nullptr) {
object_for_eval.data = object_for_eval.runtime.data_orig;
}
@@ -1186,10 +1191,10 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
if (preserve_all_data_layers || preserve_origindex) {
return mesh_new_from_mesh_object_with_layers(depsgraph, object, preserve_origindex);
}
Mesh *mesh_input = object->data;
Mesh *mesh_input = (Mesh *)object->data;
/* If we are in edit mode, use evaluated mesh from edit structure, matching to what
* viewport is using for visualization. */
if (mesh_input->edit_mesh != NULL && mesh_input->edit_mesh->mesh_eval_final) {
if (mesh_input->edit_mesh != nullptr && mesh_input->edit_mesh->mesh_eval_final) {
mesh_input = mesh_input->edit_mesh->mesh_eval_final;
}
return mesh_new_from_mesh(object, mesh_input);
@@ -1200,7 +1205,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
const bool preserve_all_data_layers,
const bool preserve_origindex)
{
Mesh *new_mesh = NULL;
Mesh *new_mesh = nullptr;
switch (object->type) {
case OB_FONT:
case OB_CURVE:
@@ -1216,11 +1221,11 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
break;
default:
/* Object does not have geometry data. */
return NULL;
return nullptr;
}
if (new_mesh == NULL) {
if (new_mesh == nullptr) {
/* Happens in special cases like request of mesh for non-mother meta ball. */
return NULL;
return nullptr;
}
/* The result must have 0 users, since it's just a mesh which is free-dangling data-block.
@@ -1233,9 +1238,9 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
* ownership.
*
* Here we are constructing a mesh which is supposed to be independent, which means no shared
* ownership is allowed, so we make sure edit mesh is reset to NULL (which is similar to as if
* ownership is allowed, so we make sure edit mesh is reset to nullptr (which is similar to as if
* one duplicates the objects and applies all the modifiers). */
new_mesh->edit_mesh = NULL;
new_mesh->edit_mesh = nullptr;
return new_mesh;
}
@@ -1243,7 +1248,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
static int foreach_libblock_make_original_callback(LibraryIDLinkCallbackData *cb_data)
{
ID **id_p = cb_data->id_pointer;
if (*id_p == NULL) {
if (*id_p == nullptr) {
return IDWALK_RET_NOP;
}
*id_p = DEG_get_original_id(*id_p);
@@ -1254,7 +1259,7 @@ static int foreach_libblock_make_original_callback(LibraryIDLinkCallbackData *cb
static int foreach_libblock_make_usercounts_callback(LibraryIDLinkCallbackData *cb_data)
{
ID **id_p = cb_data->id_pointer;
if (*id_p == NULL) {
if (*id_p == nullptr) {
return IDWALK_RET_NOP;
}
@@ -1278,7 +1283,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH));
Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
if (mesh == NULL) {
if (mesh == nullptr) {
/* Unable to convert the object to a mesh, return an empty one. */
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2);
id_us_min(&mesh_in_bmain->id);
@@ -1294,7 +1299,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
* Note that user-count updates has to be done *after* mesh has been transferred to Main database
* (since doing refcounting on non-Main IDs is forbidden). */
BKE_library_foreach_ID_link(
NULL, &mesh->id, foreach_libblock_make_original_callback, NULL, IDWALK_NOP);
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
/* Append the mesh to 'bmain'.
* We do it a bit longer way since there is no simple and clear way of adding existing data-block
@@ -1311,14 +1316,14 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
mesh_in_bmain->totcol = mesh->totcol;
mesh_in_bmain->flag = mesh->flag;
mesh_in_bmain->smoothresh = mesh->smoothresh;
mesh->mat = NULL;
mesh->mat = nullptr;
BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, NULL, &CD_MASK_MESH, true);
BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true);
/* User-count is required because so far mesh was in a limbo, where library management does
* not perform any user management (i.e. copy of a mesh will not increase users of materials). */
BKE_library_foreach_ID_link(
NULL, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, NULL, IDWALK_NOP);
nullptr, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, nullptr, IDWALK_NOP);
/* Make sure user count from BKE_mesh_add() is the one we expect here and bring it down to 0. */
BLI_assert(mesh_in_bmain->id.us == 1);
@@ -1347,7 +1352,7 @@ static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src)
return;
}
for (i = 0, kb = key->block.first; kb; kb = kb->next, i++) {
for (i = 0, kb = (KeyBlock *)key->block.first; kb; kb = kb->next, i++) {
int ci;
float *array;
@@ -1358,10 +1363,10 @@ static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src)
mesh_src->totvert,
kb->name,
kb->totelem);
array = MEM_calloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
array = (float *)MEM_calloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
}
else {
array = MEM_malloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
array = (float *)MEM_malloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
memcpy(array, kb->data, sizeof(float[3]) * (size_t)mesh_src->totvert);
}
@@ -1379,9 +1384,10 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
ModifierData *md_eval,
const bool build_shapekey_layers)
{
Mesh *me = ob_eval->runtime.data_orig ? ob_eval->runtime.data_orig : ob_eval->data;
const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type);
Mesh *result = NULL;
Mesh *me = ob_eval->runtime.data_orig ? (Mesh *)ob_eval->runtime.data_orig :
(Mesh *)ob_eval->data;
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type);
Mesh *result = nullptr;
KeyBlock *kb;
ModifierEvalContext mectx = {depsgraph, ob_eval, MOD_APPLY_TO_BASE_MESH};
@@ -1389,12 +1395,12 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
return result;
}
if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) {
if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) {
return result;
}
if (build_shapekey_layers && me->key &&
(kb = BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) {
(kb = (KeyBlock *)BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) {
BKE_keyblock_convert_to_mesh(kb, me);
}
@@ -1402,7 +1408,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
int numVerts;
float(*deformedVerts)[3] = BKE_mesh_vert_coords_alloc(me, &numVerts);
result = (Mesh *)BKE_id_copy_ex(NULL, &me->id, NULL, LIB_ID_COPY_LOCALIZE);
result = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE);
mti->deformVerts(md_eval, &mectx, result, deformedVerts, numVerts);
BKE_mesh_vert_coords_apply(result, deformedVerts);
@@ -1413,7 +1419,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
MEM_freeN(deformedVerts);
}
else {
Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(NULL, &me->id, NULL, LIB_ID_COPY_LOCALIZE);
Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE);
if (build_shapekey_layers) {
add_shapekey_layers(mesh_temp, me);
@@ -1423,7 +1429,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
ASSERT_IS_VALID_MESH(result);
if (mesh_temp != result) {
BKE_id_free(NULL, mesh_temp);
BKE_id_free(nullptr, mesh_temp);
}
}
@@ -1446,7 +1452,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act
&mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)];
float(*cos)[3], (*kbcos)[3];
for (kb = mesh_dst->key->block.first; kb; kb = kb->next) {
for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) {
if (kb->uid == layer->uid) {
break;
}
@@ -1461,10 +1467,10 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act
MEM_freeN(kb->data);
}
cos = CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i);
cos = (float(*)[3])CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i);
kb->totelem = mesh_src->totvert;
kb->data = kbcos = MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__);
kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__);
if (kb->uid == actshape_uid) {
MVert *mvert = mesh_src->mvert;
@@ -1479,7 +1485,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act
}
}
for (kb = mesh_dst->key->block.first; kb; kb = kb->next) {
for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) {
if (kb->totelem != mesh_src->totvert) {
if (kb->data) {
MEM_freeN(kb->data);
@@ -1544,7 +1550,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
int uid;
if (ob) {
kb = BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1);
kb = (KeyBlock *)BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1);
if (kb) {
uid = kb->uid;
}
@@ -1607,11 +1613,11 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
/* NOTE(nazgul): maybe some other layers should be copied? */
if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) {
if (totloop == mesh_dst->totloop) {
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
MDisps *mdisps = (MDisps *)CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
if (alloctype == CD_ASSIGN) {
/* Assign NULL to prevent double-free. */
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
/* Assign nullptr to prevent double-free. */
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, nullptr);
}
}
}
@@ -1633,7 +1639,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) {
id_us_min(&tmp.key->id);
}
tmp.key = NULL;
tmp.key = nullptr;
}
/* Clear selection history */
@@ -1660,7 +1666,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask->lmask);
CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask->pmask);
}
BKE_id_free(NULL, mesh_src);
BKE_id_free(nullptr, mesh_src);
}
}
@@ -1682,7 +1688,7 @@ void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb)
kb->data = MEM_malloc_arrayN(mesh_dst->key->elemsize, mesh_dst->totvert, "kb->data");
kb->totelem = totvert;
fp = kb->data;
fp = (float *)kb->data;
mvert = mesh_src->mvert;
for (a = 0; a < kb->totelem; a++, fp += 3, mvert++) {

View File

@@ -653,6 +653,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
BLO_read_list(reader, &ntree->nodes);
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->typeinfo = nullptr;
node->declaration = nullptr;
BLO_read_list(reader, &node->inputs);
BLO_read_list(reader, &node->outputs);
@@ -1014,10 +1015,8 @@ IDTypeInfo IDType_ID_NT = {
static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype)
{
if (ntype->declare != nullptr) {
blender::nodes::NodeDeclaration node_decl;
blender::nodes::NodeDeclarationBuilder builder{node_decl};
ntype->declare(builder);
node_decl.build(*ntree, *node);
nodeDeclarationEnsure(ntree, node);
node->declaration->build(*ntree, *node);
return;
}
bNodeSocketTemplate *sockdef;
@@ -2216,6 +2215,10 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree,
bNodeLink *link_dst, *link_src;
*node_dst = *node_src;
/* Reset the declaration of the new node. */
node_dst->declaration = nullptr;
/* can be called for nodes outside a node tree (e.g. clipboard) */
if (ntree) {
if (unique_name) {
@@ -3103,6 +3106,8 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
MEM_freeN(node->prop);
}
delete node->declaration;
MEM_freeN(node);
if (ntree) {
@@ -3933,6 +3938,24 @@ int nodeSocketLinkLimit(const bNodeSocket *sock)
return sock->limit;
}
/**
* If the node implements a `declare` function, this function makes sure that `node->declaration`
* is up to date.
*/
void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node)
{
if (node->typeinfo->declare == nullptr) {
return;
}
if (node->declaration != nullptr) {
return;
}
node->declaration = new blender::nodes::NodeDeclaration();
blender::nodes::NodeDeclarationBuilder builder{*node->declaration};
node->typeinfo->declare(builder);
}
/* ************** Node Clipboard *********** */
#define USE_NODE_CB_VALIDATE
@@ -5133,6 +5156,9 @@ static void registerGeometryNodes()
{
register_node_type_geo_group();
register_node_type_geo_legacy_material_assign();
register_node_type_geo_legacy_select_by_material();
register_node_type_geo_align_rotation_to_vector();
register_node_type_geo_attribute_clamp();
register_node_type_geo_attribute_color_ramp();
@@ -5171,6 +5197,7 @@ static void registerGeometryNodes()
register_node_type_geo_curve_set_handles();
register_node_type_geo_curve_spline_type();
register_node_type_geo_curve_subdivide();
register_node_type_geo_curve_fillet();
register_node_type_geo_curve_to_mesh();
register_node_type_geo_curve_to_points();
register_node_type_geo_curve_trim();
@@ -5205,7 +5232,7 @@ static void registerGeometryNodes()
register_node_type_geo_raycast();
register_node_type_geo_sample_texture();
register_node_type_geo_select_by_handle_type();
register_node_type_geo_select_by_material();
register_node_type_geo_material_selection();
register_node_type_geo_separate_components();
register_node_type_geo_set_position();
register_node_type_geo_subdivision_surface();

View File

@@ -312,7 +312,7 @@ IDTypeInfo IDType_ID_SCR = {
.name = "Screen",
.name_plural = "screens",
.translation_context = BLT_I18NCONTEXT_ID_SCREEN,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
.init_data = NULL,
.copy_data = NULL,

View File

@@ -1213,7 +1213,6 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so
AUD_SoundInfo info = AUD_getInfo(playback_handle);
sound_info->specs.channels = (eSoundChannels)info.specs.channels;
sound_info->length = info.length;
sound_info->start_offset = info.start_offset;
return true;
}
@@ -1231,6 +1230,44 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun
return result;
}
bool BKE_sound_stream_info_get(struct Main *main, const char *filepath, int stream, SoundStreamInfo *sound_info)
{
const char *path;
char str[FILE_MAX];
AUD_Sound *sound;
AUD_StreamInfo *stream_infos;
int stream_count;
BLI_strncpy(str, filepath, sizeof(str));
path = BKE_main_blendfile_path(main);
BLI_path_abs(str, path);
sound = AUD_Sound_file(str);
if (!sound) {
return false;
}
stream_count = AUD_Sound_getFileStreams(sound, &stream_infos);
AUD_Sound_free(sound);
if (!stream_infos) {
return false;
}
if ((stream < 0) || (stream >= stream_count)) {
free(stream_infos);
return false;
}
sound_info->start = stream_infos[stream].start;
sound_info->duration = stream_infos[stream].duration;
free(stream_infos);
return true;
}
#else /* WITH_AUDASPACE */
# include "BLI_utildefines.h"
@@ -1400,6 +1437,14 @@ bool BKE_sound_info_get(struct Main *UNUSED(main),
return false;
}
bool BKE_sound_stream_info_get(struct Main *UNUSED(main),
const char *UNUSED(filepath),
int UNUSED(stream),
SoundStreamInfo *UNUSED(sound_info))
{
return false;
}
#endif /* WITH_AUDASPACE */
void BKE_sound_reset_scene_runtime(Scene *scene)

View File

@@ -19,6 +19,8 @@
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
#include "BKE_spline.hh"
#include "FN_generic_virtual_array.hh"
@@ -28,6 +30,8 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
using blender::attribute_math::convert_to_static_type;
using blender::bke::AttributeIDRef;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray;
@@ -110,10 +114,36 @@ void Spline::transform(const blender::float4x4 &matrix)
this->mark_cache_invalid();
}
void Spline::reverse()
{
this->positions().reverse();
this->radii().reverse();
this->tilts().reverse();
this->attributes.foreach_attribute(
[&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
std::optional<blender::fn::GMutableSpan> attribute = this->attributes.get_for_write(id);
if (!attribute) {
BLI_assert_unreachable();
return false;
}
convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
attribute->typed<T>().reverse();
});
return true;
},
ATTR_DOMAIN_POINT);
this->reverse_impl();
this->mark_cache_invalid();
}
int Spline::evaluated_edges_size() const
{
const int eval_size = this->evaluated_points_size();
if (eval_size == 1) {
if (eval_size < 2) {
/* Two points are required for an edge. */
return 0;
}
@@ -161,7 +191,7 @@ static void accumulate_lengths(Span<float3> positions,
* Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
* length of the subsequent segment, i.e. the first value is the length of the first segment rather
* than 0. This calculation is rather trivial, and only depends on the evaluated positions.
* However, the results are used often, so it makes sense to cache it.
* However, the results are used often, and it is necessarily single threaded, so it is cached.
*/
Span<float> Spline::evaluated_lengths() const
{
@@ -176,9 +206,10 @@ Span<float> Spline::evaluated_lengths() const
const int total = evaluated_edges_size();
evaluated_lengths_cache_.resize(total);
Span<float3> positions = this->evaluated_positions();
accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
if (total != 0) {
Span<float3> positions = this->evaluated_positions();
accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
}
length_cache_dirty_ = false;
return evaluated_lengths_cache_;

View File

@@ -166,6 +166,17 @@ MutableSpan<float3> BezierSpline::handle_positions_right()
return handle_positions_right_;
}
void BezierSpline::reverse_impl()
{
this->handle_positions_left().reverse();
this->handle_positions_right().reverse();
std::swap(this->handle_positions_left_, this->handle_positions_right_);
this->handle_types_left().reverse();
this->handle_types_right().reverse();
std::swap(this->handle_types_left_, this->handle_types_right_);
}
static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
{
if (i == 0) {

View File

@@ -142,6 +142,11 @@ Span<float> NURBSpline::weights() const
return weights_;
}
void NURBSpline::reverse_impl()
{
this->weights().reverse();
}
void NURBSpline::mark_cache_invalid()
{
basis_cache_dirty_ = true;

View File

@@ -91,6 +91,10 @@ Span<float> PolySpline::tilts() const
return tilts_;
}
void PolySpline::reverse_impl()
{
}
void PolySpline::mark_cache_invalid()
{
tangent_cache_dirty_ = true;

View File

@@ -1232,7 +1232,7 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
// BKE_mesh_validate(result, true, true);
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
if (!subdiv_context.can_evaluate_normals) {
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
BKE_mesh_normals_tag_dirty(result);
}
/* Free used memory. */
subdiv_mesh_context_free(&subdiv_context);

View File

@@ -744,16 +744,15 @@ static UndoStep *undosys_step_iter_first(UndoStep *us_reference, const eUndoStep
/**
* Undo/Redo until the given `us_target` step becomes the active (currently loaded) one.
*
* \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the
* active step.
* \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target`
* will become the active step.
*
* \note In case `use_skip` is true, the final target will always be **beyond** the given one (if
* the given one has to be skipped).
* \note In case `use_skip` is true, the final target will always be **beyond** the given one
* (if the given one has to be skipped).
*
* \param us_reference If NULL, will be set to current active step in the undo stack. Otherwise, it
* is assumed to match the current state, and will be used as basis for the
* undo/redo process (i.e. all steps in-between `us_reference` and `us_target`
* will be processed).
* \param us_reference: If NULL, will be set to current active step in the undo stack. Otherwise,
* it is assumed to match the current state, and will be used as basis for the undo/redo process
* (i.e. all steps in-between `us_reference` and `us_target` will be processed).
*/
bool BKE_undosys_step_load_data_ex(UndoStack *ustack,
bContext *C,

View File

@@ -186,7 +186,7 @@ IDTypeInfo IDType_ID_WS = {
.name = "WorkSpace",
.name_plural = "workspaces",
.translation_context = BLT_I18NCONTEXT_ID_WORKSPACE,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA,
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
.init_data = workspace_init_data,
.copy_data = NULL,

View File

@@ -39,6 +39,7 @@
#include "BLI_index_range.hh"
#include "BLI_span.hh"
#include "BLI_vector.hh"
namespace blender {
@@ -221,6 +222,8 @@ class IndexMask {
{
return indices_.is_empty();
}
IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const;
};
} // namespace blender

View File

@@ -50,11 +50,10 @@ class ResourceScope : NonCopyable, NonMovable {
struct ResourceData {
void *data;
void (*free)(void *data);
const char *debug_name;
};
LinearAllocator<> m_allocator;
Vector<ResourceData> m_resources;
LinearAllocator<> allocator_;
Vector<ResourceData> resources_;
public:
ResourceScope() = default;
@@ -62,8 +61,8 @@ class ResourceScope : NonCopyable, NonMovable {
~ResourceScope()
{
/* Free in reversed order. */
for (int64_t i = m_resources.size(); i--;) {
ResourceData &data = m_resources[i];
for (int64_t i = resources_.size(); i--;) {
ResourceData &data = resources_[i];
data.free(data.data);
}
}
@@ -72,20 +71,17 @@ class ResourceScope : NonCopyable, NonMovable {
* Pass ownership of the resource to the ResourceScope. It will be destructed and freed when
* the collector is destructed.
*/
template<typename T> T *add(std::unique_ptr<T> resource, const char *name)
template<typename T> T *add(std::unique_ptr<T> resource)
{
BLI_assert(resource.get() != nullptr);
T *ptr = resource.release();
if (ptr == nullptr) {
return nullptr;
}
this->add(
ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
delete typed_data;
},
name);
this->add(ptr, [](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
delete typed_data;
});
return ptr;
}
@@ -93,7 +89,7 @@ class ResourceScope : NonCopyable, NonMovable {
* Pass ownership of the resource to the ResourceScope. It will be destructed when the
* collector is destructed.
*/
template<typename T> T *add(destruct_ptr<T> resource, const char *name)
template<typename T> T *add(destruct_ptr<T> resource)
{
T *ptr = resource.release();
if (ptr == nullptr) {
@@ -104,13 +100,10 @@ class ResourceScope : NonCopyable, NonMovable {
return ptr;
}
this->add(
ptr,
[](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
typed_data->~T();
},
name);
this->add(ptr, [](void *data) {
T *typed_data = reinterpret_cast<T *>(data);
typed_data->~T();
});
return ptr;
}
@@ -118,22 +111,31 @@ class ResourceScope : NonCopyable, NonMovable {
* Pass ownership of some resource to the ResourceScope. The given free function will be
* called when the collector is destructed.
*/
void add(void *userdata, void (*free)(void *), const char *name)
void add(void *userdata, void (*free)(void *))
{
ResourceData data;
data.debug_name = name;
data.data = userdata;
data.free = free;
m_resources.append(data);
resources_.append(data);
}
/**
* Construct an object with the same value in the ResourceScope and return a reference to the
* new value.
*/
template<typename T> T &add_value(T &&value, const char *name)
template<typename T> T &add_value(T &&value)
{
return this->construct<T>(name, std::forward<T>(value));
return this->construct<T>(std::forward<T>(value));
}
/**
* The passed in function will be called when the scope is destructed.
*/
template<typename Func> void add_destruct_call(Func func)
{
void *buffer = allocator_.allocate(sizeof(Func), alignof(Func));
new (buffer) Func(std::move(func));
this->add(buffer, [](void *data) { (*(Func *)data)(); });
}
/**
@@ -142,37 +144,19 @@ class ResourceScope : NonCopyable, NonMovable {
*/
LinearAllocator<> &linear_allocator()
{
return m_allocator;
return allocator_;
}
/**
* Utility method to construct an instance of type T that will be owned by the ResourceScope.
*/
template<typename T, typename... Args> T &construct(const char *name, Args &&...args)
template<typename T, typename... Args> T &construct(Args &&...args)
{
destruct_ptr<T> value_ptr = m_allocator.construct<T>(std::forward<Args>(args)...);
destruct_ptr<T> value_ptr = allocator_.construct<T>(std::forward<Args>(args)...);
T &value_ref = *value_ptr;
this->add(std::move(value_ptr), name);
this->add(std::move(value_ptr));
return value_ref;
}
/**
* Print the names of all the resources that are owned by this ResourceScope. This can be
* useful for debugging.
*/
void print(StringRef name) const
{
if (m_resources.size() == 0) {
std::cout << "\"" << name << "\" has no resources.\n";
return;
}
else {
std::cout << "Resources for \"" << name << "\":\n";
for (const ResourceData &data : m_resources) {
std::cout << " " << data.data << ": " << data.debug_name << '\n';
}
}
}
};
} // namespace blender

View File

@@ -18,6 +18,13 @@
/** \file
* \ingroup bli
*
* Functions for generating and handling "Session UUIDs".
*
* Note that these are not true universally-unique identifiers, but only unique during the current
* Blender session.
*
* For true UUIDs, see `BLI_uuid.h`.
*/
#ifdef __cplusplus

View File

@@ -643,6 +643,16 @@ template<typename T> class MutableSpan {
return MutableSpan(data_ + size_ - new_size, new_size);
}
/**
* Reverse the data in the MutableSpan.
*/
constexpr void reverse()
{
for (const int i : IndexRange(size_ / 2)) {
std::swap(data_[size_ - 1 - i], data_[i]);
}
}
/**
* Returns an (immutable) Span that references the same array. This is usually not needed,
* due to implicit conversions. However, sometimes automatic type deduction needs some help.

View File

@@ -0,0 +1,67 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#pragma once
/** \file
* \ingroup bli
*
* Functions for generating and handling UUID structs according to RFC4122.
*
* Note that these are true UUIDs, not to be confused with the "session uuid" defined in
* `BLI_session_uuid.h`.
*/
#include "DNA_uuid_types.h"
#include "BLI_compiler_attrs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* UUID generator for random (version 4) UUIDs. See RFC4122 section 4.4.
* This function is not thread-safe. */
UUID BLI_uuid_generate_random(void);
/** Compare two UUIDs, return true iff they are equal. */
bool BLI_uuid_equal(UUID uuid1, UUID uuid2);
/**
* Format UUID as string.
* The buffer must be at least 37 bytes (36 bytes for the UUID + terminating 0).
*/
void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL();
/**
* Parse a string as UUID.
* The string MUST be in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
* as produced by #BLI_uuid_format().
*
* Return true if the string could be parsed, and false otherwise. In the latter case, the UUID may
* have been partially updated.
*/
bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) ATTR_NONNULL();
#ifdef __cplusplus
}
# include <ostream>
/** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */
std::ostream &operator<<(std::ostream &stream, UUID uuid);
#endif

View File

@@ -86,6 +86,7 @@ set(SRC
intern/hash_md5.c
intern/hash_mm2a.c
intern/hash_mm3.c
intern/index_mask.cc
intern/jitter_2d.c
intern/kdtree_1d.c
intern/kdtree_2d.c
@@ -146,6 +147,7 @@ set(SRC
intern/time.c
intern/timecode.c
intern/timeit.cc
intern/uuid.cc
intern/uvproject.c
intern/voronoi_2d.c
intern/voxel.c
@@ -309,6 +311,7 @@ set(SRC
BLI_utildefines_stack.h
BLI_utildefines_variadic.h
BLI_utility_mixins.hh
BLI_uuid.h
BLI_uvproject.h
BLI_vector.hh
BLI_vector_adaptor.hh
@@ -454,6 +457,7 @@ if(WITH_GTESTS)
tests/BLI_string_utf8_test.cc
tests/BLI_task_graph_test.cc
tests/BLI_task_test.cc
tests/BLI_uuid_test.cc
tests/BLI_vector_set_test.cc
tests/BLI_vector_test.cc
tests/BLI_virtual_array_test.cc

View File

@@ -0,0 +1,57 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "BLI_index_mask.hh"
namespace blender {
/**
* Create a sub-mask that is also shifted to the beginning. The shifting to the beginning allows
* code to work with smaller indices, which is more memory efficient.
*
* \return New index mask with the size of #slice. It is either empty or starts with 0. It might
* reference indices that have been appended to #r_new_indices.
*
* Example:
* this: [2, 3, 5, 7, 8, 9, 10]
* slice: ^--------^
* output: [0, 2, 4, 5]
*
* All the indices in the sub-mask are shifted by 3 towards zero, so that the first index in the
* output is zero.
*/
IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r_new_indices) const
{
const int slice_size = slice.size();
if (slice_size == 0) {
return {};
}
IndexMask sliced_mask{indices_.slice(slice)};
if (sliced_mask.is_range()) {
return IndexMask(slice_size);
}
const int64_t offset = sliced_mask.indices().first();
if (offset == 0) {
return sliced_mask;
}
r_new_indices.resize(slice_size);
for (const int i : IndexRange(slice_size)) {
r_new_indices[i] = sliced_mask[i] - offset;
}
return IndexMask(r_new_indices.as_span());
}
} // namespace blender

View File

@@ -0,0 +1,116 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup bli
*/
#include "BLI_uuid.h"
#include <cstdio>
#include <cstring>
#include <ctime>
#include <random>
#include <string>
/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */
static_assert(sizeof(UUID) == 16, "expect UUIDs to be 128 bit exactly");
UUID BLI_uuid_generate_random()
{
static std::mt19937_64 rng = []() {
std::mt19937_64 rng;
/* Ensure the RNG really can output 64-bit values. */
static_assert(std::mt19937_64::min() == 0LL);
static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL);
struct timespec ts;
timespec_get(&ts, TIME_UTC);
rng.seed(ts.tv_nsec);
return rng;
}();
UUID uuid;
/* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining
* bits. The opposite is easier to implement, though, so that's what's done here. */
/* Read two 64-bit numbers to randomize all 128 bits of the UUID. */
uint64_t *uuid_as_int64 = reinterpret_cast<uint64_t *>(&uuid);
uuid_as_int64[0] = rng();
uuid_as_int64[1] = rng();
/* Set the most significant four bits to 0b0100 to indicate version 4 (random UUID). */
uuid.time_hi_and_version &= ~0xF000;
uuid.time_hi_and_version |= 0x4000;
/* Set the most significant two bits to 0b10 to indicate compatibility with RFC4122. */
uuid.clock_seq_hi_and_reserved &= ~0x40;
uuid.clock_seq_hi_and_reserved |= 0x80;
return uuid;
}
bool BLI_uuid_equal(const UUID uuid1, const UUID uuid2)
{
return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0;
}
void BLI_uuid_format(char *buffer, const UUID uuid)
{
std::sprintf(buffer,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
uuid.time_low,
uuid.time_mid,
uuid.time_hi_and_version,
uuid.clock_seq_hi_and_reserved,
uuid.clock_seq_low,
uuid.node[0],
uuid.node[1],
uuid.node[2],
uuid.node[3],
uuid.node[4],
uuid.node[5]);
}
bool BLI_uuid_parse_string(UUID *uuid, const char *buffer)
{
const int num_fields_parsed = std::sscanf(
buffer,
"%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
&uuid->time_low,
&uuid->time_mid,
&uuid->time_hi_and_version,
&uuid->clock_seq_hi_and_reserved,
&uuid->clock_seq_low,
&uuid->node[0],
&uuid->node[1],
&uuid->node[2],
&uuid->node[3],
&uuid->node[4],
&uuid->node[5]);
return num_fields_parsed == 11;
}
std::ostream &operator<<(std::ostream &stream, UUID uuid)
{
std::string buffer(36, '\0');
BLI_uuid_format(buffer.data(), uuid);
stream << buffer;
return stream;
}

View File

@@ -128,6 +128,6 @@ TEST(color, SceneLinearByteDecoding)
EXPECT_NEAR(0.5f, decoded.a, 0.01f);
}
/* \} */
/** \} */
} // namespace blender::tests

View File

@@ -40,4 +40,28 @@ TEST(index_mask, RangeConstructor)
EXPECT_EQ(indices[2], 5);
}
TEST(index_mask, SliceAndOffset)
{
Vector<int64_t> indices;
{
IndexMask mask{IndexRange(10)};
IndexMask new_mask = mask.slice_and_offset(IndexRange(3, 5), indices);
EXPECT_TRUE(new_mask.is_range());
EXPECT_EQ(new_mask.size(), 5);
EXPECT_EQ(new_mask[0], 0);
EXPECT_EQ(new_mask[1], 1);
}
{
Vector<int64_t> original_indices = {2, 3, 5, 7, 8, 9, 10};
IndexMask mask{original_indices.as_span()};
IndexMask new_mask = mask.slice_and_offset(IndexRange(1, 4), indices);
EXPECT_FALSE(new_mask.is_range());
EXPECT_EQ(new_mask.size(), 4);
EXPECT_EQ(new_mask[0], 0);
EXPECT_EQ(new_mask[1], 2);
EXPECT_EQ(new_mask[2], 4);
EXPECT_EQ(new_mask[3], 5);
}
}
} // namespace blender::tests

Some files were not shown because too many files have changed in this diff Show More